From 2475def30d2acacc52b51dd862662038ade29d6d Mon Sep 17 00:00:00 2001 From: Vladislav Bolkhovitin Date: Tue, 20 Dec 2016 06:14:55 +0000 Subject: [PATCH] scst: add max_tgt_dev_commands sysfs attribute This per-device attribute allows to control maximum number of SCSI commands any session to this device can have in flight. It makes currently hardcoded constant be run time configurable. git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@7073 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- scst/README | 3 ++ scst/README_in-tree | 3 ++ scst/include/scst.h | 14 ++++++++++ scst/src/scst_lib.c | 51 +++++++++++++++++++++++++++++++++- scst/src/scst_main.c | 4 +++ scst/src/scst_priv.h | 4 ++- scst/src/scst_sysfs.c | 64 +++++++++++++++++++++++++++++++++++++++++++ scst/src/scst_targ.c | 2 +- 8 files changed, 142 insertions(+), 3 deletions(-) diff --git a/scst/README b/scst/README index acf646832..8e10b83e5 100644 --- a/scst/README +++ b/scst/README @@ -563,6 +563,9 @@ SCST dev handlers can have the following common entries: - type - SCSI type of this device + - max_tgt_dev_commands - maximum number of SCSI commands any session to + this device can have in flight. + Attribute "block" allows to temporary block and unblock this device. "Blocking" means that no new commands for this device will go into the execution stage, but instead will be suspended just before it. The diff --git a/scst/README_in-tree b/scst/README_in-tree index 66b6e5ba6..329ce0fdd 100644 --- a/scst/README_in-tree +++ b/scst/README_in-tree @@ -427,6 +427,9 @@ SCST dev handlers can have the following common entries: - type - SCSI type of this device + - max_tgt_dev_commands - maximum number of SCSI commands any session to + this device can have in flight. + Attribute "block" allows to temporary block and unblock this device. "Blocking" means that no new commands for this device will go into the execution stage, but instead will be suspended just before it. The diff --git a/scst/include/scst.h b/scst/include/scst.h index 3c7fd2716..2dec04a16 100644 --- a/scst/include/scst.h +++ b/scst/include/scst.h @@ -1667,6 +1667,13 @@ struct scst_dev_type { */ int threads_num; + /* + * Maximum count of uncompleted commands that an initiator could + * queue on any device of this handler by default. Then it will start + * getting TASK QUEUE FULL status. + */ + int max_tgt_dev_commands; + /* Threads pool type. Valid only if threads_num > 0. */ enum scst_dev_type_threads_pool_type threads_pool_type; @@ -2909,6 +2916,13 @@ struct scst_device { atomic_t dev_cmd_count; #endif + /* + * Maximum count of uncompleted commands that an initiator could + * queue on this device. Then it will start getting TASK QUEUE FULL + * status. + */ + int max_tgt_dev_commands; + /* * How many times device was blocked for new cmds execution. * Protected by dev_lock. diff --git a/scst/src/scst_lib.c b/scst/src/scst_lib.c index 83eae90a3..293ff65f4 100644 --- a/scst/src/scst_lib.c +++ b/scst/src/scst_lib.c @@ -13657,7 +13657,56 @@ out: */ int scst_get_max_lun_commands(struct scst_session *sess, uint64_t lun) { - return SCST_MAX_TGT_DEV_COMMANDS; + const int init_res = 0xFFFFFF; + int res = init_res, i; + + TRACE_ENTRY(); + + mutex_lock(&scst_mutex); + + if (sess == NULL) { + struct scst_device *dev; + list_for_each_entry(dev, &scst_dev_list, dev_list_entry) { + if (dev->handler == &scst_null_devtype) + continue; + TRACE_DBG("dev %s, max_tgt_dev_commands %d (res %d)", + dev->virt_name, dev->max_tgt_dev_commands, res); + if (res > dev->max_tgt_dev_commands) + res = dev->max_tgt_dev_commands; + } + goto out_unlock; + } + + if (lun != NO_SUCH_LUN) { + struct list_head *head = + &sess->sess_tgt_dev_list[SESS_TGT_DEV_LIST_HASH_FN(lun)]; + struct scst_tgt_dev *tgt_dev; + list_for_each_entry(tgt_dev, head, sess_tgt_dev_list_entry) { + if (tgt_dev->lun == lun) { + res = tgt_dev->dev->max_tgt_dev_commands; + TRACE_DBG("tgt_dev %p, dev %s, max_tgt_dev_commands " + "%d (res %d)", tgt_dev, tgt_dev->dev->virt_name, + tgt_dev->dev->max_tgt_dev_commands, res); + break; + } + } + goto out_unlock; + } + + for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) { + struct list_head *head = &sess->sess_tgt_dev_list[i]; + struct scst_tgt_dev *tgt_dev; + list_for_each_entry(tgt_dev, head, sess_tgt_dev_list_entry) { + if (res > tgt_dev->dev->max_tgt_dev_commands) + res = tgt_dev->dev->max_tgt_dev_commands; + } + } + +out_unlock: + mutex_unlock(&scst_mutex); + + TRACE_EXIT_RES(res); + return res; } EXPORT_SYMBOL(scst_get_max_lun_commands); diff --git a/scst/src/scst_main.c b/scst/src/scst_main.c index 7db8f14dc..1047f6dc3 100644 --- a/scst/src/scst_main.c +++ b/scst/src/scst_main.c @@ -1367,6 +1367,9 @@ static int scst_dev_handler_check(struct scst_dev_type *dev_handler) if (dev_handler->dev_done == NULL) dev_handler->dev_done_atomic = 1; + if (dev_handler->max_tgt_dev_commands == 0) + dev_handler->max_tgt_dev_commands = SCST_MAX_TGT_DEV_COMMANDS; + out: TRACE_EXIT_RES(res); return res; @@ -2154,6 +2157,7 @@ assign: dev->handler = handler; dev->threads_num = handler->threads_num; dev->threads_pool_type = handler->threads_pool_type; + dev->max_tgt_dev_commands = handler->max_tgt_dev_commands; dev->max_write_same_len = 256 * 1024 * 1024; /* 256 MB */ if (handler->attach) { diff --git a/scst/src/scst_priv.h b/scst/src/scst_priv.h index 6f31b3135..dabbbbf2f 100644 --- a/scst/src/scst_priv.h +++ b/scst/src/scst_priv.h @@ -97,7 +97,9 @@ extern unsigned long scst_trace_flag; /** ** Maximum count of uncompleted commands that an initiator could - ** queue on any device. Then it will start getting TASK QUEUE FULL status. + ** queue on any device by default, i.e. its dev handler doesn't have + ** max_tgt_dev_commands defined. Then it will start getting TASK QUEUE FULL + ** status. **/ #define SCST_MAX_TGT_DEV_COMMANDS 64 diff --git a/scst/src/scst_sysfs.c b/scst/src/scst_sysfs.c index cbc36ab5f..a41157980 100644 --- a/scst/src/scst_sysfs.c +++ b/scst/src/scst_sysfs.c @@ -3474,6 +3474,69 @@ static struct kobj_attribute dev_threads_pool_type_attr = scst_dev_sysfs_threads_pool_type_show, scst_dev_sysfs_threads_pool_type_store); +static ssize_t scst_dev_sysfs_max_tgt_dev_commands_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + int pos = 0; + struct scst_device *dev; + + TRACE_ENTRY(); + + dev = container_of(kobj, struct scst_device, dev_kobj); + + pos = sprintf(buf, "%d\n%s", dev->max_tgt_dev_commands, + (dev->max_tgt_dev_commands != dev->handler->max_tgt_dev_commands) ? + SCST_SYSFS_KEY_MARK "\n" : ""); + + TRACE_EXIT_RES(pos); + return pos; +} + +static ssize_t scst_dev_sysfs_max_tgt_dev_commands_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + int res; + struct scst_device *dev; + long newtn; + + TRACE_ENTRY(); + + dev = container_of(kobj, struct scst_device, dev_kobj); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + res = kstrtol(buf, 0, &newtn); +#else + res = strict_strtol(buf, 0, &newtn); +#endif + if (res != 0) { + PRINT_ERROR("strtol() for %s failed: %d ", buf, res); + goto out; + } + if (newtn < 0) { + PRINT_ERROR("Illegal max tgt dev value %ld", newtn); + res = -EINVAL; + goto out; + } + + if (dev->max_tgt_dev_commands != newtn) { + PRINT_INFO("Setting new queue depth %ld for device %s (old %d)", + newtn, dev->virt_name, dev->max_tgt_dev_commands); + dev->max_tgt_dev_commands = newtn; + } + +out: + if (res == 0) + res = count; + + TRACE_EXIT_RES(res); + return res; +} + +static struct kobj_attribute dev_max_tgt_dev_commands_attr = + __ATTR(max_tgt_dev_commands, S_IRUGO | S_IWUSR, + scst_dev_sysfs_max_tgt_dev_commands_show, + scst_dev_sysfs_max_tgt_dev_commands_store); + static ssize_t scst_dev_block_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { @@ -3604,6 +3667,7 @@ static struct kobj_attribute dev_block_attr = static struct attribute *scst_dev_attrs[] = { &dev_type_attr.attr, + &dev_max_tgt_dev_commands_attr.attr, &dev_block_attr.attr, NULL, }; diff --git a/scst/src/scst_targ.c b/scst/src/scst_targ.c index ee376680e..c07ce377b 100644 --- a/scst/src/scst_targ.c +++ b/scst/src/scst_targ.c @@ -5073,7 +5073,7 @@ static int __scst_init_cmd(struct scst_cmd *cmd) cmd->state = SCST_CMD_STATE_PARSE; cnt = atomic_inc_return(&cmd->tgt_dev->tgt_dev_cmd_count); - if (unlikely(cnt > SCST_MAX_TGT_DEV_COMMANDS)) { + if (unlikely(cnt > cmd->dev->max_tgt_dev_commands)) { TRACE(TRACE_FLOW_CONTROL, "Too many pending commands (%d) in " "session, returning BUSY to initiator \"%s\"",