[RFC PATCH v4 12/38] NTB: core: Add driver_override support for NTB devices

Koichiro Den
2026-01-18T13:55:16Z

When multiple NTB client drivers are available, selecting which driver
should bind to a given NTB device becomes necessary. The NTB bus match
logic currently has no way to force a specific driver.

Add a standard driver_override sysfs attribute to NTB devices and honor
it in the bus match callback. Also export ntb_bus_reprobe() so newly
loaded drivers can trigger probing of currently unbound NTB devices.

Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
drivers/ntb/core.c | 68 +++++++++++++++++++++++++++++++++++++++++++++
include/linux/ntb.h | 4 +++
2 files changed, 72 insertions(+)

diff --git a/drivers/ntb/core.c b/drivers/ntb/core.c
index ed6f4adc6130..404fa1433fab 100644
--- a/drivers/ntb/core.c
+++ b/drivers/ntb/core.c
@@ -56,6 +56,7 @@
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/sysfs.h>

#include <linux/ntb.h>
#include <linux/pci.h>
@@ -298,10 +299,77 @@ static void ntb_dev_release(struct device *dev)
complete(&ntb->released);
}

+static int ntb_bus_reprobe_one(struct device *dev, void *data)
+{
+ if (!dev->driver)
+ return device_attach(dev);
+ return 0;
+}
+
+void ntb_bus_reprobe(void)
+{
+ bus_for_each_dev(&ntb_bus, NULL, NULL, ntb_bus_reprobe_one);
+}
+EXPORT_SYMBOL_GPL(ntb_bus_reprobe);
+
+static ssize_t driver_override_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ntb_dev *ntb = dev_ntb(dev);
+ ssize_t len;
+
+ device_lock(dev);
+ len = sysfs_emit(buf, "%s\n", ntb->driver_override);
+ device_unlock(dev);
+
+ return len;
+}
+
+static ssize_t driver_override_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct ntb_dev *ntb = dev_ntb(dev);
+ int ret;
+
+ ret = driver_set_override(dev, &ntb->driver_override, buf, count);
+ if (ret)
+ return ret;
+
+ return count;
+}
+static DEVICE_ATTR_RW(driver_override);
+
+static struct attribute *ntb_attrs[] = {
+ &dev_attr_driver_override.attr,
+ NULL,
+};
+
+static const struct attribute_group ntb_group = {
+ .attrs = ntb_attrs,
+};
+__ATTRIBUTE_GROUPS(ntb);
+
+static int ntb_match(struct device *dev, const struct device_driver *drv)
+{
+ struct ntb_dev *ntb = dev_ntb(dev);
+
+ /*
+ * If driver_override is set, only allow binding to the named driver.
+ * Otherwise keep the historical behavior (match all clients).
+ */
+ if (ntb->driver_override)
+ return sysfs_streq(ntb->driver_override, drv->name);
+
+ return 1;
+}
+
static const struct bus_type ntb_bus = {
.name = "ntb",
+ .match = ntb_match,
.probe = ntb_probe,
.remove = ntb_remove,
+ .dev_groups = ntb_groups,
};

static int __init ntb_driver_init(void)
diff --git a/include/linux/ntb.h b/include/linux/ntb.h
index 7ac8cb13e90d..d0115b0bb14b 100644
--- a/include/linux/ntb.h
+++ b/include/linux/ntb.h
@@ -431,6 +431,7 @@ struct ntb_client {
* @ops: See &ntb_dev_ops.
* @ctx: See &ntb_ctx_ops.
* @ctx_ops: See &ntb_ctx_ops.
+ * @driver_override: Driver name to force a match.
*/
struct ntb_dev {
struct device dev;
@@ -439,6 +440,7 @@ struct ntb_dev {
const struct ntb_dev_ops *ops;
void *ctx;
const struct ntb_ctx_ops *ctx_ops;
+ const char *driver_override;

/* private: */

@@ -1770,4 +1772,6 @@ static inline int ntbm_msi_request_irq(struct ntb_dev *ntb,
dev_id, msi_desc);
}

+void ntb_bus_reprobe(void);
+
#endif
--
2.51.0