diff mbox

[v2] mdev: create devices from /sys/dev

Message ID 1460551003-21560-1-git-send-email-linus.walleij@linaro.org
State New
Headers show

Commit Message

Linus Walleij April 13, 2016, 12:36 p.m. UTC
Currently some new devices that have a bus but no class will
be missed by mdev coldplug device creation after boot. This
happens because mdev recursively searches /sys/class which will
by definition only find class devices.

Some important devices such as iio and gpiochip does not have
a class. But users will need them.

This switches from using /sys/class as the place to look for
devices to create to using /sys/dev where all char and block
devices are listed.

The subsystem lookup code that provide the G.subsystem
environment variable is changed from using the directory
name of the class device to instead dereference the
"subsystem" symlink for the device, and look at the last
element of the path of the symlink for the subsystem, which
will work with class devices and bus devices alike. (The new
bus-only devices only symlink to the /sys/bus/* hierarchy.)

We delete the legacy kernel v2.6.2x /sys/block device path
code as part of this change. It's too old to be kept alive.

Tested on kernel v4.6-rc2 with a bunch of devices, including
some IIO and gpiochip devices.

With a print inserted before make_device() the log looks
like so:

Create device from "/sys/dev/char/1:1", subsystem "mem"
Create device from "/sys/dev/char/1:2", subsystem "mem"
Create device from "/sys/dev/char/1:3", subsystem "mem"
Create device from "/sys/dev/char/1:5", subsystem "mem"
(...)
Create device from "/sys/dev/block/179:56", subsystem "block"
Create device from "/sys/dev/block/179:64", subsystem "block"

Cc: Isaac Dunham <ibid.ag@gmail.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Jonathan Cameron <jic23@cam.ac.uk>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>

---
ChangeLog v1->v2:
- Restore the subsystem environment setting behaviour by
  following the subsystem symlink like decribed in the
  commit message. This may be needed by mdev scripts.
- Kill of /sys/block device scanning
- Do not fall back to /sys/class device scanning, just scan
  /sys/dev
---
 util-linux/mdev.c | 61 ++++++++++++++++++++++++++-----------------------------
 1 file changed, 29 insertions(+), 32 deletions(-)

-- 
2.4.3
diff mbox

Patch

diff --git a/util-linux/mdev.c b/util-linux/mdev.c
index 37fa56827520..00219092fb25 100644
--- a/util-linux/mdev.c
+++ b/util-linux/mdev.c
@@ -816,6 +816,9 @@  static int FAST_FUNC fileAction(const char *fileName,
 {
 	size_t len = strlen(fileName) - 4; /* can't underflow */
 	char *scratch = userData;
+	char subsys[PATH_MAX];
+	char sublink[PATH_MAX];
+	int res;
 
 	/* len check is for paranoid reasons */
 	if (strcmp(fileName + len, "/dev") != 0 || len >= PATH_MAX)
@@ -823,26 +826,24 @@  static int FAST_FUNC fileAction(const char *fileName,
 
 	strcpy(scratch, fileName);
 	scratch[len] = '\0';
-	make_device(/*DEVNAME:*/ NULL, scratch, OP_add);
 
-	return TRUE;
-}
+	/* Check the symlink for subsystem */
+	strcpy(subsys, scratch);
+	subsys[len] = '\0';
+	strncat(subsys, "/subsystem", sizeof(subsys));
+	subsys[sizeof(subsys)-1] = '\0';
 
-/* Directory callback for /sys/ traversal */
-static int FAST_FUNC dirAction(const char *fileName UNUSED_PARAM,
-		struct stat *statbuf UNUSED_PARAM,
-		void *userData UNUSED_PARAM,
-		int depth)
-{
-	/* Extract device subsystem -- the name of the directory
-	 * under /sys/class/ */
-	if (1 == depth) {
+	/* Reserve the last byte of the buffer for a NULL terminator */
+	res = readlink(subsys, sublink, sizeof(sublink)-1);
+
+	if (res > 0) {
+		sublink[res] = '\0';
 		free(G.subsystem);
 		if (G.subsys_env) {
 			bb_unsetenv_and_free(G.subsys_env);
 			G.subsys_env = NULL;
 		}
-		G.subsystem = strrchr(fileName, '/');
+		G.subsystem = strrchr(sublink, '/');
 		if (G.subsystem) {
 			G.subsystem = xstrdup(G.subsystem + 1);
 			G.subsys_env = xasprintf("%s=%s", "SUBSYSTEM", G.subsystem);
@@ -850,6 +851,17 @@  static int FAST_FUNC dirAction(const char *fileName UNUSED_PARAM,
 		}
 	}
 
+	make_device(/*DEVNAME:*/ NULL, scratch, OP_add);
+
+	return TRUE;
+}
+
+/* Directory callback for /sys/ traversal */
+static int FAST_FUNC dirAction(const char *fileName UNUSED_PARAM,
+		struct stat *statbuf UNUSED_PARAM,
+		void *userData UNUSED_PARAM,
+		int depth)
+{
 	return (depth >= MAX_SYSFS_DEPTH ? SKIP : TRUE);
 }
 
@@ -1063,25 +1075,10 @@  int mdev_main(int argc UNUSED_PARAM, char **argv)
 
 		putenv((char*)"ACTION=add");
 
-		/* ACTION_FOLLOWLINKS is needed since in newer kernels
-		 * /sys/block/loop* (for example) are symlinks to dirs,
-		 * not real directories.
-		 * (kernel's CONFIG_SYSFS_DEPRECATED makes them real dirs,
-		 * but we can't enforce that on users)
-		 */
-		if (access("/sys/class/block", F_OK) != 0) {
-			/* Scan obsolete /sys/block only if /sys/class/block
-			 * doesn't exist. Otherwise we'll have dupes.
-			 * Also, do not complain if it doesn't exist.
-			 * Some people configure kernel to have no blockdevs.
-			 */
-			recursive_action("/sys/block",
-				ACTION_RECURSE | ACTION_FOLLOWLINKS | ACTION_QUIET,
-				fileAction, dirAction, temp, 0);
-		}
-		recursive_action("/sys/class",
-			ACTION_RECURSE | ACTION_FOLLOWLINKS,
-			fileAction, dirAction, temp, 0);
+		/* By default create all devices from /sys/dev hierarchy */
+		recursive_action("/sys/dev",
+				 ACTION_RECURSE | ACTION_FOLLOWLINKS,
+				 fileAction, dirAction, temp, 0);
 	} else {
 		char *fw;
 		char *seq;