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 <[email protected]>
---
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 <[email protected]>, 
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


_______________________________________________
barebox mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/barebox

Reply via email to