From: Danilo Krummrich <dakr@kernel.org>
To: gregkh@linuxfoundation.org, rafael@kernel.org,
hanguidong02@gmail.com, ysato@users.sourceforge.jp,
dalias@libc.org, glaubitz@physik.fu-berlin.de,
abelvesa@kernel.org, srini@kernel.org, s.nawrocki@samsung.com,
nuno.sa@analog.com
Cc: driver-core@lists.linux.dev, linux-kernel@vger.kernel.org,
imx@lists.linux.dev, linux-hwmon@vger.kernel.org,
linux-arm-msm@vger.kernel.org, linux-sound@vger.kernel.org,
linux-sh@vger.kernel.org, Danilo Krummrich <dakr@kernel.org>
Subject: [PATCH 1/3] driver core: generalize driver_override in struct device
Date: Mon, 2 Mar 2026 01:25:56 +0100 [thread overview]
Message-ID: <20260302002729.19438-2-dakr@kernel.org> (raw)
In-Reply-To: <20260302002729.19438-1-dakr@kernel.org>
Currently, there are 12 busses (including platform and PCI) that
duplicate the driver_override logic for their individual devices.
All of them seem to be prone to the bug described in [1].
While this could be solved for every bus individually using a separate
lock, solving this in the driver-core generically results in less (and
cleaner) changes overall.
Thus, move driver_override to struct device, provide corresponding
accessors for busses and handle locking with a separate lock internally.
In particular, add device_set_driver_override(),
device_has_driver_override(), device_match_driver_override() and a
helper, DEVICE_ATTR_DRIVER_OVERRIDE(), to declare the corresponding
sysfs store() and show() callbacks.
Until all busses have migrated, keep driver_set_override() in place.
Note that we can't use the device lock for the reasons described in [2].
Link: https://bugzilla.kernel.org/show_bug.cgi?id=220789 [1]
Link: https://lore.kernel.org/driver-core/DGRGTIRHA62X.3RY09D9SOK77P@kernel.org/ [2]
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
drivers/base/core.c | 2 ++
drivers/base/dd.c | 60 +++++++++++++++++++++++++++++++
include/linux/device.h | 81 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 143 insertions(+)
diff --git a/drivers/base/core.c b/drivers/base/core.c
index 791f9e444df8..a8cb90577d10 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -2566,6 +2566,7 @@ static void device_release(struct kobject *kobj)
else
WARN(1, KERN_ERR "Device '%s' does not have a release() function, it is broken and must be fixed. See Documentation/core-api/kobject.rst.\n",
dev_name(dev));
+ kfree(dev->driver_override.name);
kfree(p);
}
@@ -3159,6 +3160,7 @@ void device_initialize(struct device *dev)
kobject_init(&dev->kobj, &device_ktype);
INIT_LIST_HEAD(&dev->dma_pools);
mutex_init(&dev->mutex);
+ spin_lock_init(&dev->driver_override.lock);
lockdep_set_novalidate_class(&dev->mutex);
spin_lock_init(&dev->devres_lock);
INIT_LIST_HEAD(&dev->devres_head);
diff --git a/drivers/base/dd.c b/drivers/base/dd.c
index 0354f209529c..697e36e63cab 100644
--- a/drivers/base/dd.c
+++ b/drivers/base/dd.c
@@ -381,6 +381,66 @@ static void __exit deferred_probe_exit(void)
}
__exitcall(deferred_probe_exit);
+int __device_set_driver_override(struct device *dev, const char *s, size_t len)
+{
+ const char *new, *old;
+ char *cp;
+
+ if (!s)
+ return -EINVAL;
+
+ /*
+ * The stored value will be used in sysfs show callback (sysfs_emit()),
+ * which has a length limit of PAGE_SIZE and adds a trailing newline.
+ * Thus we can store one character less to avoid truncation during sysfs
+ * show.
+ */
+ if (len >= (PAGE_SIZE - 1))
+ return -EINVAL;
+
+ /*
+ * Compute the real length of the string in case userspace sends us a
+ * bunch of \0 characters like python likes to do.
+ */
+ len = strlen(s);
+
+ if (!len) {
+ /* Empty string passed - clear override */
+ spin_lock(&dev->driver_override.lock);
+ old = dev->driver_override.name;
+ dev->driver_override.name = NULL;
+ spin_unlock(&dev->driver_override.lock);
+ kfree(old);
+
+ return 0;
+ }
+
+ cp = strnchr(s, len, '\n');
+ if (cp)
+ len = cp - s;
+
+ new = kstrndup(s, len, GFP_KERNEL);
+ if (!new)
+ return -ENOMEM;
+
+ spin_lock(&dev->driver_override.lock);
+ old = dev->driver_override.name;
+ if (cp != s) {
+ dev->driver_override.name = new;
+ spin_unlock(&dev->driver_override.lock);
+ } else {
+ /* "\n" passed - clear override */
+ dev->driver_override.name = NULL;
+ spin_unlock(&dev->driver_override.lock);
+
+ kfree(new);
+ }
+ kfree(old);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(__device_set_driver_override);
+
/**
* device_is_bound() - Check if device is bound to a driver
* @dev: device to check
diff --git a/include/linux/device.h b/include/linux/device.h
index 0be95294b6e6..4599156d5cbd 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -266,6 +266,33 @@ ssize_t device_show_string(struct device *dev, struct device_attribute *attr,
struct dev_ext_attribute dev_attr_##_name = \
{ __ATTR(_name, (_mode) & ~0222, device_show_string, NULL), (_var) }
+/**
+ * DEVICE_ATTR_DRIVER_OVERRIDE - Define sysfs driver_override attribute callbacks
+ *
+ * Generates the standard driver_override_show() and driver_override_store()
+ * sysfs callbacks and the static DEVICE_ATTR_RW(driver_override) declaration.
+ */
+#define DEVICE_ATTR_DRIVER_OVERRIDE() \
+static ssize_t driver_override_store(struct device *dev, \
+ struct device_attribute *attr, \
+ const char *buf, size_t count) \
+{ \
+ int ret; \
+ \
+ ret = __device_set_driver_override(dev, buf, count); \
+ if (ret) \
+ return ret; \
+ \
+ return count; \
+} \
+static ssize_t driver_override_show(struct device *dev, \
+ struct device_attribute *attr, char *buf) \
+{ \
+ guard(spinlock)(&dev->driver_override.lock); \
+ return sysfs_emit(buf, "%s\n", dev->driver_override.name); \
+} \
+static DEVICE_ATTR_RW(driver_override)
+
#define DEVICE_ATTR_IGNORE_LOCKDEP(_name, _mode, _show, _store) \
struct device_attribute dev_attr_##_name = \
__ATTR_IGNORE_LOCKDEP(_name, _mode, _show, _store)
@@ -483,6 +510,8 @@ struct device_physical_location {
* on. This shrinks the "Board Support Packages" (BSPs) and
* minimizes board-specific #ifdefs in drivers.
* @driver_data: Private pointer for driver specific info.
+ * @driver_override: Driver name to force a match. Do not touch directly; use
+ * device_set_driver_override() instead.
* @links: Links to suppliers and consumers of this device.
* @power: For device power management.
* See Documentation/driver-api/pm/devices.rst for details.
@@ -576,6 +605,10 @@ struct device {
core doesn't touch it */
void *driver_data; /* Driver data, set and get with
dev_set_drvdata/dev_get_drvdata */
+ struct {
+ const char *name;
+ spinlock_t lock;
+ } driver_override;
struct mutex mutex; /* mutex to synchronize calls to
* its driver.
*/
@@ -701,6 +734,54 @@ struct device_link {
#define kobj_to_dev(__kobj) container_of_const(__kobj, struct device, kobj)
+int __device_set_driver_override(struct device *dev, const char *s, size_t len);
+
+/**
+ * device_set_driver_override() - Helper to set or clear driver override.
+ * @dev: Device to change
+ * @s: NUL-terminated string, new driver name to force a match, pass empty
+ * string to clear it ("" or "\n", where the latter is only for sysfs
+ * interface).
+ *
+ * Helper to set or clear driver override of a device.
+ *
+ * Returns: 0 on success or a negative error code on failure.
+ */
+static inline int device_set_driver_override(struct device *dev, const char *s)
+{
+ return __device_set_driver_override(dev, s, strlen(s));
+}
+
+/**
+ * device_has_driver_override() - Check if a driver override has been set.
+ * @dev: device to check
+ *
+ * Returns true if a driver override has been set for this device.
+ */
+static inline bool device_has_driver_override(struct device *dev)
+{
+ guard(spinlock)(&dev->driver_override.lock);
+ return !!dev->driver_override.name;
+}
+
+/**
+ * device_match_driver_override() - Match a driver against the device's driver_override.
+ * @dev: device to check
+ * @drv: driver to match against
+ *
+ * Returns > 0 if a driver override is set and matches the given driver, 0 if a
+ * driver override is set but does not match, or < 0 if a driver override is not
+ * set at all.
+ */
+static inline int device_match_driver_override(struct device *dev,
+ const struct device_driver *drv)
+{
+ guard(spinlock)(&dev->driver_override.lock);
+ if (dev->driver_override.name)
+ return !strcmp(dev->driver_override.name, drv->name);
+ return -1;
+}
+
/**
* device_iommu_mapped - Returns true when the device DMA is translated
* by an IOMMU
--
2.53.0
next prev parent reply other threads:[~2026-03-02 0:27 UTC|newest]
Thread overview: 21+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-03-02 0:25 [PATCH 0/3] driver core: generalize driver_override infrastructure Danilo Krummrich
2026-03-02 0:25 ` Danilo Krummrich [this message]
2026-03-02 7:35 ` [PATCH 1/3] driver core: generalize driver_override in struct device Gui-Dong Han
2026-03-02 8:36 ` Gui-Dong Han
2026-03-02 10:05 ` Danilo Krummrich
2026-03-02 11:04 ` Gui-Dong Han
2026-03-02 10:00 ` Geert Uytterhoeven
2026-03-02 10:26 ` Danilo Krummrich
2026-03-02 10:38 ` Geert Uytterhoeven
2026-03-02 11:03 ` Danilo Krummrich
2026-03-02 10:23 ` Armin Wolf
2026-03-02 16:28 ` Danilo Krummrich
2026-03-02 0:25 ` [PATCH 2/3] hwmon: axi-fan: don't use driver_override as IRQ name Danilo Krummrich
2026-03-02 0:51 ` Guenter Roeck
2026-03-02 10:00 ` Danilo Krummrich
2026-03-02 11:02 ` Nuno Sá
2026-03-02 0:25 ` [PATCH 3/3] driver core: platform: use generic driver_override infrastructure Danilo Krummrich
2026-03-02 8:55 ` Gui-Dong Han
2026-03-02 9:41 ` [PATCH 0/3] driver core: generalize " Gui-Dong Han
2026-03-02 10:12 ` Danilo Krummrich
2026-03-02 10:59 ` Gui-Dong Han
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=20260302002729.19438-2-dakr@kernel.org \
--to=dakr@kernel.org \
--cc=abelvesa@kernel.org \
--cc=dalias@libc.org \
--cc=driver-core@lists.linux.dev \
--cc=glaubitz@physik.fu-berlin.de \
--cc=gregkh@linuxfoundation.org \
--cc=hanguidong02@gmail.com \
--cc=imx@lists.linux.dev \
--cc=linux-arm-msm@vger.kernel.org \
--cc=linux-hwmon@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-sh@vger.kernel.org \
--cc=linux-sound@vger.kernel.org \
--cc=nuno.sa@analog.com \
--cc=rafael@kernel.org \
--cc=s.nawrocki@samsung.com \
--cc=srini@kernel.org \
--cc=ysato@users.sourceforge.jp \
/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.