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


Reply via email to