Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/anholt...
[safe/jmp/linux-2.6] / drivers / md / dm-snap.c
index b2d9d1a..65ff82f 100644 (file)
@@ -9,6 +9,7 @@
 #include <linux/blkdev.h>
 #include <linux/ctype.h>
 #include <linux/device-mapper.h>
+#include <linux/delay.h>
 #include <linux/fs.h>
 #include <linux/init.h>
 #include <linux/kdev_t.h>
@@ -20,6 +21,7 @@
 #include <linux/log2.h>
 #include <linux/dm-kcopyd.h>
 
+#include "dm-exception-store.h"
 #include "dm-snap.h"
 #include "dm-bio-list.h"
 
@@ -229,19 +231,21 @@ static void __insert_origin(struct origin *o)
  */
 static int register_snapshot(struct dm_snapshot *snap)
 {
-       struct origin *o;
+       struct origin *o, *new_o;
        struct block_device *bdev = snap->origin->bdev;
 
+       new_o = kmalloc(sizeof(*new_o), GFP_KERNEL);
+       if (!new_o)
+               return -ENOMEM;
+
        down_write(&_origins_lock);
        o = __lookup_origin(bdev);
 
-       if (!o) {
+       if (o)
+               kfree(new_o);
+       else {
                /* New origin */
-               o = kmalloc(sizeof(*o), GFP_KERNEL);
-               if (!o) {
-                       up_write(&_origins_lock);
-                       return -ENOMEM;
-               }
+               o = new_o;
 
                /* Initialise the struct */
                INIT_LIST_HEAD(&o->snapshots);
@@ -368,6 +372,7 @@ static struct dm_snap_pending_exception *alloc_pending_exception(struct dm_snaps
        struct dm_snap_pending_exception *pe = mempool_alloc(s->pending_pool,
                                                             GFP_NOIO);
 
+       atomic_inc(&s->pending_exceptions_count);
        pe->snap = s;
 
        return pe;
@@ -375,7 +380,11 @@ static struct dm_snap_pending_exception *alloc_pending_exception(struct dm_snaps
 
 static void free_pending_exception(struct dm_snap_pending_exception *pe)
 {
-       mempool_free(pe, pe->snap->pending_pool);
+       struct dm_snapshot *s = pe->snap;
+
+       mempool_free(pe, s->pending_pool);
+       smp_mb__before_atomic_dec();
+       atomic_dec(&s->pending_exceptions_count);
 }
 
 static void insert_completed_exception(struct dm_snapshot *s,
@@ -421,8 +430,13 @@ out:
        list_add(&new_e->hash_list, e ? &e->hash_list : l);
 }
 
-int dm_add_exception(struct dm_snapshot *s, chunk_t old, chunk_t new)
+/*
+ * Callback used by the exception stores to load exceptions when
+ * initialising.
+ */
+static int dm_add_exception(void *context, chunk_t old, chunk_t new)
 {
+       struct dm_snapshot *s = context;
        struct dm_snap_exception *e;
 
        e = alloc_exception();
@@ -600,6 +614,7 @@ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv)
 
        s->valid = 1;
        s->active = 0;
+       atomic_set(&s->pending_exceptions_count, 0);
        init_rwsem(&s->lock);
        spin_lock_init(&s->pe_lock);
        s->ti = ti;
@@ -650,7 +665,7 @@ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv)
        spin_lock_init(&s->tracked_chunk_lock);
 
        /* Metadata must only be loaded into one table at once */
-       r = s->store.read_metadata(&s->store);
+       r = s->store.read_metadata(&s->store, dm_add_exception, (void *)s);
        if (r < 0) {
                ti->error = "Failed to read snapshot metadata";
                goto bad_load_and_register;
@@ -726,6 +741,14 @@ static void snapshot_dtr(struct dm_target *ti)
        /* After this returns there can be no new kcopyd jobs. */
        unregister_snapshot(s);
 
+       while (atomic_read(&s->pending_exceptions_count))
+               msleep(1);
+       /*
+        * Ensure instructions in mempool_destroy aren't reordered
+        * before atomic_read.
+        */
+       smp_mb();
+
 #ifdef CONFIG_DM_DEBUG
        for (i = 0; i < DM_TRACKED_CHUNK_HASH_SIZE; i++)
                BUG_ON(!hlist_empty(&s->tracked_chunk_hash[i]));
@@ -872,10 +895,10 @@ static void pending_complete(struct dm_snap_pending_exception *pe, int success)
 
        /*
         * Check for conflicting reads. This is extremely improbable,
-        * so yield() is sufficient and there is no need for a wait queue.
+        * so msleep(1) is sufficient and there is no need for a wait queue.
         */
        while (__chunk_is_tracked(s, pe->e.old_chunk))
-               yield();
+               msleep(1);
 
        /*
         * Add a proper exception, and remove the
@@ -1388,6 +1411,12 @@ static int __init dm_snapshot_init(void)
 {
        int r;
 
+       r = dm_exception_store_init();
+       if (r) {
+               DMERR("Failed to initialize exception stores");
+               return r;
+       }
+
        r = dm_register_target(&snapshot_target);
        if (r) {
                DMERR("snapshot target register failed %d", r);
@@ -1436,39 +1465,34 @@ static int __init dm_snapshot_init(void)
 
        return 0;
 
-      bad_pending_pool:
+bad_pending_pool:
        kmem_cache_destroy(tracked_chunk_cache);
-      bad5:
+bad5:
        kmem_cache_destroy(pending_cache);
-      bad4:
+bad4:
        kmem_cache_destroy(exception_cache);
-      bad3:
+bad3:
        exit_origin_hash();
-      bad2:
+bad2:
        dm_unregister_target(&origin_target);
-      bad1:
+bad1:
        dm_unregister_target(&snapshot_target);
        return r;
 }
 
 static void __exit dm_snapshot_exit(void)
 {
-       int r;
-
        destroy_workqueue(ksnapd);
 
-       r = dm_unregister_target(&snapshot_target);
-       if (r)
-               DMERR("snapshot unregister failed %d", r);
-
-       r = dm_unregister_target(&origin_target);
-       if (r)
-               DMERR("origin unregister failed %d", r);
+       dm_unregister_target(&snapshot_target);
+       dm_unregister_target(&origin_target);
 
        exit_origin_hash();
        kmem_cache_destroy(pending_cache);
        kmem_cache_destroy(exception_cache);
        kmem_cache_destroy(tracked_chunk_cache);
+
+       dm_exception_store_exit();
 }
 
 /* Module hooks */