Blob Blame History Raw
From 3e687d8072f3ed53ae727ec2cb99ae56dbcdf02b Mon Sep 17 00:00:00 2001
From: Peter Jones <pjones@redhat.com>
Date: Mon, 1 Oct 2018 14:35:01 -0400
Subject: [PATCH 39/39] sas: handle port expanders at all.

Signed-off-by: Peter Jones <pjones@redhat.com>
---
 src/linux-ata.c  |   3 +-
 src/linux-sas.c  | 168 ++++++++++++++++++++++++++++++++++++++++++-----
 src/linux-scsi.c | 105 +++++++++++++++++++++++++----
 src/linux.h      |   6 +-
 4 files changed, 250 insertions(+), 32 deletions(-)

diff --git a/src/linux-ata.c b/src/linux-ata.c
index 32cb99361e5..43e5f4c5d23 100644
--- a/src/linux-ata.c
+++ b/src/linux-ata.c
@@ -114,7 +114,8 @@ parse_ata(struct device *dev, const char *current, const char *root UNUSED)
 
         pos = parse_scsi_link(host + 1, &scsi_host,
                               &scsi_bus, &scsi_device,
-                              &scsi_target, &scsi_lun);
+                              &scsi_target, &scsi_lun,
+                              NULL, NULL, NULL);
         if (pos < 0)
                 return -1;
 
diff --git a/src/linux-sas.c b/src/linux-sas.c
index 4d77d39a24d..bb04fe83064 100644
--- a/src/linux-sas.c
+++ b/src/linux-sas.c
@@ -28,6 +28,91 @@
 
 #include "efiboot.h"
 
