#include <linux/types.h>
#include <linux/parser.h>
+#include <linux/log2.h>
#include "iscsi.h"
#include "iscsi_dbg.h"
}
enum {
- Opt_type,
- Opt_iomode,
- Opt_err,
+ opt_type,
+ opt_iomode,
+ opt_scsiid,
+ opt_scsisn,
+ opt_blk_size,
+ opt_err,
};
static match_table_t tokens = {
- {Opt_type, "Type=%s"},
- {Opt_iomode, "IOMode=%s"},
- {Opt_err, NULL},
+ {opt_type, "type=%s"},
+ {opt_iomode, "iomode=%s"},
+ {opt_scsiid, "scsiid=%s"},
+ {opt_scsisn, "scsisn=%s"},
+ {opt_blk_size, "blocksize=%u"},
+ {opt_err, NULL},
};
-static int set_iotype(struct iet_volume *volume, char *params)
+static int set_scsiid(struct iet_volume *volume, const char *id)
+{
+ size_t len;
+
+ if ((len = strlen(id)) > SCSI_ID_LEN) {
+ eprintk("SCSI ID too long, %zd provided, %u max\n", len,
+ SCSI_ID_LEN);
+ return -EINVAL;
+ }
+
+ memcpy(volume->scsi_id, id, len);
+
+ return 0;
+}
+
+static int set_scsisn(struct iet_volume *volume, const char *sn)
+{
+ size_t len;
+ int i;
+
+ if ((len = strlen(sn)) > SCSI_SN_LEN) {
+ eprintk("SCSI SN too long, %zd provided, %u max\n", len,
+ SCSI_SN_LEN);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < len; i++) {
+ if (!isascii(*(sn + i)) || !isprint(*(sn + i))) {
+ eprintk("invalid characters in SCSI SN, %s\n",
+ "only printable ascii characters allowed!");
+ return -EINVAL;
+ }
+ }
+
+ memcpy(volume->scsi_sn, sn, len);
+
+ return 0;
+}
+
+/* Generate a MD5 hash of the target IQN and LUN number */
+static void gen_scsiid(struct iet_volume *volume)
+{
+ struct hash_desc hash;
+
+ hash.tfm = crypto_alloc_hash("md5", 0, CRYPTO_ALG_ASYNC);
+ hash.flags = 0;
+
+ if (hash.tfm) {
+ struct scatterlist sg[2];
+ unsigned int nbytes = 0;
+
+ sg_init_table(sg, 2);
+
+ sg_set_buf(&sg[0], volume->target->name,
+ strlen(volume->target->name));
+ nbytes += strlen(volume->target->name);
+
+ sg_set_buf(&sg[1], &volume->lun, sizeof(volume->lun));
+ nbytes += sizeof(volume->lun);
+
+ crypto_hash_init(&hash);
+ crypto_hash_update(&hash, sg, nbytes);
+ crypto_hash_final(&hash, volume->scsi_id);
+
+ crypto_free_hash(hash.tfm);
+ } else {
+ /* If no MD5 available set ID to TID and LUN */
+ memcpy(volume->scsi_id, &volume->target->tid,
+ sizeof(volume->target->tid));
+ memcpy(volume->scsi_id + sizeof(volume->target->tid),
+ &volume->lun, sizeof(volume->lun));
+ }
+
+}
+
+static int parse_volume_params(struct iet_volume *volume, char *params)
{
int err = 0;
+ unsigned blk_sz;
substring_t args[MAX_OPT_ARGS];
char *p, *argp = NULL, *buf = (char *) get_zeroed_page(GFP_USER);
if (!buf)
return -ENOMEM;
+
strncpy(buf, params, PAGE_CACHE_SIZE);
while ((p = strsep(&buf, ",")) != NULL) {
if (!*p)
continue;
+ iet_strtolower(p);
token = match_token(p, tokens, args);
switch (token) {
- case Opt_type:
- if (!(argp = match_strdup(&args[0])))
+ case opt_type:
+ argp = match_strdup(&args[0]);
+ if (!argp) {
err = -ENOMEM;
- if (argp && !(volume->iotype = get_iotype(argp)))
+ break;
+ }
+ if (!(volume->iotype = get_iotype(argp)))
err = -ENOENT;
kfree(argp);
break;
- case Opt_iomode:
- if (!(argp = match_strdup(&args[0])))
+ case opt_iomode:
+ argp = match_strdup(&args[0]);
+ if (!argp) {
err = -ENOMEM;
- if (argp && !strcmp(argp, "ro"))
+ break;
+ }
+ if (!strcmp(argp, "ro"))
SetLUReadonly(volume);
- else if (argp && !strcmp(argp, "wb"))
+ else if (!strcmp(argp, "wb"))
SetLUWCache(volume);
+ else if (strcmp(argp, "wt"))
+ err = -EINVAL;
+ kfree(argp);
+ break;
+ case opt_scsiid:
+ argp = match_strdup(&args[0]);
+ if (!argp) {
+ err = -ENOMEM;
+ break;
+ }
+ err = set_scsiid(volume, argp);
+ kfree(argp);
+ break;
+ case opt_scsisn:
+ argp = match_strdup(&args[0]);
+ if (!argp) {
+ err = -ENOMEM;
+ break;
+ }
+ err = set_scsisn(volume, argp);
+ kfree(argp);
+ break;
+ case opt_blk_size:
+ argp = match_strdup(&args[0]);
+ if (!argp) {
+ err = -ENOMEM;
+ break;
+ }
+ blk_sz = simple_strtoull(argp, NULL, 10);
+ if (is_power_of_2(blk_sz) &&
+ 512 <= blk_sz && blk_sz <= IET_MAX_BLOCK_SIZE)
+ volume->blk_shift = ilog2(blk_sz);
+ else {
+ eprintk("invalid BlockSize=%u\n", blk_sz);
+ err = -EINVAL;
+ }
kfree(argp);
break;
default:
goto free_args;
}
- ret = set_iotype(volume, args);
+ ret = parse_volume_params(volume, args);
if (ret < 0)
goto free_args;
if (ret < 0)
goto free_args;
+ if (!volume->scsi_id[0])
+ gen_scsiid(volume);
+
+ if (!volume->scsi_sn[0]) {
+ int i;
+
+ for (i = 0; i < SCSI_ID_LEN; i++)
+ snprintf(volume->scsi_sn + (i * 2), 3, "%02x",
+ volume->scsi_id[i]);
+ }
+
INIT_LIST_HEAD(&volume->queue.wait_list);
spin_lock_init(&volume->queue.queue_lock);
spin_lock_init(&volume->reserve_lock);
int volume_reserve(struct iet_volume *volume, u64 sid)
{
+ int err = 0;
+
if (!volume)
return -ENOENT;
spin_lock(&volume->reserve_lock);
- if (volume->reserve_sid && volume->reserve_sid != sid) {
- spin_unlock(&volume->reserve_lock);
- return -EBUSY;
- }
+ if (volume->reserve_sid && volume->reserve_sid != sid)
+ err = -EBUSY;
+ else
+ volume->reserve_sid = sid;
- volume->reserve_sid = sid;
spin_unlock(&volume->reserve_lock);
-
- return 0;
+ return err;
}
int is_volume_reserved(struct iet_volume *volume, u64 sid)
{
- if (!volume || !volume->reserve_sid || volume->reserve_sid == sid)
- return 0;
+ int err = 0;
- return -EBUSY;
+ if (!volume)
+ return -ENOENT;
+
+ spin_lock(&volume->reserve_lock);
+ if (!volume->reserve_sid || volume->reserve_sid == sid)
+ err = 0;
+ else
+ err = -EBUSY;
+
+ spin_unlock(&volume->reserve_lock);
+ return err;
}
int volume_release(struct iet_volume *volume, u64 sid, int force)
{
+ int err = 0;
+
+ if (!volume)
+ return -ENOENT;
+
+ spin_lock(&volume->reserve_lock);
+
if (force || volume->reserve_sid == sid)
volume->reserve_sid = 0;
+ else
+ err = -EBUSY;
- return 0;
+ spin_unlock(&volume->reserve_lock);
+ return err;
}
static void iet_volume_info_show(struct seq_file *seq, struct iscsi_target *target)
else
seq_printf(seq, " iomode:wt");
+ seq_printf(seq, " blocks:%llu blocksize:%u",
+ volume->blk_cnt, 1 << volume->blk_shift);
if (volume->iotype->show)
volume->iotype->show(volume, seq);
else