]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
scsi: core: Add 'serial' sysfs attribute for SCSI/SATA
authorIgor Pylypiv <ipylypiv@google.com>
Mon, 9 Feb 2026 21:21:51 +0000 (13:21 -0800)
committerMartin K. Petersen <martin.petersen@oracle.com>
Sun, 1 Mar 2026 01:03:00 +0000 (20:03 -0500)
Add a 'serial' sysfs attribute for SCSI and SATA devices. This attribute
exposes the Unit Serial Number, which is derived from the Device
Identification Vital Product Data (VPD) page 0x80.

Whitespace is stripped from the retrieved serial number to handle the
different alignment (right-aligned for SCSI, potentially left-aligned for
SATA). As noted in SAT-5 10.5.3, "Although SPC-5 defines the PRODUCT SERIAL
NUMBER field as right-aligned, ACS-5 does not require its SERIAL NUMBER
field to be right-aligned. Therefore, right-alignment of the PRODUCT SERIAL
NUMBER field for the translation is not assured."

This attribute is used by tools such as lsblk to display the serial number
of block devices.

[mkp: length adjustment]

Signed-off-by: Igor Pylypiv <ipylypiv@google.com>
Reviewed-by: Bart Van Assche <bvanassche@acm.org>
Reviewed-by: Hannes Reinecke <hare@suse.de>
Link: https://patch.msgid.link/20260209212151.342151-1-ipylypiv@google.com
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
drivers/scsi/scsi_lib.c
drivers/scsi/scsi_sysfs.c
include/scsi/scsi_device.h

index d3a8cd4166f92fc87ccbc3c49125cae8f5b51512..6e8c7a42603e5b13deeaa487f62c4bec71fcc106 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/bitops.h>
 #include <linux/blkdev.h>
 #include <linux/completion.h>
+#include <linux/ctype.h>
 #include <linux/kernel.h>
 #include <linux/export.h>
 #include <linux/init.h>
@@ -3460,6 +3461,52 @@ int scsi_vpd_lun_id(struct scsi_device *sdev, char *id, size_t id_len)
 }
 EXPORT_SYMBOL(scsi_vpd_lun_id);
 
+/**
+ * scsi_vpd_lun_serial - return a unique device serial number
+ * @sdev: SCSI device
+ * @sn:   buffer for the serial number
+ * @sn_size: size of the buffer
+ *
+ * Copies the device serial number into @sn based on the information in
+ * the VPD page 0x80 of the device. The string will be null terminated
+ * and have leading and trailing whitespace stripped.
+ *
+ * Returns the length of the serial number or error on failure.
+ */
+int scsi_vpd_lun_serial(struct scsi_device *sdev, char *sn, size_t sn_size)
+{
+       const struct scsi_vpd *vpd_pg80;
+       const unsigned char *d;
+       int len;
+
+       guard(rcu)();
+       vpd_pg80 = rcu_dereference(sdev->vpd_pg80);
+       if (!vpd_pg80)
+               return -ENXIO;
+
+       len = vpd_pg80->len - 4;
+       d = vpd_pg80->data + 4;
+
+       /* Skip leading spaces */
+       while (len > 0 && isspace(*d)) {
+               len--;
+               d++;
+       }
+
+       /* Skip trailing spaces */
+       while (len > 0 && isspace(d[len - 1]))
+               len--;
+
+       if (sn_size < len + 1)
+               return -EINVAL;
+
+       memcpy(sn, d, len);
+       sn[len] = '\0';
+
+       return len;
+}
+EXPORT_SYMBOL(scsi_vpd_lun_serial);
+
 /**
  * scsi_vpd_tpg_id - return a target port group identifier
  * @sdev: SCSI device
index 6b8c5c05f29442f059ea3424799cda37189881fb..dfc3559e7e04f71478ba0f905c3a5e0f1e836a8d 100644 (file)
@@ -1051,6 +1051,21 @@ sdev_show_wwid(struct device *dev, struct device_attribute *attr,
 }
 static DEVICE_ATTR(wwid, S_IRUGO, sdev_show_wwid, NULL);
 
+static ssize_t
+sdev_show_serial(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct scsi_device *sdev = to_scsi_device(dev);
+       ssize_t ret;
+
+       ret = scsi_vpd_lun_serial(sdev, buf, PAGE_SIZE - 1);
+       if (ret < 0)
+               return ret;
+
+       buf[ret] = '\n';
+       return ret + 1;
+}
+static DEVICE_ATTR(serial, S_IRUGO, sdev_show_serial, NULL);
+
 #define BLIST_FLAG_NAME(name)                                  \
        [const_ilog2((__force __u64)BLIST_##name)] = #name
 static const char *const sdev_bflags_name[] = {
@@ -1295,6 +1310,7 @@ static struct attribute *scsi_sdev_attrs[] = {
        &dev_attr_device_busy.attr,
        &dev_attr_vendor.attr,
        &dev_attr_model.attr,
+       &dev_attr_serial.attr,
        &dev_attr_rev.attr,
        &dev_attr_rescan.attr,
        &dev_attr_delete.attr,
index d32f5841f4f85e36e16e75e4faac206533f5ef7e..9c2a7bbe5891efbcb3bd12b29983138f04a15abc 100644 (file)
@@ -571,6 +571,7 @@ void scsi_put_internal_cmd(struct scsi_cmnd *scmd);
 extern void sdev_disable_disk_events(struct scsi_device *sdev);
 extern void sdev_enable_disk_events(struct scsi_device *sdev);
 extern int scsi_vpd_lun_id(struct scsi_device *, char *, size_t);
+extern int scsi_vpd_lun_serial(struct scsi_device *, char *, size_t);
 extern int scsi_vpd_tpg_id(struct scsi_device *, int *);
 
 #ifdef CONFIG_PM