All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] firmware: maintain a firmware list
@ 2026-07-15 13:13 Corentin Labbe
  2026-07-15 17:23 ` Danilo Krummrich
  0 siblings, 1 reply; 3+ messages in thread
From: Corentin Labbe @ 2026-07-15 13:13 UTC (permalink / raw)
  To: dakr, gregkh, mcgrof, rafael, russ.weight; +Cc: linux-kernel, Corentin LABBE

From: Corentin LABBE <clabbe@baylibre.com>

Checking if some firmware is missing need to check dmesg for error message,
and on some machine this information could be lost since dmesg uses a ring buffer.
Having the list of all firmware requests is useful in many situations,
like selecting the minimal list when doing some buildroot config
or reducing the size of linux-firmware on gentoo via saveconfig.

So for letting thoses tasks to be automated easily, it is better to have all
loaded firmware in one place.
This patch adds a debugfs with all firmware that was tried to be loaded and the
result of those actions.

Signed-off-by: Corentin LABBE <clabbe@baylibre.com>
---

Hello

I believe the list could setup more appropriate elsewhere in /sys, but
without idea, I used debugfs.
If you have better idea, let me know it.

Regards
 drivers/base/firmware_loader/main.c | 100 ++++++++++++++++++++++++++++
 1 file changed, 100 insertions(+)

diff --git a/drivers/base/firmware_loader/main.c b/drivers/base/firmware_loader/main.c
index 24213a0ea831..786d9b2b096e 100644
--- a/drivers/base/firmware_loader/main.c
+++ b/drivers/base/firmware_loader/main.c
@@ -11,7 +11,9 @@
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
 #include <linux/capability.h>
+#include <linux/debugfs.h>
 #include <linux/device.h>
+#include <linux/errname.h>
 #include <linux/kernel_read_file.h>
 #include <linux/module.h>
 #include <linux/init.h>
@@ -35,6 +37,7 @@
 #include <linux/syscore_ops.h>
 #include <linux/reboot.h>
 #include <linux/security.h>
+#include <linux/seq_file.h>
 #include <linux/zstd.h>
 #include <linux/xz.h>
 
@@ -48,6 +51,95 @@ MODULE_AUTHOR("Manuel Estrada Sainz");
 MODULE_DESCRIPTION("Multi purpose firmware loading support");
 MODULE_LICENSE("GPL");
 
+struct fw_loaded_entry {
+	struct list_head node;
+	const char *driver;
+	const char *fw_name;
+	int result;
+};
+
+static LIST_HEAD(fw_loaded_list);
+static DEFINE_MUTEX(fw_loaded_lock);
+static struct dentry *fw_debugfs_dir;
+
+static void fw_record_request(struct device *device, const char *name, int ret)
+{
+	const char *driver = device ? dev_driver_string(device) : "(none)";
+	struct fw_loaded_entry *entry;
+
+	if (!name || name[0] == '\0')
+		return;
+
+	guard(mutex)(&fw_loaded_lock);
+
+	/* Update in place if we have already seen this request. */
+	list_for_each_entry(entry, &fw_loaded_list, node) {
+		if (!strcmp(entry->driver, driver) &&
+		    !strcmp(entry->fw_name, name)) {
+			entry->result = ret;
+			return;
+		}
+	}
+
+	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+	if (!entry)
+		return;
+
+	entry->driver = kstrdup(driver, GFP_KERNEL);
+	entry->fw_name = kstrdup(name, GFP_KERNEL);
+	if (!entry->driver || !entry->fw_name) {
+		kfree(entry->driver);
+		kfree(entry->fw_name);
+		kfree(entry);
+		return;
+	}
+
+	entry->result = ret;
+	list_add_tail(&entry->node, &fw_loaded_list);
+}
+
+static int fw_list_show(struct seq_file *m, void *v)
+{
+	struct fw_loaded_entry *entry;
+
+	guard(mutex)(&fw_loaded_lock);
+
+	list_for_each_entry(entry, &fw_loaded_list, node) {
+		const char *name;
+
+		if (entry->result >= 0) {
+			seq_printf(m, "%s %s OK\n",
+				   entry->driver, entry->fw_name);
+			continue;
+		}
+
+		name = errname(-entry->result);
+		if (name)
+			seq_printf(m, "%s %s %s\n",
+				   entry->driver, entry->fw_name, name);
+		else
+			seq_printf(m, "%s %s %d\n",
+				   entry->driver, entry->fw_name, entry->result);
+	}
+
+	return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(fw_list);
+
+static void fw_loaded_list_free(void)
+{
+	struct fw_loaded_entry *entry, *tmp;
+
+	guard(mutex)(&fw_loaded_lock);
+
+	list_for_each_entry_safe(entry, tmp, &fw_loaded_list, node) {
+		list_del(&entry->node);
+		kfree(entry->driver);
+		kfree(entry->fw_name);
+		kfree(entry);
+	}
+}
+
 struct firmware_cache {
 	/* firmware_buf instance will be added into the below list */
 	spinlock_t lock;
@@ -911,6 +1003,8 @@ _request_firmware(const struct firmware **firmware_p, const char *name,
 		fw_log_firmware_info(fw, name, device);
 	}
 
+	fw_record_request(device, name, ret);
+
 	*firmware_p = fw;
 	return ret;
 }
@@ -1727,6 +1821,10 @@ static int __init firmware_class_init(void)
 	if (ret)
 		goto out;
 
+	fw_debugfs_dir = debugfs_create_dir("firmware_loader", NULL);
+	debugfs_create_file("firmware_list", 0444, fw_debugfs_dir, NULL,
+			    &fw_list_fops);
+
 	return register_sysfs_loader();
 
 out:
@@ -1739,6 +1837,8 @@ static void __exit firmware_class_exit(void)
 	unregister_fw_pm_ops();
 	unregister_reboot_notifier(&fw_shutdown_nb);
 	unregister_sysfs_loader();
+	debugfs_remove_recursive(fw_debugfs_dir);
+	fw_loaded_list_free();
 }
 
 fs_initcall(firmware_class_init);
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2026-07-16  9:29 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-15 13:13 [PATCH] firmware: maintain a firmware list Corentin Labbe
2026-07-15 17:23 ` Danilo Krummrich
2026-07-16  9:29   ` Corentin LABBE

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.