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>
This commit is contained in:
parent
d0d444c7d4
commit
f44db678ed
1 changed files with 41 additions and 3 deletions
|
@ -24,6 +24,7 @@
|
||||||
#define DM_IO_PAGES 64
|
#define DM_IO_PAGES 64
|
||||||
|
|
||||||
#define DM_RAID1_HANDLE_ERRORS 0x01
|
#define DM_RAID1_HANDLE_ERRORS 0x01
|
||||||
|
#define errors_handled(p) ((p)->features & DM_RAID1_HANDLE_ERRORS)
|
||||||
|
|
||||||
static DECLARE_WAIT_QUEUE_HEAD(_kmirrord_recovery_stopped);
|
static DECLARE_WAIT_QUEUE_HEAD(_kmirrord_recovery_stopped);
|
||||||
|
|
||||||
|
@ -85,6 +86,7 @@ struct region_hash {
|
||||||
struct list_head clean_regions;
|
struct list_head clean_regions;
|
||||||
struct list_head quiesced_regions;
|
struct list_head quiesced_regions;
|
||||||
struct list_head recovered_regions;
|
struct list_head recovered_regions;
|
||||||
|
struct list_head failed_recovered_regions;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
|
@ -204,6 +206,7 @@ static int rh_init(struct region_hash *rh, struct mirror_set *ms,
|
||||||
INIT_LIST_HEAD(&rh->clean_regions);
|
INIT_LIST_HEAD(&rh->clean_regions);
|
||||||
INIT_LIST_HEAD(&rh->quiesced_regions);
|
INIT_LIST_HEAD(&rh->quiesced_regions);
|
||||||
INIT_LIST_HEAD(&rh->recovered_regions);
|
INIT_LIST_HEAD(&rh->recovered_regions);
|
||||||
|
INIT_LIST_HEAD(&rh->failed_recovered_regions);
|
||||||
|
|
||||||
rh->region_pool = mempool_create_kmalloc_pool(MIN_REGIONS,
|
rh->region_pool = mempool_create_kmalloc_pool(MIN_REGIONS,
|
||||||
sizeof(struct region));
|
sizeof(struct region));
|
||||||
|
@ -368,6 +371,7 @@ static void rh_update_states(struct region_hash *rh)
|
||||||
|
|
||||||
LIST_HEAD(clean);
|
LIST_HEAD(clean);
|
||||||
LIST_HEAD(recovered);
|
LIST_HEAD(recovered);
|
||||||
|
LIST_HEAD(failed_recovered);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Quickly grab the lists.
|
* Quickly grab the lists.
|
||||||
|
@ -389,6 +393,15 @@ static void rh_update_states(struct region_hash *rh)
|
||||||
list_for_each_entry (reg, &recovered, list)
|
list_for_each_entry (reg, &recovered, list)
|
||||||
list_del(®->hash_list);
|
list_del(®->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(®->hash_list);
|
||||||
|
}
|
||||||
|
|
||||||
spin_unlock(&rh->region_lock);
|
spin_unlock(&rh->region_lock);
|
||||||
write_unlock_irq(&rh->hash_lock);
|
write_unlock_irq(&rh->hash_lock);
|
||||||
|
|
||||||
|
@ -403,6 +416,11 @@ static void rh_update_states(struct region_hash *rh)
|
||||||
mempool_free(reg, rh->region_pool);
|
mempool_free(reg, rh->region_pool);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
rh->log->type->clear_region(rh->log, reg->key);
|
||||||
mempool_free(reg, rh->region_pool);
|
mempool_free(reg, rh->region_pool);
|
||||||
|
@ -555,13 +573,17 @@ static struct region *rh_recovery_start(struct region_hash *rh)
|
||||||
return reg;
|
return reg;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* FIXME: success ignored for now */
|
|
||||||
static void rh_recovery_end(struct region *reg, int success)
|
static void rh_recovery_end(struct region *reg, int success)
|
||||||
{
|
{
|
||||||
struct region_hash *rh = reg->rh;
|
struct region_hash *rh = reg->rh;
|
||||||
|
|
||||||
spin_lock_irq(&rh->region_lock);
|
spin_lock_irq(&rh->region_lock);
|
||||||
|
if (success)
|
||||||
list_add(®->list, ®->rh->recovered_regions);
|
list_add(®->list, ®->rh->recovered_regions);
|
||||||
|
else {
|
||||||
|
reg->state = RH_NOSYNC;
|
||||||
|
list_add(®->list, ®->rh->failed_recovered_regions);
|
||||||
|
}
|
||||||
spin_unlock_irq(&rh->region_lock);
|
spin_unlock_irq(&rh->region_lock);
|
||||||
|
|
||||||
wake(rh->ms);
|
wake(rh->ms);
|
||||||
|
@ -633,7 +655,14 @@ static void recovery_complete(int read_err, unsigned int write_err,
|
||||||
{
|
{
|
||||||
struct region *reg = (struct region *) context;
|
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));
|
rh_recovery_end(reg, !(read_err || write_err));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1145,6 +1174,15 @@ static int mirror_ctr(struct dm_target *ti, unsigned int argc, char **argv)
|
||||||
argv += args_used;
|
argv += args_used;
|
||||||
argc -= 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) {
|
if (argc) {
|
||||||
ti->error = "Too many mirror arguments";
|
ti->error = "Too many mirror arguments";
|
||||||
free_context(ms, ti, ms->nr_mirrors);
|
free_context(ms, ti, ms->nr_mirrors);
|
||||||
|
|
Loading…
Reference in a new issue