]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/md/dm-raid1.c
Merge branch 'features' of git://farnsworth.org/dale/linux-2.6-mv643xx_eth into upstream
[karo-tx-linux.git] / drivers / md / dm-raid1.c
index dee4221caa7335ad371e38604ec0490204710d33..31123d4a6b9cc39dbeec455d50b540b97ac99c06 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/time.h>
 #include <linux/vmalloc.h>
 #include <linux/workqueue.h>
+#include <linux/log2.h>
 
 #define DM_MSG_PREFIX "raid1"
 #define DM_IO_PAGES 64
@@ -113,6 +114,7 @@ struct region {
  * Mirror set structures.
  *---------------------------------------------------------------*/
 struct mirror {
+       struct mirror_set *ms;
        atomic_t error_count;
        struct dm_dev *dev;
        sector_t offset;
@@ -134,6 +136,7 @@ struct mirror_set {
        /* recovery */
        region_t nr_regions;
        int in_sync;
+       int log_failure;
 
        struct mirror *default_mirror;  /* Default mirror */
 
@@ -589,9 +592,9 @@ static void rh_recovery_end(struct region *reg, int success)
        wake(rh->ms);
 }
 
-static void rh_flush(struct region_hash *rh)
+static int rh_flush(struct region_hash *rh)
 {
-       rh->log->type->flush(rh->log);
+       return rh->log->type->flush(rh->log);
 }
 
 static void rh_delay(struct region_hash *rh, struct bio *bio)
@@ -819,7 +822,7 @@ static void write_callback(unsigned long error, void *context)
                                break;
                        }
        }
-       bio_endio(bio, bio->bi_size, 0);
+       bio_endio(bio, 0);
 }
 
 static void do_write(struct mirror_set *ms, struct bio *bio)
@@ -892,12 +895,15 @@ static void do_writes(struct mirror_set *ms, struct bio_list *writes)
         */
        rh_inc_pending(&ms->rh, &sync);
        rh_inc_pending(&ms->rh, &nosync);
-       rh_flush(&ms->rh);
+       ms->log_failure = rh_flush(&ms->rh) ? 1 : 0;
 
        /*
         * Dispatch io.
         */
-       while ((bio = bio_list_pop(&sync)))
+       if (unlikely(ms->log_failure))
+               while ((bio = bio_list_pop(&sync)))
+                       bio_endio(bio, -EIO);
+       else while ((bio = bio_list_pop(&sync)))
                do_write(ms, bio);
 
        while ((bio = bio_list_pop(&recover)))
@@ -947,13 +953,12 @@ static struct mirror_set *alloc_context(unsigned int nr_mirrors,
 
        len = sizeof(*ms) + (sizeof(ms->mirror[0]) * nr_mirrors);
 
-       ms = kmalloc(len, GFP_KERNEL);
+       ms = kzalloc(len, GFP_KERNEL);
        if (!ms) {
                ti->error = "Cannot allocate mirror context";
                return NULL;
        }
 
-       memset(ms, 0, len);
        spin_lock_init(&ms->lock);
 
        ms->ti = ti;
@@ -971,6 +976,7 @@ static struct mirror_set *alloc_context(unsigned int nr_mirrors,
 
        if (rh_init(&ms->rh, ms, dl, region_size, ms->nr_regions)) {
                ti->error = "Error creating dirty region hash";
+               dm_io_client_destroy(ms->io_client);
                kfree(ms);
                return NULL;
        }
@@ -991,7 +997,7 @@ static void free_context(struct mirror_set *ms, struct dm_target *ti,
 
 static inline int _check_region_size(struct dm_target *ti, uint32_t size)
 {
-       return !(size % (PAGE_SIZE >> 9) || (size & (size - 1)) ||
+       return !(size % (PAGE_SIZE >> 9) || !is_power_of_2(size) ||
                 size > ti->len);
 }
 
@@ -1012,6 +1018,7 @@ static int get_mirror(struct mirror_set *ms, struct dm_target *ti,
                return -ENXIO;
        }
 
+       ms->mirror[mirror].ms = ms;
        ms->mirror[mirror].offset = offset;
 
        return 0;
@@ -1160,16 +1167,14 @@ static int mirror_ctr(struct dm_target *ti, unsigned int argc, char **argv)
        ms->kmirrord_wq = create_singlethread_workqueue("kmirrord");
        if (!ms->kmirrord_wq) {
                DMERR("couldn't start kmirrord");
-               free_context(ms, ti, m);
-               return -ENOMEM;
+               r = -ENOMEM;
+               goto err_free_context;
        }
        INIT_WORK(&ms->kmirrord_work, do_mirror);
 
        r = parse_features(ms, argc, argv, &args_used);
-       if (r) {
-               free_context(ms, ti, ms->nr_mirrors);
-               return r;
-       }
+       if (r)
+               goto err_destroy_wq;
 
        argv += args_used;
        argc -= args_used;
@@ -1185,19 +1190,22 @@ static int mirror_ctr(struct dm_target *ti, unsigned int argc, char **argv)
 
        if (argc) {
                ti->error = "Too many mirror arguments";
-               free_context(ms, ti, ms->nr_mirrors);
-               return -EINVAL;
+               r = -EINVAL;
+               goto err_destroy_wq;
        }
 
        r = kcopyd_client_create(DM_IO_PAGES, &ms->kcopyd_client);
-       if (r) {
-               destroy_workqueue(ms->kmirrord_wq);
-               free_context(ms, ti, ms->nr_mirrors);
-               return r;
-       }
+       if (r)
+               goto err_destroy_wq;
 
        wake(ms);
        return 0;
+
+err_destroy_wq:
+       destroy_workqueue(ms->kmirrord_wq);
+err_free_context:
+       free_context(ms, ti, ms->nr_mirrors);
+       return r;
 }
 
 static void mirror_dtr(struct dm_target *ti)
@@ -1299,7 +1307,7 @@ static void mirror_postsuspend(struct dm_target *ti)
        wait_event(_kmirrord_recovery_stopped,
                   !atomic_read(&ms->rh.recovery_in_flight));
 
-       if (log->type->suspend && log->type->suspend(log))
+       if (log->type->postsuspend && log->type->postsuspend(log))
                /* FIXME: need better error handling */
                DMWARN("log suspend failed");
 }