device-dax: introduce 'seed' devices
[platform/kernel/linux-rpi.git] / drivers / dax / bus.c
index 9549f11..dce9413 100644 (file)
@@ -139,8 +139,26 @@ static int dax_bus_probe(struct device *dev)
 {
        struct dax_device_driver *dax_drv = to_dax_drv(dev->driver);
        struct dev_dax *dev_dax = to_dev_dax(dev);
+       struct dax_region *dax_region = dev_dax->region;
+       struct range *range = &dev_dax->range;
+       int rc;
+
+       if (range_len(range) == 0 || dev_dax->id < 0)
+               return -ENXIO;
+
+       rc = dax_drv->probe(dev_dax);
+
+       if (rc || is_static(dax_region))
+               return rc;
+
+       /*
+        * Track new seed creation only after successful probe of the
+        * previous seed.
+        */
+       if (dax_region->seed == dev)
+               dax_region->seed = NULL;
 
-       return dax_drv->probe(dev_dax);
+       return 0;
 }
 
 static int dax_bus_remove(struct device *dev)
@@ -237,14 +255,216 @@ static ssize_t available_size_show(struct device *dev,
 }
 static DEVICE_ATTR_RO(available_size);
 
+static ssize_t seed_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct dax_region *dax_region = dev_get_drvdata(dev);
+       struct device *seed;
+       ssize_t rc;
+
+       if (is_static(dax_region))
+               return -EINVAL;
+
+       device_lock(dev);
+       seed = dax_region->seed;
+       rc = sprintf(buf, "%s\n", seed ? dev_name(seed) : "");
+       device_unlock(dev);
+
+       return rc;
+}
+static DEVICE_ATTR_RO(seed);
+
+static ssize_t create_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct dax_region *dax_region = dev_get_drvdata(dev);
+       struct device *youngest;
+       ssize_t rc;
+
+       if (is_static(dax_region))
+               return -EINVAL;
+
+       device_lock(dev);
+       youngest = dax_region->youngest;
+       rc = sprintf(buf, "%s\n", youngest ? dev_name(youngest) : "");
+       device_unlock(dev);
+
+       return rc;
+}
+
+static ssize_t create_store(struct device *dev, struct device_attribute *attr,
+               const char *buf, size_t len)
+{
+       struct dax_region *dax_region = dev_get_drvdata(dev);
+       unsigned long long avail;
+       ssize_t rc;
+       int val;
+
+       if (is_static(dax_region))
+               return -EINVAL;
+
+       rc = kstrtoint(buf, 0, &val);
+       if (rc)
+               return rc;
+       if (val != 1)
+               return -EINVAL;
+
+       device_lock(dev);
+       avail = dax_region_avail_size(dax_region);
+       if (avail == 0)
+               rc = -ENOSPC;
+       else {
+               struct dev_dax_data data = {
+                       .dax_region = dax_region,
+                       .size = 0,
+                       .id = -1,
+               };
+               struct dev_dax *dev_dax = devm_create_dev_dax(&data);
+
+               if (IS_ERR(dev_dax))
+                       rc = PTR_ERR(dev_dax);
+               else {
+                       /*
+                        * In support of crafting multiple new devices
+                        * simultaneously multiple seeds can be created,
+                        * but only the first one that has not been
+                        * successfully bound is tracked as the region
+                        * seed.
+                        */
+                       if (!dax_region->seed)
+                               dax_region->seed = &dev_dax->dev;
+                       dax_region->youngest = &dev_dax->dev;
+                       rc = len;
+               }
+       }
+       device_unlock(dev);
+
+       return rc;
+}
+static DEVICE_ATTR_RW(create);
+
+void kill_dev_dax(struct dev_dax *dev_dax)
+{
+       struct dax_device *dax_dev = dev_dax->dax_dev;
+       struct inode *inode = dax_inode(dax_dev);
+
+       kill_dax(dax_dev);
+       unmap_mapping_range(inode->i_mapping, 0, 0, 1);
+}
+EXPORT_SYMBOL_GPL(kill_dev_dax);
+
+static void free_dev_dax_range(struct dev_dax *dev_dax)
+{
+       struct dax_region *dax_region = dev_dax->region;
+       struct range *range = &dev_dax->range;
+
+       device_lock_assert(dax_region->dev);
+       if (range_len(range))
+               __release_region(&dax_region->res, range->start,
+                               range_len(range));
+}
+
+static void unregister_dev_dax(void *dev)
+{
+       struct dev_dax *dev_dax = to_dev_dax(dev);
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       kill_dev_dax(dev_dax);
+       free_dev_dax_range(dev_dax);
+       device_del(dev);
+       put_device(dev);
+}
+
+/* a return value >= 0 indicates this invocation invalidated the id */
+static int __free_dev_dax_id(struct dev_dax *dev_dax)
+{
+       struct dax_region *dax_region = dev_dax->region;
+       struct device *dev = &dev_dax->dev;
+       int rc = dev_dax->id;
+
+       device_lock_assert(dev);
+
+       if (is_static(dax_region) || dev_dax->id < 0)
+               return -1;
+       ida_free(&dax_region->ida, dev_dax->id);
+       dev_dax->id = -1;
+       return rc;
+}
+
+static int free_dev_dax_id(struct dev_dax *dev_dax)
+{
+       struct device *dev = &dev_dax->dev;
+       int rc;
+
+       device_lock(dev);
+       rc = __free_dev_dax_id(dev_dax);
+       device_unlock(dev);
+       return rc;
+}
+
+static ssize_t delete_store(struct device *dev, struct device_attribute *attr,
+               const char *buf, size_t len)
+{
+       struct dax_region *dax_region = dev_get_drvdata(dev);
+       struct dev_dax *dev_dax;
+       struct device *victim;
+       bool do_del = false;
+       int rc;
+
+       if (is_static(dax_region))
+               return -EINVAL;
+
+       victim = device_find_child_by_name(dax_region->dev, buf);
+       if (!victim)
+               return -ENXIO;
+
+       device_lock(dev);
+       device_lock(victim);
+       dev_dax = to_dev_dax(victim);
+       if (victim->driver || range_len(&dev_dax->range))
+               rc = -EBUSY;
+       else {
+               /*
+                * Invalidate the device so it does not become active
+                * again, but always preserve device-id-0 so that
+                * /sys/bus/dax/ is guaranteed to be populated while any
+                * dax_region is registered.
+                */
+               if (dev_dax->id > 0) {
+                       do_del = __free_dev_dax_id(dev_dax) >= 0;
+                       rc = len;
+                       if (dax_region->seed == victim)
+                               dax_region->seed = NULL;
+                       if (dax_region->youngest == victim)
+                               dax_region->youngest = NULL;
+               } else
+                       rc = -EBUSY;
+       }
+       device_unlock(victim);
+
+       /* won the race to invalidate the device, clean it up */
+       if (do_del)
+               devm_release_action(dev, unregister_dev_dax, victim);
+       device_unlock(dev);
+       put_device(victim);
+
+       return rc;
+}
+static DEVICE_ATTR_WO(delete);
+
 static umode_t dax_region_visible(struct kobject *kobj, struct attribute *a,
                int n)
 {
        struct device *dev = container_of(kobj, struct device, kobj);
        struct dax_region *dax_region = dev_get_drvdata(dev);
 
-       if (is_static(dax_region) && a == &dev_attr_available_size.attr)
-               return 0;
+       if (is_static(dax_region))
+               if (a == &dev_attr_available_size.attr
+                               || a == &dev_attr_create.attr
+                               || a == &dev_attr_seed.attr
+                               || a == &dev_attr_delete.attr)
+                       return 0;
        return a->mode;
 }
 
