[PATCH v2 3/3] commands: add new devunbind debugging command

Ahmad Fatoum a.fatoum at pengutronix.de
Thu Jan 13 08:04:14 PST 2022


Memory corruption around device removal may go unnoticed, because
barebox is shutting down anyway and doing no new allocations.

Add a new devunbind command that should help with debugging such issues
by allowing selective unbinding and removal of devices.

Signed-off-by: Ahmad Fatoum <a.fatoum at pengutronix.de>
---
v1 -> v2:
  - updated Kconfig discription with -l option
  - fixed too early exit after unregistering first device (Sascha)
  - report skipping of missing device
---
 commands/Kconfig      | 12 +++++++
 commands/Makefile     |  1 +
 commands/devunbind.c  | 79 +++++++++++++++++++++++++++++++++++++++++++
 drivers/base/driver.c |  7 ++--
 include/driver.h      |  4 +++
 5 files changed, 100 insertions(+), 3 deletions(-)
 create mode 100644 commands/devunbind.c

diff --git a/commands/Kconfig b/commands/Kconfig
index 5506d1b8f07c..ba8ca5cdebce 100644
--- a/commands/Kconfig
+++ b/commands/Kconfig
@@ -75,6 +75,18 @@ config CMD_DEVINFO
 	  If called with a device path being the argument, devinfo shows more
 	  default information about this device and its parameters.
 
+config CMD_DEVUNBIND
+	tristate
+	prompt "devunbind"
+	help
+	  Debugging aid to unbind device(s) from driver at runtime
+
+	  devunbind [-fl] DEVICES..
+
+	  Options:
+		-f   unbind driver and force removal of device and children
+		-l   list remove callbacks in shutdown order
+
 config CMD_DMESG
 	tristate
 	prompt "dmesg"
diff --git a/commands/Makefile b/commands/Makefile
index c1a060da5204..db78d0b877f6 100644
--- a/commands/Makefile
+++ b/commands/Makefile
@@ -107,6 +107,7 @@ obj-$(CONFIG_CMD_MIITOOL)	+= miitool.o
 obj-$(CONFIG_CMD_DETECT)	+= detect.o
 obj-$(CONFIG_CMD_BOOT)		+= boot.o
 obj-$(CONFIG_CMD_DEVINFO)	+= devinfo.o
+obj-$(CONFIG_CMD_DEVUNBIND)	+= devunbind.o
 obj-$(CONFIG_CMD_DRVINFO)	+= drvinfo.o
 obj-$(CONFIG_CMD_READF)		+= readf.o
 obj-$(CONFIG_CMD_MENUTREE)	+= menutree.o
diff --git a/commands/devunbind.c b/commands/devunbind.c
new file mode 100644
index 000000000000..3f9cd7b849c8
--- /dev/null
+++ b/commands/devunbind.c
@@ -0,0 +1,79 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// SPDX-FileCopyrightText: © 2021 Ahmad Fatoum <a.fatoum at pengutronix.de>, Pengutronix
+
+#include <command.h>
+#include <common.h>
+#include <complete.h>
+#include <driver.h>
+#include <getopt.h>
+
+static int do_devunbind(int argc, char *argv[])
+{
+	bool unregister = false;
+	struct device_d *dev;
+	int ret = COMMAND_SUCCESS, i, opt;
+
+	while ((opt = getopt(argc, argv, "fl")) > 0) {
+		switch (opt) {
+		case 'f':
+			unregister = true;
+			break;
+		case 'l':
+			list_for_each_entry(dev, &active_device_list, active) {
+				BUG_ON(!dev->driver);
+				if (dev->bus->remove)
+					printf("%pS(%s, %s)\n", dev->bus->remove,
+					       dev->driver->name, dev_name(dev));
+			}
+			return 0;
+		default:
+			return COMMAND_ERROR_USAGE;
+		}
+	}
+
+	if (!argv[optind])
+		return COMMAND_ERROR_USAGE;
+
+	for (i = optind; i < argc; i++) {
+		dev = get_device_by_name(argv[i]);
+		if (!dev) {
+			printf("skipping missing %s\n", argv[i]);
+			ret = -ENODEV;
+			continue;
+		}
+
+		if (unregister) {
+			unregister_device(dev);
+			continue;
+		}
+
+		if (!dev->driver || !dev->bus->remove) {
+			printf("skipping unbound %s\n", argv[i]);
+			ret = COMMAND_ERROR;
+			continue;
+		}
+
+		dev->bus->remove(dev);
+		dev->driver = NULL;
+		list_del(&dev->active);
+	}
+
+	return ret;
+}
+
+BAREBOX_CMD_HELP_START(devunbind)
+BAREBOX_CMD_HELP_TEXT("Debugging aid to unbind device from driver at runtime")
+BAREBOX_CMD_HELP_TEXT("")
+BAREBOX_CMD_HELP_TEXT("Options:")
+BAREBOX_CMD_HELP_OPT ("-f",  "unbind driver and force removal of device and children")
+BAREBOX_CMD_HELP_OPT ("-l",  "list remove callbacks in shutdown order")
+BAREBOX_CMD_HELP_END
+
+BAREBOX_CMD_START(devunbind)
+	.cmd		= do_devunbind,
+	BAREBOX_CMD_DESC("unbind device(s) from driver")
+	BAREBOX_CMD_OPTS("[-fl] DEVICES..")
+	BAREBOX_CMD_GROUP(CMD_GRP_INFO)
+	BAREBOX_CMD_HELP(cmd_devunbind_help)
+	BAREBOX_CMD_COMPLETE(device_complete)
+BAREBOX_CMD_END
diff --git a/drivers/base/driver.c b/drivers/base/driver.c
index bb07e96dcaf4..f54f4d0b3746 100644
--- a/drivers/base/driver.c
+++ b/drivers/base/driver.c
@@ -41,7 +41,8 @@ EXPORT_SYMBOL(device_list);
 LIST_HEAD(driver_list);
 EXPORT_SYMBOL(driver_list);
 
-static LIST_HEAD(active);
+LIST_HEAD(active_device_list);
+EXPORT_SYMBOL(active_device_list);
 static LIST_HEAD(deferred);
 
 struct device_d *get_device_by_name(const char *name)
@@ -91,7 +92,7 @@ int device_probe(struct device_d *dev)
 	pinctrl_select_state_default(dev);
 	of_clk_set_defaults(dev->device_node, false);
 
-	list_add(&dev->active, &active);
+	list_add(&dev->active, &active_device_list);
 
 	ret = dev->bus->probe(dev);
 	if (ret == 0)
@@ -504,7 +505,7 @@ static void devices_shutdown(void)
 	struct device_d *dev;
 	int depth = 0;
 
-	list_for_each_entry(dev, &active, active) {
+	list_for_each_entry(dev, &active_device_list, active) {
 		if (dev->bus->remove) {
 			depth++;
 			pr_report_probe("%*sremove-> %s\n", depth * 4, "", dev_name(dev));
diff --git a/include/driver.h b/include/driver.h
index 4f6d40e17c14..1215a2d57ab3 100644
--- a/include/driver.h
+++ b/include/driver.h
@@ -328,6 +328,10 @@ extern struct list_head device_list;
  */
 extern struct list_head driver_list;
 
+/* linear list over all active devices
+ */
+extern struct list_head active_device_list;
+
 /* Iterate over all devices
  */
 #define for_each_device(dev) list_for_each_entry(dev, &device_list, list)
-- 
2.30.2




More information about the barebox mailing list