Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f44db678 authored by Jonathan Brassow's avatar Jonathan Brassow Committed by Linus Torvalds
Browse files

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: default avatarJonathan Brassow <jbrassow@redhat.com>
Signed-off-by: default avatarAlasdair G Kergon <agk@redhat.com>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent d0d444c7
Loading
Loading
Loading
Loading
+41 −3
Original line number Original line Diff line number Diff line
@@ -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(&reg->hash_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);
	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(&reg->list, &reg->rh->recovered_regions);
		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);
	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);