]> err.no Git - linux-2.6/blobdiff - drivers/md/dm-raid1.c
Merge branch 'nfs-server-stable' of git://linux-nfs.org/~bfields/linux
[linux-2.6] / drivers / md / dm-raid1.c
index 2a3e2f80aa4b556df456a9619747e6a166f80a1b..d09ff15490a5db439aff81566b02cf3f401b364a 100644 (file)
 #include <linux/workqueue.h>
 
 #define DM_MSG_PREFIX "raid1"
+#define DM_IO_PAGES 64
 
 #define DM_RAID1_HANDLE_ERRORS 0x01
+#define errors_handled(p)      ((p)->features & DM_RAID1_HANDLE_ERRORS)
 
 static DECLARE_WAIT_QUEUE_HEAD(_kmirrord_recovery_stopped);
 
@@ -84,6 +86,7 @@ struct region_hash {
        struct list_head clean_regions;
        struct list_head quiesced_regions;
        struct list_head recovered_regions;
+       struct list_head failed_recovered_regions;
 };
 
 enum {
@@ -126,9 +129,12 @@ struct mirror_set {
        struct bio_list reads;
        struct bio_list writes;
 
+       struct dm_io_client *io_client;
+
        /* recovery */
        region_t nr_regions;
        int in_sync;
+       int log_failure;
 
        struct mirror *default_mirror;  /* Default mirror */
 
@@ -201,6 +207,7 @@ static int rh_init(struct region_hash *rh, struct mirror_set *ms,
        INIT_LIST_HEAD(&rh->clean_regions);
        INIT_LIST_HEAD(&rh->quiesced_regions);
        INIT_LIST_HEAD(&rh->recovered_regions);
+       INIT_LIST_HEAD(&rh->failed_recovered_regions);
 
        rh->region_pool = mempool_create_kmalloc_pool(MIN_REGIONS,
                                                      sizeof(struct region));
@@ -365,6 +372,7 @@ static void rh_update_states(struct region_hash *rh)
 
        LIST_HEAD(clean);
        LIST_HEAD(recovered);
+       LIST_HEAD(failed_recovered);
 
        /*
         * Quickly grab the lists.
@@ -375,10 +383,8 @@ static void rh_update_states(struct region_hash *rh)
                list_splice(&rh->clean_regions, &clean);
                INIT_LIST_HEAD(&rh->clean_regions);
 
-               list_for_each_entry (reg, &clean, list) {
-                       rh->log->type->clear_region(rh->log, reg->key);
+               list_for_each_entry(reg, &clean, list)
                        list_del(&reg->hash_list);
-               }
        }
 
        if (!list_empty(&rh->recovered_regions)) {
@@ -388,6 +394,15 @@ static void rh_update_states(struct region_hash *rh)
                list_for_each_entry (reg, &recovered, list)
                        list_del(&reg->hash_list);
        }
+
+       if (!list_empty(&rh->failed_recovered_regions)) {
+               list_splice(&rh->failed_recovered_regions, &failed_recovered);
+               INIT_LIST_HEAD(&rh->failed_recovered_regions);
+
+               list_for_each_entry(reg, &failed_recovered, list)
+                       list_del(&reg->hash_list);
+       }
+
        spin_unlock(&rh->region_lock);
        write_unlock_irq(&rh->hash_lock);
 
@@ -402,11 +417,17 @@ static void rh_update_states(struct region_hash *rh)
                mempool_free(reg, rh->region_pool);
        }
 
-       if (!list_empty(&recovered))
-               rh->log->type->flush(rh->log);
+       list_for_each_entry_safe(reg, next, &failed_recovered, list) {
+               complete_resync_work(reg, errors_handled(rh->ms) ? 0 : 1);
+               mempool_free(reg, rh->region_pool);
+       }
 
-       list_for_each_entry_safe (reg, next, &clean, list)
+       list_for_each_entry_safe(reg, next, &clean, list) {
+               rh->log->type->clear_region(rh->log, reg->key);
                mempool_free(reg, rh->region_pool);
+       }
+
+       rh->log->type->flush(rh->log);
 }
 
 static void rh_inc(struct region_hash *rh, region_t region)
@@ -553,21 +574,25 @@ static struct region *rh_recovery_start(struct region_hash *rh)
        return reg;
 }
 
-/* FIXME: success ignored for now */
 static void rh_recovery_end(struct region *reg, int success)
 {
        struct region_hash *rh = reg->rh;
 
        spin_lock_irq(&rh->region_lock);
-       list_add(&reg->list, &reg->rh->recovered_regions);
+       if (success)
+               list_add(&reg->list, &reg->rh->recovered_regions);
+       else {
+               reg->state = RH_NOSYNC;
+               list_add(&reg->list, &reg->rh->failed_recovered_regions);
+       }
        spin_unlock_irq(&rh->region_lock);
 
        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)
@@ -631,7 +656,14 @@ static void recovery_complete(int read_err, unsigned int write_err,
 {
        struct region *reg = (struct region *) context;
 
-       /* FIXME: better error handling */
+       if (read_err)
+               /* Read error means the failure of default mirror. */
+               DMERR_LIMIT("Unable to read primary mirror during recovery");
+
+       if (write_err)
+               DMERR_LIMIT("Write error during recovery (error = 0x%x)",
+                           write_err);
+
        rh_recovery_end(reg, !(read_err || write_err));
 }
 
@@ -739,7 +771,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;
@@ -788,7 +820,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)
@@ -796,6 +828,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;
@@ -806,9 +846,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)
@@ -854,12 +893,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)))
@@ -909,13 +951,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;
@@ -924,6 +965,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 +987,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);
 }
@@ -1062,7 +1111,6 @@ static int parse_features(struct mirror_set *ms, unsigned argc, char **argv,
  *
  * 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;
@@ -1129,6 +1177,15 @@ static int mirror_ctr(struct dm_target *ti, unsigned int argc, char **argv)
        argv += args_used;
        argc -= args_used;
 
+       /*
+        * Any read-balancing addition depends on the
+        * DM_RAID1_HANDLE_ERRORS flag being present.
+        * This is because the decision to balance depends
+        * on the sync state of a region.  If the above
+        * flag is not present, we ignore errors; and
+        * the sync state may be inaccurate.
+        */
+
        if (argc) {
                ti->error = "Too many mirror arguments";
                free_context(ms, ti, ms->nr_mirrors);
@@ -1272,12 +1329,12 @@ static int mirror_status(struct dm_target *ti, status_type_t type,
                for (m = 0; m < ms->nr_mirrors; m++)
                        DMEMIT("%s ", ms->mirror[m].dev->name);
 
-               DMEMIT("%llu/%llu",
+               DMEMIT("%llu/%llu 0 ",
                        (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);
+               sz += ms->rh.log->type->status(ms->rh.log, type, result+sz, maxlen-sz);
 
                break;
 
@@ -1319,8 +1376,7 @@ static int __init dm_mirror_init(void)
 
        r = dm_register_target(&mirror_target);
        if (r < 0) {
-               DMERR("%s: Failed to register mirror target",
-                     mirror_target.name);
+               DMERR("Failed to register mirror target");
                dm_dirty_log_exit();
        }
 
@@ -1333,7 +1389,7 @@ static void __exit dm_mirror_exit(void)
 
        r = dm_unregister_target(&mirror_target);
        if (r < 0)
-               DMERR("%s: unregister failed %d", mirror_target.name, r);
+               DMERR("unregister failed %d", r);
 
        dm_dirty_log_exit();
 }