and changed files
[powerpc.git] / drivers / md / dm-raid1.c
index fc8cbb1..ef124b7 100644 (file)
 #include <linux/workqueue.h>
 
 #define DM_MSG_PREFIX "raid1"
+#define DM_IO_PAGES 64
 
-static struct workqueue_struct *_kmirrord_wq;
-static struct work_struct _kmirrord_work;
-static DECLARE_WAIT_QUEUE_HEAD(_kmirrord_recovery_stopped);
+#define DM_RAID1_HANDLE_ERRORS 0x01
 
-static inline void wake(void)
-{
-       queue_work(_kmirrord_wq, &_kmirrord_work);
-}
+static DECLARE_WAIT_QUEUE_HEAD(_kmirrord_recovery_stopped);
 
 /*-----------------------------------------------------------------
  * Region hash
@@ -125,17 +121,23 @@ struct mirror_set {
        struct list_head list;
        struct region_hash rh;
        struct kcopyd_client *kcopyd_client;
+       uint64_t features;
 
        spinlock_t lock;        /* protects the next two lists */
        struct bio_list reads;
        struct bio_list writes;
 
+       struct dm_io_client *io_client;
+
        /* recovery */
        region_t nr_regions;
        int in_sync;
 
        struct mirror *default_mirror;  /* Default mirror */
 
+       struct workqueue_struct *kmirrord_wq;
+       struct work_struct kmirrord_work;
+
        unsigned int nr_mirrors;
        struct mirror mirror[0];
 };
@@ -153,6 +155,11 @@ static inline sector_t region_to_sector(struct region_hash *rh, region_t region)
        return region << rh->region_shift;
 }
 
+static void wake(struct mirror_set *ms)
+{
+       queue_work(ms->kmirrord_wq, &ms->kmirrord_work);
+}
+
 /* FIXME move this */
 static void queue_bio(struct mirror_set *ms, struct bio *bio, int rw);
 
@@ -344,6 +351,17 @@ static void dispatch_bios(struct mirror_set *ms, struct bio_list *bio_list)
        }
 }
 
+static void complete_resync_work(struct region *reg, int success)
+{
+       struct region_hash *rh = reg->rh;
+
+       rh->log->type->set_region_sync(rh->log, reg->key, success);
+       dispatch_bios(rh->ms, &reg->delayed_bios);
+       if (atomic_dec_and_test(&rh->recovery_in_flight))
+               wake_up_all(&_kmirrord_recovery_stopped);
+       up(&rh->recovery_count);
+}
+
 static void rh_update_states(struct region_hash *rh)
 {
        struct region *reg, *next;
@@ -383,16 +401,11 @@ static void rh_update_states(struct region_hash *rh)
         */
        list_for_each_entry_safe (reg, next, &recovered, list) {
                rh->log->type->clear_region(rh->log, reg->key);
-               rh->log->type->complete_resync_work(rh->log, reg->key, 1);
-               dispatch_bios(rh->ms, &reg->delayed_bios);
-               if (atomic_dec_and_test(&rh->recovery_in_flight))
-                       wake_up_all(&_kmirrord_recovery_stopped);
-               up(&rh->recovery_count);
+               complete_resync_work(reg, 1);
                mempool_free(reg, rh->region_pool);
        }
 
-       if (!list_empty(&recovered))
-               rh->log->type->flush(rh->log);
+       rh->log->type->flush(rh->log);
 
        list_for_each_entry_safe (reg, next, &clean, list)
                mempool_free(reg, rh->region_pool);
@@ -464,7 +477,7 @@ static void rh_dec(struct region_hash *rh, region_t region)
        spin_unlock_irqrestore(&rh->region_lock, flags);
 
        if (should_wake)
-               wake();
+               wake(rh->ms);
 }
 
 /*
@@ -551,7 +564,7 @@ static void rh_recovery_end(struct region *reg, int success)
        list_add(&reg->list, &reg->rh->recovered_regions);
        spin_unlock_irq(&rh->region_lock);
 
-       wake();
+       wake(rh->ms);
 }
 
 static void rh_flush(struct region_hash *rh)
@@ -585,7 +598,7 @@ static void rh_start_recovery(struct region_hash *rh)
        for (i = 0; i < MAX_RECOVERY; i++)
                up(&rh->recovery_count);
 
-       wake();
+       wake(rh->ms);
 }
 
 /*
@@ -728,7 +741,7 @@ static void do_reads(struct mirror_set *ms, struct bio_list *reads)
                /*
                 * We can only read balance if the region is in sync.
                 */