+static int
+get_port_expander_sas_address(uint64_t *sas_address, uint32_t scsi_host,
+                              uint32_t local_port_id,
+                              uint32_t remote_port_id, uint32_t remote_scsi_target)
+{
+        uint8_t *filebuf = NULL;
+        int rc;
+
+        /*
+         * We find sas_address via this insanity:
+         * /sys/class/scsi_host/host2 -> ../../devices/pci0000:74/0000:74:02.0/host2/scsi_host/host2
+         * /sys/devices/pci0000:74/0000:74:02.0/host2/scsi_host/host2/device -> ../../../host2
+         * /sys/devices/pci0000:74/0000:74:02.0/host2/device -> ../../../host2
+         * /sys/devices/host2/port-2:0/expander-2:0/sas_device/expander-2:0/sas_address
+         *
+         * But since host2 is always host2, we can skip most of that and just
+         * go for:
+         * /sys/devices/pci0000:74/0000:74:02.0/host2/port-2:0/expander-2:0/port-2:0:2/end_device-2:0:2/sas_device/end_device-2:0:2/sas_address
+        */
+
+#if 0 /* previously thought this was right, but it's the expander's address, not the target's address */
+        /*
+         * /sys/class/scsi_host/host2/device/port-2:0/expander-2:0/sas_device/expander-2:0/sas_address
+         * ... I think.  I would have expected that to be port-2:0:0 and I
+         * don't understand why it isn't. (I do now; this is the expander not
+         * the port.)
+         */
+
+        debug("looking for /sys/class/scsi_host/host%d/device/port-%d:%d/expander-%d:%d/sas_device/expander-%d:%d/sas_address",
+              scsi_host, scsi_host, port_id, scsi_host, remote_scsi_target, scsi_host, remote_scsi_target);
+        rc = read_sysfs_file(&filebuf,
+                             "class/scsi_host/host%d/device/port-%d:%d/expander-%d:%d/sas_device/expander-%d:%d/sas_address",
+                             scsi_host, scsi_host, port_id, scsi_host, remote_scsi_target, scsi_host, remote_scsi_target);
+        if (rc < 0 || filebuf == NULL) {
+                debug("didn't find it.");
+                return -1;
+        }
+#else
+        debug("looking for /sys/class/scsi_host/host%d/device/port-%d:%d/expander-%d:%d/port-%d:%d:%d/end_device-%d:%d:%d/sas_device/end_device-%d:%d:%d/sas_address",
+              scsi_host,
+              scsi_host, local_port_id,
+              scsi_host, remote_scsi_target,
+              scsi_host, remote_scsi_target, remote_port_id,
+              scsi_host, remote_scsi_target, remote_port_id,
+              scsi_host, remote_scsi_target, remote_port_id);
+        rc = read_sysfs_file(&filebuf,
+                             "class/scsi_host/host%d/device/port-%d:%d/expander-%d:%d/port-%d:%d:%d/end_device-%d:%d:%d/sas_device/end_device-%d:%d:%d/sas_address",
+                             scsi_host,
+                             scsi_host, local_port_id,
+                             scsi_host, remote_scsi_target,
+                             scsi_host, remote_scsi_target, remote_port_id,
+                             scsi_host, remote_scsi_target, remote_port_id,
+                             scsi_host, remote_scsi_target, remote_port_id);
+        if (rc < 0 || filebuf == NULL) {
+                debug("didn't find it.");
+                return -1;
+        }
+#endif
+
+        rc = sscanf((char *)filebuf, "%"PRIx64, sas_address);
+        if (rc != 1)
+                return -1;
+
+        return 0;
+}
+
+static int
+get_local_sas_address(uint64_t *sas_address, struct device *dev)
+{
+        int rc;
+        char *filebuf = NULL;
+
+        rc = read_sysfs_file(&filebuf,
+                             "class/block/%s/device/sas_address",
+                             dev->disk_name);
+        if (rc < 0 || filebuf == NULL)
+                return -1;
+
+        rc = sscanf((char *)filebuf, "%"PRIx64, sas_address);
+        if (rc != 1)
+                return -1;
+
+        return 0;
+}
+
 /*
  * support for SAS devices
  *
@@ -43,6 +128,24 @@
  * /sys/class/block/sdc/device/sas_address
  *
  * I'm not sure at the moment if they're the same or not.
+ *
+ * There are also other devices that look like:
+ *
+ * 8:0 -> ../../devices/pci0000:74/0000:74:02.0/host2/port-2:0/expander-2:0/port-2:0:2/end_device-2:0:2/target2:0:0/2:0:0:0/block/sda
+ * 8:1 -> ../../devices/pci0000:74/0000:74:02.0/host2/port-2:0/expander-2:0/port-2:0:2/end_device-2:0:2/target2:0:0/2:0:0:0/block/sda/sda1
+ *
+ * /sys/dev/block/8:0/device -> ../../../2:0:0:0
+ *
+ * This exists:
+ *
+ * /sys/class/scsi_host/host2 -> ../../devices/pci0000:74/0000:74:02.0/host2/scsi_host/host2
+ * /sys/devices/pci0000:74/0000:74:02.0/host2/scsi_host/host2/device -> ../../../host2
+ * /sys/devices/pci0000:74/0000:74:02.0/host2/device -> ../../../host2
+ * /sys/devices/pci0000:74/0000:74:02.0/host2/port-2:0/expander-2:0/sas_device/expander-2:0/sas_address
+ *
+ * but the device doesn't actually have a sas_host_address, because it's on a
+ * port expander, and sas_address doesn't directly exist under /sys/class/
+ * anywhere.
  */
 static ssize_t
 parse_sas(struct device *dev, const char *current, const char *root UNUSED)
@@ -50,16 +153,19 @@ parse_sas(struct device *dev, const char *current, const char *root UNUSED)
         struct stat statbuf = { 0, };
         int rc;
         uint32_t scsi_host, scsi_bus, scsi_device, scsi_target;
+        uint32_t local_port_id = 0, remote_port_id = 0;
+        uint32_t remote_scsi_target = 0;
         uint64_t scsi_lun;
         ssize_t pos;
