From: Corentin Labbe <clabbe@baylibre.com>
To: dakr@kernel.org, gregkh@linuxfoundation.org, mcgrof@kernel.org,
rafael@kernel.org, russ.weight@linux.dev
Cc: linux-kernel@vger.kernel.org, Corentin LABBE <clabbe@baylibre.com>
Subject: [PATCH] firmware: maintain a firmware list
Date: Wed, 15 Jul 2026 13:13:25 +0000 [thread overview]
Message-ID: <20260715131325.26099-1-clabbe@baylibre.com> (raw)
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
next reply other threads:[~2026-07-15 13:13 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-15 13:13 Corentin Labbe [this message]
2026-07-15 17:23 ` [PATCH] firmware: maintain a firmware list Danilo Krummrich
2026-07-16 9:29 ` Corentin LABBE
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260715131325.26099-1-clabbe@baylibre.com \
--to=clabbe@baylibre.com \
--cc=dakr@kernel.org \
--cc=gregkh@linuxfoundation.org \
--cc=linux-kernel@vger.kernel.org \
--cc=mcgrof@kernel.org \
--cc=rafael@kernel.org \
--cc=russ.weight@linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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.