[PATCH 3/3] commands: add new devunbind debugging command
Ahmad Fatoum
a.fatoum at pengutronix.de
Sat Jan 8 09:14:26 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>
---
commands/Kconfig | 12 +++++++
commands/Makefile | 1 +
commands/devunbind.c | 74 +++++++++++++++++++++++++++++++++++++++++++
drivers/base/driver.c | 7 ++--
include/driver.h | 4 +++
5 files changed, 95 insertions(+), 3 deletions(-)
create mode 100644 commands/devunbind.c
diff --git a/commands/Kconfig b/commands/Kconfig
index e2c36949347e..9abd97271952 100644
--- a/commands/Kconfig
+++ b/commands/Kconfig
@@ -68,6 +68,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
+ default y
+ prompt "devunbind"
+ help
+ Debugging aid to unbind device from driver at runtime
+
+ devunbind [-f] DEVICE
+
+ Options:
+ -f unbind driver and force removal of device and children
+
config CMD_DMESG
tristate
prompt "dmesg"
diff --git a/commands/Makefile b/commands/Makefile
index 0b7c1563b534..875826743ffe 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..4bebb27e8e68
--- /dev/null
+++ b/commands/devunbind.c
@@ -0,0 +1,74 @@
+// 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)
+ return -ENODEV;
+
+ if (unregister)
+ return unregister_device(dev);
+
+ 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