RAID5: batch adjacent full stripe write
[deliverable/linux.git] / drivers / md / dm-snap.c
index 8b204ae216ab62d354c814277dc413c34f0bf9a4..f83a0f3fc3656680c7bdba2dcd4bdaaac9f2f624 100644 (file)
@@ -20,6 +20,8 @@
 #include <linux/log2.h>
 #include <linux/dm-kcopyd.h>
 
+#include "dm.h"
+
 #include "dm-exception-store.h"
 
 #define DM_MSG_PREFIX "snapshots"
@@ -290,6 +292,16 @@ struct origin {
        struct list_head snapshots;
 };
 
+/*
+ * This structure is allocated for each origin target
+ */
+struct dm_origin {
+       struct dm_dev *dev;
+       struct dm_target *ti;
+       unsigned split_boundary;
+       struct list_head hash_list;
+};
+
 /*
  * Size of the hash table for origin volumes. If we make this
  * the size of the minors list then it should be nearly perfect
@@ -297,6 +309,7 @@ struct origin {
 #define ORIGIN_HASH_SIZE 256
 #define ORIGIN_MASK      0xFF
 static struct list_head *_origins;
+static struct list_head *_dm_origins;
 static struct rw_semaphore _origins_lock;
 
 static DECLARE_WAIT_QUEUE_HEAD(_pending_exceptions_done);
@@ -310,12 +323,22 @@ static int init_origin_hash(void)
        _origins = kmalloc(ORIGIN_HASH_SIZE * sizeof(struct list_head),
                           GFP_KERNEL);
        if (!_origins) {
-               DMERR("unable to allocate memory");
+               DMERR("unable to allocate memory for _origins");
                return -ENOMEM;
        }
-
        for (i = 0; i < ORIGIN_HASH_SIZE; i++)
                INIT_LIST_HEAD(_origins + i);
+
+       _dm_origins = kmalloc(ORIGIN_HASH_SIZE * sizeof(struct list_head),
+                             GFP_KERNEL);
+       if (!_dm_origins) {
+               DMERR("unable to allocate memory for _dm_origins");
+               kfree(_origins);
+               return -ENOMEM;
+       }
+       for (i = 0; i < ORIGIN_HASH_SIZE; i++)
+               INIT_LIST_HEAD(_dm_origins + i);
+
        init_rwsem(&_origins_lock);
 
        return 0;
@@ -324,6 +347,7 @@ static int init_origin_hash(void)
 static void exit_origin_hash(void)
 {
        kfree(_origins);
+       kfree(_dm_origins);
 }
 
 static unsigned origin_hash(struct block_device *bdev)
@@ -350,6 +374,30 @@ static void __insert_origin(struct origin *o)
        list_add_tail(&o->hash_list, sl);
 }
 
+static struct dm_origin *__lookup_dm_origin(struct block_device *origin)
+{
+       struct list_head *ol;
+       struct dm_origin *o;
+
+       ol = &_dm_origins[origin_hash(origin)];
+       list_for_each_entry (o, ol, hash_list)
+               if (bdev_equal(o->dev->bdev, origin))
+                       return o;
+
+       return NULL;
+}
+
+static void __insert_dm_origin(struct dm_origin *o)
+{
+       struct list_head *sl = &_dm_origins[origin_hash(o->dev->bdev)];
+       list_add_tail(&o->hash_list, sl);
+}
+
+static void __remove_dm_origin(struct dm_origin *o)
+{
+       list_del(&o->hash_list);
+}
+
 /*
  * _origins_lock must be held when calling this function.
  * Returns number of snapshots registered using the supplied cow device, plus:
@@ -1840,9 +1888,40 @@ static int snapshot_preresume(struct dm_target *ti)
 static void snapshot_resume(struct dm_target *ti)
 {
        struct dm_snapshot *s = ti->private;
-       struct dm_snapshot *snap_src = NULL, *snap_dest = NULL;
+       struct dm_snapshot *snap_src = NULL, *snap_dest = NULL, *snap_merging = NULL;
+       struct dm_origin *o;
+       struct mapped_device *origin_md = NULL;
+       bool must_restart_merging = false;
 
        down_read(&_origins_lock);
+
+       o = __lookup_dm_origin(s->origin->bdev);
+       if (o)
+               origin_md = dm_table_get_md(o->ti->table);
+       if (!origin_md) {
+               (void) __find_snapshots_sharing_cow(s, NULL, NULL, &snap_merging);
+               if (snap_merging)
+                       origin_md = dm_table_get_md(snap_merging->ti->table);
+       }
+       if (origin_md == dm_table_get_md(ti->table))
+               origin_md = NULL;
+       if (origin_md) {
+               if (dm_hold(origin_md))
+                       origin_md = NULL;
+       }
+
+       up_read(&_origins_lock);
+
+       if (origin_md) {
+               dm_internal_suspend_fast(origin_md);
+               if (snap_merging && test_bit(RUNNING_MERGE, &snap_merging->state_bits)) {
+                       must_restart_merging = true;
+                       stop_merge(snap_merging);
+               }
+       }
+
+       down_read(&_origins_lock);
+
        (void) __find_snapshots_sharing_cow(s, &snap_src, &snap_dest, NULL);
        if (snap_src && snap_dest) {
                down_write(&snap_src->lock);
@@ -1851,8 +1930,16 @@ static void snapshot_resume(struct dm_target *ti)
                up_write(&snap_dest->lock);
                up_write(&snap_src->lock);
        }
+
        up_read(&_origins_lock);
 
+       if (origin_md) {
+               if (must_restart_merging)
+                       start_merge(snap_merging);
+               dm_internal_resume_fast(origin_md);
+               dm_put(origin_md);
+       }
+
        /* Now we have correct chunk size, reregister */
        reregister_snapshot(s);
 
