]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/md/dm-snap.c
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/sparc-2.6
[linux-2.6-omap-h63xx.git] / drivers / md / dm-snap.c
index 0821a2b68a73a98719cb931c82196a74bfe471d4..ae24eab8cd811c573b619b4e226244ff372f4119 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/module.h>
 #include <linux/slab.h>
 #include <linux/vmalloc.h>
+#include <linux/log2.h>
 
 #include "dm-snap.h"
 #include "dm-bio-list.h"
@@ -42,8 +43,8 @@
 static struct workqueue_struct *ksnapd;
 static void flush_queued_bios(struct work_struct *work);
 
-struct pending_exception {
-       struct exception e;
+struct dm_snap_pending_exception {
+       struct dm_snap_exception e;
 
        /*
         * Origin buffers waiting for this to complete are held
@@ -63,7 +64,7 @@ struct pending_exception {
         * group of pending_exceptions.  It is always last to get freed.
         * These fields get set up when writing to the origin.
         */
-       struct pending_exception *primary_pe;
+       struct dm_snap_pending_exception *primary_pe;
 
        /*
         * Number of pending_exceptions processing this chunk.
@@ -137,7 +138,7 @@ static void exit_origin_hash(void)
        kfree(_origins);
 }
 
-static inline unsigned int origin_hash(struct block_device *bdev)
+static unsigned origin_hash(struct block_device *bdev)
 {
        return bdev->bd_dev & ORIGIN_MASK;
 }
@@ -212,11 +213,15 @@ static void unregister_snapshot(struct dm_snapshot *s)
 
 /*
  * Implementation of the exception hash tables.
+ * The lowest hash_shift bits of the chunk number are ignored, allowing
+ * some consecutive chunks to be grouped together.
  */
-static int init_exception_table(struct exception_table *et, uint32_t size)
+static int init_exception_table(struct exception_table *et, uint32_t size,
+                               unsigned hash_shift)
 {
        unsigned int i;
 
+       et->hash_shift = hash_shift;
        et->hash_mask = size - 1;
        et->table = dm_vcalloc(size, sizeof(struct list_head));
        if (!et->table)
@@ -231,7 +236,7 @@ static int init_exception_table(struct exception_table *et, uint32_t size)
 static void exit_exception_table(struct exception_table *et, struct kmem_cache *mem)
 {
        struct list_head *slot;
-       struct exception *ex, *next;
+       struct dm_snap_exception *ex, *next;
        int i, size;
 
        size = et->hash_mask + 1;
@@ -245,18 +250,19 @@ static void exit_exception_table(struct exception_table *et, struct kmem_cache *
        vfree(et->table);
 }
 
-static inline uint32_t exception_hash(struct exception_table *et, chunk_t chunk)
+static uint32_t exception_hash(struct exception_table *et, chunk_t chunk)
 {
-       return chunk & et->hash_mask;
+       return (chunk >> et->hash_shift) & et->hash_mask;
 }
 
-static void insert_exception(struct exception_table *eh, struct exception *e)
+static void insert_exception(struct exception_table *eh,
+                            struct dm_snap_exception *e)
 {
        struct list_head *l = &eh->table[exception_hash(eh, e->old_chunk)];
        list_add(&e->hash_list, l);
 }
 
-static inline void remove_exception(struct exception *e)
+static void remove_exception(struct dm_snap_exception *e)
 {
        list_del(&e->hash_list);
 }
@@ -265,23 +271,24 @@ static inline void remove_exception(struct exception *e)
  * Return the exception data for a sector, or NULL if not
  * remapped.
  */
-static struct exception *lookup_exception(struct exception_table *et,
-                                         chunk_t chunk)
+static struct dm_snap_exception *lookup_exception(struct exception_table *et,
+                                                 chunk_t chunk)
 {
        struct list_head *slot;
-       struct exception *e;
+       struct dm_snap_exception *e;
 
        slot = &et->table[exception_hash(et, chunk)];
        list_for_each_entry (e, slot, hash_list)
-               if (e->old_chunk == chunk)
+               if (chunk >= e->old_chunk &&
+                   chunk <= e->old_chunk + dm_consecutive_chunk_count(e))
                        return e;
 
        return NULL;
 }
 
-static inline struct exception *alloc_exception(void)
+static struct dm_snap_exception *alloc_exception(void)
 {
-       struct exception *e;
+       struct dm_snap_exception *e;
 
        e = kmem_cache_alloc(exception_cache, GFP_NOIO);
        if (!e)
@@ -290,32 +297,79 @@ static inline struct exception *alloc_exception(void)
        return e;
 }
 
-static inline void free_exception(struct exception *e)
+static void free_exception(struct dm_snap_exception *e)
 {
        kmem_cache_free(exception_cache, e);
 }
 
-static inline struct pending_exception *alloc_pending_exception(void)
+static struct dm_snap_pending_exception *alloc_pending_exception(void)
 {
        return mempool_alloc(pending_pool, GFP_NOIO);
 }
 
-static inline void free_pending_exception(struct pending_exception *pe)
+static void free_pending_exception(struct dm_snap_pending_exception *pe)
 {
        mempool_free(pe, pending_pool);
 }
 
+static void insert_completed_exception(struct dm_snapshot *s,
+                                      struct dm_snap_exception *new_e)
+{
+       struct exception_table *eh = &s->complete;
+       struct list_head *l;
+       struct dm_snap_exception *e = NULL;
+
+       l = &eh->table[exception_hash(eh, new_e->old_chunk)];
+
+       /* Add immediately if this table doesn't support consecutive chunks */
+       if (!eh->hash_shift)
+               goto out;
+
+       /* List is ordered by old_chunk */
+       list_for_each_entry_reverse(e, l, hash_list) {
+               /* Insert after an existing chunk? */
+               if (new_e->old_chunk == (e->old_chunk +
+                                        dm_consecutive_chunk_count(e) + 1) &&
+                   new_e->new_chunk == (dm_chunk_number(e->new_chunk) +
+                                        dm_consecutive_chunk_count(e) + 1)) {
+                       dm_consecutive_chunk_count_inc(e);
+                       free_exception(new_e);
+                       return;
+               }
+
+               /* Insert before an existing chunk? */
+               if (new_e->old_chunk == (e->old_chunk - 1) &&
+                   new_e->new_chunk == (dm_chunk_number(e->new_chunk) - 1)) {
+                       dm_consecutive_chunk_count_inc(e);
+                       e->old_chunk--;
+                       e->new_chunk--;
+                       free_exception(new_e);
+                       return;
+               }
+
+               if (new_e->old_chunk > e->old_chunk)
+                       break;
+       }
+
+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)
 {
-       struct exception *e;
+       struct dm_snap_exception *e;
 
        e = alloc_exception();
        if (!e)
                return -ENOMEM;
 
        e->old_chunk = old;
+
+       /* Consecutive_count is implicitly initialised to zero */
        e->new_chunk = new;
-       insert_exception(&s->complete, e);
+
+       insert_completed_exception(s, e);
+
        return 0;
 }
 
@@ -331,16 +385,6 @@ static int calc_max_buckets(void)
        return mem;
 }
 
-/*
- * Rounds a number down to a power of 2.
- */
-static inline uint32_t round_down(uint32_t n)
-{
-       while (n & (n - 1))
-               n &= (n - 1);
-       return n;
-}
-
 /*
  * Allocate room for a suitable hash table.
  */
@@ -359,9 +403,9 @@ static int init_hash_tables(struct dm_snapshot *s)
        hash_size = min(origin_dev_size, cow_dev_size) >> s->chunk_shift;
        hash_size = min(hash_size, max_buckets);
 
-       /* Round it down to a power of 2 */
-       hash_size = round_down(hash_size);
-       if (init_exception_table(&s->complete, hash_size))
+       hash_size = rounddown_pow_of_two(hash_size);
+       if (init_exception_table(&s->complete, hash_size,
+                                DM_CHUNK_CONSECUTIVE_BITS))
                return -ENOMEM;
 
        /*
@@ -372,7 +416,7 @@ static int init_hash_tables(struct dm_snapshot *s)
        if (hash_size < 64)
                hash_size = 64;
 
-       if (init_exception_table(&s->pending, hash_size)) {
+       if (init_exception_table(&s->pending, hash_size, 0)) {
                exit_exception_table(&s->complete, exception_cache);
                return -ENOMEM;
        }
@@ -384,7 +428,7 @@ static int init_hash_tables(struct dm_snapshot *s)
  * Round a number up to the nearest 'size' boundary.  size must
  * be a power of 2.
  */
-static inline ulong round_up(ulong n, ulong size)
+static ulong round_up(ulong n, ulong size)
 {
        size--;
        return (n + size) & ~size;
@@ -414,7 +458,7 @@ static int set_chunk_size(struct dm_snapshot *s, const char *chunk_size_arg,
        chunk_size = round_up(chunk_size, PAGE_SIZE >> 9);
 
        /* Check chunk_size is a power of 2 */
-       if (chunk_size & (chunk_size - 1)) {
+       if (!is_power_of_2(chunk_size)) {
                *error = "Chunk size is not a power of 2";
                return -EINVAL;
        }
@@ -522,9 +566,12 @@ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv)
 
        /* Metadata must only be loaded into one table at once */
        r = s->store.read_metadata(&s->store);
-       if (r) {
+       if (r < 0) {
                ti->error = "Failed to read snapshot metadata";
                goto bad6;
+       } else if (r > 0) {
+               s->valid = 0;
+               DMWARN("Snapshot is marked invalid.");
        }
 
        bio_list_init(&s->queued_bios);
@@ -577,7 +624,7 @@ static void __free_exceptions(struct dm_snapshot *s)
 
 static void snapshot_dtr(struct dm_target *ti)
 {
-       struct dm_snapshot *s = (struct dm_snapshot *) ti->private;
+       struct dm_snapshot *s = ti->private;
 
        flush_workqueue(ksnapd);
 
@@ -632,7 +679,7 @@ static void error_bios(struct bio *bio)
        while (bio) {
                n = bio->bi_next;
                bio->bi_next = NULL;
-               bio_io_error(bio, bio->bi_size);
+               bio_io_error(bio);
                bio = n;
        }
 }
@@ -655,14 +702,14 @@ static void __invalidate_snapshot(struct dm_snapshot *s, int err)
        dm_table_event(s->table);
 }
 
-static void get_pending_exception(struct pending_exception *pe)
+static void get_pending_exception(struct dm_snap_pending_exception *pe)
 {
        atomic_inc(&pe->ref_count);
 }
 
-static struct bio *put_pending_exception(struct pending_exception *pe)
+static struct bio *put_pending_exception(struct dm_snap_pending_exception *pe)
 {
-       struct pending_exception *primary_pe;
+       struct dm_snap_pending_exception *primary_pe;
        struct bio *origin_bios = NULL;
 
        primary_pe = pe->primary_pe;
@@ -692,9 +739,9 @@ static struct bio *put_pending_exception(struct pending_exception *pe)
        return origin_bios;
 }
 
-static void pending_complete(struct pending_exception *pe, int success)
+static void pending_complete(struct dm_snap_pending_exception *pe, int success)
 {
-       struct exception *e;
+       struct dm_snap_exception *e;
        struct dm_snapshot *s = pe->snap;
        struct bio *origin_bios = NULL;
        struct bio *snapshot_bios = NULL;
@@ -728,7 +775,7 @@ static void pending_complete(struct pending_exception *pe, int success)
         * Add a proper exception, and remove the
         * in-flight exception from the list.
         */
-       insert_exception(&s->complete, e);
+       insert_completed_exception(s, e);
 
  out:
        remove_exception(&pe->e);
@@ -748,7 +795,8 @@ static void pending_complete(struct pending_exception *pe, int success)
 
 static void commit_callback(void *context, int success)
 {
-       struct pending_exception *pe = (struct pending_exception *) context;
+       struct dm_snap_pending_exception *pe = context;
+
        pending_complete(pe, success);
 }
 
@@ -758,7 +806,7 @@ static void commit_callback(void *context, int success)
  */
 static void copy_callback(int read_err, unsigned int write_err, void *context)
 {
-       struct pending_exception *pe = (struct pending_exception *) context;
+       struct dm_snap_pending_exception *pe = context;
        struct dm_snapshot *s = pe->snap;
 
        if (read_err || write_err)
@@ -773,7 +821,7 @@ static void copy_callback(int read_err, unsigned int write_err, void *context)
 /*
  * Dispatches the copy operation to kcopyd.
  */
-static void start_copy(struct pending_exception *pe)
+static void start_copy(struct dm_snap_pending_exception *pe)
 {
        struct dm_snapshot *s = pe->snap;
        struct io_region src, dest;
@@ -803,11 +851,11 @@ static void start_copy(struct pending_exception *pe)
  * NOTE: a write lock must be held on snap->lock before calling
  * this.
  */
-static struct pending_exception *
+static struct dm_snap_pending_exception *
 __find_pending_exception(struct dm_snapshot *s, struct bio *bio)
 {
-       struct exception *e;
-       struct pending_exception *pe;
+       struct dm_snap_exception *e;
+       struct dm_snap_pending_exception *pe;
        chunk_t chunk = sector_to_chunk(s, bio->bi_sector);
 
        /*
@@ -816,7 +864,7 @@ __find_pending_exception(struct dm_snapshot *s, struct bio *bio)
        e = lookup_exception(&s->pending, chunk);
        if (e) {
                /* cast the exception to a pending exception */
-               pe = container_of(e, struct pending_exception, e);
+               pe = container_of(e, struct dm_snap_pending_exception, e);
                goto out;
        }
 
@@ -836,7 +884,7 @@ __find_pending_exception(struct dm_snapshot *s, struct bio *bio)
        e = lookup_exception(&s->pending, chunk);
        if (e) {
                free_pending_exception(pe);
-               pe = container_of(e, struct pending_exception, e);
+               pe = container_of(e, struct dm_snap_pending_exception, e);
                goto out;
        }
 
@@ -860,22 +908,23 @@ __find_pending_exception(struct dm_snapshot *s, struct bio *bio)
        return pe;
 }
 
-static inline void remap_exception(struct dm_snapshot *s, struct exception *e,
-                                  struct bio *bio)
+static void remap_exception(struct dm_snapshot *s, struct dm_snap_exception *e,
+                           struct bio *bio, chunk_t chunk)
 {
        bio->bi_bdev = s->cow->bdev;
-       bio->bi_sector = chunk_to_sector(s, e->new_chunk) +
-               (bio->bi_sector & s->chunk_mask);
+       bio->bi_sector = chunk_to_sector(s, dm_chunk_number(e->new_chunk) +
+                        (chunk - e->old_chunk)) +
+                        (bio->bi_sector & s->chunk_mask);
 }
 
 static int snapshot_map(struct dm_target *ti, struct bio *bio,
                        union map_info *map_context)
 {
-       struct exception *e;
-       struct dm_snapshot *s = (struct dm_snapshot *) ti->private;
+       struct dm_snap_exception *e;
+       struct dm_snapshot *s = ti->private;
        int r = DM_MAPIO_REMAPPED;
        chunk_t chunk;
-       struct pending_exception *pe = NULL;
+       struct dm_snap_pending_exception *pe = NULL;
 
        chunk = sector_to_chunk(s, bio->bi_sector);
 
@@ -884,9 +933,6 @@ static int snapshot_map(struct dm_target *ti, struct bio *bio,
        if (!s->valid)
                return -EIO;
 
-       if (unlikely(bio_barrier(bio)))
-               return -EOPNOTSUPP;
-
        /* FIXME: should only take write lock if we need
         * to copy an exception */
        down_write(&s->lock);
@@ -899,7 +945,7 @@ static int snapshot_map(struct dm_target *ti, struct bio *bio,
        /* If the block is already remapped - use that, else remap it */
        e = lookup_exception(&s->complete, chunk);
        if (e) {
-               remap_exception(s, e, bio);
+               remap_exception(s, e, bio, chunk);
                goto out_unlock;
        }
 
@@ -916,7 +962,7 @@ static int snapshot_map(struct dm_target *ti, struct bio *bio,
                        goto out_unlock;
                }
 
-               remap_exception(s, &pe->e, bio);
+               remap_exception(s, &pe->e, bio, chunk);
                bio_list_add(&pe->snapshot_bios, bio);
 
                r = DM_MAPIO_SUBMITTED;
@@ -945,7 +991,7 @@ static int snapshot_map(struct dm_target *ti, struct bio *bio,
 
 static void snapshot_resume(struct dm_target *ti)
 {
-       struct dm_snapshot *s = (struct dm_snapshot *) ti->private;
+       struct dm_snapshot *s = ti->private;
 
        down_write(&s->lock);
        s->active = 1;
@@ -955,7 +1001,7 @@ static void snapshot_resume(struct dm_target *ti)
 static int snapshot_status(struct dm_target *ti, status_type_t type,
                           char *result, unsigned int maxlen)
 {
-       struct dm_snapshot *snap = (struct dm_snapshot *) ti->private;
+       struct dm_snapshot *snap = ti->private;
 
        switch (type) {
        case STATUSTYPE_INFO:
@@ -999,8 +1045,8 @@ static int __origin_write(struct list_head *snapshots, struct bio *bio)
 {
        int r = DM_MAPIO_REMAPPED, first = 0;
        struct dm_snapshot *snap;
-       struct exception *e;
-       struct pending_exception *pe, *next_pe, *primary_pe = NULL;
+       struct dm_snap_exception *e;
+       struct dm_snap_pending_exception *pe, *next_pe, *primary_pe = NULL;
        chunk_t chunk;
        LIST_HEAD(pe_queue);
 
@@ -1147,19 +1193,16 @@ static int origin_ctr(struct dm_target *ti, unsigned int argc, char **argv)
 
 static void origin_dtr(struct dm_target *ti)
 {
-       struct dm_dev *dev = (struct dm_dev *) ti->private;
+       struct dm_dev *dev = ti->private;
        dm_put_device(ti, dev);
 }
 
 static int origin_map(struct dm_target *ti, struct bio *bio,
                      union map_info *map_context)
 {
-       struct dm_dev *dev = (struct dm_dev *) ti->private;
+       struct dm_dev *dev = ti->private;
        bio->bi_bdev = dev->bdev;
 
-       if (unlikely(bio_barrier(bio)))
-               return -EOPNOTSUPP;
-
        /* Only tell snapshots if this is a write */
        return (bio_rw(bio) == WRITE) ? do_origin(dev, bio) : DM_MAPIO_REMAPPED;
 }
@@ -1172,7 +1215,7 @@ static int origin_map(struct dm_target *ti, struct bio *bio,
  */
 static void origin_resume(struct dm_target *ti)
 {
-       struct dm_dev *dev = (struct dm_dev *) ti->private;
+       struct dm_dev *dev = ti->private;
        struct dm_snapshot *snap;
        struct origin *o;
        chunk_t chunk_size = 0;
@@ -1190,7 +1233,7 @@ static void origin_resume(struct dm_target *ti)
 static int origin_status(struct dm_target *ti, status_type_t type, char *result,
                         unsigned int maxlen)
 {
-       struct dm_dev *dev = (struct dm_dev *) ti->private;
+       struct dm_dev *dev = ti->private;
 
        switch (type) {
        case STATUSTYPE_INFO:
@@ -1207,7 +1250,7 @@ static int origin_status(struct dm_target *ti, status_type_t type, char *result,
 
 static struct target_type origin_target = {
        .name    = "snapshot-origin",
-       .version = {1, 5, 0},
+       .version = {1, 6, 0},
        .module  = THIS_MODULE,
        .ctr     = origin_ctr,
        .dtr     = origin_dtr,
@@ -1218,7 +1261,7 @@ static struct target_type origin_target = {
 
 static struct target_type snapshot_target = {
        .name    = "snapshot",
-       .version = {1, 5, 0},
+       .version = {1, 6, 0},
        .module  = THIS_MODULE,
        .ctr     = snapshot_ctr,
        .dtr     = snapshot_dtr,
@@ -1249,21 +1292,14 @@ static int __init dm_snapshot_init(void)
                goto bad2;
        }
 
-       exception_cache = kmem_cache_create("dm-snapshot-ex",
-                                           sizeof(struct exception),
-                                           __alignof__(struct exception),
-                                           0, NULL, NULL);
+       exception_cache = KMEM_CACHE(dm_snap_exception, 0);
        if (!exception_cache) {
                DMERR("Couldn't create exception cache.");
                r = -ENOMEM;
                goto bad3;
        }
 
-       pending_cache =
-           kmem_cache_create("dm-snapshot-in",
-                             sizeof(struct pending_exception),
-                             __alignof__(struct pending_exception),
-                             0, NULL, NULL);
+       pending_cache = KMEM_CACHE(dm_snap_pending_exception, 0);
        if (!pending_cache) {
                DMERR("Couldn't create pending cache.");
                r = -ENOMEM;