From 160016b9e81d40f593b60f517e0ee3addb77c47b Mon Sep 17 00:00:00 2001 From: Vladislav Bolkhovitin Date: Fri, 25 Apr 2014 02:03:04 +0000 Subject: [PATCH] Saved mode pages added git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@5479 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- scst/README | 15 +- scst/README_in-tree | 15 +- scst/include/scst.h | 37 +- scst/include/scst_const.h | 12 +- scst/src/dev_handlers/Makefile | 1 + scst/src/dev_handlers/scst_user.c | 24 +- scst/src/dev_handlers/scst_vdisk.c | 587 ++++++++++++++++++++++++++--- scst/src/scst_lib.c | 434 ++++++++++++++++++++- scst/src/scst_priv.h | 1 - scst/src/scst_targ.c | 11 +- usr/fileio/fileio.c | 6 +- 11 files changed, 1053 insertions(+), 90 deletions(-) diff --git a/scst/README b/scst/README index 1e1ef1bf0..59ba00107 100644 --- a/scst/README +++ b/scst/README @@ -945,6 +945,9 @@ cache. The following parameters possible for vdisk_fileio: will go from the initiator. This option overrides "write_through" option. Disabled by default. + - tst - sets TST field of SCSI Control mode page. See SPC-4 for more + details about this field as well as about possible values. + - thin_provisioned - enables thin provisioning facility, when remote initiators can unmap blocks of storage, if they don't need them anymore. Backend storage also must support this facility. @@ -966,8 +969,8 @@ between application and disk or need the large block throughput. See below for more info. The following parameters possible for vdisk_blockio: filename, -blocksize, nv_cache, read_only, removable, rotational, thin_provisioned. -See vdisk_fileio above for description of those parameters. +blocksize, nv_cache, read_only, removable, rotational, thin_provisioned, +tst. See vdisk_fileio above for description of those parameters. Handler vdisk_nullio provides NULLIO mode to create virtual devices. In this mode no real I/O is done, but success returned to initiators. @@ -1025,6 +1028,9 @@ Each vdisk_fileio's device has the following attributes in SCST device belongs to (in SCSI terminology all SCST devices called Logical Units). See SPC for more info. + - tst - contains TST field of SCSI Control mode page. See SPC-4 for + more details about this field. + - thin_provisioned - contains thin provisioning status of this virtual device. @@ -1087,6 +1093,7 @@ For example: |-- thin_provisioned |-- threads_num |-- threads_pool_type +|-- tst |-- type |-- usn `-- write_through @@ -1094,8 +1101,8 @@ For example: Each vdisk_blockio's device has the following attributes in /sys/kernel/scst_tgt/devices/device_name: blocksize, filename, nv_cache, read_only, removable, resync_size, rotational, size_mb, t10_dev_id, -thin_provisioned, threads_num, threads_pool_type, type, usn. See above -description of those parameters. +thin_provisioned, threads_num, threads_pool_type, tst, type, usn. See +above description of those parameters. Each vdisk_nullio's device has the following attributes in /sys/kernel/scst_tgt/devices/device_name: blocksize, read_only, diff --git a/scst/README_in-tree b/scst/README_in-tree index f66ef58e6..2571a1dd1 100644 --- a/scst/README_in-tree +++ b/scst/README_in-tree @@ -803,6 +803,9 @@ cache. The following parameters possible for vdisk_fileio: will go from the initiator. This option overrides "write_through" option. Disabled by default. + - tst - sets TST field of SCSI Control mode page. See SPC-4 for more + details about this field as well as about possible values. + - thin_provisioned - enables thin provisioning facility, when remote initiators can unmap blocks of storage, if they don't need them anymore. Backend storage also must support this facility. @@ -824,8 +827,8 @@ between application and disk or need the large block throughput. See below for more info. The following parameters possible for vdisk_blockio: filename, -blocksize, nv_cache, read_only, removable, rotational, thin_provisioned. -See vdisk_fileio above for description of those parameters. +blocksize, nv_cache, read_only, removable, rotational, thin_provisioned, +tst. See vdisk_fileio above for description of those parameters. Handler vdisk_nullio provides NULLIO mode to create virtual devices. In this mode no real I/O is done, but success returned to initiators. @@ -883,6 +886,9 @@ Each vdisk_fileio's device has the following attributes in SCST device belongs to (in SCSI terminology all SCST devices called Logical Units). See SPC for more info. + - tst - contains TST field of SCSI Control mode page. See SPC-4 for + more details about this field. + - thin_provisioned - contains thin provisioning status of this virtual device. @@ -939,6 +945,7 @@ For example: |-- thin_provisioned |-- threads_num |-- threads_pool_type +|-- tst |-- type |-- usn `-- write_through @@ -946,8 +953,8 @@ For example: Each vdisk_blockio's device has the following attributes in /sys/kernel/scst_tgt/devices/device_name: blocksize, filename, nv_cache, read_only, removable, resync_size, rotational, size_mb, t10_dev_id, -thin_provisioned, threads_num, threads_pool_type, type, usn. See above -description of those parameters. +thin_provisioned, threads_num, threads_pool_type, tst, type, usn. See +above description of those parameters. Each vdisk_nullio's device has the following attributes in /sys/kernel/scst_tgt/devices/device_name: blocksize, read_only, diff --git a/scst/include/scst.h b/scst/include/scst.h index c80d43f2b..e7063b0e8 100644 --- a/scst/include/scst.h +++ b/scst/include/scst.h @@ -2414,10 +2414,31 @@ struct scst_device { unsigned int swp:1; unsigned int d_sense:1; + /** + ** Saved and default versions of them, which supported. TST is not + ** among them, because it's hard to switch curr_order_data on the + ** fly. To ensure that no commands lost, we need to flush the previous + ** curr_order_data at first and with one being active command (MODE + ** SELECT), we don't have facility for that at the moment. Suspending + ** activities will hang waiting for the active MODE SELECT. ToDo. + **/ + + unsigned int queue_alg_saved:4; + unsigned int queue_alg_default:4; + + unsigned int tas_saved:1; + unsigned int tas_default:1; + + unsigned int swp_saved:1; + unsigned int swp_default:1; + + unsigned int d_sense_saved:1; + unsigned int d_sense_default:1; + /* * Set if device implements own ordered commands management. If not set - * and queue_alg is SCST_CONTR_MODE_QUEUE_ALG_RESTRICTED_REORDER, - * expected_sn will be incremented only after commands finished. + * and queue_alg is SCST_QUEUE_ALG_0_RESTRICTED_REORDER, expected_sn + * will be incremented only after commands finished. */ unsigned int has_own_order_mgmt:1; @@ -4695,4 +4716,16 @@ void scst_write_same(struct scst_cmd *cmd); __be64 scst_pack_lun(const uint64_t lun, enum scst_lun_addr_method addr_method); uint64_t scst_unpack_lun(const uint8_t *lun, int len); +int scst_save_global_mode_pages(const struct scst_device *dev, + uint8_t *buf, int size); +int scst_restore_global_mode_pages(struct scst_device *dev, char *params, + char **last_param); + +int scst_read_file_transactional(const char *name, const char *name1, + const char *signature, int signature_len, uint8_t *buf, int size); +int scst_write_file_transactional(const char *name, const char *name1, + const char *signature, int signature_len, const uint8_t *buf, int size); + +int scst_remove_file(const char *name); + #endif /* __SCST_H */ diff --git a/scst/include/scst_const.h b/scst/include/scst_const.h index e4ebb6944..a4322819c 100644 --- a/scst/include/scst_const.h +++ b/scst/include/scst_const.h @@ -495,20 +495,20 @@ enum { /************************************************************* ** Values for the control mode page TST field *************************************************************/ -#define SCST_CONTR_MODE_ONE_TASK_SET 0 -#define SCST_CONTR_MODE_SEP_TASK_SETS 1 +#define SCST_TST_0_SINGLE_TASK_SET 0 +#define SCST_TST_1_SEP_TASK_SETS 1 /******************************************************************* ** Values for the control mode page QUEUE ALGORITHM MODIFIER field *******************************************************************/ -#define SCST_CONTR_MODE_QUEUE_ALG_RESTRICTED_REORDER 0 -#define SCST_CONTR_MODE_QUEUE_ALG_UNRESTRICTED_REORDER 1 +#define SCST_QUEUE_ALG_0_RESTRICTED_REORDER 0 +#define SCST_QUEUE_ALG_1_UNRESTRICTED_REORDER 1 /************************************************************* ** Values for the control mode page D_SENSE field *************************************************************/ -#define SCST_CONTR_MODE_FIXED_SENSE 0 -#define SCST_CONTR_MODE_DESCR_SENSE 1 +#define SCST_D_SENSE_0_FIXED_SENSE 0 +#define SCST_D_SENSE_1_DESCR_SENSE 1 /************************************************************* ** TransportID protocol identifiers diff --git a/scst/src/dev_handlers/Makefile b/scst/src/dev_handlers/Makefile index 3ae5471d6..464ff06db 100644 --- a/scst/src/dev_handlers/Makefile +++ b/scst/src/dev_handlers/Makefile @@ -71,6 +71,7 @@ all: $(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd) install: all + mkdir -p $(DESTDIR)/var/lib/scst/vdev_mode_pages $(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd) \ modules_install diff --git a/scst/src/dev_handlers/scst_user.c b/scst/src/dev_handlers/scst_user.c index 962a496b7..bc6e33bb6 100644 --- a/scst/src/dev_handlers/scst_user.c +++ b/scst/src/dev_handlers/scst_user.c @@ -2620,8 +2620,14 @@ static int dev_user_attach(struct scst_device *sdev) sdev->tst = dev->tst; sdev->queue_alg = dev->queue_alg; sdev->swp = dev->swp; + sdev->swp_saved = dev->swp; + sdev->swp_default = dev->swp; sdev->tas = dev->tas; + sdev->tas_saved = dev->tas; + sdev->tas_default = dev->tas; sdev->d_sense = dev->d_sense; + sdev->d_sense_saved = dev->d_sense; + sdev->d_sense_default = dev->d_sense; sdev->has_own_order_mgmt = dev->has_own_order_mgmt; dev->sdev = sdev; @@ -3350,10 +3356,10 @@ static int __dev_user_set_opt(struct scst_user_dev *dev, goto out; } - if (((opt->tst != SCST_CONTR_MODE_ONE_TASK_SET) && - (opt->tst != SCST_CONTR_MODE_SEP_TASK_SETS)) || - ((opt->queue_alg != SCST_CONTR_MODE_QUEUE_ALG_RESTRICTED_REORDER) && - (opt->queue_alg != SCST_CONTR_MODE_QUEUE_ALG_UNRESTRICTED_REORDER)) || + if (((opt->tst != SCST_TST_0_SINGLE_TASK_SET) && + (opt->tst != SCST_TST_1_SEP_TASK_SETS)) || + ((opt->queue_alg != SCST_QUEUE_ALG_0_RESTRICTED_REORDER) && + (opt->queue_alg != SCST_QUEUE_ALG_1_UNRESTRICTED_REORDER)) || (opt->swp > 1) || (opt->tas > 1) || (opt->has_own_order_mgmt > 1) || (opt->d_sense > 1)) { PRINT_ERROR("Invalid SCSI option (tst %x, queue_alg %x, swp %x," @@ -3364,6 +3370,16 @@ static int __dev_user_set_opt(struct scst_user_dev *dev, goto out; } +#if 1 + if ((dev->tst != opt->tst) && (dev->sdev != NULL) && + !list_empty(&dev->sdev->dev_tgt_dev_list)) { + PRINT_ERROR("On the fly setting of TST not supported. " + "See comment in struct scst_device."); + res = -EINVAL; + goto out; + } +#endif + dev->parse_type = opt->parse_type; dev->on_free_cmd_type = opt->on_free_cmd_type; dev->memory_reuse_type = opt->memory_reuse_type; diff --git a/scst/src/dev_handlers/scst_vdisk.c b/scst/src/dev_handlers/scst_vdisk.c index 58ae8a9f9..5519476bd 100644 --- a/scst/src/dev_handlers/scst_vdisk.c +++ b/scst/src/dev_handlers/scst_vdisk.c @@ -38,6 +38,7 @@ #include #include #include +#include #ifndef INSIDE_KERNEL_TREE #include #endif @@ -114,18 +115,18 @@ static struct scst_trace_log vdisk_local_trace_tbl[] = { #define VDISK_NULLIO_SIZE (5LL*1024*1024*1024*1024/2) -#define DEF_TST SCST_CONTR_MODE_SEP_TASK_SETS +#define DEF_TST SCST_TST_1_SEP_TASK_SETS /* * Since we can't control backstorage device's reordering, we have to always * report unrestricted reordering. */ -#define DEF_QUEUE_ALG_WT SCST_CONTR_MODE_QUEUE_ALG_UNRESTRICTED_REORDER -#define DEF_QUEUE_ALG SCST_CONTR_MODE_QUEUE_ALG_UNRESTRICTED_REORDER +#define DEF_QUEUE_ALG_WT SCST_QUEUE_ALG_1_UNRESTRICTED_REORDER +#define DEF_QUEUE_ALG SCST_QUEUE_ALG_1_UNRESTRICTED_REORDER #define DEF_SWP 0 #define DEF_TAS 0 -#define DEF_DSENSE SCST_CONTR_MODE_FIXED_SENSE +#define DEF_DSENSE SCST_D_SENSE_0_FIXED_SENSE #ifdef CONFIG_SCST_PROC #define VDISK_PROC_HELP "help" @@ -162,6 +163,8 @@ struct scst_vdisk_dev { unsigned int thin_provisioned_manually_set:1; unsigned int dev_thin_provisioned:1; unsigned int rotational:1; + unsigned int wt_flag_saved:1; + unsigned int tst:3; unsigned int format_active:1; struct file *fd; @@ -217,6 +220,8 @@ struct vdisk_cmd_params { bool use_zero_copy; }; +static bool vdev_saved_mode_pages_enabled = true; + enum compl_status_e { #if defined(SCST_DEBUG) COMPL_STATUS_START_AT = 777, @@ -332,6 +337,8 @@ static ssize_t vdisk_sysfs_wt_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf); static ssize_t vdisk_sysfs_tp_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf); +static ssize_t vdisk_sysfs_tst_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf); static ssize_t vdisk_sysfs_rotational_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf); static ssize_t vdisk_sysfs_nv_cache_show(struct kobject *kobj, @@ -402,6 +409,8 @@ static struct kobj_attribute vdisk_wt_attr = __ATTR(write_through, S_IRUGO, vdisk_sysfs_wt_show, NULL); static struct kobj_attribute vdisk_tp_attr = __ATTR(thin_provisioned, S_IRUGO, vdisk_sysfs_tp_show, NULL); +static struct kobj_attribute vdisk_tst_attr = + __ATTR(tst, S_IRUGO, vdisk_sysfs_tst_show, NULL); static struct kobj_attribute vdisk_rotational_attr = __ATTR(rotational, S_IRUGO, vdisk_sysfs_rotational_show, NULL); static struct kobj_attribute vdisk_nv_cache_attr = @@ -455,6 +464,7 @@ static const struct attribute *vdisk_fileio_attrs[] = { &vdisk_rd_only_attr.attr, &vdisk_wt_attr.attr, &vdisk_tp_attr.attr, + &vdisk_tst_attr.attr, &vdisk_rotational_attr.attr, &vdisk_nv_cache_attr.attr, &vdisk_o_direct_attr.attr, @@ -480,6 +490,7 @@ static const struct attribute *vdisk_blockio_attrs[] = { &vdisk_rd_only_attr.attr, &vdisk_wt_attr.attr, &vdisk_nv_cache_attr.attr, + &vdisk_tst_attr.attr, &vdisk_removable_attr.attr, &vdisk_rotational_attr.attr, &vdisk_filename_attr.attr, @@ -501,6 +512,7 @@ static const struct attribute *vdisk_nullio_attrs[] = { &vdev_size_mb_rw_attr.attr, &vdisk_blocksize_attr.attr, &vdisk_rd_only_attr.attr, + &vdisk_tst_attr.attr, &vdev_dummy_attr.attr, &vdisk_removable_attr.attr, &vdev_t10_vend_id_attr.attr, @@ -519,6 +531,7 @@ static const struct attribute *vcdrom_attrs[] = { &vdev_size_ro_attr.attr, &vdev_size_mb_ro_attr.attr, &vcdrom_filename_attr.attr, + &vdisk_tst_attr.attr, &vdev_t10_vend_id_attr.attr, &vdev_vend_specific_id_attr.attr, &vdev_prod_id_attr.attr, @@ -589,6 +602,7 @@ static struct scst_dev_type vdisk_file_devtype = { "removable, " "rotational, " "thin_provisioned, " + "tst, " "write_through, " "zero_copy", #endif @@ -634,6 +648,7 @@ static struct scst_dev_type vdisk_blk_devtype = { "removable, " "rotational, " "thin_provisioned, " + "tst, " "write_through", #endif #if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) @@ -675,7 +690,8 @@ static struct scst_dev_type vdisk_null_devtype = { "removable, " "rotational, " "size, " - "size_mb", + "size_mb, " + "tst", #endif #if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) .default_trace_flags = SCST_DEFAULT_DEV_LOG_FLAGS, @@ -711,7 +727,7 @@ static struct scst_dev_type vcdrom_devtype = { .add_device = vcdrom_add_device, .del_device = vcdrom_del_device, .dev_attrs = vcdrom_attrs, - .add_device_parameters = NULL, + .add_device_parameters = "tst", #endif #if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) .default_trace_flags = SCST_DEFAULT_DEV_LOG_FLAGS, @@ -944,6 +960,262 @@ static struct scst_vdisk_dev *vdev_find(const char *name) return res; } +#define VDEV_WT_LABEL "WRITE_THROUGH" +#define VDEV_MODE_PAGES_BUF_SIZE (64*1024) +#define VDEV_MODE_PAGES_DIR "/var/lib/scst/vdev_mode_pages" + +static int __vdev_save_mode_pages(const struct scst_vdisk_dev *virt_dev, + uint8_t *buf, int size) +{ + int res = 0; + + TRACE_ENTRY(); + + if (virt_dev->wt_flag != DEF_WRITE_THROUGH) { + res += scnprintf(&buf[res], size - res, "%s=%d\n", + VDEV_WT_LABEL, virt_dev->wt_flag); + if (res >= size-1) + goto out_overflow; + } + +out: + TRACE_EXIT_RES(res); + return res; + +out_overflow: + PRINT_ERROR("Mode pages buffer overflow (size %d)", size); + res = -EOVERFLOW; + goto out; +} + +static int vdev_save_mode_pages(const struct scst_vdisk_dev *virt_dev) +{ + int res, rc, offs; + uint8_t *buf; + int size; + char *name, *name1; + + TRACE_ENTRY(); + + size = VDEV_MODE_PAGES_BUF_SIZE; + + buf = vzalloc(size); + if (buf == NULL) { + PRINT_ERROR("Unable to alloc mode pages buffer (size %d)", size); + res = -ENOMEM; + goto out; + } + + name = kasprintf(GFP_KERNEL, "%s/%s", VDEV_MODE_PAGES_DIR, virt_dev->name); + if (name == NULL) { + PRINT_ERROR("Unable to create name %s/%s", VDEV_MODE_PAGES_DIR, + virt_dev->name); + res = -ENOMEM; + goto out_vfree; + } + + name1 = kasprintf(GFP_KERNEL, "%s/%s1", VDEV_MODE_PAGES_DIR, virt_dev->name); + if (name1 == NULL) { + PRINT_ERROR("Unable to create name %s/%s1", VDEV_MODE_PAGES_DIR, + virt_dev->name); + res = -ENOMEM; + goto out_free_name; + } + + offs = scst_save_global_mode_pages(virt_dev->dev, buf, size); + if (offs < 0) { + res = offs; + goto out_free_name1; + } + + rc = __vdev_save_mode_pages(virt_dev, &buf[offs], size - offs); + if (rc < 0) { + res = rc; + goto out_free_name1; + } + + offs += rc; + if (offs == 0) { + res = 0; + scst_remove_file(name); + scst_remove_file(name1); + goto out_free_name1; + } + + res = scst_write_file_transactional(name, name1, + virt_dev->name, strlen(virt_dev->name), buf, offs); + +out_free_name1: + kfree(name1); + +out_free_name: + kfree(name); + +out_vfree: + vfree(buf); + +out: + TRACE_EXIT_RES(res); + return res; +} + +static int vdev_restore_wt(struct scst_vdisk_dev *virt_dev, unsigned int val) +{ + int res; + + TRACE_ENTRY(); + + if (val > 1) { + PRINT_ERROR("Invalid value %d for parameter %s (device %s)", + val, VDEV_WT_LABEL, virt_dev->name); + res = -EINVAL; + goto out; + } + + virt_dev->wt_flag = val; + virt_dev->wt_flag_saved = val; + + PRINT_INFO("WT_FLAG restored to %d for vdev %s", virt_dev->wt_flag, + virt_dev->name); + + res = 0; + +out: + TRACE_EXIT_RES(res); + return res; +} + +/* Params are NULL-terminated */ +static int __vdev_load_mode_pages(struct scst_vdisk_dev *virt_dev, char *params) +{ + int res; + char *param, *p, *pp; + unsigned long val; + + TRACE_ENTRY(); + + while (1) { + param = scst_get_next_token_str(¶ms); + if (param == NULL) + break; + + p = scst_get_next_lexem(¶m); + if (*p == '\0') + break; + + pp = scst_get_next_lexem(¶m); + if (*pp == '\0') + goto out_need_param; + + if (scst_get_next_lexem(¶m)[0] != '\0') + goto out_too_many; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + res = kstrtoul(pp, 0, &val); +#else + res = strict_strtoul(pp, 0, &val); +#endif + if (res != 0) + goto out_strtoul_failed; + + if (strcasecmp(VDEV_WT_LABEL, p) == 0) + res = vdev_restore_wt(virt_dev, val); + else { + TRACE_DBG("Unknown parameter %s", p); + res = -EINVAL; + break; + } + if (res != 0) + goto out; + } + + res = 0; + +out: + TRACE_EXIT_RES(res); + return res; + +out_strtoul_failed: + PRINT_ERROR("strtoul() for %s failed: %d (device %s)", pp, res, + virt_dev->name); + goto out; + +out_need_param: + PRINT_ERROR("Parameter %s value missed for device %s", p, virt_dev->name); + res = -EINVAL; + goto out; + +out_too_many: + PRINT_ERROR("Too many parameter's %s values (device %s)", p, virt_dev->name); + res = -EINVAL; + goto out; +} + +static int vdev_load_mode_pages(struct scst_vdisk_dev *virt_dev) +{ + int res; + struct scst_device *dev = virt_dev->dev; + uint8_t *buf; + int size; + char *name, *name1, *params; + + TRACE_ENTRY(); + + size = VDEV_MODE_PAGES_BUF_SIZE; + + buf = vzalloc(size); + if (buf == NULL) { + PRINT_ERROR("Unable to alloc mode pages buffer (size %d)", size); + res = -ENOMEM; + goto out; + } + + name = kasprintf(GFP_KERNEL, "%s/%s", VDEV_MODE_PAGES_DIR, virt_dev->name); + if (name == NULL) { + PRINT_ERROR("Unable to create name %s/%s", VDEV_MODE_PAGES_DIR, + virt_dev->name); + res = -ENOMEM; + goto out_vfree; + } + + name1 = kasprintf(GFP_KERNEL, "%s/%s1", VDEV_MODE_PAGES_DIR, virt_dev->name); + if (name1 == NULL) { + PRINT_ERROR("Unable to create name %s/%s1", VDEV_MODE_PAGES_DIR, + virt_dev->name); + res = -ENOMEM; + goto out_free_name; + } + + size = scst_read_file_transactional(name, name1, + virt_dev->name, strlen(virt_dev->name), buf, size-1); + if (size <= 0) { + res = size; + goto out_free_name1; + } + + buf[size-1] = '\0'; + + res = scst_restore_global_mode_pages(dev, &buf[strlen(virt_dev->name)+1], + ¶ms); + if ((res != 0) || (params == NULL)) + goto out_free_name1; + + res = __vdev_load_mode_pages(virt_dev, params); + +out_free_name1: + kfree(name1); + +out_free_name: + kfree(name); + +out_vfree: + vfree(buf); + +out: + TRACE_EXIT_RES(res); + return res; +} + static int vdisk_attach(struct scst_device *dev) { int res = 0; @@ -1021,14 +1293,25 @@ static int vdisk_attach(struct scst_device *dev) dev->dh_priv = virt_dev; - dev->tst = DEF_TST; + dev->tst = virt_dev->tst; dev->d_sense = DEF_DSENSE; + dev->d_sense_saved = DEF_DSENSE; + dev->d_sense_default = DEF_DSENSE; if (virt_dev->wt_flag && !virt_dev->nv_cache) dev->queue_alg = DEF_QUEUE_ALG_WT; else dev->queue_alg = DEF_QUEUE_ALG; + dev->queue_alg_saved = dev->queue_alg; + dev->queue_alg_default = dev->queue_alg; dev->swp = DEF_SWP; + dev->swp_saved = DEF_SWP; + dev->swp_default = DEF_SWP; dev->tas = DEF_TAS; + dev->tas_saved = DEF_TAS; + dev->tas_default = DEF_TAS; + + if (vdev_saved_mode_pages_enabled) + vdev_load_mode_pages(virt_dev); out: TRACE_EXIT(); @@ -3383,22 +3666,47 @@ static int vdisk_format_pg(unsigned char *p, int pcontrol, static int vdisk_caching_pg(unsigned char *p, int pcontrol, struct scst_vdisk_dev *virt_dev) { /* Caching page for mode_sense */ - const unsigned char caching_pg[] = {0x8, 0x12, 0x0, 0, 0, 0, 0, 0, + unsigned char caching_pg[] = {0x8, 0x12, 0x0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x80, 0x14, 0, 0, 0, 0, 0, 0}; - memcpy(p, caching_pg, sizeof(caching_pg)); - p[2] |= !(virt_dev->wt_flag || virt_dev->nv_cache) ? WCE : 0; - if (1 == pcontrol) + if (!virt_dev->nv_cache && vdev_saved_mode_pages_enabled) + caching_pg[0] |= 0x80; + + switch (pcontrol) { + case 0: /* current */ + memcpy(p, caching_pg, sizeof(caching_pg)); + p[2] |= (virt_dev->wt_flag || virt_dev->nv_cache) ? 0 : WCE; + break; + case 1: /* changeable */ memset(p + 2, 0, sizeof(caching_pg) - 2); + if (!virt_dev->nv_cache) + p[2] |= WCE; + break; + case 2: /* default */ + memcpy(p, caching_pg, sizeof(caching_pg)); + p[2] |= (DEF_WRITE_THROUGH || virt_dev->nv_cache) ? 0: WCE; + break; + case 3: /* saved */ + memcpy(p, caching_pg, sizeof(caching_pg)); + p[2] |= (virt_dev->wt_flag_saved || virt_dev->nv_cache) ? 0: WCE; + break; + default: + sBUG_ON(1); + break; + } + return sizeof(caching_pg); } static int vdisk_ctrl_m_pg(unsigned char *p, int pcontrol, struct scst_vdisk_dev *virt_dev) { /* Control mode page for mode_sense */ - const unsigned char ctrl_m_pg[] = {0xa, 0xa, 0, 0, 0, 0, 0, 0, + unsigned char ctrl_m_pg[] = {0xa, 0xa, 0, 0, 0, 0, 0, 0, 0, 0, 0x2, 0x4b}; + if (vdev_saved_mode_pages_enabled) + ctrl_m_pg[0] |= 0x80; + memcpy(p, ctrl_m_pg, sizeof(ctrl_m_pg)); switch (pcontrol) { case 0: /* current */ @@ -3411,27 +3719,31 @@ static int vdisk_ctrl_m_pg(unsigned char *p, int pcontrol, case 1: /* changeable */ memset(p + 2, 0, sizeof(ctrl_m_pg) - 2); #if 0 /* - * It's too early to implement it, since we can't control the - * backstorage device parameters. ToDo + * See comment in struct scst_device definition. + * + * If enable it, fix the default and saved cases below! */ p[2] |= 7 << 5; /* TST */ - p[3] |= 0xF << 4; /* QUEUE ALGORITHM MODIFIER */ #endif p[2] |= 1 << 2; /* D_SENSE */ + p[3] |= 0xF << 4; /* QUEUE ALGORITHM MODIFIER */ p[4] |= 1 << 3; /* SWP */ p[5] |= 1 << 6; /* TAS */ break; case 2: /* default */ - p[2] |= DEF_TST << 5; - p[2] |= DEF_DSENSE << 2; - if (virt_dev->wt_flag || virt_dev->nv_cache) - p[3] |= DEF_QUEUE_ALG_WT << 4; - else - p[3] |= DEF_QUEUE_ALG << 4; - p[4] |= DEF_SWP << 3; - p[5] |= DEF_TAS << 6; + p[2] |= virt_dev->tst << 5; + p[2] |= virt_dev->dev->d_sense_default << 2; + p[3] |= virt_dev->dev->queue_alg_default << 4; + p[4] |= virt_dev->dev->swp_default << 3; + p[5] |= virt_dev->dev->tas_default << 6; + break; + case 3: /* saved */ + p[2] |= virt_dev->dev->tst << 5; + p[2] |= virt_dev->dev->d_sense_saved << 2; + p[3] |= virt_dev->dev->queue_alg_saved << 4; + p[4] |= virt_dev->dev->swp_saved << 3; + p[5] |= virt_dev->dev->tas_saved << 6; break; - case 3: /* saved, blocked by the caller */ default: sBUG(); } @@ -3491,7 +3803,7 @@ static enum compl_status_e vdisk_exec_mode_sense(struct vdisk_cmd_params *p) if (unlikely(length <= 0)) goto out_free; - if (0x3 == pcontrol) { + if (!vdev_saved_mode_pages_enabled && (0x3 == pcontrol)) { TRACE_DBG("%s", "MODE SENSE: Saving values not supported"); scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_saving_params_unsup)); @@ -3648,31 +3960,119 @@ out: } static void vdisk_ctrl_m_pg_select(unsigned char *p, - struct scst_vdisk_dev *virt_dev, struct scst_cmd *cmd) + struct scst_vdisk_dev *virt_dev, struct scst_cmd *cmd, bool save) { struct scst_device *dev = virt_dev->dev; int old_swp = dev->swp, old_tas = dev->tas, old_dsense = dev->d_sense; + int old_queue_alg = dev->queue_alg; + int rc; -#if 0 /* Not implemented yet, see comment in vdisk_ctrl_m_pg() */ - dev->tst = (p[2] >> 5) & 1; - dev->queue_alg = p[3] >> 4; -#else - if ((dev->tst != ((p[2] >> 5) & 1)) || (dev->queue_alg != (p[3] >> 4))) { - TRACE(TRACE_MINOR|TRACE_SCSI, "%s", "MODE SELECT: Changing of " - "TST and QUEUE ALGORITHM not supported"); + TRACE_ENTRY(); + + if (save && !vdev_saved_mode_pages_enabled) { + TRACE(TRACE_MINOR|TRACE_SCSI, "MODE SELECT: saved control page " + "not supported"); scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_invalid_field_in_cdb)); - return; + goto out; + } + +#if 0 /* Not implemented yet, see comment in struct scst_device */ + dev->tst = (p[2] >> 5) & 1; +#else + if (dev->tst != ((p[2] >> 5) & 1)) { + TRACE(TRACE_MINOR|TRACE_SCSI, "%s", "MODE SELECT: Changing of " + "TST not supported"); + scst_set_cmd_error(cmd, + SCST_LOAD_SENSE(scst_sense_invalid_field_in_cdb)); + goto out; } #endif + dev->queue_alg = p[3] >> 4; dev->swp = (p[4] & 0x8) >> 3; dev->tas = (p[5] & 0x40) >> 6; dev->d_sense = (p[2] & 0x4) >> 2; + if ((dev->swp == old_swp) && (dev->tas == old_tas) && + (dev->d_sense == old_dsense) && (dev->queue_alg == old_queue_alg)) + goto out; + + if (!save) + goto out_ok; + + rc = vdev_save_mode_pages(virt_dev); + if (rc != 0) { + dev->swp = old_swp; + dev->tas = old_tas; + dev->d_sense = old_dsense; + dev->queue_alg = old_queue_alg; + /* Hopefully, the error is temporary */ + scst_set_busy(cmd); + goto out; + } + + dev->swp_saved = dev->swp; + dev->tas_saved = dev->tas; + dev->d_sense_saved = dev->d_sense; + dev->queue_alg_saved = dev->queue_alg; + +out_ok: PRINT_INFO("Device %s: new control mode page parameters: SWP %x " - "(was %x), TAS %x (was %x), D_SENSE %d (was %d)", - virt_dev->name, dev->swp, old_swp, dev->tas, old_tas, - dev->d_sense, old_dsense); + "(was %x), TAS %x (was %x), D_SENSE %d (was %d), " + "QUEUE ALG %d (was %d)", virt_dev->name, dev->swp, + old_swp, dev->tas, old_tas, dev->d_sense, old_dsense, + dev->queue_alg, old_queue_alg); + +out: + TRACE_EXIT(); + return; +} + +static void vdisk_caching_m_pg_select(unsigned char *p, + struct scst_vdisk_dev *virt_dev, struct scst_cmd *cmd, bool save, + bool read_only) +{ + int old_wt = virt_dev->wt_flag, new_wt, rc; + + TRACE_ENTRY(); + + if (save && (!vdev_saved_mode_pages_enabled || virt_dev->nv_cache)) { + TRACE(TRACE_MINOR|TRACE_SCSI, "MODE SELECT: saved cache page " + "not supported"); + scst_set_cmd_error(cmd, + SCST_LOAD_SENSE(scst_sense_invalid_field_in_cdb)); + goto out; + } + + new_wt = (p[2] & WCE) ? 0 : 1; + + if (new_wt == old_wt) + goto out; + + if (vdisk_set_wt(virt_dev, new_wt, read_only) != 0) { + scst_set_busy(cmd); + goto out; + } + + if (!save) + goto out_ok; + + rc = vdev_save_mode_pages(virt_dev); + if (rc != 0) { + vdisk_set_wt(virt_dev, old_wt, read_only); + /* Hopefully, the error is temporary */ + scst_set_busy(cmd); + goto out; + } + + virt_dev->wt_flag_saved = virt_dev->wt_flag; + +out_ok: + PRINT_INFO("Device %s: new wt_flag: %x (was %x)", virt_dev->name, + virt_dev->wt_flag, old_wt); + +out: + TRACE_EXIT(); return; } @@ -3694,10 +4094,9 @@ static enum compl_status_e vdisk_exec_mode_select(struct vdisk_cmd_params *p) if (unlikely(length <= 0)) goto out; - if (!(cmd->cdb[1] & PF) || (cmd->cdb[1] & SP)) { + if (!(cmd->cdb[1] & PF)) { TRACE(TRACE_MINOR|TRACE_SCSI, "MODE SELECT: Unsupported " - "value(s) of PF and/or SP bits (cdb[1]=%x)", - cmd->cdb[1]); + "PF bit zero (cdb[1]=%x)", cmd->cdb[1]); scst_set_invalid_field_in_cdb(cmd, 1, 0); goto out_put; } @@ -3730,11 +4129,8 @@ static enum compl_status_e vdisk_exec_mode_select(struct vdisk_cmd_params *p) scst_set_invalid_field_in_parm_list(cmd, offset+1, 0); goto out_put; } - if (vdisk_set_wt(virt_dev, (address[offset + 2] & WCE) ? 0 : 1, - cmd->tgt_dev->tgt_dev_rd_only) != 0) { - scst_set_busy(cmd); - goto out_put; - } + vdisk_caching_m_pg_select(&address[offset], virt_dev, + cmd, cmd->cdb[1] & SP, cmd->tgt_dev->tgt_dev_rd_only); break; } else if ((address[offset] & 0x3f) == 0xA) { /* Control page */ @@ -3744,7 +4140,8 @@ static enum compl_status_e vdisk_exec_mode_select(struct vdisk_cmd_params *p) scst_set_invalid_field_in_parm_list(cmd, offset+1, 0); goto out_put; } - vdisk_ctrl_m_pg_select(&address[offset], virt_dev, cmd); + vdisk_ctrl_m_pg_select(&address[offset], virt_dev, cmd, + cmd->cdb[1] & SP); } else { TRACE(TRACE_MINOR, "MODE SELECT: Invalid request %x", address[offset] & 0x3f); @@ -5220,15 +5617,12 @@ static void vdisk_task_mgmt_fn_done(struct scst_mgmt_cmd *mcmd, struct scst_vdisk_dev *virt_dev = dev->dh_priv; int rc; - dev->tst = DEF_TST; - dev->d_sense = DEF_DSENSE; - dev->swp = DEF_SWP; - dev->tas = DEF_TAS; + dev->d_sense = dev->d_sense_saved; + dev->swp = dev->swp_saved; + dev->tas = dev->tas_saved; + dev->queue_alg = dev->queue_alg_saved; - if (virt_dev->wt_flag && !virt_dev->nv_cache) - dev->queue_alg = DEF_QUEUE_ALG_WT; - else - dev->queue_alg = DEF_QUEUE_ALG; + dev->tst = virt_dev->tst; rc = vdisk_set_wt(virt_dev, DEF_WRITE_THROUGH, tgt_dev->tgt_dev_rd_only); @@ -5288,6 +5682,10 @@ static void vdisk_report_registering(const struct scst_vdisk_dev *virt_dev) i += snprintf(&buf[i], sizeof(buf) - i, "%sREMOVABLE", (j == i) ? "(" : ", "); + if (virt_dev->tst != DEF_TST) + i += snprintf(&buf[i], sizeof(buf) - i, "%sTST %d", + (j == i) ? "(" : ", ", virt_dev->tst); + if (virt_dev->rotational) i += snprintf(&buf[i], sizeof(buf) - i, "%sROTATIONAL", (j == i) ? "(" : ", "); @@ -5383,6 +5781,7 @@ static int vdev_create(struct scst_dev_type *devt, virt_dev->removable = DEF_REMOVABLE; virt_dev->rotational = DEF_ROTATIONAL; virt_dev->thin_provisioned = DEF_THIN_PROVISIONED; + virt_dev->tst = DEF_TST; virt_dev->blk_shift = DEF_DISK_BLOCK_SHIFT; @@ -5571,6 +5970,15 @@ static int vdev_parse_add_dev_params(struct scst_vdisk_dev *virt_dev, } else if (!strcasecmp("rotational", p)) { virt_dev->rotational = val; TRACE_DBG("ROTATIONAL %d", virt_dev->rotational); + } else if (!strcasecmp("tst", p)) { + if ((val != SCST_TST_0_SINGLE_TASK_SET) && + (val != SCST_TST_1_SEP_TASK_SETS)) { + PRINT_ERROR("Invalid TST value %d", (int)val); + res = -EINVAL; + goto out; + } + virt_dev->tst = val; + TRACE_DBG("TST %d", virt_dev->tst); } else if (!strcasecmp("thin_provisioned", p)) { virt_dev->thin_provisioned = val; virt_dev->thin_provisioned_manually_set = 1; @@ -5676,7 +6084,7 @@ static int vdev_blockio_add_device(const char *device_name, char *params) int res = 0; const char *const allowed_params[] = { "filename", "read_only", "write_through", "removable", "blocksize", "nv_cache", - "rotational", "thin_provisioned", NULL }; + "rotational", "thin_provisioned", "tst", NULL }; struct scst_vdisk_dev *virt_dev; TRACE_ENTRY(); @@ -5734,7 +6142,7 @@ static int vdev_nullio_add_device(const char *device_name, char *params) int res = 0; static const char *const allowed_params[] = { "read_only", "dummy", "removable", "blocksize", "rotational", - "size", "size_mb", NULL + "size", "size_mb", "tst", NULL }; struct scst_vdisk_dev *virt_dev; @@ -5891,7 +6299,7 @@ out: static ssize_t __vcdrom_add_device(const char *device_name, char *params) { int res = 0; - const char *allowed_params[] = { NULL }; /* no params */ + const char *allowed_params[] = { "tst", NULL }; struct scst_vdisk_dev *virt_dev; TRACE_ENTRY(); @@ -6462,6 +6870,27 @@ static ssize_t vdisk_sysfs_removable_show(struct kobject *kobj, return pos; } +static ssize_t vdisk_sysfs_tst_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + int pos = 0; + struct scst_device *dev; + struct scst_vdisk_dev *virt_dev; + + TRACE_ENTRY(); + + dev = container_of(kobj, struct scst_device, dev_kobj); + virt_dev = dev->dh_priv; + + pos = sprintf(buf, "%d\n", virt_dev->tst); + + if (virt_dev->tst != DEF_TST) + pos += sprintf(&buf[pos], "%s\n", SCST_SYSFS_KEY_MARK); + + TRACE_EXIT_RES(pos); + return pos; +} + static ssize_t vdisk_sysfs_rotational_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { @@ -7848,6 +8277,46 @@ static void init_ops(vdisk_op_fn *ops, int count) return; } +static int __init vdev_check_mode_pages_path(void) +{ + int res; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39) + struct nameidata nd; +#else + struct path path; +#endif + mm_segment_t old_fs = get_fs(); + + TRACE_ENTRY(); + + set_fs(KERNEL_DS); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39) + res = path_lookup(VDEV_MODE_PAGES_DIR, 0, &nd); + if (res == 0) + scst_path_put(&nd); +#else + res = kern_path(VDEV_MODE_PAGES_DIR, 0, &path); + if (res == 0) + path_put(&path); +#endif + if (res != 0) { + PRINT_WARNING("Unable to find %s (err %d), saved mode pages " + "disabled. You should create this directory manually " + "or reinstall SCST", VDEV_MODE_PAGES_DIR, res); + vdev_saved_mode_pages_enabled = false; + goto out_setfs; + } + +out_setfs: + set_fs(old_fs); + + res = 0; /* always succeed */ + + TRACE_EXIT_RES(res); + return res; +} + static int __init init_scst_vdisk_driver(void) { int res; @@ -7856,6 +8325,10 @@ static int __init init_scst_vdisk_driver(void) init_ops(blockio_ops, ARRAY_SIZE(blockio_ops)); init_ops(nullio_ops, ARRAY_SIZE(nullio_ops)); + res = vdev_check_mode_pages_path(); + if (res != 0) + goto out; + vdisk_cmd_param_cachep = KMEM_CACHE(vdisk_cmd_params, SCST_SLAB_FLAGS|SLAB_HWCACHE_ALIGN); if (vdisk_cmd_param_cachep == NULL) { diff --git a/scst/src/scst_lib.c b/scst/src/scst_lib.c index 06735360b..9f42faca6 100644 --- a/scst/src/scst_lib.c +++ b/scst/src/scst_lib.c @@ -3676,7 +3676,7 @@ int scst_alloc_device(gfp_t gfp_mask, struct scst_device **out_dev) INIT_LIST_HEAD(&dev->dev_tgt_dev_list); INIT_LIST_HEAD(&dev->dev_acg_dev_list); dev->dev_double_ua_possible = 1; - dev->queue_alg = SCST_CONTR_MODE_QUEUE_ALG_UNRESTRICTED_REORDER; + dev->queue_alg = SCST_QUEUE_ALG_1_UNRESTRICTED_REORDER; mutex_init(&dev->dev_pr_mutex); dev->pr_generation = 0; @@ -4422,7 +4422,7 @@ static int scst_alloc_add_tgt_dev(struct scst_session *sess, INIT_LIST_HEAD(&tgt_dev->UA_list); scst_init_order_data(&tgt_dev->tgt_dev_order_data); - if (dev->tst == SCST_CONTR_MODE_SEP_TASK_SETS) + if (dev->tst == SCST_TST_1_SEP_TASK_SETS) tgt_dev->curr_order_data = &tgt_dev->tgt_dev_order_data; else tgt_dev->curr_order_data = &dev->dev_order_data; @@ -8738,7 +8738,7 @@ int scst_obtain_device_parameters(struct scst_device *dev, dev->tst = buffer[4+2] >> 5; q = buffer[4+3] >> 4; - if (q > SCST_CONTR_MODE_QUEUE_ALG_UNRESTRICTED_REORDER) { + if (q > SCST_QUEUE_ALG_1_UNRESTRICTED_REORDER) { PRINT_ERROR("Too big QUEUE ALG %x, dev %s", dev->queue_alg, dev->virt_name); } @@ -9266,6 +9266,245 @@ static void scst_free_descriptors(struct scst_cmd *cmd) return; } +/** + ** We currently have only few saved parameters and it is impossible to get + ** pointer on a bit field, so let's have a simple straightforward + ** implementation. + **/ + +#define SCST_TAS_LABEL "TAS" +#define SCST_SWP_LABEL "SWP" +#define SCST_DSENSE_LABEL "D_SENSE" +#define SCST_QUEUE_ALG_LABEL "QUEUE_ALG" + +int scst_save_global_mode_pages(const struct scst_device *dev, + uint8_t *buf, int size) +{ + int res = 0; + + TRACE_ENTRY(); + + if (dev->tas != dev->tas_default) { + res += scnprintf(&buf[res], size - res, "%s=%d\n", + SCST_TAS_LABEL, dev->tas); + if (res >= size-1) + goto out_overflow; + } + + if (dev->swp != dev->swp_default) { + res += scnprintf(&buf[res], size - res, "%s=%d\n", + SCST_SWP_LABEL, dev->swp); + if (res >= size-1) + goto out_overflow; + } + + if (dev->d_sense != dev->d_sense_default) { + res += scnprintf(&buf[res], size - res, "%s=%d\n", + SCST_DSENSE_LABEL, dev->d_sense); + if (res >= size-1) + goto out_overflow; + } + + if (dev->queue_alg != dev->queue_alg_default) { + res += scnprintf(&buf[res], size - res, "%s=%d\n", + SCST_QUEUE_ALG_LABEL, dev->queue_alg); + if (res >= size-1) + goto out_overflow; + } + +out: + TRACE_EXIT_RES(res); + return res; + +out_overflow: + PRINT_ERROR("Global mode pages buffer overflow (size %d)", size); + res = -EOVERFLOW; + goto out; +} +EXPORT_SYMBOL_GPL(scst_save_global_mode_pages); + +static int scst_restore_tas(struct scst_device *dev, unsigned int val) +{ + int res; + + TRACE_ENTRY(); + + if (val > 1) { + PRINT_ERROR("Invalid value %d for parameter %s (device %s)", + val, SCST_TAS_LABEL, dev->virt_name); + res = -EINVAL; + goto out; + } + + dev->tas = val; + dev->tas_saved = val; + + PRINT_INFO("%s restored to %d for device %s", SCST_TAS_LABEL, + dev->tas, dev->virt_name); + + res = 0; + +out: + TRACE_EXIT_RES(res); + return res; +} + +static int scst_restore_swp(struct scst_device *dev, unsigned int val) +{ + int res; + + TRACE_ENTRY(); + + if (val > 1) { + PRINT_ERROR("Invalid value %d for parameter %s (device %s)", + val, SCST_SWP_LABEL, dev->virt_name); + res = -EINVAL; + goto out; + } + + dev->swp = val; + dev->swp_saved = val; + + PRINT_INFO("%s restored to %d for device %s", SCST_SWP_LABEL, + dev->swp, dev->virt_name); + + res = 0; + +out: + TRACE_EXIT_RES(res); + return res; +} + +static int scst_restore_dsense(struct scst_device *dev, unsigned int val) +{ + int res; + + TRACE_ENTRY(); + + if (val > 1) { + PRINT_ERROR("Invalid value %d for parameter %s (device %s)", + val, SCST_DSENSE_LABEL, dev->virt_name); + res = -EINVAL; + goto out; + } + + dev->d_sense = val; + dev->d_sense_saved = val; + + PRINT_INFO("%s restored to %d for device %s", SCST_DSENSE_LABEL, + dev->d_sense, dev->virt_name); + + res = 0; + +out: + TRACE_EXIT_RES(res); + return res; +} + +static int scst_restore_queue_alg(struct scst_device *dev, unsigned int val) +{ + int res; + + TRACE_ENTRY(); + + if ((val != SCST_QUEUE_ALG_0_RESTRICTED_REORDER) && + (val != SCST_QUEUE_ALG_1_UNRESTRICTED_REORDER)) { + PRINT_ERROR("Invalid value %d for parameter %s (device %s)", + val, SCST_QUEUE_ALG_LABEL, dev->virt_name); + res = -EINVAL; + goto out; + } + + dev->queue_alg = val; + dev->queue_alg_saved = val; + + PRINT_INFO("%s restored to %d for device %s", SCST_QUEUE_ALG_LABEL, + dev->queue_alg, dev->virt_name); + + res = 0; + +out: + TRACE_EXIT_RES(res); + return res; +} + +/* Params are NULL-terminated */ +int scst_restore_global_mode_pages(struct scst_device *dev, char *params, + char **last_param) +{ + int res; + char *param, *p, *pp; + unsigned long val; + + TRACE_ENTRY(); + + while (1) { + param = scst_get_next_token_str(¶ms); + if (param == NULL) + break; + + p = scst_get_next_lexem(¶m); + if (*p == '\0') + break; + + pp = scst_get_next_lexem(¶m); + if (*pp == '\0') + goto out_need_param; + + if (scst_get_next_lexem(¶m)[0] != '\0') + goto out_too_many; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + res = kstrtoul(pp, 0, &val); +#else + res = strict_strtoul(pp, 0, &val); +#endif + if (res != 0) + goto out_strtoul_failed; + + if (strcasecmp(SCST_TAS_LABEL, p) == 0) + res = scst_restore_tas(dev, val); + else if (strcasecmp(SCST_SWP_LABEL, p) == 0) + res = scst_restore_swp(dev, val); + else if (strcasecmp(SCST_DSENSE_LABEL, p) == 0) + res = scst_restore_dsense(dev, val); + else if (strcasecmp(SCST_QUEUE_ALG_LABEL, p) == 0) + res = scst_restore_queue_alg(dev, val); + else { + TRACE_DBG("Unknown parameter %s", p); + scst_restore_token_str(p, param); + *last_param = p; + goto out; + } + if (res != 0) + goto out; + } + + *last_param = NULL; + res = 0; + +out: + TRACE_EXIT_RES(res); + return res; + +out_strtoul_failed: + PRINT_ERROR("strtoul() for %s failed: %d (device %s)", pp, res, + dev->virt_name); + goto out; + +out_need_param: + PRINT_ERROR("Parameter %s value missed for device %s", p, dev->virt_name); + res = -EINVAL; + goto out; + +out_too_many: + PRINT_ERROR("Too many parameter's %s values (device %s)", p, dev->virt_name); + res = -EINVAL; + goto out; +} +EXPORT_SYMBOL_GPL(scst_restore_global_mode_pages); + + /* Abstract vfs_unlink() for different kernel versions (as possible) */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39) void scst_vfs_unlink_and_put(struct nameidata *nd) @@ -9450,6 +9689,195 @@ int scst_remove_file(const char *name) TRACE_EXIT_RES(res); return res; } +EXPORT_SYMBOL_GPL(scst_remove_file); + +/* Returns 0 on success, error code otherwise */ +int scst_write_file_transactional(const char *name, const char *name1, + const char *signature, int signature_len, const uint8_t *buf, int size) +{ + int res; + struct file *file; + mm_segment_t old_fs = get_fs(); + loff_t pos = 0; + char n = '\n'; + + TRACE_ENTRY(); + + res = scst_copy_file(name, name1); + if ((res != 0) && (res != -ENOENT)) + goto out; + + set_fs(KERNEL_DS); + + file = filp_open(name, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (IS_ERR(file)) { + res = PTR_ERR(file); + PRINT_ERROR("Unable to (re)create file '%s' - error %d", + name, res); + goto out_set_fs; + } + + TRACE_DBG("Writing file '%s'", name); + + pos = signature_len+1; + + res = vfs_write(file, (void __force __user *)buf, size, &pos); + if (res != size) + goto write_error; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) + res = scst_vfs_fsync(file, 0, pos); +#elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) + res = vfs_fsync(file, file->f_path.dentry, 1); +#else + res = vfs_fsync(file, 1); +#endif + if (res != 0) { + PRINT_ERROR("fsync() of file %s failed: %d", name, res); + goto write_error_close; + } + + pos = 0; + res = vfs_write(file, (void __force __user *)signature, signature_len, &pos); + if (res != signature_len) + goto write_error; + + res = vfs_write(file, (void __force __user *)&n, sizeof(n), &pos); + if (res != sizeof(n)) + goto write_error; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) + res = scst_vfs_fsync(file, 0, sizeof(signature)); +#elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) + res = vfs_fsync(file, file->f_path.dentry, 1); +#else + res = vfs_fsync(file, 1); +#endif + if (res != 0) { + PRINT_ERROR("fsync() of file %s failed: %d", name, res); + goto write_error_close; + } + + res = 0; + + filp_close(file, NULL); + +out_set_fs: + set_fs(old_fs); + + if (res == 0) + scst_remove_file(name1); + else + scst_remove_file(name); + +out: + TRACE_EXIT_RES(res); + return res; + +write_error: + PRINT_ERROR("Error writing to '%s' - error %d", name, res); + +write_error_close: + filp_close(file, NULL); + if (res > 0) + res = -EIO; + goto out_set_fs; +} +EXPORT_SYMBOL_GPL(scst_write_file_transactional); + +static int __scst_read_file_transactional(const char *file_name, + const char *signature, int signature_len, uint8_t *buf, int size) +{ + int res; + struct file *file = NULL; + struct inode *inode; + loff_t file_size, pos; + mm_segment_t old_fs; + + TRACE_ENTRY(); + + old_fs = get_fs(); + set_fs(KERNEL_DS); + + TRACE_DBG("Loading file '%s'", file_name); + + file = filp_open(file_name, O_RDONLY, 0); + if (IS_ERR(file)) { + res = PTR_ERR(file); + TRACE_DBG("Unable to open file '%s' - error %d", file_name, res); + goto out; + } + + inode = file->f_dentry->d_inode; + + if (S_ISREG(inode->i_mode)) + /* Nothing to do */; + else if (S_ISBLK(inode->i_mode)) + inode = inode->i_bdev->bd_inode; + else { + PRINT_ERROR("Invalid file mode 0x%x", inode->i_mode); + res = -EINVAL; + goto out_close; + } + + file_size = inode->i_size; + + if (file_size > size) { + PRINT_ERROR("Supplied buffer (%d) too small (need %d)", size, + (int)file_size); + res = -EOVERFLOW; + goto out_close; + } + + pos = 0; + res = vfs_read(file, (void __force __user *)buf, file_size, &pos); + if (res != file_size) { + PRINT_ERROR("Unable to read file '%s' - error %d", file_name, res); + if (res > 0) + res = -EIO; + goto out_close; + } + + if (memcmp(buf, signature, signature_len) != 0) { + res = -EINVAL; + PRINT_ERROR("Invalid signature in file %s", file_name); + goto out_close; + } + +out_close: + filp_close(file, NULL); + +out: + set_fs(old_fs); + + TRACE_EXIT_RES(res); + return res; +} + +/* + * Returns read data size on success, error code otherwise. The first + * signature_len+1 bytes of the read data contain signature, so should be + * skipped. + */ +int scst_read_file_transactional(const char *name, const char *name1, + const char *signature, int signature_len, uint8_t *buf, int size) +{ + int res; + + TRACE_ENTRY(); + + res = __scst_read_file_transactional(name, signature, signature_len, buf, size); + if (res <= 0) + res = __scst_read_file_transactional(name1, signature, + signature_len, buf, size); + + if (res > 0) + TRACE_BUFFER("Read data", buf, res); + + TRACE_EXIT_RES(res); + return res; +} +EXPORT_SYMBOL_GPL(scst_read_file_transactional); static void __init scst_scsi_op_list_init(void) { diff --git a/scst/src/scst_priv.h b/scst/src/scst_priv.h index f56d185be..fb7c92fbb 100644 --- a/scst/src/scst_priv.h +++ b/scst/src/scst_priv.h @@ -779,7 +779,6 @@ int scst_vfs_fsync(struct file *file, loff_t loff, loff_t len); #endif int scst_copy_file(const char *src, const char *dest); -int scst_remove_file(const char *name); #ifdef CONFIG_SCST_DEBUG_TM extern void tm_dbg_check_released_cmds(void); diff --git a/scst/src/scst_targ.c b/scst/src/scst_targ.c index e0d50b10f..b68e646b4 100644 --- a/scst/src/scst_targ.c +++ b/scst/src/scst_targ.c @@ -570,7 +570,7 @@ int scst_pre_parse(struct scst_cmd *cmd) #else cmd->inc_expected_sn_on_done = devt->exec_sync || (!dev->has_own_order_mgmt && - (dev->queue_alg == SCST_CONTR_MODE_QUEUE_ALG_RESTRICTED_REORDER || + (dev->queue_alg == SCST_QUEUE_ALG_0_RESTRICTED_REORDER || cmd->queue_type == SCST_CMD_QUEUE_ORDERED)); #endif @@ -2358,7 +2358,7 @@ static int scst_reserve_local(struct scst_cmd *cmd) /* * There's no need to block this device, even for - * SCST_CONTR_MODE_ONE_TASK_SET, or anyhow else protect reservations + * SCST_TST_0_SINGLE_TASK_SET, or anyhow else protect reservations * changes, because: * * 1. The reservation changes are (rather) atomic, i.e., in contrast @@ -2367,7 +2367,7 @@ static int scst_reserve_local(struct scst_cmd *cmd) * * 2. It's a duty of initiators to ensure order of regular commands * around the reservation command either by ORDERED attribute, or by - * queue draining, or etc. For case of SCST_CONTR_MODE_ONE_TASK_SET + * queue draining, or etc. For case of SCST_TST_0_SINGLE_TASK_SET * there are no target drivers which can ensure even for ORDERED * commands order of their delivery, so, because initiators know * it, also there's no point to do any extra protection actions. @@ -4177,7 +4177,7 @@ static void scst_cmd_set_sn(struct scst_cmd *cmd) cmd->queue_type = SCST_CMD_QUEUE_ORDERED; #endif - if (cmd->dev->queue_alg == SCST_CONTR_MODE_QUEUE_ALG_RESTRICTED_REORDER) { + if (cmd->dev->queue_alg == SCST_QUEUE_ALG_0_RESTRICTED_REORDER) { if (likely(cmd->queue_type != SCST_CMD_QUEUE_HEAD_OF_QUEUE)) { /* * Not the best way, but good enough until there is a @@ -6183,8 +6183,7 @@ static int scst_mgmt_cmd_exec(struct scst_mgmt_cmd *mcmd) break; case SCST_CLEAR_TASK_SET: - if (mcmd->mcmd_tgt_dev->dev->tst == - SCST_CONTR_MODE_SEP_TASK_SETS) + if (mcmd->mcmd_tgt_dev->dev->tst == SCST_TST_1_SEP_TASK_SETS) res = scst_abort_task_set(mcmd); else res = scst_clear_task_set(mcmd); diff --git a/usr/fileio/fileio.c b/usr/fileio/fileio.c index 7a30acebf..43b314fd9 100644 --- a/usr/fileio/fileio.c +++ b/usr/fileio/fileio.c @@ -383,9 +383,9 @@ int start(int argc, char **argv) desc.opt.on_free_cmd_type = on_free_cmd_type; desc.opt.memory_reuse_type = memory_reuse_type; - desc.opt.tst = SCST_CONTR_MODE_SEP_TASK_SETS; - desc.opt.queue_alg = SCST_CONTR_MODE_QUEUE_ALG_UNRESTRICTED_REORDER; - desc.opt.d_sense = SCST_CONTR_MODE_FIXED_SENSE; + desc.opt.tst = SCST_TST_1_SEP_TASK_SETS; + desc.opt.queue_alg = SCST_QUEUE_ALG_1_UNRESTRICTED_REORDER; + desc.opt.d_sense = SCST_D_SENSE_0_FIXED_SENSE; res = ioctl(devs[i].scst_usr_fd, SCST_USER_REGISTER_DEVICE, &desc); if (res != 0) {