]> Pileus Git - ~andy/linux/blobdiff - drivers/block/rbd.c
rbd: set up watch in rbd_dev_image_probe()
[~andy/linux] / drivers / block / rbd.c
index b6024a2d7b86dce221b2acbce3513b93adad171c..ac94aa4b4d22c9db942d619910376973c6074278 100644 (file)
@@ -427,8 +427,9 @@ void rbd_warn(struct rbd_device *rbd_dev, const char *fmt, ...)
 #  define rbd_assert(expr)     ((void) 0)
 #endif /* !RBD_DEBUG */
 
-static void rbd_img_parent_read(struct rbd_obj_request *obj_request);
 static int rbd_img_obj_request_submit(struct rbd_obj_request *obj_request);
+static void rbd_img_parent_read(struct rbd_obj_request *obj_request);
+static void rbd_dev_remove_parent(struct rbd_device *rbd_dev);
 
 static int rbd_dev_refresh(struct rbd_device *rbd_dev, u64 *hver);
 static int rbd_dev_v2_refresh(struct rbd_device *rbd_dev, u64 *hver);
@@ -865,7 +866,7 @@ static struct rbd_snap *snap_by_name(struct rbd_device *rbd_dev,
        return NULL;
 }
 
-static int rbd_dev_set_mapping(struct rbd_device *rbd_dev)
+static int rbd_dev_mapping_set(struct rbd_device *rbd_dev)
 {
        if (!memcmp(rbd_dev->spec->snap_name, RBD_SNAP_HEAD_NAME,
                    sizeof (RBD_SNAP_HEAD_NAME))) {
@@ -885,6 +886,13 @@ static int rbd_dev_set_mapping(struct rbd_device *rbd_dev)
        return 0;
 }
 
+static void rbd_dev_mapping_clear(struct rbd_device *rbd_dev)
+{
+       rbd_dev->mapping.size = 0;
+       rbd_dev->mapping.features = 0;
+       rbd_dev->mapping.read_only = true;
+}
+
 static void rbd_header_free(struct rbd_image_header *header)
 {
        kfree(header->object_prefix);
@@ -4016,20 +4024,12 @@ static char *rbd_dev_snap_info(struct rbd_device *rbd_dev, u32 which,
 static int rbd_dev_v2_refresh(struct rbd_device *rbd_dev, u64 *hver)
 {
        int ret;
-       __u8 obj_order;
 
        down_write(&rbd_dev->header_rwsem);
 
-       /* Grab old order first, to see if it changes */
-
-       obj_order = rbd_dev->header.obj_order,
        ret = rbd_dev_v2_image_size(rbd_dev);
        if (ret)
                goto out;
-       if (rbd_dev->header.obj_order != obj_order) {
-               ret = -EIO;
-               goto out;
-       }
        rbd_update_mapping_size(rbd_dev);
 
        ret = rbd_dev_v2_snap_context(rbd_dev, hver);
@@ -4584,18 +4584,6 @@ out:
 static int rbd_dev_v1_probe(struct rbd_device *rbd_dev)
 {
        int ret;
-       size_t size;
-
-       /* Record the header object name for this rbd image. */
-
-       size = strlen(rbd_dev->spec->image_name) + sizeof (RBD_SUFFIX);
-       rbd_dev->header_name = kmalloc(size, GFP_KERNEL);
-       if (!rbd_dev->header_name) {
-               ret = -ENOMEM;
-               goto out_err;
-       }
-       sprintf(rbd_dev->header_name, "%s%s",
-               rbd_dev->spec->image_name, RBD_SUFFIX);
 
        /* Populate rbd image metadata */
 
@@ -4624,22 +4612,9 @@ out_err:
 
 static int rbd_dev_v2_probe(struct rbd_device *rbd_dev)
 {
-       size_t size;
        int ret;
        u64 ver = 0;
 
-       /*
-        * Image id was filled in by the caller.  Record the header
-        * object name for this rbd image.
-        */
-       size = sizeof (RBD_HEADER_PREFIX) + strlen(rbd_dev->spec->image_id);
-       rbd_dev->header_name = kmalloc(size, GFP_KERNEL);
-       if (!rbd_dev->header_name)
-               return -ENOMEM;
-       sprintf(rbd_dev->header_name, "%s%s",
-                       RBD_HEADER_PREFIX, rbd_dev->spec->image_id);
-
-       /* Get the size and object order for the image */
        ret = rbd_dev_v2_image_size(rbd_dev);
        if (ret)
                goto out_err;
@@ -4702,25 +4677,54 @@ out_err:
        return ret;
 }
 
-static int rbd_dev_probe_finish(struct rbd_device *rbd_dev)
+static int rbd_dev_probe_parent(struct rbd_device *rbd_dev)
 {
        struct rbd_device *parent = NULL;
-       struct rbd_spec *parent_spec = NULL;
-       struct rbd_client *rbdc = NULL;
+       struct rbd_spec *parent_spec;
+       struct rbd_client *rbdc;
        int ret;
 
-       /* no need to lock here, as rbd_dev is not registered yet */
-       ret = rbd_dev_snaps_update(rbd_dev);
-       if (ret)
-               return ret;
+       if (!rbd_dev->parent_spec)
+               return 0;
+       /*
+        * We need to pass a reference to the client and the parent
+        * spec when creating the parent rbd_dev.  Images related by
+        * parent/child relationships always share both.
+        */
+       parent_spec = rbd_spec_get(rbd_dev->parent_spec);
+       rbdc = __rbd_get_client(rbd_dev->rbd_client);
 
-       ret = rbd_dev_spec_update(rbd_dev);
-       if (ret)
-               goto err_out_snaps;
+       ret = -ENOMEM;
+       parent = rbd_dev_create(rbdc, parent_spec);
+       if (!parent)
+               goto out_err;
+
+       ret = rbd_dev_image_probe(parent);
+       if (ret < 0)
+               goto out_err;
+       rbd_dev->parent = parent;
+
+       return 0;
+out_err:
+       if (parent) {
+               rbd_spec_put(rbd_dev->parent_spec);
+               kfree(rbd_dev->header_name);
+               rbd_dev_destroy(parent);
+       } else {
+               rbd_put_client(rbdc);
+               rbd_spec_put(parent_spec);
+       }
+
+       return ret;
+}
+
+static int rbd_dev_probe_finish(struct rbd_device *rbd_dev)
+{
+       int ret;
 
-       ret = rbd_dev_set_mapping(rbd_dev);
+       ret = rbd_dev_mapping_set(rbd_dev);
        if (ret)
-               goto err_out_snaps;
+               return ret;
 
        /* generate unique id: find highest unique id, add one */
        rbd_dev_id_get(rbd_dev);
@@ -4747,39 +4751,6 @@ static int rbd_dev_probe_finish(struct rbd_device *rbd_dev)
        if (ret)
                goto err_out_disk;
 
-       /*
-        * At this point cleanup in the event of an error is the job
-        * of the sysfs code (initiated by rbd_bus_del_dev()).
-        */
-       /* Probe the parent if there is one */
-
-       if (rbd_dev->parent_spec) {
-               /*
-                * We need to pass a reference to the client and the
-                * parent spec when creating the parent rbd_dev.
-                * Images related by parent/child relationships
-                * always share both.
-                */
-               parent_spec = rbd_spec_get(rbd_dev->parent_spec);
-               rbdc = __rbd_get_client(rbd_dev->rbd_client);
-
-               parent = rbd_dev_create(rbdc, parent_spec);
-               if (!parent) {
-                       ret = -ENOMEM;
-                       goto err_out_spec;
-               }
-               rbdc = NULL;            /* parent now owns reference */
-               parent_spec = NULL;     /* parent now owns reference */
-               ret = rbd_dev_image_probe(parent);
-               if (ret < 0)
-                       goto err_out_parent;
-               rbd_dev->parent = parent;
-       }
-
-       ret = rbd_dev_header_watch_sync(rbd_dev, 1);
-       if (ret)
-               goto err_out_bus;
-
        /* Everything's ready.  Announce the disk to the world. */
 
        set_capacity(rbd_dev->disk, rbd_dev->mapping.size / SECTOR_SIZE);
@@ -4791,31 +4762,44 @@ static int rbd_dev_probe_finish(struct rbd_device *rbd_dev)
 
        return ret;
 
-err_out_parent:
-       rbd_spec_put(rbd_dev->parent_spec);
-       kfree(rbd_dev->header_name);
-       rbd_dev_destroy(parent);
-err_out_spec:
-       rbd_spec_put(parent_spec);
-       rbd_put_client(rbdc);
-err_out_bus:
-       /* this will also clean up rest of rbd_dev stuff */
-
-       rbd_bus_del_dev(rbd_dev);
-
-       return ret;
 err_out_disk:
        rbd_free_disk(rbd_dev);
 err_out_blkdev:
        unregister_blkdev(rbd_dev->major, rbd_dev->name);
 err_out_id:
        rbd_dev_id_put(rbd_dev);
-err_out_snaps:
-       rbd_remove_all_snaps(rbd_dev);
+       rbd_dev_mapping_clear(rbd_dev);
 
        return ret;
 }
 
+static int rbd_dev_header_name(struct rbd_device *rbd_dev)
+{
+       struct rbd_spec *spec = rbd_dev->spec;
+       size_t size;
+
+       /* Record the header object name for this rbd image. */
+
+       rbd_assert(rbd_image_format_valid(rbd_dev->image_format));
+
+       if (rbd_dev->image_format == 1)
+               size = strlen(spec->image_name) + sizeof (RBD_SUFFIX);
+       else
+               size = sizeof (RBD_HEADER_PREFIX) + strlen(spec->image_id);
+
+       rbd_dev->header_name = kmalloc(size, GFP_KERNEL);
+       if (!rbd_dev->header_name)
+               return -ENOMEM;
+
+       if (rbd_dev->image_format == 1)
+               sprintf(rbd_dev->header_name, "%s%s",
+                       spec->image_name, RBD_SUFFIX);
+       else
+               sprintf(rbd_dev->header_name, "%s%s",
+                       RBD_HEADER_PREFIX, spec->image_id);
+       return 0;
+}
+
 /*
  * Probe for the existence of the header object for the given rbd
  * device.  For format 2 images this includes determining the image
@@ -4824,6 +4808,7 @@ err_out_snaps:
 static int rbd_dev_image_probe(struct rbd_device *rbd_dev)
 {
        int ret;
+       int tmp;
 
        /*
         * Get the id from the image id object.  If it's not a
@@ -4836,19 +4821,52 @@ static int rbd_dev_image_probe(struct rbd_device *rbd_dev)
        rbd_assert(rbd_dev->spec->image_id);
        rbd_assert(rbd_image_format_valid(rbd_dev->image_format));
 
+       ret = rbd_dev_header_name(rbd_dev);
+       if (ret)
+               goto err_out_format;
+
+       ret = rbd_dev_header_watch_sync(rbd_dev, 1);
+       if (ret)
+               goto out_header_name;
+
        if (rbd_dev->image_format == 1)
                ret = rbd_dev_v1_probe(rbd_dev);
        else
                ret = rbd_dev_v2_probe(rbd_dev);
        if (ret)
-               goto out_err;
+               goto err_out_watch;
+
+       ret = rbd_dev_snaps_update(rbd_dev);
+       if (ret)
+               goto err_out_watch;
+
+       ret = rbd_dev_spec_update(rbd_dev);
+       if (ret)
+               goto err_out_snaps;
+
+       ret = rbd_dev_probe_parent(rbd_dev);
+       if (ret)
+               goto err_out_snaps;
 
        ret = rbd_dev_probe_finish(rbd_dev);
        if (ret)
-               rbd_header_free(&rbd_dev->header);
+               goto err_out_parent;
 
        return ret;
-out_err:
+err_out_parent:
+       rbd_dev_remove_parent(rbd_dev);
+       rbd_header_free(&rbd_dev->header);
+err_out_snaps:
+       rbd_remove_all_snaps(rbd_dev);
+err_out_watch:
+       tmp = rbd_dev_header_watch_sync(rbd_dev, 0);
+       if (tmp)
+               rbd_warn(rbd_dev, "unable to tear down watch request\n");
+out_header_name:
+       kfree(rbd_dev->header_name);
+       rbd_dev->header_name = NULL;
+err_out_format:
+       rbd_dev->image_format = 0;
        kfree(rbd_dev->spec->image_id);
        rbd_dev->spec->image_id = NULL;
 
@@ -4916,7 +4934,6 @@ static ssize_t rbd_add(struct bus_type *bus,
 
        return count;
 err_out_rbd_dev:
-       rbd_spec_put(rbd_dev->parent_spec);
        kfree(rbd_dev->header_name);
        rbd_dev_destroy(rbd_dev);
 err_out_client:
@@ -4955,9 +4972,6 @@ static void rbd_dev_release(struct device *dev)
 {
        struct rbd_device *rbd_dev = dev_to_rbd_dev(dev);
 
-       if (rbd_dev->watch_event)
-               rbd_dev_header_watch_sync(rbd_dev, 0);
-
        /* clean up and free blkdev */
        rbd_free_disk(rbd_dev);
        unregister_blkdev(rbd_dev->major, rbd_dev->name);
@@ -4967,6 +4981,7 @@ static void rbd_dev_release(struct device *dev)
 
        /* done with the id, and with the rbd_dev */
        rbd_dev_id_put(rbd_dev);
+       rbd_dev_mapping_clear(rbd_dev);
        rbd_assert(rbd_dev->rbd_client != NULL);
        rbd_spec_put(rbd_dev->parent_spec);
        kfree(rbd_dev->header_name);
@@ -4976,10 +4991,36 @@ static void rbd_dev_release(struct device *dev)
        module_put(THIS_MODULE);
 }
 
-static void __rbd_remove(struct rbd_device *rbd_dev)
+static void rbd_dev_remove_parent(struct rbd_device *rbd_dev)
 {
-       rbd_remove_all_snaps(rbd_dev);
-       rbd_bus_del_dev(rbd_dev);
+       while (rbd_dev->parent) {
+               struct rbd_device *first = rbd_dev;
+               struct rbd_device *second = first->parent;
+               struct rbd_device *third;
+               int ret;
+
+               /*
+                * Follow to the parent with no grandparent and
+                * remove it.
+                */
+               while (second && (third = second->parent)) {
+                       first = second;
+                       second = third;
+               }
+               rbd_assert(second);
+               ret = rbd_dev_header_watch_sync(rbd_dev, 0);
+               if (ret)
+                       rbd_warn(rbd_dev,
+                               "failed to cancel watch event (%d)\n", ret);
+               rbd_remove_all_snaps(second);
+               rbd_bus_del_dev(second);
+               first->parent = NULL;
+               first->parent_overlap = 0;
+
+               rbd_assert(first->parent_spec);
+               rbd_spec_put(first->parent_spec);
+               first->parent_spec = NULL;
+       }
 }
 
 static ssize_t rbd_remove(struct bus_type *bus,
@@ -4987,13 +5028,13 @@ static ssize_t rbd_remove(struct bus_type *bus,
                          size_t count)
 {
        struct rbd_device *rbd_dev = NULL;
-       int target_id, rc;
+       int target_id;
        unsigned long ul;
-       int ret = count;
+       int ret;
 
-       rc = strict_strtoul(buf, 10, &ul);
-       if (rc)
-               return rc;
+       ret = strict_strtoul(buf, 10, &ul);
+       if (ret)
+               return ret;
 
        /* convert to int; abort if we lost anything in the conversion */
        target_id = (int) ul;
@@ -5017,27 +5058,19 @@ static ssize_t rbd_remove(struct bus_type *bus,
        if (ret < 0)
                goto done;
 
-       while (rbd_dev->parent_spec) {
-               struct rbd_device *first = rbd_dev;
-               struct rbd_device *second = first->parent;
-               struct rbd_device *third;
-
-               /*
-                * Follow to the parent with no grandparent and
-                * remove it.
-                */
-               while (second && (third = second->parent)) {
-                       first = second;
-                       second = third;
-               }
-               __rbd_remove(second);
-               rbd_spec_put(first->parent_spec);
-               first->parent_spec = NULL;
-               first->parent_overlap = 0;
-               first->parent = NULL;
+       ret = rbd_dev_header_watch_sync(rbd_dev, 0);
+       if (ret) {
+               rbd_warn(rbd_dev, "failed to cancel watch event (%d)\n", ret);
+               clear_bit(RBD_DEV_FLAG_REMOVING, &rbd_dev->flags);
+               smp_mb();
+               return ret;
        }
-       __rbd_remove(rbd_dev);
+       ret = count;
+
+       rbd_dev_remove_parent(rbd_dev);
 
+       rbd_remove_all_snaps(rbd_dev);
+       rbd_bus_del_dev(rbd_dev);
 done:
        mutex_unlock(&ctl_mutex);