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@xxxxxxxxxxxxxx> --- 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@xxxxxxxxxxxxxx>, 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 _______________________________________________ barebox mailing list barebox@xxxxxxxxxxxxxxxxxxx http://lists.infradead.org/mailman/listinfo/barebox