Blame SOURCES/0056-Try-even-harder-to-find-disk-device-symlinks-in-sysf.patch

d5c737
From 156d7a1e123f85863db854aae5c10acd3864f9d8 Mon Sep 17 00:00:00 2001
d5c737
From: Peter Jones <pjones@redhat.com>
d5c737
Date: Fri, 11 Oct 2019 14:20:54 -0400
d5c737
Subject: [PATCH 56/63] Try even harder to find disk device symlinks in sysfs.
d5c737
d5c737
Today's realization is that the thing encoded into the structure of
d5c737
sysfs is, in the best case, the dependency graph of the makefile targets
d5c737
to build a device driver.
d5c737
d5c737
In the case of nvme-fabric, or really wherever the kernel has
d5c737
class_create() and device_create() in the same function, there's an
d5c737
extra level of indirection.
d5c737
d5c737
Anyway, in this patch we stop pretending sysfs isn't completely absurd,
d5c737
and just try adding "/device" in the middle of the driver symlink path,
d5c737
until we actually either get ENOENT on the device symlink or find a
d5c737
device symlink that actually has a driver symlink under it.
d5c737
d5c737
Signed-off-by: Peter Jones <pjones@redhat.com>
d5c737
---
d5c737
 src/linux-nvme.c | 13 +++++----
d5c737
 src/linux.c      | 46 ++++++++++++++++++--------------
d5c737
 src/linux.h      | 69 ++++++++++++++++++++++++++++++++++++++++++++++++
d5c737
 3 files changed, 101 insertions(+), 27 deletions(-)
d5c737
d5c737
diff --git a/src/linux-nvme.c b/src/linux-nvme.c
d5c737
index 7b18d7990ac..455c4c7ba9b 100644
d5c737
--- a/src/linux-nvme.c
d5c737
+++ b/src/linux-nvme.c
d5c737
@@ -87,13 +87,12 @@ parse_nvme(struct device *dev, const char *current, const char *root UNUSED)
d5c737
 	/*
d5c737
 	 * now fish the eui out of sysfs is there is one...
d5c737
 	 */
