Merge tag 'phy-for-4.4-rc' of git://git.kernel.org/pub/scm/linux/kernel/git/kishon...
[firefly-linux-kernel-4.4.55.git] / drivers / block / rbd.c
index 128e7df5b807222ba2d6f50148cb164919a308f7..81ea69fee7ca183313b8e8322833062262279187 100644 (file)
@@ -418,8 +418,6 @@ MODULE_PARM_DESC(single_major, "Use a single major number for all rbd devices (d
 
 static int rbd_img_request_submit(struct rbd_img_request *img_request);
 
-static void rbd_dev_device_release(struct device *dev);
-
 static ssize_t rbd_add(struct bus_type *bus, const char *buf,
                       size_t count);
 static ssize_t rbd_remove(struct bus_type *bus, const char *buf,
@@ -3444,6 +3442,7 @@ static void rbd_queue_workfn(struct work_struct *work)
                goto err_rq;
        }
        img_request->rq = rq;
+       snapc = NULL; /* img_request consumes a ref */
 
        if (op_type == OBJ_OP_DISCARD)
                result = rbd_img_request_fill(img_request, OBJ_REQUEST_NODATA,
@@ -3991,14 +3990,12 @@ static const struct attribute_group *rbd_attr_groups[] = {
        NULL
 };
 
-static void rbd_sysfs_dev_release(struct device *dev)
-{
-}
+static void rbd_dev_release(struct device *dev);
 
 static struct device_type rbd_device_type = {
        .name           = "rbd",
        .groups         = rbd_attr_groups,
-       .release        = rbd_sysfs_dev_release,
+       .release        = rbd_dev_release,
 };
 
 static struct rbd_spec *rbd_spec_get(struct rbd_spec *spec)
@@ -4041,6 +4038,25 @@ static void rbd_spec_free(struct kref *kref)
        kfree(spec);
 }
 
+static void rbd_dev_release(struct device *dev)
+{
+       struct rbd_device *rbd_dev = dev_to_rbd_dev(dev);
+       bool need_put = !!rbd_dev->opts;
+
+       rbd_put_client(rbd_dev->rbd_client);
+       rbd_spec_put(rbd_dev->spec);
+       kfree(rbd_dev->opts);
+       kfree(rbd_dev);
+
+       /*
+        * This is racy, but way better than putting module outside of
+        * the release callback.  The race window is pretty small, so
+        * doing something similar to dm (dm-builtin.c) is overkill.
+        */
+       if (need_put)
+               module_put(THIS_MODULE);
+}
+
 static struct rbd_device *rbd_dev_create(struct rbd_client *rbdc,
                                         struct rbd_spec *spec,
                                         struct rbd_options *opts)
@@ -4057,6 +4073,11 @@ static struct rbd_device *rbd_dev_create(struct rbd_client *rbdc,
        INIT_LIST_HEAD(&rbd_dev->node);
        init_rwsem(&rbd_dev->header_rwsem);
 
+       rbd_dev->dev.bus = &rbd_bus_type;
+       rbd_dev->dev.type = &rbd_device_type;
+       rbd_dev->dev.parent = &rbd_root_dev;
+       device_initialize(&rbd_dev->dev);
+
        rbd_dev->rbd_client = rbdc;
        rbd_dev->spec = spec;
        rbd_dev->opts = opts;
@@ -4068,15 +4089,21 @@ static struct rbd_device *rbd_dev_create(struct rbd_client *rbdc,
        rbd_dev->layout.fl_object_size = cpu_to_le32(1 << RBD_MAX_OBJ_ORDER);
        rbd_dev->layout.fl_pg_pool = cpu_to_le32((u32) spec->pool_id);
 
+       /*
+        * If this is a mapping rbd_dev (as opposed to a parent one),
+        * pin our module.  We have a ref from do_rbd_add(), so use
+        * __module_get().
+        */
+       if (rbd_dev->opts)
+               __module_get(THIS_MODULE);
+
        return rbd_dev;
 }
 
 static void rbd_dev_destroy(struct rbd_device *rbd_dev)
 {
-       rbd_put_client(rbd_dev->rbd_client);
-       rbd_spec_put(rbd_dev->spec);
-       kfree(rbd_dev->opts);
-       kfree(rbd_dev);
+       if (rbd_dev)
+               put_device(&rbd_dev->dev);
 }
 
 /*
@@ -4702,27 +4729,6 @@ static int rbd_dev_header_info(struct rbd_device *rbd_dev)
        return rbd_dev_v2_header_info(rbd_dev);
 }
 
-static int rbd_bus_add_dev(struct rbd_device *rbd_dev)
-{
-       struct device *dev;
-       int ret;
-
-       dev = &rbd_dev->dev;
-       dev->bus = &rbd_bus_type;
-       dev->type = &rbd_device_type;
-       dev->parent = &rbd_root_dev;
-       dev->release = rbd_dev_device_release;
-       dev_set_name(dev, "%d", rbd_dev->dev_id);
-       ret = device_register(dev);
-
-       return ret;
-}
-
-static void rbd_bus_del_dev(struct rbd_device *rbd_dev)
-{
-       device_unregister(&rbd_dev->dev);
-}
-
 /*
  * Get a unique rbd identifier for the given new rbd_dev, and add
  * the rbd_dev to the global list.
@@ -5225,7 +5231,8 @@ static int rbd_dev_device_setup(struct rbd_device *rbd_dev)
        set_capacity(rbd_dev->disk, rbd_dev->mapping.size / SECTOR_SIZE);
        set_disk_ro(rbd_dev->disk, rbd_dev->mapping.read_only);
 
-       ret = rbd_bus_add_dev(rbd_dev);
+       dev_set_name(&rbd_dev->dev, "%d", rbd_dev->dev_id);
+       ret = device_add(&rbd_dev->dev);
        if (ret)
                goto err_out_mapping;
 
@@ -5248,8 +5255,6 @@ err_out_blkdev:
                unregister_blkdev(rbd_dev->major, rbd_dev->name);
 err_out_id:
        rbd_dev_id_put(rbd_dev);
-       rbd_dev_mapping_clear(rbd_dev);
-
        return ret;
 }
 
@@ -5397,7 +5402,7 @@ static ssize_t do_rbd_add(struct bus_type *bus,
        struct rbd_spec *spec = NULL;
        struct rbd_client *rbdc;
        bool read_only;
-       int rc = -ENOMEM;
+       int rc;
 
        if (!try_module_get(THIS_MODULE))
                return -ENODEV;
@@ -5405,7 +5410,7 @@ static ssize_t do_rbd_add(struct bus_type *bus,
        /* parse add command */
        rc = rbd_add_parse_args(buf, &ceph_opts, &rbd_opts, &spec);
        if (rc < 0)
-               goto err_out_module;
+               goto out;
 
        rbdc = rbd_get_client(ceph_opts);
        if (IS_ERR(rbdc)) {
@@ -5432,8 +5437,10 @@ static ssize_t do_rbd_add(struct bus_type *bus,
        }
 
        rbd_dev = rbd_dev_create(rbdc, spec, rbd_opts);
-       if (!rbd_dev)
+       if (!rbd_dev) {
+               rc = -ENOMEM;
                goto err_out_client;
+       }
        rbdc = NULL;            /* rbd_dev now owns this */
        spec = NULL;            /* rbd_dev now owns this */
        rbd_opts = NULL;        /* rbd_dev now owns this */
@@ -5458,10 +5465,13 @@ static ssize_t do_rbd_add(struct bus_type *bus,
                 */
                rbd_dev_header_unwatch_sync(rbd_dev);
                rbd_dev_image_release(rbd_dev);
-               goto err_out_module;
+               goto out;
        }
 
-       return count;
+       rc = count;
+out:
+       module_put(THIS_MODULE);
+       return rc;
 
 err_out_rbd_dev:
        rbd_dev_destroy(rbd_dev);
@@ -5470,12 +5480,7 @@ err_out_client:
 err_out_args:
        rbd_spec_put(spec);
        kfree(rbd_opts);
-err_out_module:
-       module_put(THIS_MODULE);
-
-       dout("Error adding device %s\n", buf);
-
-       return (ssize_t)rc;
+       goto out;
 }
 
 static ssize_t rbd_add(struct bus_type *bus,
@@ -5495,17 +5500,15 @@ static ssize_t rbd_add_single_major(struct bus_type *bus,
        return do_rbd_add(bus, buf, count);
 }
 
-static void rbd_dev_device_release(struct device *dev)
+static void rbd_dev_device_release(struct rbd_device *rbd_dev)
 {
-       struct rbd_device *rbd_dev = dev_to_rbd_dev(dev);
-
        rbd_free_disk(rbd_dev);
        clear_bit(RBD_DEV_FLAG_EXISTS, &rbd_dev->flags);
+       device_del(&rbd_dev->dev);
        rbd_dev_mapping_clear(rbd_dev);
        if (!single_major)
                unregister_blkdev(rbd_dev->major, rbd_dev->name);
        rbd_dev_id_put(rbd_dev);
-       rbd_dev_mapping_clear(rbd_dev);
 }
 
 static void rbd_dev_remove_parent(struct rbd_device *rbd_dev)
@@ -5590,9 +5593,8 @@ static ssize_t do_rbd_remove(struct bus_type *bus,
         * rbd_bus_del_dev() will race with rbd_watch_cb(), resulting
         * in a potential use after free of rbd_dev->disk or rbd_dev.
         */
-       rbd_bus_del_dev(rbd_dev);
+       rbd_dev_device_release(rbd_dev);
        rbd_dev_image_release(rbd_dev);
-       module_put(THIS_MODULE);
 
        return count;
 }
@@ -5663,10 +5665,8 @@ static int rbd_slab_init(void)
        if (rbd_segment_name_cache)
                return 0;
 out_err:
-       if (rbd_obj_request_cache) {
-               kmem_cache_destroy(rbd_obj_request_cache);
-               rbd_obj_request_cache = NULL;
-       }
+       kmem_cache_destroy(rbd_obj_request_cache);
+       rbd_obj_request_cache = NULL;
 
        kmem_cache_destroy(rbd_img_request_cache);
        rbd_img_request_cache = NULL;