-        uint8_t *filebuf = NULL;
-        uint64_t sas_address;
+        uint64_t sas_address = 0;
 
         debug("entry");
 
         pos = parse_scsi_link(current, &scsi_host,
                               &scsi_bus, &scsi_device,
-                              &scsi_target, &scsi_lun);
+                              &scsi_target, &scsi_lun,
+                              &local_port_id, &remote_port_id,
+                              &remote_scsi_target);
         /*
          * If we can't parse the scsi data, it isn't a sas device, so return 0
          * not error.
@@ -71,6 +177,7 @@ parse_sas(struct device *dev, const char *current, const char *root UNUSED)
          * Make sure it has the actual /SAS/ bits before we continue
          * validating all this junk.
          */
+        debug("looking for /sys/class/scsi_host/host%d/host_sas_address", scsi_host);
         rc = sysfs_stat(&statbuf,
                         "class/scsi_host/host%d/host_sas_address",
                         scsi_host);
@@ -79,21 +186,48 @@ parse_sas(struct device *dev, const char *current, const char *root UNUSED)
          * 0 not error. Later errors mean it is an ata device, but we can't
          * parse it right, so they return -1.
          */
-        if (rc < 0)
-                return 0;
+        if (rc < 0) {
+                debug("didn't find it.");
+                /*
+                 * If it's on a port expander, it won't have the
+                 * host_sas_address, so we need to check if it's a sas_host
+                 * instead.
+                 * It may work to just check this to begin with, but I don't
+                 * have such a device in front of me right now.
+                 */
+                debug("looking for /sys/class/sas_host/host%d", scsi_host);
+                rc = sysfs_stat(&statbuf,
+                                "class/sas_host/host%d", scsi_host);
+                if (rc < 0) {
+                        debug("didn't find it.");
+                        return 0;
+                }
+                debug("found it.");
 
-        /*
-         * we also need to get the actual sas_address from someplace...
-         */
-        rc = read_sysfs_file(&filebuf,
-                             "class/block/%s/device/sas_address",
-                             dev->disk_name);
-        if (rc < 0 || filebuf == NULL)
-                return -1;
-
-        rc = sscanf((char *)filebuf, "%"PRIx64, &sas_address);
-        if (rc != 1)
-                return -1;
+                /*
+                 * So it *is* a sas_host, and we have to fish the sas_address
+                 * from the remote port
+                 */
+                rc = get_port_expander_sas_address(&sas_address, scsi_host,
+                                                   local_port_id,
+                                                   remote_port_id,
+                                                   remote_scsi_target);
+                if (rc < 0) {
+                        debug("Couldn't find port expander sas address");
+                        return 0;
+                }
+        } else {
+                /*
+                 * we also need to get the actual sas_address from someplace...
+                 */
+                debug("found it.");
+                rc = get_local_sas_address(&sas_address, dev);
+                if (rc < 0) {
+                        debug("Couldn't find sas address");
+                        return 0;
+                }
+        }
+        debug("sas address is 0x%"PRIx64, sas_address);
 
         dev->sas_info.sas_address = sas_address;
 
