linux 2.6.16.38 w/ vs2.0.3-rc1
[linux-2.6.git] / drivers / md / dm-raid1.c
index abab265..0242cc3 100644 (file)
@@ -67,7 +67,7 @@ static inline void wake(void)
 struct mirror_set;
 struct region_hash {
        struct mirror_set *ms;
-       sector_t region_size;
+       uint32_t region_size;
        unsigned region_shift;
 
        /* holds persistent region state */
@@ -106,12 +106,42 @@ struct region {
        struct bio_list delayed_bios;
 };
 
+
+/*-----------------------------------------------------------------
+ * Mirror set structures.
+ *---------------------------------------------------------------*/
+struct mirror {
+       atomic_t error_count;
+       struct dm_dev *dev;
+       sector_t offset;
+};
+
+struct mirror_set {
+       struct dm_target *ti;
+       struct list_head list;
+       struct region_hash rh;
+       struct kcopyd_client *kcopyd_client;
+
+       spinlock_t lock;        /* protects the next two lists */
+       struct bio_list reads;
+       struct bio_list writes;
+
+       /* recovery */
+       region_t nr_regions;
+       int in_sync;
+
+       struct mirror *default_mirror;  /* Default mirror */
+
+       unsigned int nr_mirrors;
+       struct mirror mirror[0];
+};
+
 /*
  * Conversion fns
  */
 static inline region_t bio_to_region(struct region_hash *rh, struct bio *bio)
 {
-       return bio->bi_sector >> rh->region_shift;
+       return (bio->bi_sector - rh->ms->ti->begin) >> rh->region_shift;
 }
 
 static inline sector_t region_to_sector(struct region_hash *rh, region_t region)
@@ -122,7 +152,7 @@ static inline sector_t region_to_sector(struct region_hash *rh, region_t region)
 /* FIXME move this */
 static void queue_bio(struct mirror_set *ms, struct bio *bio, int rw);
 
-static void *region_alloc(int gfp_mask, void *pool_data)
+static void *region_alloc(gfp_t gfp_mask, void *pool_data)
 {
        return kmalloc(sizeof(struct region), gfp_mask);
 }
@@ -135,7 +165,7 @@ static void region_free(void *element, void *pool_data)
 #define MIN_REGIONS 64
 #define MAX_RECOVERY 1
 static int rh_init(struct region_hash *rh, struct mirror_set *ms,
-                  struct dirty_log *log, sector_t region_size,
+                  struct dirty_log *log, uint32_t region_size,
                   region_t nr_regions)
 {
        unsigned int nr_buckets, max_buckets;
@@ -233,7 +263,9 @@ static struct region *__rh_alloc(struct region_hash *rh, region_t region)
        struct region *reg, *nreg;
 
        read_unlock(&rh->hash_lock);
-       nreg = mempool_alloc(rh->region_pool, GFP_NOIO);
+       nreg = mempool_alloc(rh->region_pool, GFP_ATOMIC);
+       if (unlikely(!nreg))
+               nreg = kmalloc(sizeof(struct region), GFP_NOIO);
        nreg->state = rh->log->type->in_sync(rh->log, region, 1) ?
                RH_CLEAN : RH_NOSYNC;
        nreg->rh = rh;
@@ -253,9 +285,9 @@ static struct region *__rh_alloc(struct region_hash *rh, region_t region)
        else {
                __rh_insert(rh, nreg);
                if (nreg->state == RH_CLEAN) {
-                       spin_lock_irq(&rh->region_lock);
+                       spin_lock(&rh->region_lock);
                        list_add(&nreg->list, &rh->clean_regions);
-                       spin_unlock_irq(&rh->region_lock);
+                       spin_unlock(&rh->region_lock);
                }
                reg = nreg;
        }
@@ -375,16 +407,20 @@ static void rh_inc(struct region_hash *rh, region_t region)
 
        read_lock(&rh->hash_lock);
        reg = __rh_find(rh, region);
-       if (reg->state == RH_CLEAN) {
-               rh->log->type->mark_region(rh->log, reg->key);
 
-               spin_lock_irq(&rh->region_lock);
+       spin_lock_irq(&rh->region_lock);
+       atomic_inc(&reg->pending);
+
+       if (reg->state == RH_CLEAN) {
                reg->state = RH_DIRTY;
                list_del_init(&reg->list);      /* take off the clean list */
                spin_unlock_irq(&rh->region_lock);
-       }
 
-       atomic_inc(&reg->pending);
+               rh->log->type->mark_region(rh->log, reg->key);
+       } else
+               spin_unlock_irq(&rh->region_lock);
+
+
        read_unlock(&rh->hash_lock);
 }
 
@@ -406,17 +442,17 @@ static void rh_dec(struct region_hash *rh, region_t region)
        reg = __rh_lookup(rh, region);
        read_unlock(&rh->hash_lock);
 
+       spin_lock_irqsave(&rh->region_lock, flags);
        if (atomic_dec_and_test(&reg->pending)) {
-               spin_lock_irqsave(&rh->region_lock, flags);
                if (reg->state == RH_RECOVERING) {
                        list_add_tail(&reg->list, &rh->quiesced_regions);
                } else {
                        reg->state = RH_CLEAN;
                        list_add(&reg->list, &rh->clean_regions);
                }
-               spin_unlock_irqrestore(&rh->region_lock, flags);
                should_wake = 1;
        }
+       spin_unlock_irqrestore(&rh->region_lock, flags);
 
        if (should_wake)
                wake();
@@ -535,33 +571,6 @@ static void rh_start_recovery(struct region_hash *rh)
        wake();
 }
 
