- Added "replace" command to replace one LUN by another and generate INQUIRY DATA HAS CHANGED Unit Attention

- Sending INQUIRY DATA HAS CHANGED Unit Attention through AENs added
 - Minor fixes and cleanups



git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@1036 d57e44dd-8a1f-0410-8b47-8ef2f437770f
This commit is contained in:
Vladislav Bolkhovitin
2009-08-10 16:59:16 +00:00
parent fb6bad5bd9
commit dda7f2f71b
6 changed files with 182 additions and 106 deletions

View File

@@ -460,6 +460,14 @@ following files and directories under /proc/scsi_tgt:
the device could be marked as read only. The recommended way to find out
H:C:I:L numbers is use of lsscsi utility.
- "replace H:C:I:L lun [READ_ONLY]" to /proc/scsi_tgt/groups/GROUP_NAME/devices
replaces by device with host:channel:id:lun existing with LUN "lun"
device in group "GROUP_NAME" with generation of INQUIRY DATA HAS
CHANGED Unit Attention. If the old device doesn't exist, this
command acts as the "add" command. Optionally, the device could be
marked as read only. The recommended way to find out H:C:I:L numbers
is use of lsscsi utility.
- "del H:C:I:L" to /proc/scsi_tgt/groups/GROUP_NAME/devices deletes device with
host:channel:id:lun from group "GROUP_NAME". The recommended way to find out
H:C:I:L numbers is use of lsscsi utility.
@@ -468,6 +476,13 @@ following files and directories under /proc/scsi_tgt:
device with virtual name "V_NAME" with LUN "lun" in group "GROUP_NAME".
Optionally, the device could be marked as read only.
- "replace V_NAME lun [READ_ONLY]" to /proc/scsi_tgt/groups/GROUP_NAME/devices
replaces by device with virtual name "V_NAME" existing with LUN
"lun" device in group "GROUP_NAME" with generation of INQUIRY DATA
HAS CHANGED Unit Attention. If the old device doesn't exist, this
command acts as the "add" command. Optionally, the device could
be marked as read only.
- "del V_NAME" to /proc/scsi_tgt/groups/GROUP_NAME/devices deletes device with
virtual name "V_NAME" from group "GROUP_NAME"

View File

@@ -378,13 +378,28 @@ following files and directories under /proc/scsi_tgt:
the device could be marked as read only. The recommended way to find out
H:C:I:L numbers is use of lsscsi utility.
- "replace H:C:I:L lun [READ_ONLY]" to /proc/scsi_tgt/groups/GROUP_NAME/devices
replaces by device with host:channel:id:lun existing with LUN "lun"
device in group "GROUP_NAME" with generation of INQUIRY DATA HAS
CHANGED Unit Attention. If the old device doesn't exist, this
command acts as the "add" command. Optionally, the device could be
marked as read only. The recommended way to find out H:C:I:L numbers
is use of lsscsi utility.
- "del H:C:I:L" to /proc/scsi_tgt/groups/GROUP_NAME/devices deletes device with
host:channel:id:lun from group "GROUP_NAME". The recommended way to find out
H:C:I:L numbers is use of lsscsi utility.
- "add V_NAME lun [READ_ONLY]" to /proc/scsi_tgt/groups/GROUP_NAME/devices adds
device with virtual name "V_NAME" with LUN "lun" in group "GROUP_NAME".
Optionally, the device could be marked as read only.
device with virtual name "V_NAME" with LUN "lun" in group
"GROUP_NAME". Optionally, the device could be marked as read only.
- "replace V_NAME lun [READ_ONLY]" to /proc/scsi_tgt/groups/GROUP_NAME/devices
replaces by device with virtual name "V_NAME" existing with LUN
"lun" device in group "GROUP_NAME" with generation of INQUIRY DATA
HAS CHANGED Unit Attention. If the old device doesn't exist, this
command acts as the "add" command. Optionally, the device could be
marked as read only.
- "del V_NAME" to /proc/scsi_tgt/groups/GROUP_NAME/devices deletes device with
virtual name "V_NAME" from group "GROUP_NAME"

View File

