aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/scsi/scsi_lib.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/scsi/scsi_lib.c')
-rw-r--r--drivers/scsi/scsi_lib.c149
1 files changed, 101 insertions, 48 deletions
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index 03c6d0620bfd..4848ae3c7b56 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -766,6 +766,9 @@ static void scsi_io_completion_action(struct scsi_cmnd *cmd, int result)
case 0x24: /* depopulation in progress */
action = ACTION_DELAYED_RETRY;
break;
+ case 0x0a: /* ALUA state transition */
+ blk_stat = BLK_STS_AGAIN;
+ fallthrough;
default:
action = ACTION_FAIL;
break;
@@ -1455,7 +1458,7 @@ static void scsi_softirq_done(struct request *rq)
}
/**
- * scsi_dispatch_command - Dispatch a command to the low-level driver.
+ * scsi_dispatch_cmd - Dispatch a command to the low-level driver.
* @cmd: command block we are dispatching.
*
* Return: nonzero return request was rejected and device's queue needs to be
@@ -1706,6 +1709,11 @@ out_put_budget:
if (scsi_device_blocked(sdev))
ret = BLK_STS_DEV_RESOURCE;
break;
+ case BLK_STS_AGAIN:
+ scsi_req(req)->result = DID_BUS_BUSY << 16;
+ if (req->rq_flags & RQF_DONTPREP)
+ scsi_mq_uninit_cmd(cmd);
+ break;
default:
if (unlikely(!scsi_device_online(sdev)))
scsi_req(req)->result = DID_NO_CONNECT << 16;
@@ -2334,7 +2342,7 @@ scsi_device_set_state(struct scsi_device *sdev, enum scsi_device_state state)
EXPORT_SYMBOL(scsi_device_set_state);
/**
- * sdev_evt_emit - emit a single SCSI device uevent
+ * scsi_evt_emit - emit a single SCSI device uevent
* @sdev: associated SCSI device
* @evt: event to emit
*
@@ -2382,7 +2390,7 @@ static void scsi_evt_emit(struct scsi_device *sdev, struct scsi_event *evt)
}
/**
- * sdev_evt_thread - send a uevent for each scsi event
+ * scsi_evt_thread - send a uevent for each scsi event
* @work: work struct for scsi_device
*
* Dispatch queued events to their associated scsi_device kobjects
@@ -2948,6 +2956,78 @@ void sdev_enable_disk_events(struct scsi_device *sdev)
}
EXPORT_SYMBOL(sdev_enable_disk_events);
+static unsigned char designator_prio(const unsigned char *d)
+{
+ if (d[1] & 0x30)
+ /* not associated with LUN */
+ return 0;
+
+ if (d[3] == 0)
+ /* invalid length */
+ return 0;
+
+ /*
+ * Order of preference for lun descriptor:
+ * - SCSI name string
+ * - NAA IEEE Registered Extended
+ * - EUI-64 based 16-byte
+ * - EUI-64 based 12-byte
+ * - NAA IEEE Registered
+ * - NAA IEEE Extended
+ * - EUI-64 based 8-byte
+ * - SCSI name string (truncated)
+ * - T10 Vendor ID
+ * as longer descriptors reduce the likelyhood
+ * of identification clashes.
+ */
+
+ switch (d[1] & 0xf) {
+ case 8:
+ /* SCSI name string, variable-length UTF-8 */
+ return 9;
+ case 3:
+ switch (d[4] >> 4) {
+ case 6:
+ /* NAA registered extended */
+ return 8;
+ case 5:
+ /* NAA registered */
+ return 5;
+ case 4:
+ /* NAA extended */
+ return 4;
+ case 3:
+ /* NAA locally assigned */
+ return 1;
+ default:
+ break;
+ }
+ break;
+ case 2:
+ switch (d[3]) {
+ case 16:
+ /* EUI64-based, 16 byte */
+ return 7;
+ case 12:
+ /* EUI64-based, 12 byte */
+ return 6;
+ case 8:
+ /* EUI64-based, 8 byte */
+ return 3;
+ default:
+ break;
+ }
+ break;
+ case 1:
+ /* T10 vendor ID */
+ return 1;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
/**
* scsi_vpd_lun_id - return a unique device identification
* @sdev: SCSI device
@@ -2964,7 +3044,7 @@ EXPORT_SYMBOL(sdev_enable_disk_events);
*/
int scsi_vpd_lun_id(struct scsi_device *sdev, char *id, size_t id_len)
{
- u8 cur_id_type = 0xff;
+ u8 cur_id_prio = 0;
u8 cur_id_size = 0;
const unsigned char *d, *cur_id_str;
const struct scsi_vpd *vpd_pg83;
@@ -2977,20 +3057,6 @@ int scsi_vpd_lun_id(struct scsi_device *sdev, char *id, size_t id_len)
return -ENXIO;
}
- /*
- * Look for the correct descriptor.
- * Order of preference for lun descriptor:
- * - SCSI name string
- * - NAA IEEE Registered Extended
- * - EUI-64 based 16-byte
- * - EUI-64 based 12-byte
- * - NAA IEEE Registered
- * - NAA IEEE Extended
- * - T10 Vendor ID
- * as longer descriptors reduce the likelyhood
- * of identification clashes.
- */
-
/* The id string must be at least 20 bytes + terminating NULL byte */
if (id_len < 21) {
rcu_read_unlock();
@@ -2998,39 +3064,32 @@ int scsi_vpd_lun_id(struct scsi_device *sdev, char *id, size_t id_len)
}
memset(id, 0, id_len);
- d = vpd_pg83->data + 4;
- while (d < vpd_pg83->data + vpd_pg83->len) {
- /* Skip designators not referring to the LUN */
- if ((d[1] & 0x30) != 0x00)
- goto next_desig;
+ for (d = vpd_pg83->data + 4;
+ d < vpd_pg83->data + vpd_pg83->len;
+ d += d[3] + 4) {
+ u8 prio = designator_prio(d);
+
+ if (prio == 0 || cur_id_prio > prio)
+ continue;
switch (d[1] & 0xf) {
case 0x1:
/* T10 Vendor ID */
if (cur_id_size > d[3])
break;
- /* Prefer anything */
- if (cur_id_type > 0x01 && cur_id_type != 0xff)
- break;
+ cur_id_prio = prio;
cur_id_size = d[3];
if (cur_id_size + 4 > id_len)
cur_id_size = id_len - 4;
cur_id_str = d + 4;
- cur_id_type = d[1] & 0xf;
id_size = snprintf(id, id_len, "t10.%*pE",
cur_id_size, cur_id_str);
break;
case 0x2:
/* EUI-64 */
- if (cur_id_size > d[3])
- break;
- /* Prefer NAA IEEE Registered Extended */
- if (cur_id_type == 0x3 &&
- cur_id_size == d[3])
- break;
+ cur_id_prio = prio;
cur_id_size = d[3];
cur_id_str = d + 4;
- cur_id_type = d[1] & 0xf;
switch (cur_id_size) {
case 8:
id_size = snprintf(id, id_len,
@@ -3048,17 +3107,14 @@ int scsi_vpd_lun_id(struct scsi_device *sdev, char *id, size_t id_len)
cur_id_str);
break;
default:
- cur_id_size = 0;
break;
}
break;
case 0x3:
/* NAA */
- if (cur_id_size > d[3])
- break;
+ cur_id_prio = prio;
cur_id_size = d[3];
cur_id_str = d + 4;
- cur_id_type = d[1] & 0xf;
switch (cur_id_size) {
case 8:
id_size = snprintf(id, id_len,
@@ -3071,32 +3127,29 @@ int scsi_vpd_lun_id(struct scsi_device *sdev, char *id, size_t id_len)
cur_id_str);
break;
default:
- cur_id_size = 0;
break;
}
break;
case 0x8:
/* SCSI name string */
- if (cur_id_size + 4 > d[3])
+ if (cur_id_size > d[3])
break;
/* Prefer others for truncated descriptor */
- if (cur_id_size && d[3] > id_len)
- break;
+ if (d[3] > id_len) {
+ prio = 2;
+ if (cur_id_prio > prio)
+ break;
+ }
+ cur_id_prio = prio;
cur_id_size = id_size = d[3];
cur_id_str = d + 4;
- cur_id_type = d[1] & 0xf;
if (cur_id_size >= id_len)
cur_id_size = id_len - 1;
memcpy(id, cur_id_str, cur_id_size);
- /* Decrease priority for truncated descriptor */
- if (cur_id_size != id_size)
- cur_id_size = 6;
break;
default:
break;
}
-next_desig:
- d += d[3] + 4;
}
rcu_read_unlock();