Hello,

On Wed, May 03, 2017 at 05:50:13PM -0700, Song Liu 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
>

I know its an RFC, but the user-interface definitely needs better
documentation. You also don't mention the 'wildcard' you document in the
code below.

Would there be any public tooling for this, when it gets in?

There is already some events we can generate.. for example when the
host-mapping changed, but I am not aware of any tooling that would ever
make use of them (although, this probably would even be vert useful).
And in the end that seems like dead-code for me. But maybe thats just me
not knowing the tooling.

>
> 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 <songliubrav...@fb.com>
> ---
>  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
> +}
> +

So when I turn this CONFIG_ off, do I get all kinds of warning about
unused variables?

>  /**
>   * 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);

Why try so hard with GFP_ATOMIC? This is run in its own work-item and AFAIK
there is no need to be so strict here.

> +                     if (!envp[i])
> +                             break;

The error-handling seems a bit optimistic. Especially when your write on
the pointers afterwards.

> +                     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;

How do you get to this 3x'32'? Seems like quite some waste when we talk
about frequent events.

> +#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

Why omit 0x09? Its vendor specific, but that doesn't mean no-one ever
will use it? Well, I don't know a real-life example, but anyway, doesn't
hurt IMO.

Also SPC-4 specifies 0x0F.

> +
> +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;

Why the manual parsing? AFAIK kstrtoul can do that already, if you pass
0 as second argument.


                                                    Beste Grüße / Best regards,
                                                      - Benjamin Block
> +
> +     /*
> +      * 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
>

--
Linux on z Systems Development         /         IBM Systems & Technology Group
                  IBM Deutschland Research & Development GmbH
Vorsitz. AufsR.: Martina Koederitz     /        Geschäftsführung: Dirk Wittkopp
Sitz der Gesellschaft: Böblingen / Registergericht: AmtsG Stuttgart, HRB 243294

Reply via email to