d5c737
-	rc = read_sysfs_file(&filebuf,
d5c737
-	                     "class/block/nvme%dn%d/eui",
d5c737
-	                     ctrl_id, ns_id);
d5c737
-	if ((rc < 0 && errno == ENOENT) || filebuf == NULL) {
d5c737
-	        rc = read_sysfs_file(&filebuf,
d5c737
-	                     "class/block/nvme%dn%d/device/eui",
d5c737
-	                     ctrl_id, ns_id);
d5c737
+	char *euipath = NULL;
d5c737
+	rc = read_sysfs_file(&filebuf, "class/block/nvme%dn%d/eui", ctrl_id, ns_id);
d5c737
+	if (rc < 0 && (errno == ENOENT || errno == ENOTDIR)) {
d5c737
+		rc = find_device_file(&euipath, "eui", "class/block/nvme%dn%d", ctrl_id, ns_id);
d5c737
+		if (rc >= 0 && euipath != NULL)
d5c737
+			rc = read_sysfs_file(&filebuf, "%s", euipath);
d5c737
 	}
d5c737
 	if (rc >= 0 && filebuf != NULL) {
d5c737
 	        uint8_t eui[8];
d5c737
diff --git a/src/linux.c b/src/linux.c
d5c737
index 73c67cbafd3..30db22d95dd 100644
d5c737
--- a/src/linux.c
d5c737
+++ b/src/linux.c
d5c737
@@ -401,26 +401,32 @@ struct device HIDDEN
d5c737
 	        goto err;
d5c737
 	}
d5c737
 
d5c737
-	if (dev->device[0] != 0) {
d5c737
-	        rc = sysfs_readlink(&tmpbuf, "block/%s/device/driver", dev->disk_name);
d5c737
+	/*
d5c737
+	 * So, on a normal disk, you get something like:
d5c737
+	 * /sys/block/sda/device -> ../../0:0:0:0
d5c737
+	 * /sys/block/sda/device/driver -> ../../../../../../../bus/scsi/drivers/sd
d5c737
+	 *
d5c737
+	 * On a directly attached nvme device you get:
d5c737
+	 * /sys/block/nvme0n1/device -> ../../nvme0
d5c737
+	 * /sys/block/nvme0n1/device/device -> ../../../0000:6e:00.0
d5c737
+	 * /sys/block/nvme0n1/device/device/driver -> ../../../../bus/pci/drivers/nvme
d5c737
+	 *
d5c737
+	 * On a fabric-attached nvme device, you get something like:
d5c737
+	 * /sys/block/nvme0n1/device -> ../../nvme0
d5c737
+	 * /sys/block/nvme0n1/device/device -> ../../ctl
d5c737
+	 * /sys/block/nvme0n1/device/device/device -> ../../../../../0000:6e:00.0
d5c737
+	 * /sys/block/nvme0n1/device/device/device/driver -> ../../../../../../bus/pci/drivers/nvme-fabrics
d5c737
+	 *
d5c737
+	 * ... I think?  I don't have one in front of me.
d5c737
+	 */
d5c737
+
d5c737
+	char *filepath = NULL;
d5c737
+	rc = find_device_file(&filepath, "driver", "block/%s", dev->disk_name);
d5c737
+	if (rc >= 0) {
d5c737
+		rc = sysfs_readlink(&tmpbuf, "%s", filepath);
d5c737
 	        if (rc < 0 || !tmpbuf) {
d5c737
-	                if (errno == ENOENT) {
d5c737
-	                        /*
d5c737
-	                         * nvme, for example, will have nvme0n1/device point
d5c737
-	                         * at nvme0, and we need to look for device/driver
d5c737
-	                         * there.
d5c737
-	                         */
d5c737
-	                        rc = sysfs_readlink(&tmpbuf,
d5c737
-	                                            "block/%s/device/device/driver",
d5c737
-	                                            dev->disk_name);
d5c737
-	                        if (rc >= 0 && tmpbuf)
d5c737
-	                                efi_error_pop();
d5c737
-	                }
d5c737
-	                if (rc < 0 || !tmpbuf) {
d5c737
-	                        efi_error("readlink of /sys/block/%s/device/driver failed",
d5c737
-	                                  dev->disk_name);
d5c737
-	                        goto err;
d5c737
-	                }
d5c737
+			efi_error("readlink of /sys/%s failed", filepath);
d5c737
+	                goto err;
d5c737
 	        }
d5c737
 
d5c737
 	        linkbuf = pathseg(tmpbuf, -1);
d5c737
@@ -431,7 +437,7 @@ struct device HIDDEN
d5c737
 
d5c737
 	        dev->driver = strdup(linkbuf);
d5c737
 	} else {
d5c737
-	        dev->driver = strdup("");
d5c737
+		dev->driver = strdup("");
d5c737
 	}
d5c737
 
d5c737
 	if (!dev->driver) {
d5c737
diff --git a/src/linux.h b/src/linux.h
d5c737
index 5ae64ffaacf..ae9835ef7ce 100644
d5c737
--- a/src/linux.h
d5c737
+++ b/src/linux.h
d5c737
@@ -218,6 +218,22 @@ extern ssize_t HIDDEN make_mac_path(uint8_t *buf, ssize_t size,
d5c737
 		_rc;							\
d5c737
 	})
d5c737
 
d5c737
+#define sysfs_access(mode, fmt, args...)				\
d5c737
+	({								\
d5c737
+		int rc_;						\
d5c737
+		char *pn_;						\
d5c737
+									\
d5c737
+		rc_ = asprintfa(&pn_, "/sys/" fmt, ## args);		\
d5c737
+		if (rc_ >= 0) {						\
d5c737
+			rc_ = access(pn_, mode);			\
d5c737
+			if (rc_ < 0)					\
d5c737
+				efi_error("could not access %s", pn_);  \
d5c737
+		} else {						\
d5c737
+			efi_error("could not allocate memory");		\
d5c737
+		}							\
d5c737
+		rc_;							\
d5c737
+	})
d5c737
+
d5c737
 #define sysfs_stat(statbuf, fmt, args...)				\
d5c737
 	({								\
d5c737
 		int rc_;						\
d5c737
@@ -251,6 +267,59 @@ extern ssize_t HIDDEN make_mac_path(uint8_t *buf, ssize_t size,
d5c737
 		dir_;							\
d5c737
 	})
d5c737
 
d5c737
+/*
d5c737
+ * Iterate a /sys/block directory looking for device/foo, device/device/foo,
d5c737
+ * etc.  I'm not proud of this method.
d5c737
+ */
d5c737
+#define find_device_file(result, name, fmt, args...)				\
d5c737
+	({									\
d5c737
+		int rc_ = 0;							\
d5c737
+		debug("searching for %s from in %s", name, dev->disk_name);	\
d5c737
+		for (unsigned int try_ = 0; true; try_++) {			\
d5c737
+			char slashdev_[sizeof("device")				\
d5c737
+				       + try_ * strlen("/device")];		\
d5c737
+										\
d5c737
+			char *nul_ = stpcpy(slashdev_, "device");		\
d5c737
+			for (unsigned int i_ = 0; i_ < try_; i_++)		\
d5c737
+				nul_ = stpcpy(nul_, "/device");			\
d5c737
+										\
d5c737
+			debug("trying /sys/" fmt "/%s/%s",			\
d5c737
+			      ## args, slashdev_, name);			\
d5c737
+										\
d5c737
+			rc_ = sysfs_access(F_OK, fmt "/%s", ## args, slashdev_);\
d5c737
+			if (rc_ < 0) {						\
d5c737
+				if (errno == ENOENT) {				\
d5c737
+					efi_error_pop();			\
d5c737
+					break;					\
d5c737
+				}						\
d5c737
+				efi_error("cannot access /sys/"fmt"/%s: %m",	\
d5c737
+					  ## args, slashdev_);			\
d5c737
+				goto find_device_link_err_;			\
d5c737
+			}							\
d5c737
+										\
d5c737
+			rc_ = sysfs_access(F_OK, fmt "/%s/%s",			\
d5c737
+					   ## args, slashdev_, name);		\
d5c737
+			if (rc_ < 0) {						\
d5c737
+				if (errno == ENOENT) {				\
d5c737
+					efi_error_pop();			\
d5c737
+					break;					\
d5c737
+				}						\
d5c737
+				efi_error("cannot access /sys/"fmt"/%s/%s: %m",	\
d5c737
+					  ## args, slashdev_, name);		\
d5c737
+				goto find_device_link_err_;			\
d5c737
+			}							\
d5c737
+										\
d5c737
+			rc_ = asprintfa(result, fmt "/%s/%s",			\
d5c737
+					## args, slashdev_, name);		\
d5c737
+			if (rc_ < 0) {						\
d5c737
+				efi_error("cannot allocate memory: %m");	\
d5c737
+				goto find_device_link_err_;			\
d5c737
+			}							\
d5c737
+		}								\
d5c737
+find_device_link_err_:								\
d5c737
+		rc_;								\
d5c737
+	})
d5c737
+
d5c737
 #define DEV_PROVIDES_ROOT       1
d5c737
 #define DEV_PROVIDES_HD	 2
d5c737
 #define DEV_ABBREV_ONLY	 4
d5c737
-- 
d5c737
2.26.2
d5c737