diff --git a/src/linux-scsi.c b/src/linux-scsi.c
index 2e4f710badf..a5e81cf9cb6 100644
--- a/src/linux-scsi.c
+++ b/src/linux-scsi.c
@@ -38,7 +38,9 @@
 ssize_t HIDDEN
 parse_scsi_link(const char *current, uint32_t *scsi_host,
                 uint32_t *scsi_bus, uint32_t *scsi_device,
-                uint32_t *scsi_target, uint64_t *scsi_lun)
+                uint32_t *scsi_target, uint64_t *scsi_lun,
+                uint32_t *local_port_id, uint32_t *remote_port_id,
+                uint32_t *remote_target_id)
 {
         int rc;
         int sz = 0;
@@ -70,11 +72,32 @@ parse_scsi_link(const char *current, uint32_t *scsi_host,
          * /sys/block/sdc/device looks like:
          * device-> ../../../4:2:0:0
          *
+         * OR
+         *
+         * 8:0 -> ../../devices/pci0000:74/0000:74:02.0/host2/port-2:0/expander-2:0/port-2:0:2/end_device-2:0:2/target2:0:0/2:0:0:0/block/sda
+         * 8:1 -> ../../devices/pci0000:74/0000:74:02.0/host2/port-2:0/expander-2:0/port-2:0:2/end_device-2:0:2/target2:0:0/2:0:0:0/block/sda/sda1
+         *
+         * /sys/block/sda/device looks like:
+         * device -> ../../../2:0:0:0 *
+         *
+         * sas_address exists, but it's hard to find:
+         * /sys/devices/pci0000:74/0000:74:02.0/host2/port-2:0/expander-2:0/sas_device/expander-2:0/sas_address
+         * but sas_host_address is nowhere to be found, and sas_address
+         * doesn't directly exist under /sys/class/ anywhere.  So you actually
+         * have to go to
+         * /sys/devices/pci0000:74/0000:74:02.0/host2/port-2:0/expander-2:0/sas_device/expander-2:0/sas_address
+         * and chop that off to
+         * /sys/devices/pci0000:74/0000:74:02.0/host2/port-2:0/expander-2:0/
+         * and then add a bunch of port and end device crap to it to get:
+         * /sys/devices/pci0000:74/0000:74:02.0/host2/port-2:0/expander-2:0/port-2:0:2/end_device-2:0:2/sas_device/end_device-2:0:2/sas_address
+
          */
 
         /*
          * So we start when current is:
          * host4/port-4:0/end_device-4:0/target4:0:0/4:0:0:0/block/sdc/sdc1
+         * or
+         * host2/port-2:0/expander-2:0/port-2:0:2/end_device-2:0:2/target2:0:0/2:0:0:0/block/sda/sda1
          */
         uint32_t tosser0, tosser1, tosser2;
 
@@ -91,6 +114,14 @@ parse_scsi_link(const char *current, uint32_t *scsi_host,
         sz += pos0;
         pos0 = 0;
 
+        /*
+         * We might have this next:
+         * port-2:0/expander-2:0/port-2:0:2/end_device-2:0:2/target2:0:0/2:0:0:0/block/sda/sda1
+         * or:
+         * port-2:0/end_device-2:0:2/target2:0:0/2:0:0:0/block/sda/sda1
+         * or maybe (not sure):
+         * port-2:0:2/end_device-2:0:2/target2:0:0/2:0:0:0/block/sda/sda1
+         */
         debug("searching for port-4:0 or port-4:0:0");
         rc = sscanf(current+sz, "port-%d:%d%n:%d%n", &tosser0,
                     &tosser1, &pos0, &tosser2, &pos1);
@@ -100,6 +131,52 @@ parse_scsi_link(const char *current, uint32_t *scsi_host,
         if (rc == 2 || rc == 3) {
                 sz += pos0;
                 pos0 = 0;
+                if (local_port_id && rc == 2)
+                        *local_port_id = tosser1;
+                if (remote_port_id && rc == 3)
+                        *remote_port_id = tosser2;
+
+                if (current[sz] == '/')
+                        sz += 1;
+
+                /*
+                 * We might have this next:
+                 * expander-2:0/port-2:0:2/end_device-2:0:2/target2:0:0/2:0:0:0/block/sda/sda1
+                 *                       ^ port id
+                 *                     ^ scsi target id
+                 *                   ^ host number
+                 *          ^ host number
+                 * We don't actually care about either number in expander-.../,
+                 * because they're replicated in all the other places.  We just need
+                 * to get past it.
+                 */
+                debug("searching for expander-4:0/");
+                rc = sscanf(current+sz, "expander-%d:%d/%n", &tosser0, &tosser1, &pos0);
+                debug("current:\"%s\" rc:%d pos0:%d\n", current+sz, rc, pos0);
+                arrow(LOG_DEBUG, spaces, 9, pos0, rc, 2);
+                if (rc == 2) {
+                        if (!remote_target_id) {
+                                efi_error("Device is PHY is a remote target, but remote_target_id is NULL");
+                                return -1;
+                        }
+                        *remote_target_id = tosser1;
+                        sz += pos0;
+                        pos0 = 0;
+
+                        /*
+                         * if we have that, we should have a 3-part port next
+                         */
+                        debug("searching for port-2:0:2/");
+                        rc = sscanf(current+sz, "port-%d:%d:%d/%n", &tosser0, &tosser1, &tosser2, &pos0);
+                        debug("current:\"%s\" rc:%d pos0:%d\n", current+sz, rc, pos0);
+                        arrow(LOG_DEBUG, spaces, 9, pos0, rc, 3);
+                        if (rc != 3) {
+                                efi_error("Couldn't parse port expander port string");
+                                return -1;
+                        }
+                        sz += pos0;
+                }
+                pos0 = 0;
 
                 /* next:
                  *    /end_device-4:0
@@ -107,22 +184,24 @@ parse_scsi_link(const char *current, uint32_t *scsi_host,
                  * awesomely these are the exact same fields that go into port-blah,
                  * but we don't care for now about any of them anyway.
                  */
-                debug("searching for /end_device-4:0/ or /end_device-4:0:0/");
-                rc = sscanf(current + sz, "/end_device-%d:%d%n", &tosser0, &tosser1, &pos0);
+                debug("searching for end_device-4:0/ or end_device-4:0:0/");
+                rc = sscanf(current + sz, "end_device-%d:%d%n", &tosser0, &tosser1, &pos0);
                 debug("current:\"%s\" rc:%d pos0:%d\n", current+sz, rc, pos0);
-                arrow(LOG_DEBUG, spaces, 9, pos0, rc, 2);
                 if (rc != 2)
                         return -1;
-                sz += pos0;
-                pos0 = 0;
 
-                rc = sscanf(current + sz, ":%d%n", &tosser0, &pos0);
-                debug("current:\"%s\" rc:%d pos0:%d\n", current+sz, rc, pos0);
-                arrow(LOG_DEBUG, spaces, 9, pos0, rc, 2);
+                pos1 = 0;
+                rc = sscanf(current + sz + pos0, ":%d%n", &tosser2, &pos1);
+                arrow(LOG_DEBUG, spaces, 9, pos0, rc + 2, 2);
+                arrow(LOG_DEBUG, spaces, 9, pos0 + pos1, rc + 2, 3);
                 if (rc != 0 && rc != 1)
                         return -1;
-                sz += pos0;
-                pos0 = 0;
+                if (remote_port_id && rc == 1)
+                        *remote_port_id = tosser2;
+                if (local_port_id && rc == 0)
+                        *local_port_id = tosser1;
+                sz += pos0 + pos1;
+                pos0 = pos1 = 0;
 
                 if (current[sz] == '/')
                         sz += 1;
@@ -156,6 +235,7 @@ parse_scsi_link(const char *current, uint32_t *scsi_host,
                 return -1;
         sz += pos0;
 
+        debug("returning %d", sz);
         return sz;
 }
 
@@ -191,7 +271,8 @@ parse_scsi(struct device *dev, const char *current, const char *root UNUSED)
 
         sz = parse_scsi_link(current, &scsi_host,
                               &scsi_bus, &scsi_device,
-                              &scsi_target, &scsi_lun);
+                              &scsi_target, &scsi_lun,
+                              NULL, NULL, NULL);
         if (sz < 0)
                 return 0;
 
diff --git a/src/linux.h b/src/linux.h
index 7c7ea91e771..43a9b7899f5 100644
--- a/src/linux.h
+++ b/src/linux.h
@@ -267,8 +267,10 @@ struct dev_probe {
 };
 
 extern ssize_t parse_scsi_link(const char *current, uint32_t *host,
-                                      uint32_t *bus, uint32_t *device,
-                                      uint32_t *target, uint64_t *lun);
+                               uint32_t *bus, uint32_t *device,
+                               uint32_t *target, uint64_t *lun,
+                               uint32_t *local_port_id, uint32_t *remote_port_id,
+                               uint32_t *remote_target_id);
 
 /* device support implementations */
 extern struct dev_probe pmem_parser;
-- 
2.17.1