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