From: John Groves <[email protected]> Add custom bind/unbind sysfs attributes for the dax bus that check whether a filesystem has registered as a holder (via fs_dax_get()) before allowing driver unbind.
When a filesystem like famfs mounts on a dax device, it registers itself as the holder via dax_holder_ops. Previously, there was no mechanism to prevent driver unbind while the filesystem was mounted, which could cause some havoc. The new unbind_store() checks dax_holder() and returns -EBUSY if a holder is registered, giving userspace proper feedback that the device is in use. To use our custom bind/unbind handlers instead of the default ones, set suppress_bind_attrs=true on all dax drivers during registration. Signed-off-by: John Groves <[email protected]> --- drivers/dax/bus.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/drivers/dax/bus.c b/drivers/dax/bus.c index 6e0e28116edc..ed453442739d 100644 --- a/drivers/dax/bus.c +++ b/drivers/dax/bus.c @@ -151,9 +151,61 @@ static ssize_t remove_id_store(struct device_driver *drv, const char *buf, } static DRIVER_ATTR_WO(remove_id); +static const struct bus_type dax_bus_type; + +/* + * Custom bind/unbind handlers for dax bus. + * The unbind handler checks if a filesystem holds the dax device and + * returns -EBUSY if so, preventing driver unbind while in use. + */ +static ssize_t unbind_store(struct device_driver *drv, const char *buf, + size_t count) +{ + struct device *dev; + int rc = -ENODEV; + + dev = bus_find_device_by_name(&dax_bus_type, NULL, buf); + if (dev && dev->driver == drv) { + struct dev_dax *dev_dax = to_dev_dax(dev); + + if (dax_holder(dev_dax->dax_dev)) { + dev_dbg(dev, + "%s: blocking unbind due to active holder\n", + __func__); + rc = -EBUSY; + goto out; + } + device_release_driver(dev); + rc = count; + } +out: + put_device(dev); + return rc; +} +static DRIVER_ATTR_WO(unbind); + +static ssize_t bind_store(struct device_driver *drv, const char *buf, + size_t count) +{ + struct device *dev; + int rc = -ENODEV; + + dev = bus_find_device_by_name(&dax_bus_type, NULL, buf); + if (dev) { + rc = device_driver_attach(drv, dev); + if (!rc) + rc = count; + } + put_device(dev); + return rc; +} +static DRIVER_ATTR_WO(bind); + static struct attribute *dax_drv_attrs[] = { &driver_attr_new_id.attr, &driver_attr_remove_id.attr, + &driver_attr_bind.attr, + &driver_attr_unbind.attr, NULL, }; ATTRIBUTE_GROUPS(dax_drv); @@ -1591,6 +1643,7 @@ int __dax_driver_register(struct dax_device_driver *dax_drv, drv->name = mod_name; drv->mod_name = mod_name; drv->bus = &dax_bus_type; + drv->suppress_bind_attrs = true; return driver_register(drv); } -- 2.49.0
