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
