]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/md/dm-snap.c
hwmon: (lm90) Fix handling of hysteresis value
[linux-2.6-omap-h63xx.git] / drivers / md / dm-snap.c
index de302702ab3e23b9dc95b0dee56b786d74a3365e..b2d9d1ac28adb4554537fd3568ae4fe52e748a41 100644 (file)
@@ -96,7 +96,6 @@ struct dm_snap_pending_exception {
  */
 static struct kmem_cache *exception_cache;
 static struct kmem_cache *pending_cache;
-static mempool_t *pending_pool;
 
 struct dm_snap_tracked_chunk {
        struct hlist_node node;
@@ -134,6 +133,27 @@ static void stop_tracking_chunk(struct dm_snapshot *s,
        mempool_free(c, s->tracked_chunk_pool);
 }
 
+static int __chunk_is_tracked(struct dm_snapshot *s, chunk_t chunk)
+{
+       struct dm_snap_tracked_chunk *c;
+       struct hlist_node *hn;
+       int found = 0;
+
+       spin_lock_irq(&s->tracked_chunk_lock);
+
+       hlist_for_each_entry(c, hn,
+           &s->tracked_chunk_hash[DM_TRACKED_CHUNK_HASH(chunk)], node) {
+               if (c->chunk == chunk) {
+                       found = 1;
+                       break;
+               }
+       }
+
+       spin_unlock_irq(&s->tracked_chunk_lock);
+
+       return found;
+}
+
 /*
  * One of these per registered origin, held in the snapshot_origins hash
  */
@@ -343,14 +363,19 @@ static void free_exception(struct dm_snap_exception *e)
        kmem_cache_free(exception_cache, e);
 }
 
-static struct dm_snap_pending_exception *alloc_pending_exception(void)
+static struct dm_snap_pending_exception *alloc_pending_exception(struct dm_snapshot *s)
 {
-       return mempool_alloc(pending_pool, GFP_NOIO);
+       struct dm_snap_pending_exception *pe = mempool_alloc(s->pending_pool,
+                                                            GFP_NOIO);
+
+       pe->snap = s;
+
+       return pe;
 }
 
 static void free_pending_exception(struct dm_snap_pending_exception *pe)
 {
-       mempool_free(pe, pending_pool);
+       mempool_free(pe, pe->snap->pending_pool);
 }
 
 static void insert_completed_exception(struct dm_snapshot *s,
@@ -575,7 +600,6 @@ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv)
 
        s->valid = 1;
        s->active = 0;
-       s->last_percent = 0;
        init_rwsem(&s->lock);
        spin_lock_init(&s->pe_lock);
        s->ti = ti;
@@ -606,12 +630,18 @@ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv)
                goto bad5;
        }
 
+       s->pending_pool = mempool_create_slab_pool(MIN_IOS, pending_cache);
+       if (!s->pending_pool) {
+               ti->error = "Could not allocate mempool for pending exceptions";
+               goto bad6;
+       }
+
        s->tracked_chunk_pool = mempool_create_slab_pool(MIN_IOS,
                                                         tracked_chunk_cache);
        if (!s->tracked_chunk_pool) {
                ti->error = "Could not allocate tracked_chunk mempool for "
                            "tracking reads";
-               goto bad6;
+               goto bad_tracked_chunk_pool;
        }
 
        for (i = 0; i < DM_TRACKED_CHUNK_HASH_SIZE; i++)
@@ -648,6 +678,9 @@ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv)
  bad_load_and_register:
        mempool_destroy(s->tracked_chunk_pool);
 
+ bad_tracked_chunk_pool:
+       mempool_destroy(s->pending_pool);
+
  bad6:
        dm_kcopyd_client_destroy(s->kcopyd_client);
 
@@ -702,6 +735,8 @@ static void snapshot_dtr(struct dm_target *ti)
 
        __free_exceptions(s);
 
+       mempool_destroy(s->pending_pool);
+
        dm_put_device(ti, s->origin);
        dm_put_device(ti, s->cow);
 
@@ -788,8 +823,10 @@ static struct bio *put_pending_exception(struct dm_snap_pending_exception *pe)
         * the bios for the original write to the origin.
         */
        if (primary_pe &&
-           atomic_dec_and_test(&primary_pe->ref_count))
+           atomic_dec_and_test(&primary_pe->ref_count)) {
                origin_bios = bio_list_get(&primary_pe->origin_bios);
+               free_pending_exception(primary_pe);
+       }
 
        /*
         * Free the pe if it's not linked to an origin write or if
@@ -798,12 +835,6 @@ static struct bio *put_pending_exception(struct dm_snap_pending_exception *pe)
        if (!primary_pe || primary_pe != pe)
                free_pending_exception(pe);
 
-       /*
-        * Free the primary pe if nothing references it.
-        */
-       if (primary_pe && !atomic_read(&primary_pe->ref_count))
-               free_pending_exception(primary_pe);
-
        return origin_bios;
 }
 
@@ -839,6 +870,13 @@ static void pending_complete(struct dm_snap_pending_exception *pe, int success)
                goto out;
        }
 
+       /*
+        * Check for conflicting reads. This is extremely improbable,
+        * so yield() is sufficient and there is no need for a wait queue.
+        */
+       while (__chunk_is_tracked(s, pe->e.old_chunk))
+               yield();
+
        /*
         * Add a proper exception, and remove the
         * in-flight exception from the list.
@@ -941,7 +979,7 @@ __find_pending_exception(struct dm_snapshot *s, struct bio *bio)
         * to hold the lock while we do this.
         */
        up_write(&s->lock);
-       pe = alloc_pending_exception();
+       pe = alloc_pending_exception(s);
        down_write(&s->lock);
 
        if (!s->valid) {
@@ -961,7 +999,6 @@ __find_pending_exception(struct dm_snapshot *s, struct bio *bio)
        bio_list_init(&pe->snapshot_bios);
        pe->primary_pe = NULL;
        atomic_set(&pe->ref_count, 0);
-       pe->snap = s;
        pe->started = 0;
 
        if (s->store.prepare_exception(&s->store, &pe->e)) {
@@ -1390,24 +1427,15 @@ static int __init dm_snapshot_init(void)
                goto bad5;
        }
 
-       pending_pool = mempool_create_slab_pool(128, pending_cache);
-       if (!pending_pool) {
-               DMERR("Couldn't create pending pool.");
-               r = -ENOMEM;
-               goto bad_pending_pool;
-       }
-
        ksnapd = create_singlethread_workqueue("ksnapd");
        if (!ksnapd) {
                DMERR("Failed to create ksnapd workqueue.");
                r = -ENOMEM;
-               goto bad6;
+               goto bad_pending_pool;
        }
 
        return 0;
 
-      bad6:
-       mempool_destroy(pending_pool);
       bad_pending_pool:
        kmem_cache_destroy(tracked_chunk_cache);
       bad5:
@@ -1438,7 +1466,6 @@ static void __exit dm_snapshot_exit(void)
                DMERR("origin unregister failed %d", r);
 
        exit_origin_hash();
-       mempool_destroy(pending_pool);
        kmem_cache_destroy(pending_cache);
        kmem_cache_destroy(exception_cache);
        kmem_cache_destroy(tracked_chunk_cache);