diff options
author | Jonathan Brassow <jbrassow@redhat.com> | 2007-07-12 12:29:04 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-07-12 18:01:08 -0400 |
commit | f44db678edcc6f4c2779ac43f63f0b9dfa28b724 (patch) | |
tree | bbd835d00ca6ed0805c5c7331ebc5dd7978d299c | |
parent | d0d444c7d48c14d59f665887c758fde248f1cb37 (diff) |
dm raid1: handle resync failures
Device-mapper mirroring currently takes a best effort approach to
recovery - failures during mirror synchronization are completely ignored.
This means that regions are marked 'in-sync' and 'clean' and removed
from the hash list. Future reads and writes that query the region
will incorrectly interpret the region as in-sync.
This patch handles failures during the recovery process. If a failure
occurs, the region is marked as 'not-in-sync' (aka RH_NOSYNC) and added
to a new list 'failed_recovered_regions'.
Regions on the 'failed_recovered_regions' list are not marked as 'clean'
upon removal from the list. Furthermore, if the DM_RAID1_HANDLE_ERRORS
flag is set, the region is marked as 'not-in-sync'. This action prevents
any future read-balancing from choosing an invalid device because of the
'not-in-sync' status.
If "handle_errors" is not specified when creating a mirror (leaving the
DM_RAID1_HANDLE_ERRORS flag unset), failures will be ignored exactly as they
would be without this patch. This is to preserve backwards compatibility with
user-space tools, such as 'pvmove'. However, since future read-balancing
policies will rely on the correct sync status of a region, a user must choose
"handle_errors" when using read-balancing.
Signed-off-by: Jonathan Brassow <jbrassow@redhat.com>
Signed-off-by: Alasdair G Kergon <agk@redhat.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | drivers/md/dm-raid1.c | 44 |
1 files changed, 41 insertions, 3 deletions
diff --git a/drivers/md/dm-raid1.c b/drivers/md/dm-raid1.c index 04dce7a66494..dee4221caa73 100644 --- a/drivers/md/dm-raid1.c +++ b/drivers/md/dm-raid1.c | |||
@@ -24,6 +24,7 @@ | |||
24 | #define DM_IO_PAGES 64 | 24 | #define DM_IO_PAGES 64 |
25 | 25 | ||
26 | #define DM_RAID1_HANDLE_ERRORS 0x01 | 26 | #define DM_RAID1_HANDLE_ERRORS 0x01 |
27 | #define errors_handled(p) ((p)->features & DM_RAID1_HANDLE_ERRORS) | ||
27 | 28 | ||
28 | static DECLARE_WAIT_QUEUE_HEAD(_kmirrord_recovery_stopped); | 29 | static DECLARE_WAIT_QUEUE_HEAD(_kmirrord_recovery_stopped); |
29 | 30 | ||
@@ -85,6 +86,7 @@ struct region_hash { | |||
85 | struct list_head clean_regions; | 86 | struct list_head clean_regions; |
86 | struct list_head quiesced_regions; | 87 | struct list_head quiesced_regions; |
87 | struct list_head recovered_regions; | 88 | struct list_head recovered_regions; |
89 | struct list_head failed_recovered_regions; | ||
88 | }; | 90 | }; |
89 | 91 | ||
90 | enum { | 92 | enum { |
@@ -204,6 +206,7 @@ static int rh_init(struct region_hash *rh, struct mirror_set *ms, | |||
204 | INIT_LIST_HEAD(&rh->clean_regions); | 206 | INIT_LIST_HEAD(&rh->clean_regions); |
205 | INIT_LIST_HEAD(&rh->quiesced_regions); | 207 | INIT_LIST_HEAD(&rh->quiesced_regions); |
206 | INIT_LIST_HEAD(&rh->recovered_regions); | 208 | INIT_LIST_HEAD(&rh->recovered_regions); |
209 | INIT_LIST_HEAD(&rh->failed_recovered_regions); | ||
207 | 210 | ||
208 | rh->region_pool = mempool_create_kmalloc_pool(MIN_REGIONS, | 211 | rh->region_pool = mempool_create_kmalloc_pool(MIN_REGIONS, |
209 | sizeof(struct region)); | 212 | sizeof(struct region)); |
@@ -368,6 +371,7 @@ static void rh_update_states(struct region_hash *rh) | |||
368 | 371 | ||
369 | LIST_HEAD(clean); | 372 | LIST_HEAD(clean); |
370 | LIST_HEAD(recovered); | 373 | LIST_HEAD(recovered); |
374 | LIST_HEAD(failed_recovered); | ||
371 | 375 | ||
372 | /* | 376 | /* |
373 | * Quickly grab the lists. | 377 | * Quickly grab the lists. |
@@ -389,6 +393,15 @@ static void rh_update_states(struct region_hash *rh) | |||
389 | list_for_each_entry (reg, &recovered, list) | 393 | list_for_each_entry (reg, &recovered, list) |
390 | list_del(®->hash_list); | 394 | list_del(®->hash_list); |
391 | } | 395 | } |
396 | |||
397 | if (!list_empty(&rh->failed_recovered_regions)) { | ||
398 | list_splice(&rh->failed_recovered_regions, &failed_recovered); | ||
399 | INIT_LIST_HEAD(&rh->failed_recovered_regions); | ||
400 | |||
401 | list_for_each_entry(reg, &failed_recovered, list) | ||
402 | list_del(®->hash_list); | ||
403 | } | ||
404 | |||
392 | spin_unlock(&rh->region_lock); | 405 | spin_unlock(&rh->region_lock); |
393 | write_unlock_irq(&rh->hash_lock); | 406 | write_unlock_irq(&rh->hash_lock); |
394 | 407 | ||
@@ -403,6 +416,11 @@ static void rh_update_states(struct region_hash *rh) | |||
403 | mempool_free(reg, rh->region_pool); | 416 | mempool_free(reg, rh->region_pool); |
404 | } | 417 | } |
405 | 418 | ||
419 | list_for_each_entry_safe(reg, next, &failed_recovered, list) { | ||
420 | complete_resync_work(reg, errors_handled(rh->ms) ? 0 : 1); | ||
421 | mempool_free(reg, rh->region_pool); | ||
422 | } | ||
423 | |||
406 | list_for_each_entry_safe(reg, next, &clean, list) { | 424 | list_for_each_entry_safe(reg, next, &clean, list) { |
407 | rh->log->type->clear_region(rh->log, reg->key); | 425 | rh->log->type->clear_region(rh->log, reg->key); |
408 | mempool_free(reg, rh->region_pool); | 426 | mempool_free(reg, rh->region_pool); |
@@ -555,13 +573,17 @@ static struct region *rh_recovery_start(struct region_hash *rh) | |||
555 | return reg; | 573 | return reg; |
556 | } | 574 | } |
557 | 575 | ||
558 | /* FIXME: success ignored for now */ | ||
559 | static void rh_recovery_end(struct region *reg, int success) | 576 | static void rh_recovery_end(struct region *reg, int success) |
560 | { | 577 | { |
561 | struct region_hash *rh = reg->rh; | 578 | struct region_hash *rh = reg->rh; |
562 | 579 | ||
563 | spin_lock_irq(&rh->region_lock); | 580 | spin_lock_irq(&rh->region_lock); |
564 | list_add(®->list, ®->rh->recovered_regions); | 581 | if (success) |
582 | list_add(®->list, ®->rh->recovered_regions); | ||
583 | else { | ||
584 | reg->state = RH_NOSYNC; | ||
585 | list_add(®->list, ®->rh->failed_recovered_regions); | ||
586 | } | ||
565 | spin_unlock_irq(&rh->region_lock); | 587 | spin_unlock_irq(&rh->region_lock); |
566 | 588 | ||
567 | wake(rh->ms); | 589 | wake(rh->ms); |
@@ -633,7 +655,14 @@ static void recovery_complete(int read_err, unsigned int write_err, | |||
633 | { | 655 | { |
634 | struct region *reg = (struct region *) context; | 656 | struct region *reg = (struct region *) context; |
635 | 657 | ||
636 | /* FIXME: better error handling */ | 658 | if (read_err) |
659 | /* Read error means the failure of default mirror. */ | ||
660 | DMERR_LIMIT("Unable to read primary mirror during recovery"); | ||
661 | |||
662 | if (write_err) | ||
663 | DMERR_LIMIT("Write error during recovery (error = 0x%x)", | ||
664 | write_err); | ||
665 | |||
637 | rh_recovery_end(reg, !(read_err || write_err)); | 666 | rh_recovery_end(reg, !(read_err || write_err)); |
638 | } | 667 | } |
639 | 668 | ||
@@ -1145,6 +1174,15 @@ static int mirror_ctr(struct dm_target *ti, unsigned int argc, char **argv) | |||
1145 | argv += args_used; | 1174 | argv += args_used; |
1146 | argc -= args_used; | 1175 | argc -= args_used; |
1147 | 1176 | ||
1177 | /* | ||
1178 | * Any read-balancing addition depends on the | ||
1179 | * DM_RAID1_HANDLE_ERRORS flag being present. | ||
1180 | * This is because the decision to balance depends | ||
1181 | * on the sync state of a region. If the above | ||
1182 | * flag is not present, we ignore errors; and | ||
1183 | * the sync state may be inaccurate. | ||
1184 | */ | ||
1185 | |||
1148 | if (argc) { | 1186 | if (argc) { |
1149 | ti->error = "Too many mirror arguments"; | 1187 | ti->error = "Too many mirror arguments"; |
1150 | free_context(ms, ti, ms->nr_mirrors); | 1188 | free_context(ms, ti, ms->nr_mirrors); |