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 <[email protected]>
---
 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


Reply via email to