> On May 3, 2017, at 5:50 PM, Song Liu <songliubraving@xxxxxx> wrote: > > This patch adds capability for SCSI layer to generate uevent for SCSI > sense code. The feature is gated by CONFIG_SCSI_SENSE_UEVENT. > > We can configure which sense keys generate uevent for each device > through sysfs entry sense_event_filter. For example, the following > enables uevent for MEDIUM_ERROR (0x03) and HARDWARE_ERROR (0x04) > on scsi drive sdc: > > echo 0x000c > /sys/block/sdc/device/sense_event_filter > > Here is an example output captured by udevadm: > > KERNEL[1214.945358] change /devices/pci0000:00/XXXXXXX > ACTION=change > DEVPATH=/devices/pci0000:00/0000:00:01.0/0000:01:00.0/host6/XXXXXXX > DEVTYPE=scsi_device > DRIVER=sd > LBA=0 > MODALIAS=scsi:t-0x00 > SDEV_UA=SCSI_SENSE > SENSE_CODE=3/11/14 > SEQNUM=4536 > SIZE=4096 > SUBSYSTEM=scsi > > Signed-off-by: Song Liu <songliubraving@xxxxxx> > --- > drivers/scsi/Kconfig | 14 +++++++++++ > drivers/scsi/scsi_error.c | 26 ++++++++++++++++++++ > drivers/scsi/scsi_lib.c | 27 +++++++++++++++++++-- > drivers/scsi/scsi_sysfs.c | 60 ++++++++++++++++++++++++++++++++++++++++++++++ > include/scsi/scsi_device.h | 26 +++++++++++++++++++- > 5 files changed, 150 insertions(+), 3 deletions(-) > > diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig > index 3c52867..4f7f211 100644 > --- a/drivers/scsi/Kconfig > +++ b/drivers/scsi/Kconfig > @@ -237,6 +237,20 @@ config SCSI_LOGGING > there should be no noticeable performance impact as long as you have > logging turned off. > > +config SCSI_SENSE_UEVENT > + bool "SCSI sense code logging" > + depends on SCSI > + default n > + ---help--- > + This turns on uevent for SCSI sense code. > + > + You can configure which sense keys generate uevent for each device > + through sysfs entry sense_event_filter. For example, the following > + enables uevent for MEDIUM_ERROR (0x03) and HARDWARE_ERROR (0x04) > + on scsi drive sdc: > + > + echo 0x000c > /sys/block/sdc/device/sense_event_filter > + > config SCSI_SCAN_ASYNC > bool "Asynchronous SCSI scanning" > depends on SCSI > diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c > index d70c67c..eda150e 100644 > --- a/drivers/scsi/scsi_error.c > +++ b/drivers/scsi/scsi_error.c > @@ -426,6 +426,31 @@ static void scsi_report_sense(struct scsi_device *sdev, > } > } > > +/* > + * generate uevent when receiving sense code from device > + */ > +static void scsi_send_sense_uevent(struct scsi_device *sdev, > + struct scsi_cmnd *scmd, > + struct scsi_sense_hdr *sshdr) > +{ > +#ifdef CONFIG_SCSI_SENSE_UEVENT > + struct scsi_event *evt; > + > + if (!test_bit(sshdr->sense_key & 0xf, > + &sdev->sense_event_filter)) > + return; > + evt = sdev_evt_alloc(SDEV_EVT_SCSI_SENSE, GFP_ATOMIC); > + if (!evt) > + return; > + > + evt->sense_evt_data.lba = scsi_get_lba(scmd); > + evt->sense_evt_data.size = blk_rq_bytes(scmd->request); > + memcpy(&evt->sense_evt_data.sshdr, sshdr, > + sizeof(struct scsi_sense_hdr)); > + sdev_evt_send(sdev, evt); > +#endif > +} > + > /** > * scsi_check_sense - Examine scsi cmd sense > * @scmd: Cmd to have sense checked. > @@ -446,6 +471,7 @@ int scsi_check_sense(struct scsi_cmnd *scmd) > return FAILED; /* no valid sense data */ > > scsi_report_sense(sdev, &sshdr); > + scsi_send_sense_uevent(sdev, scmd, &sshdr); > > if (scsi_sense_is_deferred(&sshdr)) > return NEEDS_RETRY; > diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c > index 95f963b..1095f27 100644 > --- a/drivers/scsi/scsi_lib.c > +++ b/drivers/scsi/scsi_lib.c > @@ -2656,8 +2656,9 @@ EXPORT_SYMBOL(scsi_device_set_state); > */ > static void scsi_evt_emit(struct scsi_device *sdev, struct scsi_event *evt) > { > - int idx = 0; > - char *envp[3]; > + int idx = 0, i; > + char *envp[5]; /* SDEV_EVT_SCSI_SENSE needs most entries (4) */ > + int free_envp = -1; > > switch (evt->evt_type) { > case SDEV_EVT_MEDIA_CHANGE: > @@ -2682,6 +2683,23 @@ static void scsi_evt_emit(struct scsi_device *sdev, struct scsi_event *evt) > case SDEV_EVT_ALUA_STATE_CHANGE_REPORTED: > envp[idx++] = "SDEV_UA=ASYMMETRIC_ACCESS_STATE_CHANGED"; > break; > +#ifdef CONFIG_SCSI_SENSE_UEVENT > + case SDEV_EVT_SCSI_SENSE: > + envp[idx++] = "SDEV_UA=SCSI_SENSE"; > + for (i = idx; i < idx + 3; ++i) { > + envp[i] = kzalloc(32, GFP_ATOMIC); > + if (!envp[i]) > + break; > + free_envp = i; > + } > + snprintf(envp[idx++], 32, "LBA=%lu", evt->sense_evt_data.lba); > + snprintf(envp[idx++], 32, "SIZE=%d", evt->sense_evt_data.size); > + snprintf(envp[idx++], 32, "SENSE_CODE=%1x/%02x/%02x", > + evt->sense_evt_data.sshdr.sense_key, > + evt->sense_evt_data.sshdr.asc, > + evt->sense_evt_data.sshdr.ascq); > + break; > +#endif > default: > /* do nothing */ > break; > @@ -2690,6 +2708,10 @@ static void scsi_evt_emit(struct scsi_device *sdev, struct scsi_event *evt) > envp[idx++] = NULL; > > kobject_uevent_env(&sdev->sdev_gendev.kobj, KOBJ_CHANGE, envp); > + > + /* no need to free envp[0], so start with i = 1 */ > + for (i = 1 ; i < free_envp; ++i) > + kfree(envp[i]); > } > > /** > @@ -2786,6 +2808,7 @@ struct scsi_event *sdev_evt_alloc(enum scsi_device_event evt_type, > case SDEV_EVT_MODE_PARAMETER_CHANGE_REPORTED: > case SDEV_EVT_LUN_CHANGE_REPORTED: > case SDEV_EVT_ALUA_STATE_CHANGE_REPORTED: > + case SDEV_EVT_SCSI_SENSE: > default: > /* do nothing */ > break; > diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c > index 82dfe07..cfc7380 100644 > --- a/drivers/scsi/scsi_sysfs.c > +++ b/drivers/scsi/scsi_sysfs.c > @@ -1072,6 +1072,63 @@ static DEVICE_ATTR(queue_ramp_up_period, S_IRUGO | S_IWUSR, > sdev_show_queue_ramp_up_period, > sdev_store_queue_ramp_up_period); > > +#ifdef CONFIG_SCSI_SENSE_UEVENT > + > +/* > + * SCSI sense key could be 0x00 - 0x08, 0x0a, 0x0b, 0x0d, 0x0e, so the > + * mask is 0x6dff. > + */ > +#define SCSI_SENSE_EVENT_FILTER_MASK 0x6dff > + > +static ssize_t > +sdev_show_sense_event_filter(struct device *dev, > + struct device_attribute *attr, > + char *buf) > +{ > + struct scsi_device *sdev; > + > + sdev = to_scsi_device(dev); > + return snprintf(buf, 20, "0x%04lx\n", > + (sdev->sense_event_filter & > + SCSI_SENSE_EVENT_FILTER_MASK)); > +} > + > +static ssize_t > +sdev_store_sense_event_filter(struct device *dev, > + struct device_attribute *attr, > + const char *buf, size_t count) > +{ > + struct scsi_device *sdev = to_scsi_device(dev); > + unsigned long filter; > + int i; > + > + if (buf[0] == '0' && buf[1] == 'x') { > + if (kstrtoul(buf + 2, 16, &filter)) > + return -EINVAL; > + } else > + if (kstrtoul(buf, 10, &filter)) > + return -EINVAL; > + > + /* > + * Accurate mask for all sense keys is 0x6dff. However, we allow > + * user to enable event for all sense keys by echoing 0xffff > + */ > + if ((filter & 0xffff) != filter) > + return -EINVAL; > + > + for (i = 0; i < 15; i++) > + if (filter & SCSI_SENSE_EVENT_FILTER_MASK & (1 << i)) > + set_bit(i, &sdev->sense_event_filter); > + else > + clear_bit(i, &sdev->sense_event_filter); > + return count; > +} > + > +static DEVICE_ATTR(sense_event_filter, 0644, > + sdev_show_sense_event_filter, > + sdev_store_sense_event_filter); > +#endif > + > static umode_t scsi_sdev_attr_is_visible(struct kobject *kobj, > struct attribute *attr, int i) > { > @@ -1142,6 +1199,9 @@ static struct attribute *scsi_sdev_attrs[] = { > &dev_attr_preferred_path.attr, > #endif > &dev_attr_queue_ramp_up_period.attr, > +#ifdef CONFIG_SCSI_SENSE_UEVENT > + &dev_attr_sense_event_filter.attr, > +#endif > REF_EVT(media_change), > REF_EVT(inquiry_change_reported), > REF_EVT(capacity_change_reported), > diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h > index 05641ae..d160bd7 100644 > --- a/include/scsi/scsi_device.h > +++ b/include/scsi/scsi_device.h > @@ -64,13 +64,23 @@ enum scsi_device_event { > SDEV_EVT_MODE_PARAMETER_CHANGE_REPORTED, /* 2A 01 UA reported */ > SDEV_EVT_LUN_CHANGE_REPORTED, /* 3F 0E UA reported */ > SDEV_EVT_ALUA_STATE_CHANGE_REPORTED, /* 2A 06 UA reported */ > + SDEV_EVT_SCSI_SENSE, > > SDEV_EVT_FIRST = SDEV_EVT_MEDIA_CHANGE, > - SDEV_EVT_LAST = SDEV_EVT_ALUA_STATE_CHANGE_REPORTED, > + SDEV_EVT_LAST = SDEV_EVT_SCSI_SENSE, > > SDEV_EVT_MAXBITS = SDEV_EVT_LAST + 1 > }; > > +#ifdef CONFIG_SCSI_SENSE_UEVENT > +/* data for for SDEV_EVT_SCSI_SENSE */ > +struct scsi_sense_uevent_data { > + sector_t lba; /* LBA from the scsi command */ > + int size; /* size of the request */ > + struct scsi_sense_hdr sshdr; > +}; > +#endif > + > struct scsi_event { > enum scsi_device_event evt_type; > struct list_head node; > @@ -78,6 +88,11 @@ struct scsi_event { > /* put union of data structures, for non-simple event types, > * here > */ > + union { > +#ifdef CONFIG_SCSI_SENSE_UEVENT > + struct scsi_sense_uevent_data sense_evt_data; > +#endif > + }; > }; > > struct scsi_device { > @@ -197,6 +212,15 @@ struct scsi_device { > atomic_t iodone_cnt; > atomic_t ioerr_cnt; > > +#ifdef CONFIG_SCSI_SENSE_UEVENT > + /* > + * filter of sense code uevent > + * setting bit X (0x00 - 0x0e) of sense_event_filter enables > + * uevent for sense key X > + */ > + unsigned long sense_event_filter; > +#endif > + > struct device sdev_gendev, > sdev_dev; > > -- > 2.9.3 Dear Hannes and James, Could you please share your feedback on this change? Thanks, Song