mirror of
https://github.com/SCST-project/scst.git
synced 2026-08-20 06:06:23 +00:00
Explicit ALUA support
Prepared with help from Prasidh Srikanth <Prasidh.Srikanth@sandisk.com> git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@6576 d57e44dd-8a1f-0410-8b47-8ef2f437770f
This commit is contained in:
+44
-29
@@ -1452,16 +1452,18 @@ persistent reservations from this device are released, upon reconnect
|
||||
the initiators will see it.
|
||||
|
||||
|
||||
Implicit ALUA Support
|
||||
---------------------
|
||||
ALUA Support
|
||||
------------
|
||||
|
||||
SCST supports implicit asymmetric logical unit access (ALUA). Implicit ALUA is
|
||||
a feature defined by the ANSI T10 SCSI committee that allows a target to tell
|
||||
the initiator which path to use in a multipath setup. The redundant paths
|
||||
between initiator and target can be used either for redundancy or for load
|
||||
sharing purposes. The target can either be a single target system running SCST
|
||||
with multiple communication interfaces or two target systems each running SCST
|
||||
and configured in a high availability setup.
|
||||
SCST supports both implicit and explicit asymmetric logical unit access
|
||||
(ALUA). ALUA is a feature defined by the ANSI T10 SCSI committee. It
|
||||
allows a target to tell the initiator which path to use in a multipath
|
||||
setup plus, in the explicit case, control state of each path via SET
|
||||
TARGET PORT GROUPS SCSI command. The redundant paths between initiator
|
||||
and target can be used either for redundancy or for load sharing
|
||||
purposes. The target can either be a single target system running SCST
|
||||
with multiple communication interfaces or two target systems each
|
||||
running SCST and configured in a high availability setup.
|
||||
|
||||
In the SPC-4 standard the following concepts are defined related to ALUA:
|
||||
* Relative target port ID. A number between 1 and 65535 that uniquely
|
||||
@@ -1482,17 +1484,17 @@ ANSI T10 standard called SPC-4.
|
||||
ALUA support in SCST
|
||||
....................
|
||||
|
||||
SCST allows to define implicit ALUA settings for each unique combination of
|
||||
SCST device and SCST target. An initiator however queries ALUA settings by
|
||||
sending an appropriate SCSI command to a specific LUN of an SCST target. Each
|
||||
such LUN maps uniquely to an SCST device. For hardware SCST target drivers,
|
||||
e.g. ib_srpt, there is a one-to-one correspondence between SCST target and
|
||||
SCSI target port. With other SCST targets, e.g. iSCSI-SCST, by default the
|
||||
only relationship between SCST targets and SCSI target ports is that all SCST
|
||||
targets defined on a system are visible via all SCSI target ports. See also
|
||||
the iSCSI-SCST documentation about the allowed_portal attribute for
|
||||
information about how to associate iSCSI targets with a single physical
|
||||
interface.
|
||||
SCST allows to define ALUA settings for each unique combination of SCST
|
||||
device and SCST target. An initiator however queries ALUA settings by
|
||||
sending an appropriate SCSI command to a specific LUN of an SCST target.
|
||||
Each such LUN maps uniquely to an SCST device. For hardware SCST target
|
||||
drivers, e.g. ib_srpt, there is a one-to-one correspondence between SCST
|
||||
target and SCSI target port. With other SCST targets, e.g. iSCSI-SCST,
|
||||
by default the only relationship between SCST targets and SCSI target
|
||||
ports is that all SCST targets defined on a system are visible via all
|
||||
SCSI target ports. See also the iSCSI-SCST documentation about the
|
||||
allowed_portal attribute for information about how to associate iSCSI
|
||||
targets with a single physical interface.
|
||||
|
||||
Notes:
|
||||
- In a H.A. setup it is the responsibility of the user to synchronize ALUA
|
||||
@@ -1515,7 +1517,7 @@ Notes:
|
||||
Configuring ALUA in SCST
|
||||
........................
|
||||
|
||||
SCST allows to configure the following settings related to implicit ALUA
|
||||
SCST allows to configure the following settings related to ALUA
|
||||
for each unique combination of SCST target and virtual SCST device
|
||||
(vdisk_fileio, vdisk_blockio, vcdrom, ...):
|
||||
* The target port group asymmetric access state. SCST supports all ALUA port
|
||||
@@ -1613,15 +1615,26 @@ DEVICE_GROUP dgroup2 {
|
||||
}
|
||||
}
|
||||
|
||||
Explicit ALUA
|
||||
.............
|
||||
|
||||
To enable explicit ALUA you need in addition to the above settings set
|
||||
expl_alua device attribute to 1 (by default it is 0). Also you need to
|
||||
run stpgd and supply to it path to a script or program, which will
|
||||
perform actual path state switching on SET TARGET PORT GROUPS command,
|
||||
for instance, by calling drbdadm. For more information see stpgd README
|
||||
as well as sample script scst_on_stpg.
|
||||
|
||||
|
||||
Checking the Target Configuration
|
||||
.................................
|
||||
|
||||
One way to verify the implicit ALUA configuration from a Linux initiator is
|
||||
via the commands provided in the sg3_utils package. The first step is to
|
||||
verify whether for a certain LUN implicit ALUA has been configured on the
|
||||
target. This is possible by checking whether the TPGS=1 text appears in the
|
||||
sg_inq output, where /dev/sdb is a device node created by the ib_srp initiator:
|
||||
One way to verify the ALUA configuration from a Linux initiator is via
|
||||
the commands provided in the sg3_utils package. The first step is to
|
||||
verify whether for a certain LUN ALUA has been configured on the target.
|
||||
This is possible by checking whether the TPGS=1 text appears in the
|
||||
sg_inq output, where /dev/sdb is a device node created by the ib_srp
|
||||
initiator:
|
||||
|
||||
# sg_inq /dev/sdb
|
||||
standard INQUIRY:
|
||||
@@ -1681,9 +1694,11 @@ Device Identification VPD page:
|
||||
Initiator Support
|
||||
.................
|
||||
|
||||
On Linux systems implicit ALUA support is provided by the scsi_dh_alua kernel
|
||||
driver in combination with the user space multipathd daemon. You will have to
|
||||
modify at least the following in /etc/multipath.conf to enable implicit ALUA:
|
||||
On Linux systems ALUA support is provided by the scsi_dh_alua kernel
|
||||
driver in combination with the user space multipathd daemon. You will
|
||||
have to modify at least the following in /etc/multipath.conf to enable
|
||||
ALUA:
|
||||
|
||||
* hardware_handler "1 alua"
|
||||
* prio alua
|
||||
* path_grouping_policy group_by_prio
|
||||
|
||||
+9
-2
@@ -2658,6 +2658,9 @@ struct scst_device {
|
||||
/* Used to serialize invocations of __scst_ext_blocking_done() */
|
||||
unsigned int ext_unblock_scheduled:1;
|
||||
|
||||
/* Set if this device was blocked during STPG command processing */
|
||||
unsigned int stpg_ext_blocked:1;
|
||||
|
||||
/* Set if this device does not support DIF IP checking */
|
||||
unsigned int dev_dif_ip_not_supported:1;
|
||||
|
||||
@@ -3212,6 +3215,8 @@ struct scst_acn {
|
||||
* @kobj: For making this object visible in sysfs.
|
||||
* @dev_kobj: Sysfs devices directory.
|
||||
* @tg_kobj: Sysfs target groups directory.
|
||||
* @stpg_transport_id Initiator transport ID for STPG originating I_T nexus, if any
|
||||
* @stpg_rel_tgt_id Relative target ID for STPG originating I_T nexus, if any
|
||||
*
|
||||
* Each device is member of zero or one device groups. With each device group
|
||||
* there are zero or more target groups associated.
|
||||
@@ -3224,6 +3229,8 @@ struct scst_dev_group {
|
||||
struct kobject kobj;
|
||||
struct kobject *dev_kobj;
|
||||
struct kobject *tg_kobj;
|
||||
uint8_t *stpg_transport_id;
|
||||
uint16_t stpg_rel_tgt_id;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -3362,7 +3369,6 @@ extern const struct scst_opcode_descriptor scst_op_descr_report_supp_opcodes;
|
||||
&scst_op_descr_report_supp_opcodes, \
|
||||
&scst_op_descr_report_supp_tm_fns,
|
||||
|
||||
|
||||
#ifndef smp_mb__after_set_bit
|
||||
/* There is no smp_mb__after_set_bit() in the kernel */
|
||||
#define smp_mb__after_set_bit() smp_mb()
|
||||
@@ -3691,9 +3697,10 @@ static inline void scst_sess_set_tgt_priv(struct scst_session *sess,
|
||||
}
|
||||
|
||||
uint16_t scst_lookup_tg_id(struct scst_device *dev, struct scst_tgt *tgt);
|
||||
bool scst_impl_alua_configured(struct scst_device *dev);
|
||||
bool scst_alua_configured(struct scst_device *dev);
|
||||
int scst_tg_get_group_info(void **buf, uint32_t *response_length,
|
||||
struct scst_device *dev, uint8_t data_format);
|
||||
int scst_tg_set_group_info(struct scst_cmd *cmd);
|
||||
|
||||
/*
|
||||
* Get/set functions for dev's static DIF APP TAG
|
||||
|
||||
@@ -290,9 +290,9 @@ static inline int scst_sense_response_code(const uint8_t *sense)
|
||||
|
||||
/* NOT_READY is 2 */
|
||||
#define scst_sense_format_in_progress NOT_READY, 0x04, 0x04
|
||||
#define scst_sense_tp_transitioning NOT_READY, 0x04, 0x0A
|
||||
#define scst_sense_tp_standby NOT_READY, 0x04, 0x0B
|
||||
#define scst_sense_tp_unav NOT_READY, 0x04, 0x0C
|
||||
#define scst_sense_alua_transitioning NOT_READY, 0x04, 0x0A
|
||||
#define scst_sense_alua_standby NOT_READY, 0x04, 0x0B
|
||||
#define scst_sense_alua_unav NOT_READY, 0x04, 0x0C
|
||||
#define scst_sense_no_medium NOT_READY, 0x3a, 0
|
||||
|
||||
/* MEDIUM_ERROR is 3 */
|
||||
@@ -301,6 +301,7 @@ static inline int scst_sense_response_code(const uint8_t *sense)
|
||||
|
||||
/* HARDWARE_ERROR is 4 */
|
||||
#define scst_sense_hardw_error HARDWARE_ERROR, 0x44, 0 /* non-retriable */
|
||||
#define scst_sense_set_target_pgs_failed HARDWARE_ERROR, 0x67, 0xA
|
||||
|
||||
/* ILLEGAL_REQUEST is 5 */
|
||||
#define scst_sense_invalid_opcode ILLEGAL_REQUEST, 0x20, 0
|
||||
|
||||
@@ -135,4 +135,23 @@ struct scst_event_tm_fn_received_payload {
|
||||
};
|
||||
};
|
||||
|
||||
#define SCST_EVENT_STPG_USER_INVOKE 5
|
||||
struct scst_event_stpg_descr {
|
||||
uint16_t group_id;
|
||||
/*
|
||||
* Better to keep below fields as small as possible to fit
|
||||
* in single page as many descriptors as possible.
|
||||
*/
|
||||
uint8_t prev_state[32];
|
||||
uint8_t new_state[32];
|
||||
uint8_t dg_name[64];
|
||||
uint8_t tg_name[64];
|
||||
};
|
||||
struct scst_event_stpg_payload {
|
||||
aligned_u64 stpg_cmd_tag;
|
||||
uint8_t device_name[64];
|
||||
uint16_t stpg_descriptors_cnt;
|
||||
struct scst_event_stpg_descr stpg_descriptors[0];
|
||||
};
|
||||
|
||||
#endif /* __SCST_EVENT_H */
|
||||
|
||||
@@ -112,6 +112,7 @@ static struct scst_trace_log vdisk_local_trace_tbl[] = {
|
||||
#define DEF_REMOVABLE 0
|
||||
#define DEF_ROTATIONAL 1
|
||||
#define DEF_THIN_PROVISIONED 0
|
||||
#define DEF_EXPL_ALUA 0
|
||||
|
||||
#define VDISK_NULLIO_SIZE (5LL*1024*1024*1024*1024/2)
|
||||
|
||||
@@ -178,6 +179,7 @@ struct scst_vdisk_dev {
|
||||
unsigned int tst:3;
|
||||
unsigned int format_active:1;
|
||||
unsigned int discard_zeroes_data:1;
|
||||
unsigned int expl_alua:1;
|
||||
|
||||
struct file *fd;
|
||||
struct file *dif_fd;
|
||||
@@ -322,6 +324,7 @@ static enum compl_status_e vdisk_exec_read_capacity(struct vdisk_cmd_params *p);
|
||||
static enum compl_status_e vdisk_exec_read_capacity16(struct vdisk_cmd_params *p);
|
||||
static enum compl_status_e vdisk_exec_get_lba_status(struct vdisk_cmd_params *p);
|
||||
static enum compl_status_e vdisk_exec_report_tpgs(struct vdisk_cmd_params *p);
|
||||
static enum compl_status_e vdisk_exec_set_tpgs(struct vdisk_cmd_params *p);
|
||||
static enum compl_status_e vdisk_exec_inquiry(struct vdisk_cmd_params *p);
|
||||
static enum compl_status_e vdisk_exec_request_sense(struct vdisk_cmd_params *p);
|
||||
static enum compl_status_e vdisk_exec_mode_sense(struct vdisk_cmd_params *p);
|
||||
@@ -382,6 +385,10 @@ 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_expl_alua_show(struct kobject *kobj,
|
||||
struct kobj_attribute *attr, char *buf);
|
||||
static ssize_t vdisk_sysfs_expl_alua_store(struct kobject *kobj,
|
||||
struct kobj_attribute *attr, const char *buf, size_t count);
|
||||
static ssize_t vdisk_sysfs_nv_cache_show(struct kobject *kobj,
|
||||
struct kobj_attribute *attr, char *buf);
|
||||
static ssize_t vdisk_sysfs_o_direct_show(struct kobject *kobj,
|
||||
@@ -474,6 +481,9 @@ 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_expl_alua_attr =
|
||||
__ATTR(expl_alua, S_IWUSR|S_IRUGO, vdisk_sysfs_expl_alua_show,
|
||||
vdisk_sysfs_expl_alua_store);
|
||||
static struct kobj_attribute vdisk_nv_cache_attr =
|
||||
__ATTR(nv_cache, S_IRUGO, vdisk_sysfs_nv_cache_show, NULL);
|
||||
static struct kobj_attribute vdisk_o_direct_attr =
|
||||
@@ -543,6 +553,7 @@ static const struct attribute *vdisk_fileio_attrs[] = {
|
||||
&vdisk_tp_attr.attr,
|
||||
&vdisk_tst_attr.attr,
|
||||
&vdisk_rotational_attr.attr,
|
||||
&vdisk_expl_alua_attr.attr,
|
||||
&vdisk_nv_cache_attr.attr,
|
||||
&vdisk_o_direct_attr.attr,
|
||||
&vdisk_removable_attr.attr,
|
||||
@@ -570,6 +581,7 @@ static const struct attribute *vdisk_blockio_attrs[] = {
|
||||
&vdisk_blocksize_attr.attr,
|
||||
&vdisk_rd_only_attr.attr,
|
||||
&vdisk_wt_attr.attr,
|
||||
&vdisk_expl_alua_attr.attr,
|
||||
&vdisk_nv_cache_attr.attr,
|
||||
&vdisk_tst_attr.attr,
|
||||
&vdisk_removable_attr.attr,
|
||||
@@ -1874,6 +1886,17 @@ static enum compl_status_e vdisk_exec_maintenance_in(struct vdisk_cmd_params *p)
|
||||
return CMD_SUCCEEDED;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static enum compl_status_e vdisk_exec_send_diagnostic(struct vdisk_cmd_params *p)
|
||||
{
|
||||
return CMD_SUCCEEDED;
|
||||
@@ -2590,6 +2613,7 @@ static const struct scst_opcode_descriptor scst_op_descr_read_toc = {
|
||||
[WRITE_SAME_16] = vdisk_exec_write_same, \
|
||||
[COMPARE_AND_WRITE] = vdisk_exec_caw, \
|
||||
[MAINTENANCE_IN] = vdisk_exec_maintenance_in, \
|
||||
[MAINTENANCE_OUT] = vdisk_exec_maintenance_out, \
|
||||
[SEND_DIAGNOSTIC] = vdisk_exec_send_diagnostic, \
|
||||
[FORMAT_UNIT] = vdisk_exec_format_unit,
|
||||
|
||||
@@ -2714,6 +2738,7 @@ static const struct scst_opcode_descriptor *vdisk_opcode_descriptors[] = {
|
||||
SHARED_OPCODE_DESCRIPTORS
|
||||
VDISK_OPCODE_DESCRIPTORS
|
||||
SCST_OPCODE_DESCRIPTORS
|
||||
&scst_op_descr_stpg, /* must be last, see vdisk_get_supported_opcodes()! */
|
||||
};
|
||||
|
||||
static const struct scst_opcode_descriptor *vdisk_opcode_descriptors_type2[] = {
|
||||
@@ -2738,6 +2763,9 @@ static int vdisk_get_supported_opcodes(struct scst_cmd *cmd,
|
||||
const struct scst_opcode_descriptor ***out_supp_opcodes,
|
||||
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;
|
||||
*out_supp_opcodes_cnt = ARRAY_SIZE(vdisk_opcode_descriptors);
|
||||
@@ -2745,6 +2773,10 @@ 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) {
|
||||
(*out_supp_opcodes_cnt)--;
|
||||
sBUG_ON((*out_supp_opcodes)[*out_supp_opcodes_cnt]->od_serv_action != MO_SET_TARGET_PGS);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -4091,8 +4123,11 @@ static int vdisk_inq(uint8_t *buf, struct scst_cmd *cmd,
|
||||
buf[4] = 31;/* n - 4 = 35 - 4 = 31 for full 36 byte data */
|
||||
if (cmd->dev->dev_dif_mode != SCST_DIF_MODE_NONE)
|
||||
buf[5] |= 1; /* PROTECT */
|
||||
if (scst_impl_alua_configured(cmd->dev))
|
||||
if (scst_alua_configured(cmd->dev)) {
|
||||
buf[5] |= SCST_INQ_TPGS_MODE_IMPLICIT;
|
||||
if (virt_dev->expl_alua)
|
||||
buf[5] |= SCST_INQ_TPGS_MODE_EXPLICIT;
|
||||
}
|
||||
buf[6] = 0x10; /* MultiP 1 */
|
||||
buf[7] = 2; /* CMDQUE 1, BQue 0 => commands queuing supported */
|
||||
|
||||
@@ -5237,6 +5272,34 @@ out:
|
||||
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;
|
||||
|
||||
out:
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
static enum compl_status_e vdisk_exec_read_toc(struct vdisk_cmd_params *p)
|
||||
{
|
||||
struct scst_cmd *cmd = p->cmd;
|
||||
@@ -7462,6 +7525,7 @@ static int vdev_create(struct scst_dev_type *devt,
|
||||
virt_dev->thin_provisioned = DEF_THIN_PROVISIONED;
|
||||
virt_dev->tst = DEF_TST;
|
||||
virt_dev->caw_len_lim = DEF_CAW_LEN_LIM;
|
||||
virt_dev->expl_alua = DEF_EXPL_ALUA;
|
||||
|
||||
virt_dev->blk_shift = DEF_DISK_BLOCK_SHIFT;
|
||||
|
||||
@@ -8574,6 +8638,53 @@ static ssize_t vdisk_sysfs_tp_show(struct kobject *kobj,
|
||||
return pos;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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 ?
|
||||
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];
|
||||
bool 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);
|
||||
expl_alua = !!simple_strtoul(ch, NULL, 0);
|
||||
|
||||
spin_lock(&virt_dev->flags_lock);
|
||||
virt_dev->expl_alua = expl_alua;
|
||||
spin_unlock(&virt_dev->flags_lock);
|
||||
|
||||
res = count;
|
||||
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
static ssize_t vdisk_sysfs_nv_cache_show(struct kobject *kobj,
|
||||
struct kobj_attribute *attr, char *buf)
|
||||
{
|
||||
|
||||
@@ -160,7 +160,7 @@ static void __scst_event_queue(struct scst_event_entry *event_entry)
|
||||
{
|
||||
const struct scst_event *event = &event_entry->event;
|
||||
struct scst_event_priv *priv;
|
||||
struct scst_event_entry *allowed_entry = NULL;
|
||||
struct scst_event_entry *allowed_entry;
|
||||
bool queued = false;
|
||||
int rc = 0;
|
||||
static atomic_t base_event_id = ATOMIC_INIT(0);
|
||||
|
||||
+28
-6
@@ -248,7 +248,7 @@ const struct scst_opcode_descriptor scst_op_descr_stpg = {
|
||||
.od_cdb_size = 12,
|
||||
.od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC,
|
||||
.od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ,
|
||||
.od_cdb_usage_bits = { MAINTENANCE_IN, MO_SET_TARGET_PGS, 0, 0, 0, 0,
|
||||
.od_cdb_usage_bits = { MAINTENANCE_OUT, MO_SET_TARGET_PGS, 0, 0, 0, 0,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0, SCST_OD_DEFAULT_CONTROL_BYTE },
|
||||
};
|
||||
EXPORT_SYMBOL(scst_op_descr_stpg);
|
||||
@@ -453,6 +453,8 @@ static int get_cdb_info_apt(struct scst_cmd *cmd,
|
||||
const struct scst_sdbops *sdbops);
|
||||
static int get_cdb_info_min(struct scst_cmd *cmd,
|
||||
const struct scst_sdbops *sdbops);
|
||||
static int get_cdb_info_mo(struct scst_cmd *cmd,
|
||||
const struct scst_sdbops *sdbops);
|
||||
static int get_cdb_info_var_len(struct scst_cmd *cmd,
|
||||
const struct scst_sdbops *sdbops);
|
||||
|
||||
@@ -1410,12 +1412,12 @@ static const struct scst_sdbops scst_scsi_op_table[] = {
|
||||
.info_op_flags = FLAG_NONE,
|
||||
.info_len_off = 8, .info_len_len = 2,
|
||||
.get_cdb_info = get_cdb_info_len_2},
|
||||
{.ops = 0xA4, .devkey = " O ",
|
||||
{.ops = 0xA4, .devkey = "OOO O OOOO MO O",
|
||||
.info_op_name = "MAINTENANCE(OUT)",
|
||||
.info_data_direction = SCST_DATA_WRITE,
|
||||
.info_op_flags = FLAG_NONE,
|
||||
.info_len_off = 6, .info_len_len = 4,
|
||||
.get_cdb_info = get_cdb_info_len_4},
|
||||
.get_cdb_info = get_cdb_info_mo},
|
||||
{.ops = 0xA5, .devkey = " M ",
|
||||
.info_op_name = "MOVE MEDIUM",
|
||||
.info_data_direction = SCST_DATA_NONE,
|
||||
@@ -10518,6 +10520,20 @@ static int get_cdb_info_min(struct scst_cmd *cmd,
|
||||
return get_cdb_info_len_4(cmd, sdbops);
|
||||
}
|
||||
|
||||
/* Parse MAINTENANCE (OUT) */
|
||||
static int get_cdb_info_mo(struct scst_cmd *cmd,
|
||||
const struct scst_sdbops *sdbops)
|
||||
{
|
||||
switch (cmd->cdb[1] & 0x1f) {
|
||||
case MO_SET_TARGET_PGS:
|
||||
cmd->op_name = "SET TARGET PORT GROUPS";
|
||||
cmd->op_flags |= SCST_STRICTLY_SERIALIZED;
|
||||
break;
|
||||
}
|
||||
|
||||
return get_cdb_info_len_4(cmd, sdbops);
|
||||
}
|
||||
|
||||
/**
|
||||
* scst_get_cdb_info() - fill various info about the command's CDB
|
||||
*
|
||||
@@ -13304,14 +13320,20 @@ out_free_success:
|
||||
goto out_success;
|
||||
}
|
||||
|
||||
void scst_ext_unblock_dev(struct scst_device *dev)
|
||||
void scst_ext_unblock_dev(struct scst_device *dev, bool stpg)
|
||||
{
|
||||
TRACE_ENTRY();
|
||||
|
||||
spin_lock_bh(&dev->dev_lock);
|
||||
|
||||
if (dev->ext_blocks_cnt == 0) {
|
||||
TRACE_DBG("Nothing to unblock (dev %p)", dev);
|
||||
TRACE_DBG("Nothing to unblock (dev %s)", dev->virt_name);
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
if ((dev->ext_blocks_cnt == 1) && dev->stpg_ext_blocked && !stpg) {
|
||||
TRACE_DBG("Can not unblock internal STPG ext block (dev %s)",
|
||||
dev->virt_name);
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
@@ -13340,7 +13362,7 @@ void scst_ext_unblock_dev(struct scst_device *dev)
|
||||
spin_unlock_bh(&dev->dev_lock);
|
||||
} else {
|
||||
TRACE_DBG("Ext unblock: pending done, unblocking...");
|
||||
scst_ext_unblock_dev(dev);
|
||||
scst_ext_unblock_dev(dev, stpg);
|
||||
}
|
||||
spin_lock_bh(&dev->dev_lock);
|
||||
}
|
||||
|
||||
@@ -110,7 +110,7 @@ static inline void tid_secure(uint8_t *tid)
|
||||
}
|
||||
|
||||
/* Returns false if tid's are not equal, true otherwise */
|
||||
static bool tid_equal(const uint8_t *tid_a, const uint8_t *tid_b)
|
||||
bool tid_equal(const uint8_t *tid_a, const uint8_t *tid_b)
|
||||
{
|
||||
int len;
|
||||
|
||||
|
||||
@@ -131,6 +131,8 @@ void scst_pr_read_full_status(struct scst_cmd *cmd, uint8_t *buffer,
|
||||
int buffer_size);
|
||||
|
||||
int scst_tid_size(const uint8_t *tid);
|
||||
bool tid_equal(const uint8_t *tid_a, const uint8_t *tid_b);
|
||||
|
||||
struct scst_dev_registrant *scst_pr_find_reg(struct scst_device *dev,
|
||||
const uint8_t *transport_id, const uint16_t rel_tgt_id);
|
||||
struct scst_dev_registrant *scst_pr_add_registrant(struct scst_device *dev,
|
||||
|
||||
@@ -717,7 +717,7 @@ void scst_unblock_dev(struct scst_device *dev);
|
||||
|
||||
int scst_ext_block_dev(struct scst_device *dev, bool sync,
|
||||
ext_blocker_done_fn_t done_fn, const uint8_t *priv, int priv_len);
|
||||
void scst_ext_unblock_dev(struct scst_device *dev);
|
||||
void scst_ext_unblock_dev(struct scst_device *dev, bool stpg);
|
||||
void __scst_ext_blocking_done(struct scst_device *dev);
|
||||
void scst_ext_blocking_done(struct scst_device *dev);
|
||||
|
||||
|
||||
@@ -3522,7 +3522,7 @@ static ssize_t scst_dev_block_store(struct kobject *kobj,
|
||||
|
||||
TRACE_DBG("Sysfs unblocking (dev %s)", dev->virt_name);
|
||||
|
||||
scst_ext_unblock_dev(dev);
|
||||
scst_ext_unblock_dev(dev, false);
|
||||
res = 0;
|
||||
goto out;
|
||||
case '1':
|
||||
|
||||
+473
-17
@@ -16,6 +16,8 @@
|
||||
*/
|
||||
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/kmod.h>
|
||||
#include <asm/unaligned.h>
|
||||
#ifdef INSIDE_KERNEL_TREE
|
||||
#include <scst/scst.h>
|
||||
@@ -23,6 +25,8 @@
|
||||
#include "scst.h"
|
||||
#endif
|
||||
#include "scst_priv.h"
|
||||
#include "scst_event.h"
|
||||
#include "scst_pres.h"
|
||||
|
||||
struct alua_state_and_name {
|
||||
enum scst_tg_state s;
|
||||
@@ -127,8 +131,8 @@ static struct scst_tg_tgt *__lookup_dg_tgt(struct scst_dev_group *dg,
|
||||
}
|
||||
|
||||
/* Look up a target group by name in the given device group. */
|
||||
static struct scst_target_group *
|
||||
__lookup_tg_by_name(struct scst_dev_group *dg, const char *name)
|
||||
static struct scst_target_group *__lookup_tg_by_name(struct scst_dev_group *dg,
|
||||
const char *name)
|
||||
{
|
||||
struct scst_target_group *tg;
|
||||
|
||||
@@ -141,9 +145,24 @@ __lookup_tg_by_name(struct scst_dev_group *dg, const char *name)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Look up a target group by group ID. */
|
||||
static struct scst_target_group *__lookup_tg_by_group_id(struct scst_dev_group *dg,
|
||||
uint16_t group_id)
|
||||
{
|
||||
struct scst_target_group *tg;
|
||||
|
||||
lockdep_assert_held(&scst_mutex);
|
||||
|
||||
list_for_each_entry(tg, &dg->tg_list, entry)
|
||||
if (tg->group_id == group_id)
|
||||
return tg;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Look up a target group by target port. */
|
||||
static struct scst_target_group *
|
||||
__lookup_tg_by_tgt(struct scst_dev_group *dg, const struct scst_tgt *tgt)
|
||||
static struct scst_target_group *__lookup_tg_by_tgt(struct scst_dev_group *dg,
|
||||
const struct scst_tgt *tgt)
|
||||
{
|
||||
struct scst_target_group *tg;
|
||||
struct scst_tg_tgt *tg_tgt;
|
||||
@@ -319,12 +338,11 @@ static bool scst_tg_accept_standby(struct scst_cmd *cmd)
|
||||
return true;
|
||||
}
|
||||
|
||||
scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_tp_standby));
|
||||
scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_alua_standby));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Whether or not to accept a command in the ALUA unavailable state.
|
||||
*/
|
||||
@@ -333,7 +351,7 @@ static bool scst_tg_accept_unav(struct scst_cmd *cmd)
|
||||
bool process_cmd = scst_tg_accept(cmd);
|
||||
|
||||
if (!process_cmd)
|
||||
scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_tp_unav));
|
||||
scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_alua_unav));
|
||||
|
||||
return process_cmd;
|
||||
}
|
||||
@@ -347,7 +365,7 @@ static bool scst_tg_accept_transitioning(struct scst_cmd *cmd)
|
||||
|
||||
if (!process_cmd)
|
||||
scst_set_cmd_error(cmd,
|
||||
SCST_LOAD_SENSE(scst_sense_tp_transitioning));
|
||||
SCST_LOAD_SENSE(scst_sense_alua_transitioning));
|
||||
|
||||
return process_cmd;
|
||||
}
|
||||
@@ -424,7 +442,9 @@ static void scst_tg_change_tgt_dev_state(struct scst_tgt_dev *tgt_dev,
|
||||
{
|
||||
lockdep_assert_held(&scst_dg_mutex);
|
||||
|
||||
TRACE_MGMT_DBG("ALUA state of tgt_dev %p has changed", tgt_dev);
|
||||
TRACE_MGMT_DBG("ALUA state of tgt_dev %p has changed (gen_ua %d)",
|
||||
tgt_dev, gen_ua);
|
||||
|
||||
scst_update_tgt_dev_alua_filter(tgt_dev, state);
|
||||
if (gen_ua)
|
||||
scst_gen_aen_or_ua(tgt_dev,
|
||||
@@ -724,14 +744,86 @@ out:
|
||||
return res;
|
||||
}
|
||||
|
||||
static void scst_event_stpg_notify_fn(struct scst_event *event,
|
||||
void *priv, int status)
|
||||
{
|
||||
struct scst_dev_group *dg;
|
||||
struct scst_cmd *cmd = (struct scst_cmd *)priv;
|
||||
struct scst_event_stpg_payload *p =
|
||||
(struct scst_event_stpg_payload *)event->payload;
|
||||
struct scst_event_stpg_descr *d;
|
||||
struct scst_dg_dev *dgd;
|
||||
int i;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
PRINT_INFO("Notification for event %u (id %d) received "
|
||||
"with status %d (priv %p)", event->event_code,
|
||||
event->event_id, status, priv);
|
||||
|
||||
mutex_lock(&scst_mutex);
|
||||
mutex_lock(&scst_dg_mutex);
|
||||
|
||||
dg = __lookup_dg_by_dev(cmd->dev);
|
||||
if (!dg) {
|
||||
PRINT_ERROR("STPG: unable to find DG for device %s",
|
||||
cmd->dev->virt_name);
|
||||
goto out_fail;
|
||||
}
|
||||
|
||||
list_for_each_entry(dgd, &dg->dev_list, entry) {
|
||||
if (dgd->dev->stpg_ext_blocked) {
|
||||
TRACE_DBG("STPG: ext unblocking dev %s",
|
||||
dgd->dev->virt_name);
|
||||
scst_ext_unblock_dev(dgd->dev, true);
|
||||
dgd->dev->stpg_ext_blocked = 0;
|
||||
}
|
||||
}
|
||||
|
||||
kfree(dg->stpg_transport_id);
|
||||
dg->stpg_transport_id = NULL;
|
||||
|
||||
if (status != 0) {
|
||||
PRINT_ERROR("on_stpg script for device group %s failed with status %d",
|
||||
dg->name, status);
|
||||
goto out_fail;
|
||||
}
|
||||
|
||||
for (i = 0, d = &p->stpg_descriptors[0]; i < p->stpg_descriptors_cnt; i++, d++) {
|
||||
struct scst_target_group *tg = __lookup_tg_by_group_id(dg, d->group_id);
|
||||
if (!tg) {
|
||||
PRINT_ERROR("STPG: unable to find TG %d", d->group_id);
|
||||
goto out_fail;
|
||||
} else if (tg->state == scst_alua_name_to_state(d->prev_state)) {
|
||||
PRINT_ERROR("on_stpg script did not change ALUA state"
|
||||
" for device group %s / target group %s",
|
||||
dg->name, tg->name);
|
||||
goto out_fail;
|
||||
}
|
||||
}
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&scst_dg_mutex);
|
||||
mutex_unlock(&scst_mutex);
|
||||
|
||||
cmd->completed = 1;
|
||||
cmd->scst_cmd_done(cmd, SCST_CMD_STATE_DEFAULT, SCST_CONTEXT_THREAD);
|
||||
|
||||
TRACE_EXIT();
|
||||
return;
|
||||
|
||||
out_fail:
|
||||
scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_set_target_pgs_failed));
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
/*
|
||||
* Update the ALUA filter of those LUNs (tgt_dev) whose target port is a member
|
||||
* of target group @tg and that export a device that is a member of the device
|
||||
* group @tg->dg.
|
||||
*/
|
||||
static void __scst_tg_set_state(struct scst_target_group *tg,
|
||||
enum scst_tg_state state,
|
||||
struct scst_tgt *no_ua_tgt)
|
||||
enum scst_tg_state state)
|
||||
{
|
||||
struct scst_dg_dev *dg_dev;
|
||||
struct scst_device *dev;
|
||||
@@ -754,8 +846,12 @@ static void __scst_tg_set_state(struct scst_target_group *tg,
|
||||
tgt = tgt_dev->sess->tgt;
|
||||
list_for_each_entry(tg_tgt, &tg->tgt_list, entry) {
|
||||
if (tg_tgt->tgt == tgt) {
|
||||
bool gen_ua = (state != SCST_TG_STATE_TRANSITIONING);
|
||||
if ((tg->dg->stpg_rel_tgt_id == tgt_dev->sess->tgt->rel_tgt_id) &&
|
||||
tid_equal(tg->dg->stpg_transport_id, tgt_dev->sess->transport_id))
|
||||
gen_ua = false;
|
||||
scst_tg_change_tgt_dev_state(tgt_dev,
|
||||
state, tgt != no_ua_tgt);
|
||||
state, gen_ua);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -780,7 +876,7 @@ int scst_tg_set_state(struct scst_target_group *tg, enum scst_tg_state state)
|
||||
if (res)
|
||||
goto out;
|
||||
|
||||
__scst_tg_set_state(tg, state, NULL);
|
||||
__scst_tg_set_state(tg, state);
|
||||
|
||||
mutex_unlock(&scst_dg_mutex);
|
||||
out:
|
||||
@@ -945,9 +1041,16 @@ out_free:
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* scst_dg_mutex supposed to be locked */
|
||||
static void __scst_dg_dev_remove(struct scst_dev_group *dg,
|
||||
struct scst_dg_dev *dgdev)
|
||||
{
|
||||
if (dgdev->dev->stpg_ext_blocked) {
|
||||
TRACE_DBG("DG %s remove: unblocking STPG ext blocked "
|
||||
"dev %s", dg->name, dgdev->dev->virt_name);
|
||||
scst_ext_unblock_dev(dgdev->dev, true);
|
||||
dgdev->dev->stpg_ext_blocked = 0;
|
||||
}
|
||||
list_del(&dgdev->entry);
|
||||
scst_dg_dev_sysfs_del(dg, dgdev);
|
||||
scst_reset_dev_alua_filter(dgdev->dev);
|
||||
@@ -1080,7 +1183,7 @@ static void __scst_dg_remove(struct scst_dev_group *dg)
|
||||
list_del(&dg->entry);
|
||||
scst_dg_sysfs_del(dg);
|
||||
list_for_each_entry(tg, &dg->tg_list, entry)
|
||||
__scst_tg_set_state(tg, SCST_TG_STATE_OPTIMIZED, NULL);
|
||||
__scst_tg_set_state(tg, SCST_TG_STATE_OPTIMIZED);
|
||||
while (!list_empty(&dg->dev_list)) {
|
||||
dgdev = list_first_entry(&dg->dev_list, struct scst_dg_dev,
|
||||
entry);
|
||||
@@ -1201,10 +1304,10 @@ out_unlock:
|
||||
EXPORT_SYMBOL_GPL(scst_lookup_tg_id);
|
||||
|
||||
/**
|
||||
* scst_impl_alua_configured() - Whether implicit ALUA has been configured.
|
||||
* scst_alua_configured() - Whether implicit ALUA has been configured.
|
||||
* @dev: Pointer to the SCST device to verify.
|
||||
*/
|
||||
bool scst_impl_alua_configured(struct scst_device *dev)
|
||||
bool scst_alua_configured(struct scst_device *dev)
|
||||
{
|
||||
struct scst_dev_group *dg;
|
||||
|
||||
@@ -1214,7 +1317,7 @@ bool scst_impl_alua_configured(struct scst_device *dev)
|
||||
|
||||
return dg != NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(scst_impl_alua_configured);
|
||||
EXPORT_SYMBOL_GPL(scst_alua_configured);
|
||||
|
||||
/**
|
||||
* scst_tg_get_group_info() - Build REPORT TARGET GROUPS response.
|
||||
@@ -1329,3 +1432,356 @@ out:
|
||||
return res;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(scst_tg_get_group_info);
|
||||
|
||||
struct scst_stpg_wait {
|
||||
atomic_t stpg_wait_left;
|
||||
int status;
|
||||
struct scst_dev_group *dg;
|
||||
struct scst_event_entry *event_entry;
|
||||
};
|
||||
|
||||
/* No locks */
|
||||
static void scst_stpg_check_blocking_done(struct scst_stpg_wait *wait)
|
||||
{
|
||||
TRACE_ENTRY();
|
||||
|
||||
TRACE_DBG("wait %p, left %d", wait, atomic_read(&wait->stpg_wait_left));
|
||||
|
||||
if (atomic_dec_and_test(&wait->stpg_wait_left)) {
|
||||
if (wait->status == 0)
|
||||
scst_event_queue(SCST_EVENT_STPG_USER_INVOKE,
|
||||
SCST_EVENT_SCST_CORE_ISSUER, wait->event_entry);
|
||||
else {
|
||||
wait->event_entry->event_notify_fn(&wait->event_entry->event,
|
||||
wait->event_entry->notify_fn_priv, wait->status);
|
||||
}
|
||||
kfree(wait);
|
||||
}
|
||||
|
||||
TRACE_EXIT();
|
||||
return;
|
||||
}
|
||||
|
||||
/* No locks */
|
||||
static void scst_stpg_ext_blocking_done(struct scst_device *dev,
|
||||
uint8_t *data, int len)
|
||||
{
|
||||
sBUG_ON(len != sizeof(data));
|
||||
scst_stpg_check_blocking_done(*((struct scst_stpg_wait **)data));
|
||||
}
|
||||
|
||||
/**
|
||||
* scst_tg_set_group_info - SET TARGET PORT GROUPS implementation.
|
||||
*
|
||||
* Returns >=0 upon success or negative error code otherwise, for instance,
|
||||
* if either an invalid group ID has been specified or the group ID
|
||||
* of a target group with one, or more non-local target ports has been
|
||||
* specified. In the error case the cmd has its sense set.
|
||||
*
|
||||
* In case of returned 0 the command completed asynchronously, i.e. upon
|
||||
* return might be already dead!!
|
||||
*/
|
||||
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;
|
||||
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;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
len = scst_get_buf_full(cmd, &buf);
|
||||
if (len < 0) {
|
||||
PRINT_ERROR("scst_get_buf_full() failed: %d", len);
|
||||
res = len;
|
||||
if (len == -ENOMEM)
|
||||
scst_set_busy(cmd);
|
||||
else
|
||||
scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_hardw_error));
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* From SPC-4: "A parameter list length of zero specifies that no data
|
||||
* shall be transferred, and that no change shall be made in the
|
||||
* target port asymmetric access state of any target port groups or
|
||||
* target ports".
|
||||
*/
|
||||
if (len == 0)
|
||||
goto out_put;
|
||||
|
||||
tpg_desc_count = (len - 4) / 4;
|
||||
/* Check for some reasonable limit */
|
||||
if (tpg_desc_count > 64) {
|
||||
PRINT_ERROR("Too many STPG descriptors (%d) for dev %s",
|
||||
tpg_desc_count, dev->virt_name);
|
||||
res = -EINVAL;
|
||||
scst_set_invalid_field_in_cdb(cmd, 6, 0);
|
||||
goto out_put;
|
||||
}
|
||||
|
||||
TRACE_DBG("tpg_desc_count %d", tpg_desc_count);
|
||||
|
||||
osi = kcalloc(tpg_desc_count, sizeof(*osi), GFP_KERNEL);
|
||||
if (!osi) {
|
||||
res = -ENOMEM;
|
||||
scst_set_busy(cmd);
|
||||
goto out_put;
|
||||
}
|
||||
|
||||
res = mutex_lock_interruptible(&scst_mutex);
|
||||
if (res) {
|
||||
PRINT_INFO("mutex_lock_interruptible() returned %d, finishing "
|
||||
"cmd %p", res, cmd);
|
||||
scst_set_busy(cmd);
|
||||
goto out_put;
|
||||
}
|
||||
|
||||
res = mutex_lock_interruptible(&scst_dg_mutex);
|
||||
if (res) {
|
||||
PRINT_INFO("mutex_lock_interruptible() returned %d, finishing "
|
||||
"cmd %p", res, cmd);
|
||||
scst_set_busy(cmd);
|
||||
goto out_unlock_sm_fail;
|
||||
}
|
||||
|
||||
dg = __lookup_dg_by_dev(dev);
|
||||
if (!dg) {
|
||||
res = -EINVAL;
|
||||
goto out_unlock_fail;
|
||||
}
|
||||
|
||||
TRACE_DBG("dg %s (%p) found, dev %s", dg->name, dg, dev->virt_name);
|
||||
|
||||
for (i = 4, j = 0; i + 4 <= len; i += 4, j++) {
|
||||
WARN_ON_ONCE(j >= tpg_desc_count);
|
||||
osi[j].new_state = buf[i] & 0x1f;
|
||||
switch (osi[j].new_state) {
|
||||
case SCST_TG_STATE_OPTIMIZED:
|
||||
case SCST_TG_STATE_NONOPTIMIZED:
|
||||
case SCST_TG_STATE_STANDBY:
|
||||
case SCST_TG_STATE_UNAVAILABLE:
|
||||
case SCST_TG_STATE_OFFLINE:
|
||||
break;
|
||||
default:
|
||||
TRACE_MGMT_DBG("Incorrect new state %d", osi[j].new_state);
|
||||
res = -EINVAL;
|
||||
goto out_unlock_fail;
|
||||
}
|
||||
|
||||
osi[j].group_id = get_unaligned_be16(&buf[i + 2]);
|
||||
if (!osi[j].group_id) {
|
||||
TRACE_MGMT_DBG("Invalid group_id %d", osi[j].group_id);
|
||||
res = -EINVAL;
|
||||
goto out_unlock_fail;
|
||||
}
|
||||
|
||||
osi[j].tg = __lookup_tg_by_group_id(dg, osi[j].group_id);
|
||||
if (!osi[j].tg) {
|
||||
TRACE_MGMT_DBG("No TG for group_id %d", osi[j].group_id);
|
||||
res = -ESRCH;
|
||||
goto out_unlock_fail;
|
||||
}
|
||||
|
||||
if (osi[j].tg->state == SCST_TG_STATE_TRANSITIONING) {
|
||||
TRACE_MGMT_DBG("TG %p is transitioning", osi[j].tg);
|
||||
res = -EBUSY;
|
||||
scst_set_cmd_error(cmd,
|
||||
SCST_LOAD_SENSE(scst_sense_alua_transitioning));
|
||||
/* second sense will not override the set one */
|
||||
goto out_unlock_fail;
|
||||
}
|
||||
osi[j].prev_state = osi[j].tg->state;
|
||||
|
||||
TRACE_DBG("j %d, group_id %u, tg %s (%p), state %d", j, osi[j].group_id,
|
||||
osi[j].tg->name, osi[j].tg, osi[j].tg->state);
|
||||
}
|
||||
|
||||
mutex_unlock(&scst_dg_mutex);
|
||||
mutex_unlock(&scst_mutex);
|
||||
|
||||
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;
|
||||
scst_set_busy(cmd);
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
spin_lock_bh(&dev->dev_lock);
|
||||
WARN_ON(dgd->dev->stpg_ext_blocked);
|
||||
dgd->dev->stpg_ext_blocked = 1;
|
||||
spin_unlock_bh(&dev->dev_lock);
|
||||
|
||||
rc = scst_ext_block_dev(dgd->dev, false,
|
||||
scst_stpg_ext_blocking_done, (uint8_t *)&wait,
|
||||
sizeof(wait));
|
||||
if (rc != 0) {
|
||||
TRACE_DBG("scst_ext_block_dev() returned %d, "
|
||||
"stepping back (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);
|
||||
|
||||
out:
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
|
||||
out_unlock_fail:
|
||||
mutex_unlock(&scst_dg_mutex);
|
||||
|
||||
out_unlock_sm_fail:
|
||||
mutex_unlock(&scst_mutex);
|
||||
|
||||
scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_set_target_pgs_failed));
|
||||
|
||||
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);
|
||||
|
||||
Reference in New Issue
Block a user