-/*-----------------------------------------------------------------
- * Mirror set structures.
- *---------------------------------------------------------------*/
-struct mirror {
-       atomic_t error_count;
-       struct dm_dev *dev;
-       sector_t offset;
-};
-
-struct mirror_set {
-       struct dm_target *ti;
-       struct list_head list;
-       struct region_hash rh;
-       struct kcopyd_client *kcopyd_client;
-
-       spinlock_t lock;        /* protects the next two lists */
-       struct bio_list reads;
-       struct bio_list writes;
-
-       /* recovery */
-       region_t nr_regions;
-       int in_sync;
-
-       unsigned int nr_mirrors;
-       struct mirror mirror[0];
-};
-
 /*
  * Every mirror should look like this one.
  */
@@ -607,7 +616,7 @@ static int recover(struct mirror_set *ms, struct region *reg)
        unsigned long flags = 0;
 
        /* fill in the source */
-       m = ms->mirror + DEFAULT_MIRROR;
+       m = ms->default_mirror;
        from.bdev = m->dev->bdev;
        from.sector = m->offset + region_to_sector(reg->rh, reg->key);
        if (reg->key == (ms->nr_regions - 1)) {
@@ -623,7 +632,7 @@ static int recover(struct mirror_set *ms, struct region *reg)
 
        /* fill in the destinations */
        for (i = 0, dest = to; i < ms->nr_mirrors; i++) {
-               if (i == DEFAULT_MIRROR)
+               if (&ms->mirror[i] == ms->default_mirror)
                        continue;
 
                m = ms->mirror + i;
@@ -678,7 +687,7 @@ static void do_recovery(struct mirror_set *ms)
 static struct mirror *choose_mirror(struct mirror_set *ms, sector_t sector)
 {
        /* FIXME: add read balancing */
-       return ms->mirror + DEFAULT_MIRROR;
+       return ms->default_mirror;
 }
 
 /*
@@ -705,7 +714,7 @@ static void do_reads(struct mirror_set *ms, struct bio_list *reads)
                if (rh_in_sync(&ms->rh, region, 0))
                        m = choose_mirror(ms, bio->bi_sector);
                else
-                       m = ms->mirror + DEFAULT_MIRROR;
+                       m = ms->default_mirror;
 
                map_bio(ms, m, bio);
                generic_make_request(bio);
@@ -829,7 +838,7 @@ static void do_writes(struct mirror_set *ms, struct bio_list *writes)
                rh_delay(&ms->rh, bio);
 
        while ((bio = bio_list_pop(&nosync))) {
-               map_bio(ms, ms->mirror + DEFAULT_MIRROR, bio);
+               map_bio(ms, ms->default_mirror, bio);
                generic_make_request(bio);
        }
 }
@@ -871,7 +880,7 @@ static void do_work(void *ignored)
  * Target functions
  *---------------------------------------------------------------*/
 static struct mirror_set *alloc_context(unsigned int nr_mirrors,
-                                       sector_t region_size,
+                                       uint32_t region_size,
                                        struct dm_target *ti,
                                        struct dirty_log *dl)
 {
@@ -894,8 +903,9 @@ static struct mirror_set *alloc_context(unsigned int nr_mirrors,
 
        ms->ti = ti;
        ms->nr_mirrors = nr_mirrors;
-       ms->nr_regions = dm_div_up(ti->len, region_size);
+       ms->nr_regions = dm_sector_div_up(ti->len, region_size);
        ms->in_sync = 0;
+       ms->default_mirror = &ms->mirror[DEFAULT_MIRROR];
 
        if (rh_init(&ms->rh, ms, dl, region_size, ms->nr_regions)) {
                ti->error = "dm-mirror: Error creating dirty region hash";
@@ -916,7 +926,7 @@ static void free_context(struct mirror_set *ms, struct dm_target *ti,
        kfree(ms);
 }
 
-static inline int _check_region_size(struct dm_target *ti, sector_t size)
+static inline int _check_region_size(struct dm_target *ti, uint32_t size)
 {
        return !(size % (PAGE_SIZE >> 9) || (size & (size - 1)) ||
                 size > ti->len);
@@ -1060,6 +1070,7 @@ static int mirror_ctr(struct dm_target *ti, unsigned int argc, char **argv)
        }
 
        ti->private = ms;
+       ti->split_io = ms->rh.region_size;
 
        r = kcopyd_client_create(DM_IO_PAGES, &ms->kcopyd_client);
        if (r) {
@@ -1105,7 +1116,7 @@ static int mirror_map(struct dm_target *ti, struct bio *bio,
        struct mirror *m;
        struct mirror_set *ms = ti->private;
 
-       map_context->ll = bio->bi_sector >> ms->rh.region_shift;
+       map_context->ll = bio_to_region(&ms->rh, bio);
 
        if (rw == WRITE) {
                queue_bio(ms, bio, rw);
@@ -1158,10 +1169,11 @@ static int mirror_end_io(struct dm_target *ti, struct bio *bio,
        return 0;
 }
 
-static void mirror_suspend(struct dm_target *ti)
+static void mirror_postsuspend(struct dm_target *ti)
 {
        struct mirror_set *ms = (struct mirror_set *) ti->private;
        struct dirty_log *log = ms->rh.log;
+
        rh_stop_recovery(&ms->rh);
        if (log->type->suspend && log->type->suspend(log))
                /* FIXME: need better error handling */
@@ -1181,7 +1193,6 @@ static void mirror_resume(struct dm_target *ti)
 static int mirror_status(struct dm_target *ti, status_type_t type,
                         char *result, unsigned int maxlen)
 {
-       char buffer[32];
        unsigned int m, sz;
        struct mirror_set *ms = (struct mirror_set *) ti->private;
 
@@ -1190,10 +1201,8 @@ static int mirror_status(struct dm_target *ti, status_type_t type,
        switch (type) {
        case STATUSTYPE_INFO:
                DMEMIT("%d ", ms->nr_mirrors);
-               for (m = 0; m < ms->nr_mirrors; m++) {
-                       format_dev_t(buffer, ms->mirror[m].dev->bdev->bd_dev);
-                       DMEMIT("%s ", buffer);
-               }
+               for (m = 0; m < ms->nr_mirrors; m++)
+                       DMEMIT("%s ", ms->mirror[m].dev->name);
 
                DMEMIT(SECTOR_FORMAT "/" SECTOR_FORMAT,
                       ms->rh.log->type->get_sync_count(ms->rh.log),
@@ -1202,11 +1211,9 @@ static int mirror_status(struct dm_target *ti, status_type_t type,
 
        case STATUSTYPE_TABLE:
                DMEMIT("%d ", ms->nr_mirrors);
-               for (m = 0; m < ms->nr_mirrors; m++) {
-                       format_dev_t(buffer, ms->mirror[m].dev->bdev->bd_dev);
+               for (m = 0; m < ms->nr_mirrors; m++)
                        DMEMIT("%s " SECTOR_FORMAT " ",
-                              buffer, ms->mirror[m].offset);
-               }
+                              ms->mirror[m].dev->name, ms->mirror[m].offset);
        }
 
        return 0;
@@ -1220,7 +1227,7 @@ static struct target_type mirror_target = {
        .dtr     = mirror_dtr,
        .map     = mirror_map,
        .end_io  = mirror_end_io,
-       .suspend = mirror_suspend,
+       .postsuspend = mirror_postsuspend,
        .resume  = mirror_resume,
        .status  = mirror_status,
 };
@@ -1233,7 +1240,7 @@ static int __init dm_mirror_init(void)
        if (r)
                return r;
 
-       _kmirrord_wq = create_workqueue("kmirrord");
+       _kmirrord_wq = create_singlethread_workqueue("kmirrord");
        if (!_kmirrord_wq) {
                DMERR("couldn't start kmirrord");
                dm_dirty_log_exit();