[linux-yocto] [PATCH 7/8] scsi_lib: add extended information to MEDIA_CHANGE

Yong, Jonathan jonathan.yong at intel.com
Thu Jun 30 00:06:53 PDT 2016


From: Vitaly Wool <vwool at dev.rtsoft.ru>

Add media change reason string to SDEV_MEDIA_CHANGE uevent
in the format of "SDEV_MEDIA_CHANGE_REASON=<reason>, where
reason is one of MEDIA_DETACH, MEDIA_ATTACH and MEDIA_BAD.

Signed-off-by: Vitaly Wool <vwool at dev.rtsoft.ru>
Signed-off-by: Nikita Yushchenko <nyushchenko at dev.rtsoft.ru>
Signed-off-by: Lim Key Seong <key.seong.lim at intel.com>
---
 drivers/scsi/scsi_lib.c    | 66 ++++++++++++++++++++++++++++++++++++++++++++++
 drivers/scsi/sr.c          |  1 +
 include/scsi/scsi_device.h | 18 +++++++++++++
 3 files changed, 85 insertions(+)

diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index 448ebda..de991c4 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -2676,6 +2676,64 @@ scsi_device_set_state(struct scsi_device *sdev, enum scsi_device_state state)
 }
 EXPORT_SYMBOL(scsi_device_set_state);
 
+#define MAX_RETRIES     3
+#define SR_TIMEOUT      (3 * HZ)
+
+/**
+ *	get_change_reason - Obtain media change reason by querying the device
+ *	@sdev:	scsi device to get media change reason from.
+ *
+ *	Returns reason as specified in @scsi_media_change_reason
+ */
+static enum scsi_media_change_reason get_change_reason(struct scsi_device *sdev)
+{
+	int ret, is_good;
+	enum scsi_media_change_reason reason = SDEV_MEDIA_BAD;
+	struct scsi_sense_hdr sshdr;
+
+	ret = scsi_test_unit_ready(sdev, SR_TIMEOUT, MAX_RETRIES, &sshdr);
+	is_good = scsi_status_is_good(ret);
+	pr_debug("%s: changed %d, is_good %d, asc 0x%x, ascq 0x%x\n",
+		__func__, sdev->changed, is_good, sshdr.asc, sshdr.ascq);
+
+	if (is_good)
+		reason = SDEV_MEDIA_ATTACH;
+	else {
+		switch (sshdr.asc) {
+		case 0x28:
+		case 0x29:
+			reason = SDEV_MEDIA_UNDEF;
+			break;
+
+		case 0x04:
+			if (sshdr.ascq == 0x01) {
+				reason = SDEV_MEDIA_UNDEF;
+				break;
+			}
+		/* otherwise fall through */
+		case 0x3A:
+			reason = SDEV_MEDIA_DETACH;
+			break;
+
+		default:
+			reason = SDEV_MEDIA_BAD;
+			break;
+		}
+	}
+	if (!sdev->changed && reason == sdev->last_change_reason)
+		reason = SDEV_MEDIA_UNDEF;
+	else
+		sdev->last_change_reason = reason;
+	return reason;
+}
+
+static char *media_change_reasons[SDEV_MEDIA_REASON_MAX + 1] = {
+	[SDEV_MEDIA_ATTACH] = "SDEV_MEDIA_CHANGE_REASON=MEDIA_ATTACH",
+	[SDEV_MEDIA_DETACH] = "SDEV_MEDIA_CHANGE_REASON=MEDIA_DETACH",
+	[SDEV_MEDIA_BAD] = "SDEV_MEDIA_CHANGE_REASON=MEDIA_BAD",
+};
+
+
 /**
  * 	sdev_evt_emit - emit a single SCSI device uevent
  *	@sdev: associated SCSI device
@@ -2687,10 +2745,18 @@ static void scsi_evt_emit(struct scsi_device *sdev, struct scsi_event *evt)
 {
 	int idx = 0;
 	char *envp[3];
+	enum scsi_media_change_reason r;
 
 	switch (evt->evt_type) {
 	case SDEV_EVT_MEDIA_CHANGE:
 		envp[idx++] = "SDEV_MEDIA_CHANGE=1";
+		if (sdev->add_change_reason) {
+			r = get_change_reason(sdev);
+			if (media_change_reasons[r])
+				envp[idx++] = media_change_reasons[r];
+			pr_debug("%s: reason %s\n", __func__,
+				media_change_reasons[r] ?: "n/a");
+		}
 		break;
 	case SDEV_EVT_INQUIRY_CHANGE_REPORTED:
 		envp[idx++] = "SDEV_UA=INQUIRY_DATA_HAS_CHANGED";
diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c
index 64c8674..ca8883c 100644
--- a/drivers/scsi/sr.c
+++ b/drivers/scsi/sr.c
@@ -732,6 +732,7 @@ static int sr_probe(struct device *dev)
 	disk->flags |= GENHD_FL_REMOVABLE;
 	add_disk(disk);
 
+	sdev->add_change_reason = 1; /* Let SCSI add change reason to uevent */
 	sdev_printk(KERN_DEBUG, sdev,
 		    "Attached scsi CD-ROM %s\n", cd->cdi.name);
 	scsi_autopm_put_device(cd->device);
diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h
index a4c9336..012626f 100644
--- a/include/scsi/scsi_device.h
+++ b/include/scsi/scsi_device.h
@@ -64,6 +64,21 @@ enum scsi_device_event {
 	SDEV_EVT_MAXBITS	= SDEV_EVT_LAST + 1
 };
 
+/**
+ * SCSI media change event reasons
+ * @SDEV_MEDIA_ATTACH: a valid medium has been inserted
+ * @SDEV_MEDIA_BAD: an unreadable medium has been inserted into the drive
+ * @SDEV_MEDIA_DETACH: a medium has been removed
+ * @SDEV_MEDIA_UNDEF: no valid reason has been detected
+ */
+enum scsi_media_change_reason {
+	SDEV_MEDIA_ATTACH = 1,
+	SDEV_MEDIA_BAD,
+	SDEV_MEDIA_DETACH,
+	SDEV_MEDIA_UNDEF,
+	SDEV_MEDIA_REASON_MAX = SDEV_MEDIA_UNDEF
+};
+
 struct scsi_event {
 	enum scsi_device_event	evt_type;
 	struct list_head	node;
@@ -172,6 +187,7 @@ struct scsi_device {
 	unsigned is_visible:1;	/* is the device visible in sysfs */
 	unsigned wce_default_on:1;	/* Cache is ON by default */
 	unsigned no_dif:1;	/* T10 PI (DIF) should be disabled */
+	unsigned add_change_reason:1; /* Add media change reason? */
 	unsigned broken_fua:1;		/* Don't set FUA bit */
 	unsigned lun_in_cdb:1;		/* Store LUN bits in CDB[1] */
 
@@ -197,6 +213,8 @@ struct scsi_device {
 
 	struct scsi_dh_data	*scsi_dh_data;
 	enum scsi_device_state sdev_state;
+
+	enum scsi_media_change_reason	last_change_reason;
 	unsigned long		sdev_data[0];
 } __attribute__((aligned(sizeof(unsigned long))));
 
-- 
2.7.3



More information about the linux-yocto mailing list