From patchwork Wed Jul 12 22:35:34 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Song Liu X-Patchwork-Id: 9837519 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id ADD51602D8 for ; Wed, 12 Jul 2017 22:36:06 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 9D28F285EC for ; Wed, 12 Jul 2017 22:36:06 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 8E7BB286AE; Wed, 12 Jul 2017 22:36:06 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.0 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 578F0285EC for ; Wed, 12 Jul 2017 22:36:05 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752994AbdGLWgE (ORCPT ); Wed, 12 Jul 2017 18:36:04 -0400 Received: from mx0a-00082601.pphosted.com ([67.231.145.42]:53270 "EHLO mx0a-00082601.pphosted.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752986AbdGLWgD (ORCPT ); Wed, 12 Jul 2017 18:36:03 -0400 Received: from pps.filterd (m0044008.ppops.net [127.0.0.1]) by mx0a-00082601.pphosted.com (8.16.0.21/8.16.0.21) with SMTP id v6CMZtrP002925 for ; Wed, 12 Jul 2017 15:36:02 -0700 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=fb.com; h=from : to : cc : subject : date : message-id : in-reply-to : references : mime-version : content-type; s=facebook; bh=X5YhAAEmqFqXyVzBlSUDOJmZQHaphX1yi9j3WlWNzb8=; b=dAFhi3Or/qBzOvhxqwjyp1MKhrvj7c2Qn40WeZ1nQhS9CmNCS06J2tw3raCaaRzdAz7d BSkm/hKH2b0/nxo5Fzcf3WnrW1qtuUfbuVTO5VsOAYjg/QxYuAvDau29BB70Cao1UxEU QRSVPPUzHkVJd2IJgNC9EonmySivlvmWDFY= Received: from mail.thefacebook.com ([199.201.64.23]) by mx0a-00082601.pphosted.com with ESMTP id 2bnrge8x31-7 (version=TLSv1 cipher=ECDHE-RSA-AES256-SHA bits=256 verify=NOT) for ; Wed, 12 Jul 2017 15:36:02 -0700 Received: from mx-out.facebook.com (192.168.52.123) by PRN-CHUB04.TheFacebook.com (192.168.16.14) with Microsoft SMTP Server (TLS) id 14.3.319.2; Wed, 12 Jul 2017 15:35:41 -0700 Received: from facebook.com (2401:db00:2120:20f2:face:0:9:0) by mx-out.facebook.com (10.102.107.97) with ESMTP id 6f23d180675211e7b0b10002c99331b0-c65ce150 for ; Wed, 12 Jul 2017 15:35:41 -0700 Received: by devbig102.frc2.facebook.com (Postfix, from userid 4523) id E4A5A4F000B9; Wed, 12 Jul 2017 15:35:39 -0700 (PDT) Smtp-Origin-Hostprefix: devbig From: Song Liu Smtp-Origin-Hostname: devbig102.frc2.facebook.com To: CC: , , , , , , Song Liu Smtp-Origin-Cluster: frc2c02 Subject: [RFC v3 1/2] scsi: generate uevent for SCSI sense code Date: Wed, 12 Jul 2017 15:35:34 -0700 Message-ID: <20170712223535.2609814-2-songliubraving@fb.com> X-Mailer: git-send-email 2.9.3 In-Reply-To: <20170712223535.2609814-1-songliubraving@fb.com> References: <20170712223535.2609814-1-songliubraving@fb.com> X-FB-Internal: Safe MIME-Version: 1.0 X-Proofpoint-Spam-Reason: safe X-FB-Internal: Safe X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10432:, , definitions=2017-07-12_07:, , signatures=0 Sender: linux-scsi-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP 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, which is a bitmap of "sense keys to generate uevent" 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[587.353177] change /devices/pci0000:00/XXXXXXXXXX ACTION=change CDB=\x88\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00 DEVPATH=/devices/pci0000:00/0000:00:01.0/0000:01:00.0/host6/XXXXXX DEVTYPE=scsi_device DRIVER=sd MODALIAS=scsi:t-0x00 SDEV_SENSE=1 SENSE_BUFFER=\x72\x03\x11\x14\x00\x00\x00\x34\x00\x0a\x80 .... SENSE_CODE=3/11/14 SEQNUM=4796 SUBSYSTEM=scsi Signed-off-by: Song Liu --- drivers/scsi/Kconfig | 14 +++++++++++ drivers/scsi/scsi_error.c | 43 ++++++++++++++++++++++++++++++++++ drivers/scsi/scsi_lib.c | 58 +++++++++++++++++++++++++++++++++++++++++++++- drivers/scsi/scsi_sysfs.c | 51 ++++++++++++++++++++++++++++++++++++++++ include/scsi/scsi_common.h | 6 +++++ include/scsi/scsi_device.h | 27 ++++++++++++++++++++- 6 files changed, 197 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index d384f4f..0fb672b 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -226,6 +226,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 ac31964..b8ef869 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c @@ -426,6 +426,48 @@ 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; + unsigned char sb_len; + + 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.cmnd = kzalloc(scmd->cmd_len, GFP_ATOMIC); + if (!evt->sense_evt_data.cmnd) + goto alloc_cmd_fail; + + sb_len = scsi_sense_data_length(scmd->sense_buffer); + + evt->sense_evt_data.sense_buffer = kzalloc(sb_len, GFP_ATOMIC); + if (!evt->sense_evt_data.sense_buffer) + goto alloc_sense_fail; + + evt->sense_evt_data.cmd_len = scmd->cmd_len; + evt->sense_evt_data.sb_len = sb_len; + memcpy(evt->sense_evt_data.cmnd, scmd->cmnd, scmd->cmd_len); + memcpy(evt->sense_evt_data.sense_buffer, scmd->sense_buffer, sb_len); + + sdev_evt_send(sdev, evt); + return; +alloc_sense_fail: + kfree(evt->sense_evt_data.cmnd); +alloc_cmd_fail: + kfree(evt); +#endif +} + /** * scsi_check_sense - Examine scsi cmd sense * @scmd: Cmd to have sense checked. @@ -446,6 +488,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 41c19c7..67cc0fb 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -2677,7 +2677,15 @@ 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]; + char *envp[5]; /* SDEV_EVT_SCSI_SENSE needs most entries (4) */ +#ifdef CONFIG_SCSI_SENSE_UEVENT + char *buf = NULL; + int i; + int buf_size; + int offset; + struct scsi_sense_hdr sshdr; +#endif + switch (evt->evt_type) { case SDEV_EVT_MEDIA_CHANGE: @@ -2702,6 +2710,49 @@ 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: + /* + * buf is used to store 3 strings: SENSE_CODE, CDB and + * SENSE_BUFFER. 4 bytes are needed for each byte in cdb + * and sense buffer. So the total size required is: + * + * 19 (SENSE_CODE=X/XX/XX\0) + 5 (CDB=\0) + + * 14 (SENSE_BUFFER=\0) + 4 * (cdb_len + sb_len); + */ + buf_size = (evt->sense_evt_data.cmd_len + + evt->sense_evt_data.sb_len) * 4 + 38; + buf = kzalloc(buf_size, GFP_KERNEL); + if (!buf) + break; + offset = 0; + + envp[idx++] = "SDEV_SENSE=1"; + envp[idx++] = buf; + scsi_normalize_sense(evt->sense_evt_data.sense_buffer, + evt->sense_evt_data.sb_len, &sshdr); + offset += snprintf(buf + offset, buf_size - offset, + "SENSE_CODE=%1x/%02x/%02x", + sshdr.sense_key, sshdr.asc, sshdr.ascq); + offset++; + + envp[idx++] = buf + offset; + offset += snprintf(buf + offset, buf_size - offset, "CDB="); + for (i = 0; i < evt->sense_evt_data.cmd_len; i++) + offset += snprintf( + buf + offset, buf_size - offset, + "\\x%02x", evt->sense_evt_data.cmnd[i]); + + offset++; + envp[idx++] = buf + offset; + offset += snprintf(buf + offset, buf_size - offset, + "SENSE_BUFFER="); + for (i = 0; i < evt->sense_evt_data.sb_len; i++) + offset += snprintf( + buf + offset, buf_size - offset, + "\\x%02x", evt->sense_evt_data.sense_buffer[i]); + break; +#endif default: /* do nothing */ break; @@ -2710,6 +2761,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); + +#ifdef CONFIG_SCSI_SENSE_UEVENT + kfree(buf); +#endif } /** @@ -2806,6 +2861,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 d6984df..937609c 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c @@ -1074,6 +1074,54 @@ 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 - 0x0f, + */ +#define SCSI_SENSE_EVENT_FILTER_MASK 0xffff + +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 (kstrtoul(buf, 0, &filter)) + return -EINVAL; + + if ((filter & 0xffff) != filter) + return -EINVAL; + + for (i = 0; i < 16; 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) { @@ -1144,6 +1192,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_common.h b/include/scsi/scsi_common.h index 20bf7ea..832ff62 100644 --- a/include/scsi/scsi_common.h +++ b/include/scsi/scsi_common.h @@ -24,6 +24,12 @@ scsi_command_size(const unsigned char *cmnd) scsi_varlen_cdb_length(cmnd) : COMMAND_SIZE(cmnd[0]); } +static inline unsigned char +scsi_sense_data_length(const unsigned char *sense_buffer) +{ + return sense_buffer[7] + 8; +} + /* Returns a human-readable name for the device */ extern const char *scsi_device_type(unsigned type); diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h index b41ee9d..aa1ac28 100644 --- a/include/scsi/scsi_device.h +++ b/include/scsi/scsi_device.h @@ -64,13 +64,24 @@ 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 { + unsigned char cmd_len; + unsigned char *cmnd; + unsigned char sb_len; + unsigned char *sense_buffer; +}; +#endif + struct scsi_event { enum scsi_device_event evt_type; struct list_head node; @@ -78,6 +89,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 { @@ -196,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;