-               if (rh_in_sync(&ms->rh, region, 0))
+               if (rh_in_sync(&ms->rh, region, 1))
                        m = choose_mirror(ms, bio->bi_sector);
                else
                        m = ms->default_mirror;
@@ -785,6 +798,14 @@ static void do_write(struct mirror_set *ms, struct bio *bio)
        unsigned int i;
        struct io_region io[KCOPYD_MAX_REGIONS+1];
        struct mirror *m;
+       struct dm_io_request io_req = {
+               .bi_rw = WRITE,
+               .mem.type = DM_IO_BVEC,
+               .mem.ptr.bvec = bio->bi_io_vec + bio->bi_idx,
+               .notify.fn = write_callback,
+               .notify.context = bio,
+               .client = ms->io_client,
+       };
 
        for (i = 0; i < ms->nr_mirrors; i++) {
                m = ms->mirror + i;
@@ -795,9 +816,8 @@ static void do_write(struct mirror_set *ms, struct bio *bio)
        }
 
        bio_set_ms(bio, ms);
-       dm_io_async_bvec(ms->nr_mirrors, io, WRITE,
-                        bio->bi_io_vec + bio->bi_idx,
-                        write_callback, bio);
+
+       (void) dm_io(&io_req, ms->nr_mirrors, io, NULL);
 }
 
 static void do_writes(struct mirror_set *ms, struct bio_list *writes)
@@ -863,11 +883,10 @@ static void do_writes(struct mirror_set *ms, struct bio_list *writes)
 /*-----------------------------------------------------------------
  * kmirrord
  *---------------------------------------------------------------*/
-static LIST_HEAD(_mirror_sets);
-static DECLARE_RWSEM(_mirror_sets_lock);
-
-static void do_mirror(struct mirror_set *ms)
+static void do_mirror(struct work_struct *work)
 {
+       struct mirror_set *ms =container_of(work, struct mirror_set,
+                                           kmirrord_work);
        struct bio_list reads, writes;
 
        spin_lock(&ms->lock);
@@ -883,16 +902,6 @@ static void do_mirror(struct mirror_set *ms)
        do_writes(ms, &writes);
 }
 
-static void do_work(struct work_struct *ignored)
-{
-       struct mirror_set *ms;
-
-       down_read(&_mirror_sets_lock);
-       list_for_each_entry (ms, &_mirror_sets, list)
-               do_mirror(ms);
-       up_read(&_mirror_sets_lock);
-}
-
 /*-----------------------------------------------------------------
  * Target functions
  *---------------------------------------------------------------*/