@@ -252,6 +472,9 @@ static struct attribute *dax_region_attributes[] = {
        &dev_attr_available_size.attr,
        &dev_attr_region_size.attr,
        &dev_attr_align.attr,
+       &dev_attr_create.attr,
+       &dev_attr_seed.attr,
+       &dev_attr_delete.attr,
        &dev_attr_id.attr,
        NULL,
 };
@@ -320,6 +543,7 @@ struct dax_region *alloc_dax_region(struct device *parent, int region_id,
        dax_region->align = align;
        dax_region->dev = parent;
        dax_region->target_node = target_node;
+       ida_init(&dax_region->ida);
        dax_region->res = (struct resource) {
                .start = res->start,
                .end = res->end,
@@ -347,6 +571,15 @@ static int alloc_dev_dax_range(struct dev_dax *dev_dax, resource_size_t size)
 
        device_lock_assert(dax_region->dev);
 
+       /* handle the seed alloc special case */
+       if (!size) {
+               dev_dax->range = (struct range) {
+                       .start = res->start,
+                       .end = res->start - 1,
+               };
+               return 0;
+       }
+
        /* TODO: handle multiple allocations per region */
        if (res->child)
                return -ENOMEM;
@@ -448,33 +681,15 @@ static const struct attribute_group *dax_attribute_groups[] = {
        NULL,
 };
 
-void kill_dev_dax(struct dev_dax *dev_dax)
-{
-       struct dax_device *dax_dev = dev_dax->dax_dev;
-       struct inode *inode = dax_inode(dax_dev);
-
-       kill_dax(dax_dev);
-       unmap_mapping_range(inode->i_mapping, 0, 0, 1);
-}
-EXPORT_SYMBOL_GPL(kill_dev_dax);
-
-static void free_dev_dax_range(struct dev_dax *dev_dax)
-{
-       struct dax_region *dax_region = dev_dax->region;
-       struct range *range = &dev_dax->range;
-
-       device_lock_assert(dax_region->dev);
-       __release_region(&dax_region->res, range->start, range_len(range));
-}
-
 static void dev_dax_release(struct device *dev)
 {
        struct dev_dax *dev_dax = to_dev_dax(dev);
        struct dax_region *dax_region = dev_dax->region;
        struct dax_device *dax_dev = dev_dax->dax_dev;
 
-       dax_region_put(dax_region);
        put_dax(dax_dev);
+       free_dev_dax_id(dev_dax);
+       dax_region_put(dax_region);
        kfree(dev_dax->pgmap);
        kfree(dev_dax);
 }
@@ -484,18 +699,6 @@ static const struct device_type dev_dax_type = {
        .groups = dax_attribute_groups,
 };
 
-static void unregister_dev_dax(void *dev)
-{
-       struct dev_dax *dev_dax = to_dev_dax(dev);
-
-       dev_dbg(dev, "%s\n", __func__);
-
-       kill_dev_dax(dev_dax);
-       free_dev_dax_range(dev_dax);
-       device_del(dev);
-       put_device(dev);
-}
-
 struct dev_dax *devm_create_dev_dax(struct dev_dax_data *data)
 {
        struct dax_region *dax_region = data->dax_region;
@@ -506,17 +709,35 @@ struct dev_dax *devm_create_dev_dax(struct dev_dax_data *data)
        struct device *dev;
        int rc;
 
-       if (data->id < 0)
-               return ERR_PTR(-EINVAL);
-
        dev_dax = kzalloc(sizeof(*dev_dax), GFP_KERNEL);
        if (!dev_dax)
                return ERR_PTR(-ENOMEM);
 
+       if (is_static(dax_region)) {
+               if (dev_WARN_ONCE(parent, data->id < 0,
+                               "dynamic id specified to static region\n")) {
+                       rc = -EINVAL;
+                       goto err_id;
+               }
+
+               dev_dax->id = data->id;
+       } else {
+               if (dev_WARN_ONCE(parent, data->id >= 0,
+                               "static id specified to dynamic region\n")) {
+                       rc = -EINVAL;
+                       goto err_id;
+               }
+
+               rc = ida_alloc(&dax_region->ida, GFP_KERNEL);
+               if (rc < 0)
+                       goto err_id;
+               dev_dax->id = rc;
+       }
+
        dev_dax->region = dax_region;
        dev = &dev_dax->dev;
        device_initialize(dev);
-       dev_set_name(dev, "dax%d.%d", dax_region->id, data->id);
+       dev_set_name(dev, "dax%d.%d", dax_region->id, dev_dax->id);
 
        rc = alloc_dev_dax_range(dev_dax, data->size);
        if (rc)
@@ -579,6 +800,8 @@ err_alloc_dax:
 err_pgmap:
        free_dev_dax_range(dev_dax);
 err_range:
+       free_dev_dax_id(dev_dax);
+err_id:
        kfree(dev_dax);
 
        return ERR_PTR(rc);