Saved mode pages added

git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@5479 d57e44dd-8a1f-0410-8b47-8ef2f437770f
This commit is contained in:
Vladislav Bolkhovitin
2014-04-25 02:03:04 +00:00
parent 13ec92196f
commit 160016b9e8
11 changed files with 1053 additions and 90 deletions

View File

@@ -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,

View File

@@ -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,

View File

@@ -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 */

View File

@@ -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

View File

@@ -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

View File

@@ -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;

View File

@@ -38,6 +38,7 @@
#include <linux/kthread.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/namei.h>
#ifndef INSIDE_KERNEL_TREE
#include <linux/version.h>
#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(&params);
if (param == NULL)
break;
p = scst_get_next_lexem(&param);
if (*p == '\0')
break;
pp = scst_get_next_lexem(&param);
if (*pp == '\0')
goto out_need_param;
if (scst_get_next_lexem(&param)[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],
&params);
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) {

View File

@@ -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(&params);
if (param == NULL)
break;
p = scst_get_next_lexem(&param);
if (*p == '\0')
break;
pp = scst_get_next_lexem(&param);
if (*pp == '\0')
goto out_need_param;
if (scst_get_next_lexem(&param)[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)
{

View File

@@ -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);

View File

@@ -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);

View File

@@ -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) {