@@ -2133,11 +2220,6 @@ static int origin_write_extent(struct dm_snapshot *merging_snap,
  * Origin: maps a linear range of a device, with hooks for snapshotting.
  */
 
-struct dm_origin {
-       struct dm_dev *dev;
-       unsigned split_boundary;
-};
-
 /*
  * Construct an origin mapping: <dev_path>
  * The context for an origin is merely a 'struct dm_dev *'
@@ -2166,6 +2248,7 @@ static int origin_ctr(struct dm_target *ti, unsigned int argc, char **argv)
                goto bad_open;
        }
 
+       o->ti = ti;
        ti->private = o;
        ti->num_flush_bios = 1;
 
@@ -2180,6 +2263,7 @@ bad_alloc:
 static void origin_dtr(struct dm_target *ti)
 {
        struct dm_origin *o = ti->private;
+
        dm_put_device(ti, o->dev);
        kfree(o);
 }
@@ -2216,6 +2300,19 @@ static void origin_resume(struct dm_target *ti)
        struct dm_origin *o = ti->private;
 
        o->split_boundary = get_origin_minimum_chunksize(o->dev->bdev);
+
+       down_write(&_origins_lock);
+       __insert_dm_origin(o);
+       up_write(&_origins_lock);
+}
+
+static void origin_postsuspend(struct dm_target *ti)
+{
+       struct dm_origin *o = ti->private;
+
+       down_write(&_origins_lock);
+       __remove_dm_origin(o);
+       up_write(&_origins_lock);
 }
 
 static void origin_status(struct dm_target *ti, status_type_t type,
@@ -2258,12 +2355,13 @@ static int origin_iterate_devices(struct dm_target *ti,
 
 static struct target_type origin_target = {
        .name    = "snapshot-origin",
-       .version = {1, 8, 1},
+       .version = {1, 9, 0},
        .module  = THIS_MODULE,
        .ctr     = origin_ctr,
        .dtr     = origin_dtr,
        .map     = origin_map,
        .resume  = origin_resume,
+       .postsuspend = origin_postsuspend,
        .status  = origin_status,
        .merge   = origin_merge,
        .iterate_devices = origin_iterate_devices,
@@ -2271,7 +2369,7 @@ static struct target_type origin_target = {
 
 static struct target_type snapshot_target = {
        .name    = "snapshot",
-       .version = {1, 12, 0},
+       .version = {1, 13, 0},
        .module  = THIS_MODULE,
        .ctr     = snapshot_ctr,
        .dtr     = snapshot_dtr,
@@ -2285,7 +2383,7 @@ static struct target_type snapshot_target = {
 
 static struct target_type merge_target = {
        .name    = dm_snapshot_merge_target_name,
-       .version = {1, 2, 0},
+       .version = {1, 3, 0},
        .module  = THIS_MODULE,
        .ctr     = snapshot_ctr,
        .dtr     = snapshot_dtr,
This page took 0.030283 seconds and 5 git commands to generate.