@@ -924,6 +933,13 @@ static struct mirror_set *alloc_context(unsigned int nr_mirrors,
        ms->in_sync = 0;
        ms->default_mirror = &ms->mirror[DEFAULT_MIRROR];
 
+       ms->io_client = dm_io_client_create(DM_IO_PAGES);
+       if (IS_ERR(ms->io_client)) {
+               ti->error = "Error creating dm_io client";
+               kfree(ms);
+               return NULL;
+       }
+
        if (rh_init(&ms->rh, ms, dl, region_size, ms->nr_regions)) {
                ti->error = "Error creating dirty region hash";
                kfree(ms);
@@ -939,6 +955,7 @@ static void free_context(struct mirror_set *ms, struct dm_target *ti,
        while (m--)
                dm_put_device(ti, ms->mirror[m].dev);
 
+       dm_io_client_destroy(ms->io_client);
        rh_exit(&ms->rh);
        kfree(ms);
 }
@@ -971,23 +988,6 @@ static int get_mirror(struct mirror_set *ms, struct dm_target *ti,
        return 0;
 }
 
-static int add_mirror_set(struct mirror_set *ms)
-{
-       down_write(&_mirror_sets_lock);
-       list_add_tail(&ms->list, &_mirror_sets);
-       up_write(&_mirror_sets_lock);
-       wake();
-
-       return 0;
-}
-
-static void del_mirror_set(struct mirror_set *ms)
-{
-       down_write(&_mirror_sets_lock);
-       list_del(&ms->list);
-       up_write(&_mirror_sets_lock);
-}
-
 /*
  * Create dirty log: log_type #log_params <log_params>
  */
@@ -1030,16 +1030,55 @@ static struct dirty_log *create_dirty_log(struct dm_target *ti,
        return dl;
 }
 
+static int parse_features(struct mirror_set *ms, unsigned argc, char **argv,
+                         unsigned *args_used)
+{
+       unsigned num_features;
+       struct dm_target *ti = ms->ti;
+
+       *args_used = 0;
+
+       if (!argc)
+               return 0;
+
+       if (sscanf(argv[0], "%u", &num_features) != 1) {
+               ti->error = "Invalid number of features";
+               return -EINVAL;
+       }
+
+       argc--;
+       argv++;
+       (*args_used)++;
+
+       if (num_features > argc) {
+               ti->error = "Not enough arguments to support feature count";
+               return -EINVAL;
+       }
+
+       if (!strcmp("handle_errors", argv[0]))
+               ms->features |= DM_RAID1_HANDLE_ERRORS;
+       else {
+               ti->error = "Unrecognised feature requested";
+               return -EINVAL;
+       }
+
+       (*args_used)++;
+
+       return 0;
+}
+
 /*
  * Construct a mirror mapping:
  *
  * log_type #log_params <log_params>
  * #mirrors [mirror_path offset]{2,}
+ * [#features <features>]
  *
  * log_type is "core" or "disk"
  * #log_params is between 1 and 3
+ *
+ * If present, features must be "handle_errors".
  */
-#define DM_IO_PAGES 64
 static int mirror_ctr(struct dm_target *ti, unsigned int argc, char **argv)
 {
        int r;
@@ -1063,8 +1102,8 @@ static int mirror_ctr(struct dm_target *ti, unsigned int argc, char **argv)
 
        argv++, argc--;
 
-       if (argc != nr_mirrors * 2) {
-               ti->error = "Wrong number of mirror arguments";
+       if (argc < nr_mirrors * 2) {
+               ti->error = "Too few mirror arguments";
                dm_destroy_dirty_log(dl);
                return -EINVAL;
        }
@@ -1089,13 +1128,37 @@ static int mirror_ctr(struct dm_target *ti, unsigned int argc, char **argv)
        ti->private = ms;
        ti->split_io = ms->rh.region_size;
 
+       ms->kmirrord_wq = create_singlethread_workqueue("kmirrord");
+       if (!ms->kmirrord_wq) {
+               DMERR("couldn't start kmirrord");
+               free_context(ms, ti, m);
+               return -ENOMEM;
+       }
+       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;
+       }
+
+       argv += args_used;
+       argc -= args_used;
+
+       if (argc) {
+               ti->error = "Too many mirror arguments";
+               free_context(ms, ti, ms->nr_mirrors);
+               return -EINVAL;
+       }
+
        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;
        }
 
-       add_mirror_set(ms);
+       wake(ms);
        return 0;
 }
 
@@ -1103,8 +1166,9 @@ static void mirror_dtr(struct dm_target *ti)
 {
        struct mirror_set *ms = (struct mirror_set *) ti->private;
 
-       del_mirror_set(ms);
+       flush_workqueue(ms->kmirrord_wq);
        kcopyd_client_destroy(ms->kcopyd_client);
+       destroy_workqueue(ms->kmirrord_wq);
        free_context(ms, ti, ms->nr_mirrors);
 }
 
@@ -1120,7 +1184,7 @@ static void queue_bio(struct mirror_set *ms, struct bio *bio, int rw)
        spin_unlock(&ms->lock);
 
        if (should_wake)
-               wake();
+               wake(ms);
 }
 
 /*
@@ -1137,7 +1201,7 @@ static int mirror_map(struct dm_target *ti, struct bio *bio,
 
        if (rw == WRITE) {
                queue_bio(ms, bio, rw);
-               return 0;
+               return DM_MAPIO_SUBMITTED;
        }
 
        r = ms->rh.log->type->in_sync(ms->rh.log,
@@ -1146,7 +1210,7 @@ static int mirror_map(struct dm_target *ti, struct bio *bio,
                return r;
 
        if (r == -EWOULDBLOCK)  /* FIXME: ugly */
