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

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