Merge branch 'svn-trunk'

This commit is contained in:
Bart Van Assche
2020-02-23 15:07:26 -08:00
14 changed files with 448 additions and 355 deletions
+5
View File
@@ -1,3 +1,8 @@
Summary of changes between versions 3.4 and 3.5
-----------------------------------------------
- Added the forward_src and forward_dst sysfs attributes. Removed
CONFIG_SCST_FORWARD_MODE_PASS_THROUGH.
Summary of changes between versions 3.3 and 3.4
-----------------------------------------------
- I/O keeps running at full speed if a LUN is added or removed and also if
+18 -17
View File
@@ -343,16 +343,6 @@ in/out in Makefile and scst.h:
functionality is working only if dif_mode doesn't contain dev_store
and dif_type is 1.
- CONFIG_SCST_FORWARD_MODE_PASS_THROUGH - if defined, the pass-through
subsystem starts working in the forwarding mode, where reservation
commands processed locally and not passed to the backend SCSI device,
while COMPARE AND WRITE, EXTENDED COPY and RECEIVE COPY RESULTS
commands, which normally processed locally by the SCST core, not
processed locally, but passed to the backend device. Intended to be
used to implement NON-OPTIMIZED ALUA state together with "forwarding"
target attribute on the remote node. See below for more details.
Disabled by default for safety.
- CONFIG_SCST_NO_TOTAL_MEM_CHECKS - disables checks of allocated
memory, see scst_max_cmd_mem below. Allows to avoid 2 global
variables on the fast path, hence get better multi-queue performance.
@@ -807,9 +797,19 @@ Every target should have at least the following entries:
until rel_tgt_id becomes unique. This attribute initialized unique by
SCST by default.
- forwarding - if set this target is forwarding target, i.e. does not check
any local SCSI events (reservations, etc.). Those event supposed to
be checked on the another, requester's side.
- forward_src - if set this target port is a forwarding source. This means
that commands like COMPARE AND WRITE, EXTENDED COPY and RECEIVE COPY
RESULTS are submitted to the SCSI device instead of being handled inside
the SCST core. PERSISTENT RESERVE IN and OUT commands are processed by the
SCST core, whether or not this mode is enabled. The name 'forwarding_src'
refers to the use case where SCSI passthrough is used to send SCSI commands
to another H.A. node.
- forward_dst - if set this target port is a forwarding destination. This means
that it does not check any local SCSI events (reservations, etc.). Those
event are supposed to be checked at the forwarding source side.
- forwarding - obsolete synonym for forward_dst.
- *count*, e.g. read_io_count_kb, - statistics about executed
commands and transferred data. Those attributes have speaking names
@@ -1788,7 +1788,7 @@ attribute.
closes open handles on all affected SCST devices and after transition
reopens them, if the new state is active or nonoptimized. Alternatively,
set "bind_alua_state=0" for SCST BLOCKIO devices and ALUA state changes
will not open/close the backing block device, the user will neeed to handle
will not open/close the backing block device, the user will need to handle
this manually or via a cluster RA in an HA setup.
Thus, the recommended implicit ALUA state change procedure for primary
@@ -1984,13 +1984,13 @@ The link between block devices I and J stands for synchronous replication.
Such a setup can be configured as follows:
1. Build SCST with CONFIG_SCST_FORWARD_MODE_PASS_THROUGH enabled in scst.h
1. Build SCST.
2. Setup on active node internal redirect target, which is going to
accept redirected commands from the passive node. It must be visible
only to the passive node.
3. Set "forwarding" attribute for this target to 1. This is necessary to
3. Set "forward_dst" attribute for this target to 1. This is necessary to
correctly handle PRs.
4. Export through this target the SAME backend SCST device as being
@@ -2005,7 +2005,8 @@ device on the passive side pointing to the active node.
*pass-through* handler (scst_disk). Pass-though is needed to redirect
non-block commands as well: ATS, XCOPY, etc.
7. Set ALUA state to this target as "nonoptimized".
7. Set ALUA state to this target as "nonoptimized". Set the forward_src
attribute to one.
That's it on the normal path. Now the initiator(s) would see 2 paths:
OPTIMIZED going to the active node and NON-OPTIMIZED going to the
+14
View File
@@ -674,6 +674,20 @@ static inline int __must_check kstrtol(const char *s, unsigned int base,
{
return strict_strtol(s, base, res);
}
static inline int __must_check kstrtoint(const char *s, unsigned int base,
int *result)
{
long val;
int ret = strict_strtol(s, base, &val);
if (ret)
return ret;
*result = val;
if (*result != val)
return -EINVAL;
return 0;
}
#endif
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 17, 0)
+23 -7
View File
@@ -28,7 +28,6 @@
/** See README for description of those conditional defines **/
#define CONFIG_SCST_DIF_INJECT_CORRUPTED_TAGS
/* #define CONFIG_SCST_FORWARD_MODE_PASS_THROUGH */
#define CONFIG_SCST_NO_TOTAL_MEM_CHECKS
#include <linux/types.h>
@@ -479,8 +478,8 @@ enum scst_exec_res {
/* Cache of acg->acg_black_hole_type */
#define SCST_TGT_DEV_BLACK_HOLE 1
/* Cache of tgt->tgt_forwarding */
#define SCST_TGT_DEV_FORWARDING 5
/* Cache of tgt->tgt_forward_dst */
#define SCST_TGT_DEV_FORWARD_DST 5
/*************************************************************
** I/O grouping types. Changing them don't forget to change
@@ -1688,11 +1687,23 @@ struct scst_tgt {
struct list_head tgt_acg_list; /* target ACG groups */
/*
* Set, if this target is forwarding target, i.e. does not check
* any local SCSI events (reservations, etc.). Those event supposed
* to be checked on the another, requester's side.
* Set if and only if this target port is a forwarding source. If
* enabled, the dev_disk handler submits COMPARE AND WRITE, EXTENDED
* COPY and RECEIVE COPY RESULTS commands to the SCSI device instead
* of handling these inside the SCST core. PERSISTENT RESERVE IN and
* OUT commands are processed by the SCST core, whether or not this
* mode is enabled. The name 'forwarding_src' refers to the use case
* where SCSI passthrough is used to send SCSI commands to another
* H.A. node.
*/
unsigned tgt_forwarding:1;
unsigned tgt_forward_src:1;
/*
* Set, if this target is forwarding destination, i.e. does not check
* any local SCSI events (reservations, etc.). Those events are
* supposed to be checked at the side of the forwarding source.
*/
unsigned tgt_forward_dst:1;
/* Per target analog of the corresponding driver's fields */
unsigned tgt_dif_supported:1;
@@ -2862,6 +2873,11 @@ struct scst_device {
*/
int dev_scsi_atomic_cmd_active;
/*
* Whether or not explicit ALUA has been enabled. Protected by dev_lock.
*/
unsigned int expl_alua:1;
/*
* List of all being executed on the dev commands.
* Protected by dev_lock.
+4 -6
View File
@@ -144,6 +144,7 @@ static void disk_detach(struct scst_device *dev)
static int disk_parse(struct scst_cmd *cmd)
{
struct scst_tgt *tgt = cmd->tgt;
int res = SCST_CMD_STATE_DEFAULT, rc;
rc = scst_sbc_generic_parse(cmd);
@@ -152,8 +153,7 @@ static int disk_parse(struct scst_cmd *cmd)
goto out;
}
#ifdef CONFIG_SCST_FORWARD_MODE_PASS_THROUGH
if (unlikely(cmd->op_flags & SCST_LOCAL_CMD)) {
if (unlikely(tgt->tgt_forward_src && cmd->op_flags & SCST_LOCAL_CMD)) {
switch (cmd->cdb[0]) {
case COMPARE_AND_WRITE:
case EXTENDED_COPY:
@@ -164,7 +164,6 @@ static int disk_parse(struct scst_cmd *cmd)
break;
}
}
#endif
cmd->retries = SCST_PASSTHROUGH_RETRIES;
out:
@@ -286,6 +285,7 @@ out_complete:
/* Executes command and split CDB, if necessary */
static enum scst_exec_res disk_exec(struct scst_cmd *cmd)
{
struct scst_tgt *tgt = cmd->tgt;
enum scst_exec_res res;
int rc;
struct disk_work work;
@@ -299,8 +299,7 @@ static enum scst_exec_res disk_exec(struct scst_cmd *cmd)
TRACE_ENTRY();
#ifdef CONFIG_SCST_FORWARD_MODE_PASS_THROUGH
if (unlikely(cmd->op_flags & SCST_LOCAL_CMD)) {
if (unlikely(tgt->tgt_forward_src && cmd->op_flags & SCST_LOCAL_CMD)) {
switch (cmd->cdb[0]) {
case RESERVE:
case RESERVE_10:
@@ -315,7 +314,6 @@ static enum scst_exec_res disk_exec(struct scst_cmd *cmd)
break;
}
}
#endif
/*
* For PC requests we are going to submit max_hw_sectors used instead
+28 -132
View File
@@ -184,7 +184,6 @@ struct scst_vdisk_dev {
unsigned int tst:3;
unsigned int format_active:1;
unsigned int discard_zeroes_data:1;
unsigned int expl_alua:1;
unsigned int reexam_pending:1;
unsigned int size_key:1;
unsigned int opt_trans_len_set:1;
@@ -2554,7 +2553,6 @@ static int vdisk_get_supported_opcodes(struct scst_cmd *cmd,
int *out_supp_opcodes_cnt)
{
struct scst_device *dev = cmd->dev;
struct scst_vdisk_dev *virt_dev = dev->dh_priv;
if (cmd->dev->dev_dif_type != 2) {
*out_supp_opcodes = vdisk_opcode_descriptors;
@@ -2563,7 +2561,7 @@ static int vdisk_get_supported_opcodes(struct scst_cmd *cmd,
*out_supp_opcodes = vdisk_opcode_descriptors_type2;
*out_supp_opcodes_cnt = ARRAY_SIZE(vdisk_opcode_descriptors_type2);
}
if (!virt_dev->expl_alua) {
if (!dev->expl_alua) {
(*out_supp_opcodes_cnt)--;
sBUG_ON((*out_supp_opcodes)[*out_supp_opcodes_cnt]->od_serv_action != MO_SET_TARGET_PGS);
}
@@ -3981,6 +3979,7 @@ static int vdisk_tp_vpd(uint8_t *buf, struct scst_cmd *cmd,
static int vdisk_inq(uint8_t *buf, struct scst_cmd *cmd,
struct scst_vdisk_dev *virt_dev)
{
struct scst_device *dev = cmd->dev;
int num;
if (virt_dev->removable)
@@ -3993,7 +3992,7 @@ static int vdisk_inq(uint8_t *buf, struct scst_cmd *cmd,
buf[5] |= 1; /* PROTECT */
if (scst_alua_configured(cmd->dev)) {
buf[5] |= SCST_INQ_TPGS_MODE_IMPLICIT;
if (virt_dev->expl_alua)
if (dev->expl_alua)
buf[5] |= SCST_INQ_TPGS_MODE_EXPLICIT;
}
buf[5] |= 8; /* 3PC */
@@ -5107,81 +5106,6 @@ static enum compl_status_e vdisk_exec_get_lba_status(struct vdisk_cmd_params *p)
return CMD_SUCCEEDED;
}
/* SPC-4 REPORT TARGET PORT GROUPS command */
static enum compl_status_e vdisk_exec_report_tpgs(struct vdisk_cmd_params *p)
{
struct scst_cmd *cmd = p->cmd;
struct scst_device *dev;
uint8_t *address;
void *buf;
int32_t buf_len;
uint32_t data_length, length;
uint8_t data_format;
int res;
TRACE_ENTRY();
buf_len = scst_get_buf_full_sense(cmd, &address);
if (buf_len <= 0)
goto out;
dev = cmd->dev;
data_format = cmd->cdb[1] >> 5;
res = scst_tg_get_group_info(&buf, &data_length, dev, data_format);
if (res == -ENOMEM) {
scst_set_busy(cmd);
goto out_put;
} else if (res < 0) {
scst_set_cmd_error(cmd,
SCST_LOAD_SENSE(scst_sense_invalid_field_in_cdb));
goto out_put;
}
length = min_t(uint32_t, data_length, buf_len);
memcpy(address, buf, length);
kfree(buf);
if (length < cmd->resp_data_len)
scst_set_resp_data_len(cmd, length);
out_put:
scst_put_buf_full(cmd, address);
out:
TRACE_EXIT();
return CMD_SUCCEEDED;
}
/* SPC-4 SET TARGET PORT GROUPS command */
static enum compl_status_e vdisk_exec_set_tpgs(struct vdisk_cmd_params *p)
{
struct scst_cmd *cmd = p->cmd;
struct scst_device *dev = cmd->dev;
struct scst_vdisk_dev *virt_dev = dev->dh_priv;
int res = CMD_SUCCEEDED, rc;
TRACE_ENTRY();
if (!virt_dev->expl_alua) {
PRINT_ERROR("SET TARGET PORT GROUPS: not explicit ALUA mode "
"(dev %s)", dev->virt_name);
/* Invalid opcode, i.e. SA field */
scst_set_invalid_field_in_cdb(cmd, 1,
0 | SCST_INVAL_FIELD_BIT_OFFS_VALID);
goto out;
}
rc = scst_tg_set_group_info(cmd);
if (rc == 0)
res = RUNNING_ASYNC;
else
scst_stpg_del_unblock_next(cmd);
out:
TRACE_EXIT_RES(res);
return res;
}
static enum compl_status_e vdisk_exec_sai_16(struct vdisk_cmd_params *p)
{
switch (p->cmd->cdb[1] & 0x1f) {
@@ -5198,25 +5122,15 @@ static enum compl_status_e vdisk_exec_sai_16(struct vdisk_cmd_params *p)
static enum compl_status_e vdisk_exec_maintenance_in(struct vdisk_cmd_params *p)
{
switch (p->cmd->cdb[1] & 0x1f) {
case MI_REPORT_TARGET_PGS:
vdisk_exec_report_tpgs(p);
return CMD_SUCCEEDED;
}
scst_set_invalid_field_in_cdb(p->cmd, 1,
0 | SCST_INVAL_FIELD_BIT_OFFS_VALID);
return CMD_SUCCEEDED;
/* For the code that handles RTPG, see also scst_local_cmd.c. */
return CMD_FAILED;
}
static enum compl_status_e vdisk_exec_maintenance_out(struct vdisk_cmd_params *p)
static enum compl_status_e
vdisk_exec_maintenance_out(struct vdisk_cmd_params *p)
{
switch (p->cmd->cdb[1] & 0x1f) {
case MO_SET_TARGET_PGS:
return vdisk_exec_set_tpgs(p);
}
scst_set_invalid_field_in_cdb(p->cmd, 1,
0 | SCST_INVAL_FIELD_BIT_OFFS_VALID);
return CMD_SUCCEEDED;
/* For the code that handles RTPG, see also scst_local_cmd.c. */
return CMD_FAILED;
}
static enum compl_status_e vdisk_exec_read_toc(struct vdisk_cmd_params *p)
@@ -7010,7 +6924,6 @@ static int vdev_create_node(struct scst_dev_type *devt,
virt_dev->rotational = DEF_ROTATIONAL;
virt_dev->thin_provisioned = DEF_THIN_PROVISIONED;
virt_dev->tst = DEF_TST;
virt_dev->expl_alua = DEF_EXPL_ALUA;
INIT_WORK(&virt_dev->vdev_inq_changed_work, vdev_inq_changed_fn);
virt_dev->blk_shift = DEF_DISK_BLOCK_SHIFT;
@@ -8217,7 +8130,7 @@ static ssize_t vdisk_sysfs_rd_only_show(struct kobject *kobj,
dev = container_of(kobj, struct scst_device, dev_kobj);
virt_dev = dev->dh_priv;
pos = sprintf(buf, "%d\n%s", virt_dev->rd_only ? 1 : 0,
pos = sprintf(buf, "%d\n%s", virt_dev->rd_only,
(virt_dev->rd_only == DEF_RD_ONLY) ? "" :
SCST_SYSFS_KEY_MARK "\n");
@@ -8237,7 +8150,7 @@ static ssize_t vdisk_sysfs_wt_show(struct kobject *kobj,
dev = container_of(kobj, struct scst_device, dev_kobj);
virt_dev = dev->dh_priv;
pos = sprintf(buf, "%d\n%s", virt_dev->wt_flag ? 1 : 0,
pos = sprintf(buf, "%d\n%s", virt_dev->wt_flag,
(virt_dev->wt_flag == DEF_WRITE_THROUGH) ? "" :
SCST_SYSFS_KEY_MARK "\n");
@@ -8257,7 +8170,7 @@ static ssize_t vdisk_sysfs_tp_show(struct kobject *kobj,
dev = container_of(kobj, struct scst_device, dev_kobj);
virt_dev = dev->dh_priv;
pos = sprintf(buf, "%d\n%s", virt_dev->thin_provisioned ? 1 : 0,
pos = sprintf(buf, "%d\n%s", virt_dev->thin_provisioned,
virt_dev->thin_provisioned_manually_set &&
(virt_dev->thin_provisioned !=
virt_dev->dev_thin_provisioned) ?
@@ -8297,50 +8210,33 @@ static ssize_t vdisk_sysfs_expl_alua_show(struct kobject *kobj,
struct kobj_attribute *attr,
char *buf)
{
struct scst_device *dev;
struct scst_vdisk_dev *virt_dev;
int pos;
struct scst_device *dev = container_of(kobj, struct scst_device,
dev_kobj);
TRACE_ENTRY();
dev = container_of(kobj, struct scst_device, dev_kobj);
virt_dev = dev->dh_priv;
pos = sprintf(buf, "%d\n%s", virt_dev->expl_alua,
virt_dev->expl_alua != DEF_EXPL_ALUA ?
return sprintf(buf, "%d\n%s", dev->expl_alua,
dev->expl_alua != DEF_EXPL_ALUA ?
SCST_SYSFS_KEY_MARK "\n" : "");
TRACE_EXIT_RES(pos);
return pos;
}
static ssize_t vdisk_sysfs_expl_alua_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t count)
{
struct scst_device *dev;
struct scst_vdisk_dev *virt_dev;
char ch[16];
struct scst_device *dev = container_of(kobj, struct scst_device,
dev_kobj);
unsigned long expl_alua;
int res;
TRACE_ENTRY();
dev = container_of(kobj, struct scst_device, dev_kobj);
virt_dev = dev->dh_priv;
sprintf(ch, "%.*s", min_t(int, sizeof(ch) - 1, count), buf);
res = kstrtoul(ch, 0, &expl_alua);
res = kstrtoul(buf, 0, &expl_alua);
if (res < 0)
goto out;
return res;
spin_lock(&virt_dev->flags_lock);
virt_dev->expl_alua = !!expl_alua;
spin_unlock(&virt_dev->flags_lock);
spin_lock_bh(&dev->dev_lock);
dev->expl_alua = !!expl_alua;
spin_unlock_bh(&dev->dev_lock);
res = count;
out:
TRACE_EXIT_RES(res);
return res;
return count;
}
static ssize_t vdisk_sysfs_nv_cache_show(struct kobject *kobj,
@@ -8355,7 +8251,7 @@ static ssize_t vdisk_sysfs_nv_cache_show(struct kobject *kobj,
dev = container_of(kobj, struct scst_device, dev_kobj);
virt_dev = dev->dh_priv;
pos = sprintf(buf, "%d\n%s", virt_dev->nv_cache ? 1 : 0,
pos = sprintf(buf, "%d\n%s", virt_dev->nv_cache,
(virt_dev->nv_cache == DEF_NV_CACHE) ? "" :
SCST_SYSFS_KEY_MARK "\n");
@@ -8375,7 +8271,7 @@ static ssize_t vdisk_sysfs_o_direct_show(struct kobject *kobj,
dev = container_of(kobj, struct scst_device, dev_kobj);
virt_dev = dev->dh_priv;
pos = sprintf(buf, "%d\n%s", virt_dev->o_direct_flag ? 1 : 0,
pos = sprintf(buf, "%d\n%s", virt_dev->o_direct_flag,
(virt_dev->o_direct_flag == DEF_O_DIRECT) ? "" :
SCST_SYSFS_KEY_MARK "\n");
@@ -8447,7 +8343,7 @@ static ssize_t vdisk_sysfs_removable_show(struct kobject *kobj,
dev = container_of(kobj, struct scst_device, dev_kobj);
virt_dev = dev->dh_priv;
pos = sprintf(buf, "%d\n", virt_dev->removable ? 1 : 0);
pos = sprintf(buf, "%d\n", virt_dev->removable);
if ((virt_dev->dev->type != TYPE_ROM) &&
(virt_dev->removable != DEF_REMOVABLE))
@@ -8490,7 +8386,7 @@ static ssize_t vdisk_sysfs_rotational_show(struct kobject *kobj,
dev = container_of(kobj, struct scst_device, dev_kobj);
virt_dev = dev->dh_priv;
pos = sprintf(buf, "%d\n", virt_dev->rotational ? 1 : 0);
pos = sprintf(buf, "%d\n", virt_dev->rotational);
if (virt_dev->rotational != DEF_ROTATIONAL)
pos += sprintf(&buf[pos], "%s\n", SCST_SYSFS_KEY_MARK);
+5 -5
View File
@@ -5337,10 +5337,10 @@ static int scst_alloc_add_tgt_dev(struct scst_session *sess,
tgt_dev->lun = acg_dev->lun;
tgt_dev->acg_dev = acg_dev;
tgt_dev->tgt_dev_rd_only = acg_dev->acg_dev_rd_only || dev->dev_rd_only;
if (sess->tgt->tgt_forwarding)
set_bit(SCST_TGT_DEV_FORWARDING, &tgt_dev->tgt_dev_flags);
if (sess->tgt->tgt_forward_dst)
set_bit(SCST_TGT_DEV_FORWARD_DST, &tgt_dev->tgt_dev_flags);
else
clear_bit(SCST_TGT_DEV_FORWARDING, &tgt_dev->tgt_dev_flags);
clear_bit(SCST_TGT_DEV_FORWARD_DST, &tgt_dev->tgt_dev_flags);
tgt_dev->hw_dif_same_sg_layout_required = sess->tgt->tgt_hw_dif_same_sg_layout_required;
tgt_dev->tgt_dev_dif_guard_format = acg_dev->acg_dev_dif_guard_format;
if (tgt_dev->tgt_dev_dif_guard_format == SCST_DIF_GUARD_FORMAT_IP)
@@ -11936,7 +11936,7 @@ static int get_cdb_info_min(struct scst_cmd *cmd,
break;
case MI_REPORT_TARGET_PGS:
cmd->op_name = "REPORT TARGET PORT GROUPS";
cmd->op_flags |= SCST_REG_RESERVE_ALLOWED |
cmd->op_flags |= SCST_LOCAL_CMD | SCST_REG_RESERVE_ALLOWED |
SCST_WRITE_EXCL_ALLOWED | SCST_EXCL_ACCESS_ALLOWED;
break;
case MI_REPORT_SUPPORTED_OPERATION_CODES:
@@ -11967,7 +11967,7 @@ static int get_cdb_info_mo(struct scst_cmd *cmd,
unsigned long flags;
cmd->op_name = "SET TARGET PORT GROUPS";
cmd->op_flags |= SCST_STRICTLY_SERIALIZED;
cmd->op_flags |= SCST_LOCAL_CMD | SCST_STRICTLY_SERIALIZED;
spin_lock_irqsave(&scst_global_stpg_list_lock, flags);
TRACE_DBG("Adding STPG cmd %p to global_stpg_list", cmd);
cmd->cmd_on_global_stpg_list = 1;
+96 -3
View File
@@ -253,6 +253,82 @@ out_unlock_put_not_completed:
goto out;
}
/* SPC-4 REPORT TARGET PORT GROUPS command */
static enum scst_exec_res scst_report_tpgs(struct scst_cmd *cmd)
{
struct scst_device *dev;
uint8_t *address;
void *buf;
int32_t buf_len;
uint32_t data_length, length;
uint8_t data_format;
int res;
TRACE_ENTRY();
buf_len = scst_get_buf_full_sense(cmd, &address);
if (buf_len <= 0)
goto out;
dev = cmd->dev;
data_format = cmd->cdb[1] >> 5;
res = scst_tg_get_group_info(&buf, &data_length, dev, data_format);
if (res == -ENOMEM) {
scst_set_busy(cmd);
goto out_put;
} else if (res < 0) {
scst_set_cmd_error(cmd,
SCST_LOAD_SENSE(scst_sense_invalid_field_in_cdb));
goto out_put;
}
length = min_t(uint32_t, data_length, buf_len);
memcpy(address, buf, length);
kfree(buf);
if (length < cmd->resp_data_len)
scst_set_resp_data_len(cmd, length);
out_put:
scst_put_buf_full(cmd, address);
out:
cmd->completed = 1;
/* Report the result */
cmd->scst_cmd_done(cmd, SCST_CMD_STATE_DEFAULT, SCST_CONTEXT_SAME);
return SCST_EXEC_COMPLETED;
}
/* SPC-4 SET TARGET PORT GROUPS command */
static enum scst_exec_res scst_exec_set_tpgs(struct scst_cmd *cmd)
{
struct scst_device *dev = cmd->dev;
int rc;
if (!dev->expl_alua) {
PRINT_ERROR("SET TARGET PORT GROUPS: not explicit ALUA mode "
"(dev %s)", dev->virt_name);
/* Invalid opcode, i.e. SA field */
scst_set_invalid_field_in_cdb(cmd, 1,
0 | SCST_INVAL_FIELD_BIT_OFFS_VALID);
goto out;
}
rc = scst_tg_set_group_info(cmd);
if (rc == 0) {
/* Running async */
return SCST_CMD_STATE_RES_CONT_NEXT;
}
scst_stpg_del_unblock_next(cmd);
out:
cmd->completed = 1;
cmd->scst_cmd_done(cmd, SCST_CMD_STATE_DEFAULT, SCST_CONTEXT_SAME);
return SCST_EXEC_COMPLETED;
}
static enum scst_exec_res scst_report_supported_tm_fns(struct scst_cmd *cmd)
{
const enum scst_exec_res res = SCST_EXEC_COMPLETED;
@@ -505,6 +581,9 @@ enum scst_exec_res scst_maintenance_in(struct scst_cmd *cmd)
TRACE_ENTRY();
switch (cmd->cdb[1] & 0x1f) {
case MI_REPORT_TARGET_PGS:
res = scst_report_tpgs(cmd);
break;
case MI_REPORT_SUPPORTED_TASK_MANAGEMENT_FUNCTIONS:
res = scst_report_supported_tm_fns(cmd);
break;
@@ -520,6 +599,22 @@ enum scst_exec_res scst_maintenance_in(struct scst_cmd *cmd)
return res;
}
enum scst_exec_res scst_maintenance_out(struct scst_cmd *cmd)
{
enum scst_exec_res res;
switch (cmd->cdb[1] & 0x1f) {
case MO_SET_TARGET_PGS:
res = scst_exec_set_tpgs(cmd);
break;
default:
res = SCST_EXEC_NOT_COMPLETED;
break;
}
return res;
}
enum scst_exec_res scst_reserve_local(struct scst_cmd *cmd)
{
enum scst_exec_res res = SCST_EXEC_NOT_COMPLETED;
@@ -714,15 +809,13 @@ enum scst_exec_res scst_persistent_reserve_in_local(struct scst_cmd *cmd)
goto out_done;
}
#ifndef CONFIG_SCST_FORWARD_MODE_PASS_THROUGH
if (dev->scsi_dev != NULL) {
if (cmd->tgt->tgt_forward_src && dev->scsi_dev) {
PRINT_WARNING("PR commands for pass-through devices not "
"supported (device %s)", dev->virt_name);
scst_set_cmd_error(cmd,
SCST_LOAD_SENSE(scst_sense_invalid_opcode));
goto out_done;
}
#endif
buffer_size = scst_get_buf_full_sense(cmd, &buffer);
if (unlikely(buffer_size <= 0))
+1
View File
@@ -8,6 +8,7 @@ enum scst_exec_res scst_cm_ext_copy_exec(struct scst_cmd *cmd);
enum scst_exec_res scst_cm_rcv_copy_res_exec(struct scst_cmd *cmd);
enum scst_exec_res scst_cmp_wr_local(struct scst_cmd *cmd);
enum scst_exec_res scst_maintenance_in(struct scst_cmd *cmd);
enum scst_exec_res scst_maintenance_out(struct scst_cmd *cmd);
enum scst_exec_res scst_persistent_reserve_in_local(struct scst_cmd *cmd);
enum scst_exec_res scst_persistent_reserve_out_local(struct scst_cmd *cmd);
enum scst_exec_res scst_release_local(struct scst_cmd *cmd);
+3 -10
View File
@@ -1111,7 +1111,7 @@ static int scst_register_device(struct scsi_device *scsidp)
dev->scsi_dev = scsidp;
#ifdef CONFIG_SCST_FORWARD_MODE_PASS_THROUGH
/* For target ports that function as forwarding source. */
res = scst_pr_set_file_name(dev, NULL, "%s/%s", SCST_PR_DIR,
dev->virt_name);
if (res != 0)
@@ -1120,7 +1120,6 @@ static int scst_register_device(struct scsi_device *scsidp)
res = scst_pr_init_dev(dev);
if (res != 0)
goto out_free_dev;
#endif
list_add_tail(&dev->dev_list_entry, &scst_dev_list);
@@ -1143,14 +1142,9 @@ out:
out_del_unlocked:
mutex_lock(&scst_mutex);
list_del_init(&dev->dev_list_entry);
mutex_unlock(&scst_mutex);
percpu_ref_kill(&dev->refcnt);
goto out;
#ifdef CONFIG_SCST_FORWARD_MODE_PASS_THROUGH
/* If used as a forwarding source (see also tgt_forward_src). */
scst_pr_clear_dev(dev);
#endif
out_free_dev:
percpu_ref_kill(&dev->refcnt);
@@ -1193,9 +1187,8 @@ static void scst_unregister_device(struct scsi_device *scsidp)
list_del_init(&dev->dev_list_entry);
#ifdef CONFIG_SCST_FORWARD_MODE_PASS_THROUGH
/* For forwarding sources (see also tgt_forward_src). */
scst_pr_clear_dev(dev);
#endif
scst_dg_dev_remove_by_dev(dev);
+1 -1
View File
@@ -1594,7 +1594,7 @@ static int scst_pr_register_all_tg_pt(struct scst_cmd *cmd, uint8_t *buffer,
continue;
if (tgt->rel_tgt_id == 0)
continue;
if (tgt->tgt_forwarding) {
if (tgt->tgt_forward_dst) {
TRACE_PR("ALL_TG_PT: skipping forwarding "
"target %s", tgt->tgt_name);
continue;
+68 -19
View File
@@ -2072,10 +2072,10 @@ static ssize_t __scst_acg_cpu_mask_store(struct scst_acg *acg,
* We can't use cpumask_parse_user() here, because it expects
* buffer in the user space.
*/
res = __bitmap_parse(buf, count, 0, cpumask_bits(&work->cpu_mask),
nr_cpumask_bits);
res = bitmap_parse(buf, count, cpumask_bits(&work->cpu_mask),
nr_cpumask_bits);
if (res != 0) {
PRINT_ERROR("__bitmap_parse() failed: %d", res);
PRINT_ERROR("bitmap_parse() failed: %d", res);
goto out_release;
}
@@ -2513,7 +2513,45 @@ static struct kobj_attribute scst_rel_tgt_id =
__ATTR(rel_tgt_id, S_IRUGO | S_IWUSR, scst_rel_tgt_id_show,
scst_rel_tgt_id_store);
static ssize_t scst_tgt_forwarding_show(struct kobject *kobj,
static ssize_t scst_tgt_forward_src_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
struct scst_tgt *tgt = container_of(kobj, struct scst_tgt, tgt_kobj);
return sprintf(buf, "%d\n%s", tgt->tgt_forward_src,
tgt->tgt_forward_src ? SCST_SYSFS_KEY_MARK "\n" : "");
}
static ssize_t scst_tgt_forward_src_store(struct kobject *kobj,
struct kobj_attribute *attr, const char *buf, size_t count)
{
struct scst_tgt *tgt = container_of(kobj, struct scst_tgt, tgt_kobj);
int res, old, new;
res = kstrtoint(buf, 0, &new);
if (res < 0)
return res;
if (new < 0 || new > 1)
return -EINVAL;
mutex_lock(&scst_mutex);
old = tgt->tgt_forward_src;
if (old != new) {
tgt->tgt_forward_src = new;
PRINT_INFO("%s target %s as forwarding source",
tgt->tgt_forward_src ? "Set" : "Clear",
tgt->tgt_name);
}
mutex_unlock(&scst_mutex);
return count;
}
static struct kobj_attribute scst_tgt_forward_src =
__ATTR(forward_src, S_IRUGO | S_IWUSR, scst_tgt_forward_src_show,
scst_tgt_forward_src_store);
static ssize_t scst_tgt_forward_dst_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
struct scst_tgt *tgt;
@@ -2523,14 +2561,14 @@ static ssize_t scst_tgt_forwarding_show(struct kobject *kobj,
tgt = container_of(kobj, struct scst_tgt, tgt_kobj);
res = sprintf(buf, "%d\n%s", tgt->tgt_forwarding,
tgt->tgt_forwarding ? SCST_SYSFS_KEY_MARK "\n" : "");
res = sprintf(buf, "%d\n%s", tgt->tgt_forward_dst,
tgt->tgt_forward_dst ? SCST_SYSFS_KEY_MARK "\n" : "");
TRACE_EXIT_RES(res);
return res;
}
static ssize_t scst_tgt_forwarding_store(struct kobject *kobj,
static ssize_t scst_tgt_forward_dst_store(struct kobject *kobj,
struct kobj_attribute *attr, const char *buf, size_t count)
{
int res = 0;
@@ -2549,14 +2587,14 @@ static ssize_t scst_tgt_forwarding_store(struct kobject *kobj,
mutex_lock(&scst_mutex);
old = tgt->tgt_forwarding;
old = tgt->tgt_forward_dst;
switch (buf[0]) {
case '0':
tgt->tgt_forwarding = 0;
tgt->tgt_forward_dst = 0;
break;
case '1':
tgt->tgt_forwarding = 1;
tgt->tgt_forward_dst = 1;
break;
default:
PRINT_ERROR("%s: Requested action not understood: %s",
@@ -2565,7 +2603,7 @@ static ssize_t scst_tgt_forwarding_store(struct kobject *kobj,
goto out_unlock;
}
if (tgt->tgt_forwarding == old)
if (tgt->tgt_forward_dst == old)
goto out_unlock;
list_for_each_entry(sess, &tgt->sess_list, sess_list_entry) {
@@ -2576,18 +2614,22 @@ static ssize_t scst_tgt_forwarding_store(struct kobject *kobj,
struct scst_tgt_dev *tgt_dev;
list_for_each_entry(tgt_dev, head, sess_tgt_dev_list_entry) {
if (tgt->tgt_forwarding)
set_bit(SCST_TGT_DEV_FORWARDING, &tgt_dev->tgt_dev_flags);
if (tgt->tgt_forward_dst)
set_bit(SCST_TGT_DEV_FORWARD_DST,
&tgt_dev->tgt_dev_flags);
else
clear_bit(SCST_TGT_DEV_FORWARDING, &tgt_dev->tgt_dev_flags);
clear_bit(SCST_TGT_DEV_FORWARD_DST,
&tgt_dev->tgt_dev_flags);
}
}
}
if (tgt->tgt_forwarding)
PRINT_INFO("Set target %s as forwarding", tgt->tgt_name);
if (tgt->tgt_forward_dst)
PRINT_INFO("Set target %s as forwarding destination",
tgt->tgt_name);
else
PRINT_INFO("Clear target %s as forwarding", tgt->tgt_name);
PRINT_INFO("Clear target %s as forwarding destination",
tgt->tgt_name);
out_unlock:
mutex_unlock(&scst_mutex);
@@ -2600,9 +2642,14 @@ out:
return res;
}
static struct kobj_attribute scst_tgt_forward_dst =
__ATTR(forward_dst, S_IRUGO | S_IWUSR, scst_tgt_forward_dst_show,
scst_tgt_forward_dst_store);
// To do: remove the 'forwarding' sysfs attribute and keep 'forward_dst'.
static struct kobj_attribute scst_tgt_forwarding =
__ATTR(forwarding, S_IRUGO | S_IWUSR, scst_tgt_forwarding_show,
scst_tgt_forwarding_store);
__ATTR(forwarding, S_IRUGO | S_IWUSR, scst_tgt_forward_dst_show,
scst_tgt_forward_dst_store);
static ssize_t scst_tgt_comment_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
@@ -2876,6 +2923,8 @@ SCST_TGT_SYSFS_STAT_ATTR(cmd_count, none_cmd_count, SCST_DATA_NONE, >> 0);
static struct attribute *scst_tgt_attrs[] = {
&scst_rel_tgt_id.attr,
&scst_tgt_forward_src.attr,
&scst_tgt_forward_dst.attr,
&scst_tgt_forwarding.attr,
&scst_tgt_comment.attr,
&scst_tgt_addr_method.attr,
+3 -1
View File
@@ -2363,7 +2363,8 @@ int __scst_check_local_events(struct scst_cmd *cmd, bool preempt_tests_only)
goto out;
}
if (unlikely(test_bit(SCST_TGT_DEV_FORWARDING, &cmd->tgt_dev->tgt_dev_flags))) {
if (unlikely(test_bit(SCST_TGT_DEV_FORWARD_DST,
&cmd->tgt_dev->tgt_dev_flags))) {
/*
* All the checks are supposed to be done on the
* forwarding requester's side.
@@ -2707,6 +2708,7 @@ static scst_local_exec_fn scst_local_fns[256] = {
[EXTENDED_COPY] = scst_cm_ext_copy_exec,
[RECEIVE_COPY_RESULTS] = scst_cm_rcv_copy_res_exec,
[MAINTENANCE_IN] = scst_maintenance_in,
[MAINTENANCE_OUT] = scst_maintenance_out,
};
static enum scst_exec_res scst_do_local_exec(struct scst_cmd *cmd)
+179 -154
View File
@@ -1663,6 +1663,169 @@ static void scst_stpg_ext_blocking_done(struct scst_device *dev,
scst_stpg_check_blocking_done(*((struct scst_stpg_wait **)data));
}
/**
* struct osi - Information needed to process an STPG command.
*
* The 'on' in 'osi' refers to 'on_stpg', a concept from the ION SCST code base
* that has not yet been ported.
*
* @group_id: Target port group ID.
* @tg: Target port group.
* @prev_state: previous target port group ALUA state.
* @new_state: new target port group ALUA state.
*/
struct osi {
uint16_t group_id;
struct scst_target_group *tg;
enum scst_tg_state prev_state;
enum scst_tg_state new_state;
};
static int scst_emit_stpg_event(struct scst_cmd *cmd, struct scst_dev_group *dg,
struct osi *osi, int tpg_desc_count)
{
int j, payload_len, event_entry_len, valid_desc_count, res;
struct scst_event_stpg_payload *payload;
struct scst_event_entry *event_entry;
struct scst_event_stpg_descr *descr;
struct scst_device *dev = cmd->dev;
struct scst_stpg_wait *wait;
struct scst_event *event;
struct scst_dg_dev *dgd;
payload_len = sizeof(*payload) + sizeof(*descr) * tpg_desc_count;
event_entry_len = sizeof(*event_entry) + payload_len;
event_entry = kzalloc(event_entry_len, GFP_KERNEL);
if (event_entry == NULL) {
PRINT_ERROR("Unable to allocate event (size %d)", event_entry_len);
res = -ENOMEM;
goto out;
}
TRACE_MEM("event_entry %p (len %d) allocated", event_entry,
event_entry_len);
event = &event_entry->event;
event->payload_len = payload_len;
payload = (struct scst_event_stpg_payload *)event->payload;
payload->stpg_cmd_tag = cmd->tag;
res = 1;
if (strlcpy(payload->device_name, dev->virt_name,
sizeof(payload->device_name)) >=
sizeof(payload->device_name)) {
PRINT_ERROR("Device name %s too long", dev->virt_name);
goto out_too_long;
}
valid_desc_count = 0;
for (j = 0, descr = &payload->stpg_descriptors[0]; j < tpg_desc_count;
j++) {
if (osi[j].prev_state == osi[j].new_state)
continue;
if (strlcpy(descr->prev_state,
scst_alua_state_name(osi[j].prev_state),
sizeof(descr->prev_state)) >=
sizeof(descr->prev_state) ||
strlcpy(descr->new_state,
scst_alua_state_name(osi[j].new_state),
sizeof(descr->new_state)) >=
sizeof(descr->new_state) ||
strlcpy(descr->dg_name, dg->name, sizeof(descr->dg_name))
>= sizeof(descr->dg_name) ||
strlcpy(descr->tg_name, osi[j].tg->name,
sizeof(descr->tg_name)) >=
sizeof(descr->tg_name))
goto out_too_long;
descr->group_id = osi[j].group_id;
TRACE_DBG("group_id %u, prev_state %s, new_state %s, dg_name %s, tg_name %s",
descr->group_id, descr->prev_state, descr->new_state,
descr->dg_name, descr->tg_name);
valid_desc_count++;
descr++;
}
payload->stpg_descriptors_cnt = valid_desc_count;
if (valid_desc_count == 0) {
TRACE_DBG("Nothing to do");
goto out;
}
dg->stpg_rel_tgt_id = cmd->tgt->rel_tgt_id;
dg->stpg_transport_id = kmemdup(cmd->sess->transport_id,
scst_tid_size(cmd->sess->transport_id), GFP_KERNEL);
if (dg->stpg_transport_id == NULL) {
PRINT_ERROR("Unable to duplicate stpg_transport_id");
goto out;
}
wait = kzalloc(sizeof(*wait), GFP_KERNEL);
if (wait == NULL) {
PRINT_ERROR("Unable to allocate STPG wait struct "
"(size %zd)", sizeof(*wait));
res = -ENOMEM;
goto out;
}
atomic_set(&wait->stpg_wait_left, 1);
wait->event_entry = event_entry;
event_entry->event_notify_fn = scst_event_stpg_notify_fn;
event_entry->notify_fn_priv = cmd;
mutex_lock(&scst_dg_mutex);
list_for_each_entry(dgd, &dg->dev_list, entry) {
int rc;
if (dgd->dev == dev)
continue;
TRACE_DBG("STPG: ext blocking dev %s",
dgd->dev->virt_name);
atomic_inc(&wait->stpg_wait_left);
rc = scst_ext_block_dev(dgd->dev,
scst_stpg_ext_blocking_done,
(uint8_t *)&wait, sizeof(wait),
SCST_EXT_BLOCK_STPG);
if (rc != 0) {
TRACE_DBG("scst_ext_block_dev() failed "
"with %d, reverting (cmd %p)", rc, cmd);
wait->status = rc;
wait->dg = dg;
atomic_dec(&wait->stpg_wait_left);
spin_lock_bh(&dev->dev_lock);
WARN_ON(dgd->dev->stpg_ext_blocked);
dgd->dev->stpg_ext_blocked = 0;
spin_unlock_bh(&dev->dev_lock);
break;
}
}
mutex_unlock(&scst_dg_mutex);
scst_stpg_check_blocking_done(wait);
/* !! cmd can be already dead here !! */
return 0;
out_too_long:
res = -EOVERFLOW;
out:
kfree(dg->stpg_transport_id);
dg->stpg_transport_id = NULL;
kfree(event_entry);
return res;
}
/*
* scst_tg_set_group_info - SET TARGET PORT GROUPS implementation.
*
@@ -1679,19 +1842,9 @@ int scst_tg_set_group_info(struct scst_cmd *cmd)
struct scst_device *dev = cmd->dev;
uint8_t *buf;
int len;
int i, j, res = 1, tpg_desc_count, valid_desc_count;
int i, j, res = 1, tpg_desc_count;
struct scst_dev_group *dg;
struct osi {
uint16_t group_id;
struct scst_target_group *tg;
enum scst_tg_state prev_state;
enum scst_tg_state new_state;
} *osi = NULL;
int event_entry_len, payload_len;
struct scst_event_entry *event_entry;
struct scst_event *event;
struct scst_event_stpg_payload *payload;
struct scst_event_stpg_descr *descr;
struct osi *osi = NULL;
TRACE_ENTRY();
@@ -1816,140 +1969,23 @@ int scst_tg_set_group_info(struct scst_cmd *cmd)
scst_put_buf_full(cmd, buf);
payload_len = sizeof(*payload) + sizeof(*descr) * tpg_desc_count;
event_entry_len = sizeof(*event_entry) + payload_len;
event_entry = kzalloc(event_entry_len, GFP_KERNEL);
if (event_entry == NULL) {
PRINT_ERROR("Unable to allocate event (size %d)", event_entry_len);
res = -ENOMEM;
res = scst_emit_stpg_event(cmd, dg, osi, tpg_desc_count);
switch (res) {
case 1:
case 0:
break;
case -ENOMEM:
scst_set_busy(cmd);
goto out_free;
break;
default:
WARN_ONCE(true, "res = %d\n", res);
/* fall through */
case -EOVERFLOW:
scst_set_cmd_error(cmd,
SCST_LOAD_SENSE(scst_sense_set_target_pgs_failed));
break;
}
TRACE_MEM("event_entry %p (len %d) allocated", event_entry,
event_entry_len);
event = &event_entry->event;
event->payload_len = payload_len;
payload = (struct scst_event_stpg_payload *)event->payload;
payload->stpg_cmd_tag = cmd->tag;
res = 1;
if (strlen(dev->virt_name) >= sizeof(payload->device_name)) {
PRINT_ERROR("Device name %s too long", dev->virt_name);
goto out_too_long;
}
strlcpy(payload->device_name, dev->virt_name, sizeof(payload->device_name));
valid_desc_count = 0;
for (j = 0, descr = &payload->stpg_descriptors[0]; j < tpg_desc_count; j++) {
if (osi[j].prev_state == osi[j].new_state)
continue;
if (strlen(scst_alua_state_name(osi[j].prev_state)) >= sizeof(descr->prev_state)) {
PRINT_ERROR("prev state too long (%d)", osi[j].prev_state);
goto out_too_long;
}
strlcpy(descr->prev_state, scst_alua_state_name(osi[j].prev_state),
sizeof(descr->prev_state));
if (strlen(scst_alua_state_name(osi[j].new_state)) >= sizeof(descr->new_state)) {
PRINT_ERROR("new state too long (%d)", osi[j].new_state);
goto out_too_long;
}
strlcpy(descr->new_state, scst_alua_state_name(osi[j].new_state),
sizeof(descr->new_state));
if (strlen(dg->name) >= sizeof(descr->dg_name)) {
PRINT_ERROR("dg_name too long (%s)", dg->name);
goto out_too_long;
}
strlcpy(descr->dg_name, dg->name, sizeof(descr->dg_name));
if (strlen(osi[j].tg->name) >= sizeof(descr->tg_name)) {
PRINT_ERROR("tg_name too long (%s)", osi[j].tg->name);
goto out_too_long;
}
strlcpy(descr->tg_name, osi[j].tg->name,
sizeof(descr->tg_name));
descr->group_id = osi[j].group_id;
TRACE_DBG("group_id %u, prev_state %s, new_state %s, dg_name %s, "
"tg_name %s", descr->group_id, descr->prev_state,
descr->new_state, descr->dg_name, descr->tg_name);
valid_desc_count++;
descr++;
}
payload->stpg_descriptors_cnt = valid_desc_count;
if (valid_desc_count > 0) {
struct scst_dg_dev *dgd;
struct scst_stpg_wait *wait;
int rc;
dg->stpg_rel_tgt_id = cmd->tgt->rel_tgt_id;
dg->stpg_transport_id = kmemdup(cmd->sess->transport_id,
scst_tid_size(cmd->sess->transport_id), GFP_KERNEL);
if (dg->stpg_transport_id == NULL) {
PRINT_ERROR("Unable to duplicate stpg_transport_id");
goto out_free_event;
}
wait = kzalloc(sizeof(*wait), GFP_KERNEL);
if (wait == NULL) {
PRINT_ERROR("Unable to allocate STPG wait struct "
"(size %zd)", sizeof(*wait));
scst_set_busy(cmd);
res = -ENOMEM;
goto out_free_tr_id;
}
atomic_set(&wait->stpg_wait_left, 1);
wait->event_entry = event_entry;
event_entry->event_notify_fn = scst_event_stpg_notify_fn;
event_entry->notify_fn_priv = cmd;
mutex_lock(&scst_dg_mutex);
list_for_each_entry(dgd, &dg->dev_list, entry) {
if (dgd->dev == dev)
continue;
TRACE_DBG("STPG: ext blocking dev %s", dgd->dev->virt_name);
atomic_inc(&wait->stpg_wait_left);
rc = scst_ext_block_dev(dgd->dev, scst_stpg_ext_blocking_done,
(uint8_t *)&wait, sizeof(wait), SCST_EXT_BLOCK_STPG);
if (rc != 0) {
TRACE_DBG("scst_ext_block_dev() failed "
"with %d, reverting (cmd %p)", rc, cmd);
wait->status = rc;
wait->dg = dg;
atomic_dec(&wait->stpg_wait_left);
spin_lock_bh(&dev->dev_lock);
WARN_ON(dgd->dev->stpg_ext_blocked);
dgd->dev->stpg_ext_blocked = 0;
spin_unlock_bh(&dev->dev_lock);
break;
}
}
mutex_unlock(&scst_dg_mutex);
scst_stpg_check_blocking_done(wait);
/* !! cmd can be already dead here !! */
} else {
TRACE_DBG("Nothing to do");
goto out_free_event;
}
res = 0;
out_free:
kfree(osi);
@@ -1968,16 +2004,5 @@ out_unlock_sm_fail:
out_put:
scst_put_buf_full(cmd, buf);
goto out_free;
out_too_long:
scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_set_target_pgs_failed));
res = -EOVERFLOW;
out_free_tr_id:
kfree(dg->stpg_transport_id);
out_free_event:
kfree(event_entry);
goto out_free;
}
EXPORT_SYMBOL_GPL(scst_tg_set_group_info);