-               r = 0;
+               r = DM_MAPIO_SUBMITTED;
 
        /*
         * We don't want to fast track a recovery just for a read
@@ -1159,7 +1223,7 @@ static int mirror_map(struct dm_target *ti, struct bio *bio,
        if (!r) {
                /* Pass this io over to the daemon */
                queue_bio(ms, bio, rw);
-               return 0;
+               return DM_MAPIO_SUBMITTED;
        }
 
        m = choose_mirror(ms, bio->bi_sector);
@@ -1167,7 +1231,7 @@ static int mirror_map(struct dm_target *ti, struct bio *bio,
                return -EIO;
 
        map_bio(ms, m, bio);
-       return 1;
+       return DM_MAPIO_REMAPPED;
 }
 
 static int mirror_end_io(struct dm_target *ti, struct bio *bio,
@@ -1215,11 +1279,9 @@ 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)
 {
-       unsigned int m, sz;
+       unsigned int m, sz = 0;
        struct mirror_set *ms = (struct mirror_set *) ti->private;
 
-       sz = ms->rh.log->type->status(ms->rh.log, type, result, maxlen);
-
        switch (type) {
        case STATUSTYPE_INFO:
                DMEMIT("%d ", ms->nr_mirrors);
@@ -1230,13 +1292,21 @@ static int mirror_status(struct dm_target *ti, status_type_t type,
                        (unsigned long long)ms->rh.log->type->
                                get_sync_count(ms->rh.log),
                        (unsigned long long)ms->nr_regions);
+
+               sz = ms->rh.log->type->status(ms->rh.log, type, result, maxlen);
+
                break;
 
        case STATUSTYPE_TABLE:
+               sz = ms->rh.log->type->status(ms->rh.log, type, result, maxlen);
+
                DMEMIT("%d", ms->nr_mirrors);
                for (m = 0; m < ms->nr_mirrors; m++)
                        DMEMIT(" %s %llu", ms->mirror[m].dev->name,
                                (unsigned long long)ms->mirror[m].offset);
+
+               if (ms->features & DM_RAID1_HANDLE_ERRORS)
+                       DMEMIT(" 1 handle_errors");
        }
 
        return 0;
@@ -1244,7 +1314,7 @@ static int mirror_status(struct dm_target *ti, status_type_t type,
 
 static struct target_type mirror_target = {
        .name    = "mirror",
-       .version = {1, 0, 2},
+       .version = {1, 0, 3},
        .module  = THIS_MODULE,
        .ctr     = mirror_ctr,
        .dtr     = mirror_dtr,
@@ -1263,20 +1333,11 @@ static int __init dm_mirror_init(void)
        if (r)
                return r;
 
-       _kmirrord_wq = create_singlethread_workqueue("kmirrord");
-       if (!_kmirrord_wq) {
-               DMERR("couldn't start kmirrord");
-               dm_dirty_log_exit();
-               return r;
-       }
-       INIT_WORK(&_kmirrord_work, do_work);
-
        r = dm_register_target(&mirror_target);
        if (r < 0) {
                DMERR("%s: Failed to register mirror target",
                      mirror_target.name);
                dm_dirty_log_exit();
-               destroy_workqueue(_kmirrord_wq);
        }
 
        return r;
@@ -1290,7 +1351,6 @@ static void __exit dm_mirror_exit(void)
        if (r < 0)
                DMERR("%s: unregister failed %d", mirror_target.name, r);
 
-       destroy_workqueue(_kmirrord_wq);
        dm_dirty_log_exit();
 }