@@ -410,7 +410,8 @@ void scst_set_initial_UA(struct scst_session *sess, int key, int asc, int ascq)
}
EXPORT_SYMBOL(scst_set_initial_UA);
static struct scst_aen *scst_alloc_aen(struct scst_tgt_dev *tgt_dev)
static struct scst_aen *scst_alloc_aen(struct scst_session *sess,
uint64_t unpacked_lun)
{
struct scst_aen *aen;
@@ -420,15 +421,15 @@ static struct scst_aen *scst_alloc_aen(struct scst_tgt_dev *tgt_dev)
if (aen == NULL) {
PRINT_ERROR("AEN memory allocation failed. Corresponding "
"event notification will not be performed (initiator "
"%s)", tgt_dev->sess->initiator_name);
"%s)", sess->initiator_name);
goto out;
}
memset(aen, 0, sizeof(*aen));
aen->sess = tgt_dev->sess;
scst_sess_get(aen->sess);
aen->sess = sess;
scst_sess_get(sess);
aen->lun = scst_pack_lun(tgt_dev->lun);
aen->lun = scst_pack_lun(unpacked_lun);
out:
TRACE_EXIT_HRES((unsigned long)aen);
@@ -446,11 +447,55 @@ static void scst_free_aen(struct scst_aen *aen)
return;
};
/* Must be called unded scst_mutex */
void scst_gen_aen_or_ua(struct scst_tgt_dev *tgt_dev,
int key, int asc, int ascq)
{
struct scst_tgt_template *tgtt = tgt_dev->sess->tgt->tgtt;
uint8_t sense_buffer[SCST_STANDARD_SENSE_LEN];
TRACE_ENTRY();
if (tgtt->report_aen != NULL) {
struct scst_aen *aen;
int rc;
aen = scst_alloc_aen(tgt_dev->sess, tgt_dev->lun);
if (aen == NULL)
goto queue_ua;
aen->event_fn = SCST_AEN_SCSI;
aen->aen_sense_len = SCST_STANDARD_SENSE_LEN;
scst_set_sense(aen->aen_sense, aen->aen_sense_len,
tgt_dev->dev->d_sense, key, asc, ascq);
TRACE_DBG("Calling target's %s report_aen(%p)",
tgtt->name, aen);
rc = tgtt->report_aen(aen);
TRACE_DBG("Target's %s report_aen(%p) returned %d",
tgtt->name, aen, rc);
if (rc == SCST_AEN_RES_SUCCESS)
goto out;
scst_free_aen(aen);
}
queue_ua:
TRACE_MGMT_DBG("AEN not supported, queuing plain UA (tgt_dev %p)",
tgt_dev);
scst_set_sense(sense_buffer, sizeof(sense_buffer),
tgt_dev->dev->d_sense, key, asc, ascq);
scst_check_set_UA(tgt_dev, sense_buffer, sizeof(sense_buffer), 0);
out:
TRACE_EXIT();
return;
}
/* No locks */
void scst_capacity_data_changed(struct scst_device *dev)
{
struct scst_tgt_dev *tgt_dev;
uint8_t sense_buffer[SCST_STANDARD_SENSE_LEN];
TRACE_ENTRY();
@@ -466,40 +511,8 @@ void scst_capacity_data_changed(struct scst_device *dev)
list_for_each_entry(tgt_dev, &dev->dev_tgt_dev_list,
dev_tgt_dev_list_entry) {
struct scst_tgt_template *tgtt = tgt_dev->sess->tgt->tgtt;
if (tgtt->report_aen != NULL) {
struct scst_aen *aen;
int rc;
aen = scst_alloc_aen(tgt_dev);
if (aen == NULL)
goto queue_ua;
aen->event_fn = SCST_AEN_SCSI;
aen->aen_sense_len = SCST_STANDARD_SENSE_LEN;
scst_set_sense(aen->aen_sense, aen->aen_sense_len,
tgt_dev->dev->d_sense,
SCST_LOAD_SENSE(scst_sense_capacity_data_changed));
TRACE_DBG("Calling target's %s report_aen(%p)",
tgtt->name, aen);
rc = tgtt->report_aen(aen);
TRACE_DBG("Target's %s report_aen(%p) returned %d",
tgtt->name, aen, rc);
if (rc == SCST_AEN_RES_SUCCESS)
continue;
scst_free_aen(aen);
}
queue_ua:
TRACE_MGMT_DBG("Queuing CAPACITY DATA CHANGED UA (tgt_dev %p)",
tgt_dev);
scst_set_sense(sense_buffer, sizeof(sense_buffer),
tgt_dev->dev->d_sense,
scst_gen_aen_or_ua(tgt_dev,
SCST_LOAD_SENSE(scst_sense_capacity_data_changed));
scst_check_set_UA(tgt_dev, sense_buffer,
sizeof(sense_buffer), 0);
}
mutex_unlock(&scst_mutex);
@@ -594,41 +607,43 @@ static void scst_queue_report_luns_changed_UA(struct scst_session *sess,
static void scst_report_luns_changed_sess(struct scst_session *sess)
{
int i;
struct list_head *shead;
struct scst_tgt_dev *tgt_dev;
struct scst_tgt_template *tgtt = sess->tgt->tgtt;
int d_sense = 0;
uint64_t lun = 0;
TRACE_ENTRY();
TRACE_DBG("REPORTED LUNS DATA CHANGED (sess %p)", sess);
for (i = 0; i < TGT_DEV_HASH_SIZE; i++) {
struct list_head *shead;
struct scst_tgt_dev *tgt_dev;
shead = &sess->sess_tgt_dev_list_hash[i];
list_for_each_entry(tgt_dev, shead,
sess_tgt_dev_list_entry) {
if (scst_is_report_luns_changed_type(
tgt_dev->dev->type))
tgt_dev->dev->type)) {
lun = tgt_dev->lun;
d_sense = tgt_dev->dev->d_sense;
goto found;
}
}
}
TRACE_MGMT_DBG("Not found a device capable REPORTED "
"LUNS DATA CHANGED UA (sess %p)", sess);
goto out;
found:
if (tgtt->report_aen != NULL) {
struct scst_aen *aen;
int rc;
aen = scst_alloc_aen(tgt_dev);
aen = scst_alloc_aen(sess, lun);
if (aen == NULL)
goto queue_ua;
aen->event_fn = SCST_AEN_SCSI;
aen->aen_sense_len = SCST_STANDARD_SENSE_LEN;
scst_set_sense(aen->aen_sense, aen->aen_sense_len,
tgt_dev->dev->d_sense,
scst_set_sense(aen->aen_sense, aen->aen_sense_len, d_sense,
SCST_LOAD_SENSE(scst_sense_reported_luns_data_changed));
TRACE_DBG("Calling target's %s report_aen(%p)",
@@ -690,10 +705,7 @@ void scst_aen_done(struct scst_aen *aen)
scst_queue_report_luns_changed_UA(aen->sess,
SCST_SET_UA_FLAG_AT_HEAD);
mutex_unlock(&scst_mutex);
} else if (scst_analyze_sense(aen->aen_sense, aen->aen_sense_len,
SCST_SENSE_ALL_VALID,
SCST_LOAD_SENSE(scst_sense_capacity_data_changed))) {
/* tgt_dev might get dead, so we need to reseek it */
} else {
struct list_head *shead;
struct scst_tgt_dev *tgt_dev;
uint64_t lun;
@@ -702,12 +714,13 @@ void scst_aen_done(struct scst_aen *aen)
mutex_lock(&scst_mutex);
/* tgt_dev might get dead, so we need to reseek it */
shead = &aen->sess->sess_tgt_dev_list_hash[HASH_VAL(lun)];
list_for_each_entry(tgt_dev, shead,
sess_tgt_dev_list_entry) {
if (tgt_dev->lun == lun) {
TRACE_MGMT_DBG("Queuing CAPACITY DATA CHANGED "
"UA (tgt_dev %p)", tgt_dev);
TRACE_MGMT_DBG("Requeuing failed AEN UA for "
"tgt_dev %p", tgt_dev);
scst_check_set_UA(tgt_dev, aen->aen_sense,
aen->aen_sense_len,
SCST_SET_UA_FLAG_AT_HEAD);
@@ -716,8 +729,7 @@ void scst_aen_done(struct scst_aen *aen)
}
mutex_unlock(&scst_mutex);
} else
PRINT_ERROR("%s", "Unknown SCSI AEN");
}
out_free:
scst_free_aen(aen);
@@ -851,8 +863,6 @@ next:
list_move_tail(&sess->acg_sess_list_entry, &acg->acg_sess_list);
if (luns_changed) {
uint8_t sense_buffer[SCST_STANDARD_SENSE_LEN];
scst_report_luns_changed_sess(sess);
for (i = 0; i < TGT_DEV_HASH_SIZE; i++) {
@@ -867,13 +877,8 @@ next:
tgt_dev->inq_changed_ua_needed = 0;
scst_set_sense(sense_buffer,
sizeof(sense_buffer),
tgt_dev->dev->d_sense,
scst_gen_aen_or_ua(tgt_dev,
SCST_LOAD_SENSE(scst_sense_inquery_data_changed));
scst_check_set_UA(tgt_dev, sense_buffer,
sizeof(sense_buffer), 0);
}
}
}
@@ -1729,7 +1734,7 @@ static void scst_sess_free_tgt_devs(struct scst_session *sess)
/* The activity supposed to be suspended and scst_mutex held */
int scst_acg_add_dev(struct scst_acg *acg, struct scst_device *dev,
uint64_t lun, int read_only)
uint64_t lun, int read_only, bool gen_scst_report_luns_changed)
{
int res = 0;
struct scst_acg_dev *acg_dev;
@@ -1741,17 +1746,6 @@ int scst_acg_add_dev(struct scst_acg *acg, struct scst_device *dev,
INIT_LIST_HEAD(&tmp_tgt_dev_list);
#ifdef CONFIG_SCST_EXTRACHECKS
list_for_each_entry(acg_dev, &acg->acg_dev_list, acg_dev_list_entry) {
if (acg_dev->dev == dev) {
PRINT_ERROR("Device is already in group %s",
acg->acg_name);
res = -EINVAL;
goto out;
}
}
#endif
acg_dev = scst_alloc_acg_dev(acg, dev, lun);
if (acg_dev == NULL) {
res = -ENOMEM;
@@ -1774,7 +1768,8 @@ int scst_acg_add_dev(struct scst_acg *acg, struct scst_device *dev,
&tmp_tgt_dev_list);
}
scst_report_luns_changed(acg);
if (gen_scst_report_luns_changed)
scst_report_luns_changed(acg);
if (dev->virt_name != NULL) {
PRINT_INFO("Added device %s to group %s (LUN %lld, "
@@ -1805,7 +1800,8 @@ out_free:
}
/* The activity supposed to be suspended and scst_mutex held */
int scst_acg_remove_dev(struct scst_acg *acg, struct scst_device *dev)
int scst_acg_remove_dev(struct scst_acg *acg, struct scst_device *dev,
bool gen_scst_report_luns_changed)
{
int res = 0;
struct scst_acg_dev *acg_dev = NULL, *a;
@@ -1833,7 +1829,8 @@ int scst_acg_remove_dev(struct scst_acg *acg, struct scst_device *dev)
}
scst_free_acg_dev(acg_dev);
scst_report_luns_changed(acg);
if (gen_scst_report_luns_changed)
scst_report_luns_changed(acg);
if (dev->virt_name != NULL) {
PRINT_INFO("Removed device %s from group %s",

View File

@@ -724,9 +724,8 @@ static void scst_unregister_device(struct scsi_device *scsidp)
list_del(&dev->dev_list_entry);
list_for_each_entry_safe(acg_dev, aa, &dev->dev_acg_dev_list,
dev_acg_dev_list_entry)
{
scst_acg_remove_dev(acg_dev->acg, dev);
dev_acg_dev_list_entry) {
scst_acg_remove_dev(acg_dev->acg, dev, true);
}
scst_assign_dev_handler(dev, &scst_null_devtype);
@@ -876,7 +875,7 @@ void scst_unregister_virtual_device(int id)
list_for_each_entry_safe(acg_dev, aa, &dev->dev_acg_dev_list,
dev_acg_dev_list_entry)
{
scst_acg_remove_dev(acg_dev->acg, dev);
scst_acg_remove_dev(acg_dev->acg, dev, true);
}
scst_assign_dev_handler(dev, &scst_null_devtype);

View File

@@ -301,8 +301,9 @@ int scst_sess_alloc_tgt_devs(struct scst_session *sess);
void scst_nexus_loss(struct scst_tgt_dev *tgt_dev, bool queue_UA);
int scst_acg_add_dev(struct scst_acg *acg, struct scst_device *dev,
uint64_t lun, int read_only);
int scst_acg_remove_dev(struct scst_acg *acg, struct scst_device *dev);
uint64_t lun, int read_only, bool gen_scst_report_luns_changed);
int scst_acg_remove_dev(struct scst_acg *acg, struct scst_device *dev,
bool gen_scst_report_luns_changed);
int scst_acg_add_name(struct scst_acg *acg, const char *name);
int scst_acg_remove_name(struct scst_acg *acg, const char *name, bool reassign);
@@ -437,6 +438,9 @@ void scst_process_reset(struct scst_device *dev,
bool scst_is_ua_global(const uint8_t *sense, int len);
void scst_requeue_ua(struct scst_cmd *cmd);
void scst_gen_aen_or_ua(struct scst_tgt_dev *tgt_dev,
int key, int asc, int ascq);
static inline bool scst_is_ua_command(struct scst_cmd *cmd)
{
return (cmd->op_flags & SCST_SKIP_UA) == 0;

View File

@@ -80,11 +80,12 @@ static struct scst_proc_data scst_dev_handler_proc_data;
#define SCST_PROC_ACTION_CLEAR 6
#define SCST_PROC_ACTION_MOVE 7
#define SCST_PROC_ACTION_DEL 8
#define SCST_PROC_ACTION_VALUE 9
#define SCST_PROC_ACTION_ASSIGN 10
#define SCST_PROC_ACTION_ADD_GROUP 11
#define SCST_PROC_ACTION_DEL_GROUP 12
#define SCST_PROC_ACTION_RENAME_GROUP 13
#define SCST_PROC_ACTION_REPLACE 9
#define SCST_PROC_ACTION_VALUE 10
#define SCST_PROC_ACTION_ASSIGN 11
#define SCST_PROC_ACTION_ADD_GROUP 12
#define SCST_PROC_ACTION_DEL_GROUP 13
#define SCST_PROC_ACTION_RENAME_GROUP 14
static struct proc_dir_entry *scst_proc_scsi_tgt;
static struct proc_dir_entry *scst_proc_groups_root;
@@ -133,8 +134,12 @@ static char *scst_proc_help_string =
"\n"
" echo \"add|del H:C:I:L lun [READ_ONLY]\""
" >/proc/scsi_tgt/groups/GROUP_NAME/devices\n"
" echo \"replace H:C:I:L lun [READ_ONLY]\""
" >/proc/scsi_tgt/groups/GROUP_NAME/devices\n"
" echo \"add|del V_NAME lun [READ_ONLY]\""
" >/proc/scsi_tgt/groups/GROUP_NAME/devices\n"
" echo \"replace V_NAME lun [READ_ONLY]\""
" >/proc/scsi_tgt/groups/GROUP_NAME/devices\n"
" echo \"clear\" >/proc/scsi_tgt/groups/GROUP_NAME/devices\n"
"\n"
" echo \"add|del NAME\" >/proc/scsi_tgt/groups/GROUP_NAME/names\n"
@@ -1613,8 +1618,12 @@ static ssize_t scst_proc_groups_devices_write(struct file *file,
/*
* Usage: echo "add|del H:C:I:L lun [READ_ONLY]" \
* >/proc/scsi_tgt/groups/GROUP_NAME/devices
* or echo "replace H:C:I:L lun [READ_ONLY]" \
* >/proc/scsi_tgt/groups/GROUP_NAME/devices
* or echo "add|del V_NAME lun [READ_ONLY]" \
* >/proc/scsi_tgt/groups/GROUP_NAME/devices
* or echo "replace V_NAME lun [READ_ONLY]" \
* >/proc/scsi_tgt/groups/GROUP_NAME/devices
* or echo "clear" >/proc/scsi_tgt/groups/GROUP_NAME/devices
*/
p = buffer;
@@ -1628,6 +1637,9 @@ static ssize_t scst_proc_groups_devices_write(struct file *file,
} else if (!strncasecmp("del ", p, 4)) {
p += 4;
action = SCST_PROC_ACTION_DEL;
} else if (!strncasecmp("replace ", p, 8)) {
p += 8;
action = SCST_PROC_ACTION_REPLACE;
} else {
PRINT_ERROR("Unknown action \"%s\"", p);
res = -EINVAL;
@@ -1648,6 +1660,7 @@ static ssize_t scst_proc_groups_devices_write(struct file *file,
switch (action) {
case SCST_PROC_ACTION_ADD:
case SCST_PROC_ACTION_DEL:
case SCST_PROC_ACTION_REPLACE:
while (isspace(*p) && *p != '\0')
p++;
e = p; /* save p */
@@ -1703,6 +1716,10 @@ static ssize_t scst_proc_groups_devices_write(struct file *file,
switch (action) {
case SCST_PROC_ACTION_ADD:
case SCST_PROC_ACTION_REPLACE:
{
bool dev_replaced = false;
e++;
while (isspace(*e) && *e != '\0')
e++;
@@ -1728,35 +1745,64 @@ static ssize_t scst_proc_groups_devices_write(struct file *file,
break;
}
}
if (acg_dev) {
acg_dev = acg_dev_tmp;
PRINT_ERROR("virt lun %d already exists in group %s",
virt_lun, acg->acg_name);
res = -EINVAL;
if (acg_dev != NULL) {
if (action == SCST_PROC_ACTION_ADD) {
PRINT_ERROR("virt lun %d already exists in "
"group %s", virt_lun, acg->acg_name);
res = -EINVAL;
goto out_free_up;
} else {
/* Replace */
rc = scst_acg_remove_dev(acg, acg_dev->dev,
false);
if (rc) {
res = rc;
goto out_free_up;
}
dev_replaced = true;
}
}
rc = scst_acg_add_dev(acg, dev, virt_lun, read_only,
action == SCST_PROC_ACTION_ADD);
if (rc) {
res = rc;
goto out_free_up;
}
rc = scst_acg_add_dev(acg, dev, virt_lun, read_only);
if (rc) {
PRINT_ERROR("scst_acg_add_dev() returned %d", rc);
res = rc;
if (dev_replaced) {
struct scst_tgt_dev *tgt_dev;
list_for_each_entry(tgt_dev, &dev->dev_tgt_dev_list,
dev_tgt_dev_list_entry) {
if ((tgt_dev->acg_dev->acg == acg) &&
(tgt_dev->lun == virt_lun)) {
TRACE_MGMT_DBG("INQUIRY DATA HAS CHANGED"
" on tgt_dev %p", tgt_dev);
scst_gen_aen_or_ua(tgt_dev,
SCST_LOAD_SENSE(scst_sense_inquery_data_changed));
}
}
}
break;
}
case SCST_PROC_ACTION_DEL:
rc = scst_acg_remove_dev(acg, dev);
rc = scst_acg_remove_dev(acg, dev, true);
if (rc) {
PRINT_ERROR("scst_acg_remove_dev() returned %d", rc);
res = rc;
goto out_free_up;
}
break;
case SCST_PROC_ACTION_CLEAR:
list_for_each_entry_safe(acg_dev, acg_dev_tmp,
&acg->acg_dev_list,
acg_dev_list_entry) {
rc = scst_acg_remove_dev(acg, acg_dev->dev);
rc = scst_acg_remove_dev(acg, acg_dev->dev,
list_is_last(&acg_dev->acg_dev_list_entry,
&acg->acg_dev_list));
if (rc) {
PRINT_ERROR("scst_acg_remove_dev() "
"return %d", rc);
res = rc;
goto out_free_up;
}
}
break;