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

Commit c9e45581 authored by Dave Wysochanski's avatar Dave Wysochanski Committed by Alasdair G Kergon
Browse files

dm mpath: add retry pg init



This patch allows a failed path group initialisation command to be retried.

It adds a generic MP_RETRY flag and a "pg_init_retries" feature to
device-mapper multipath which limits the number of retries.

1. A hw handler sends a path initialization command to the storage and
the command completes with an error code indicating the command
should be retried.

2. The hardware handler calls dm_pg_init_complete() with MP_RETRY
set in err_flags to ask the dm multipath core to retry.

3. If the retry limit has not been exceeded, pg_init() is retried.
Otherwise fail_path() is called.

If you are using the userspace multipath-tools or device-mapper-multipath
package, you can set pg_init_retries in the 'device' section of your
/etc/multipath.conf file. For example:

features                "2 pg_init_retries 7"

The number of PG retries attempted is reported in the 'dmsetup status' output.

Signed-off-by: default avatarDave Wysochanski <dwysocha@redhat.com>
Acked-by: default avatarMike Christie <michaelc@cs.wisc.edu>
Acked-by: default avatarChandra Seetharaman <sekharan@us.ibm.com>
Signed-off-by: default avatarAlasdair G Kergon <agk@redhat.com>
parent 636d5786
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -58,5 +58,6 @@ unsigned dm_scsi_err_handler(struct hw_handler *hwh, struct bio *bio);
#define MP_FAIL_PATH 1
#define MP_BYPASS_PG 2
#define MP_ERROR_IO  4	/* Don't retry this I/O */
#define MP_RETRY 8

#endif
+67 −14
Original line number Diff line number Diff line
@@ -75,6 +75,8 @@ struct multipath {
	unsigned queue_io;		/* Must we queue all I/O? */
	unsigned queue_if_no_path;	/* Queue I/O if last path fails? */
	unsigned saved_queue_if_no_path;/* Saved state during suspension */
	unsigned pg_init_retries;	/* Number of times to retry pg_init */
	unsigned pg_init_count;		/* Number of times pg_init called */

	struct work_struct process_queued_ios;
	struct bio_list queued_ios;
@@ -225,6 +227,8 @@ static void __switch_pg(struct multipath *m, struct pgpath *pgpath)
		m->pg_init_required = 0;
		m->queue_io = 0;
	}

	m->pg_init_count = 0;
}

static int __choose_path_in_pg(struct multipath *m, struct priority_group *pg)
@@ -424,6 +428,7 @@ static void process_queued_ios(struct work_struct *work)
		must_queue = 0;

	if (m->pg_init_required && !m->pg_init_in_progress) {
		m->pg_init_count++;
		m->pg_init_required = 0;
		m->pg_init_in_progress = 1;
		init_required = 1;
@@ -689,9 +694,11 @@ static int parse_features(struct arg_set *as, struct multipath *m)
	int r;
	unsigned argc;
	struct dm_target *ti = m->ti;
	const char *param_name;

	static struct param _params[] = {
		{0, 1, "invalid number of feature args"},
		{0, 3, "invalid number of feature args"},
		{1, 50, "pg_init_retries must be between 1 and 50"},
	};

	r = read_param(_params, shift(as), &argc, &ti->error);
@@ -701,12 +708,28 @@ static int parse_features(struct arg_set *as, struct multipath *m)
	if (!argc)
		return 0;

	if (!strnicmp(shift(as), MESG_STR("queue_if_no_path")))
		return queue_if_no_path(m, 1, 0);
	else {
		ti->error = "Unrecognised multipath feature request";
		return -EINVAL;
	do {
		param_name = shift(as);
		argc--;

		if (!strnicmp(param_name, MESG_STR("queue_if_no_path"))) {
			r = queue_if_no_path(m, 1, 0);
			continue;
		}

		if (!strnicmp(param_name, MESG_STR("pg_init_retries")) &&
		    (argc >= 1)) {
			r = read_param(_params + 1, shift(as),
				       &m->pg_init_retries, &ti->error);
			argc--;
			continue;
		}

		ti->error = "Unrecognised multipath feature request";
		r = -EINVAL;
	} while (argc && !r);

	return r;
}

static int multipath_ctr(struct dm_target *ti, unsigned int argc,
@@ -975,6 +998,26 @@ static int bypass_pg_num(struct multipath *m, const char *pgstr, int bypassed)
	return 0;
}

/*
 * Should we retry pg_init immediately?
 */
static int pg_init_limit_reached(struct multipath *m, struct pgpath *pgpath)
{
	unsigned long flags;
	int limit_reached = 0;

	spin_lock_irqsave(&m->lock, flags);

	if (m->pg_init_count <= m->pg_init_retries)
		m->pg_init_required = 1;
	else
		limit_reached = 1;

	spin_unlock_irqrestore(&m->lock, flags);

	return limit_reached;
}

/*
 * pg_init must call this when it has completed its initialisation
 */
@@ -985,8 +1028,14 @@ void dm_pg_init_complete(struct dm_path *path, unsigned err_flags)
	struct multipath *m = pg->m;
	unsigned long flags;

	/* We insist on failing the path if the PG is already bypassed. */
	if (err_flags && pg->bypassed)
	/*
	 * If requested, retry pg_init until maximum number of retries exceeded.
	 * If retry not requested and PG already bypassed, always fail the path.
	 */
	if (err_flags & MP_RETRY) {
		if (pg_init_limit_reached(m, pgpath))
			err_flags |= MP_FAIL_PATH;
	} else if (err_flags && pg->bypassed)
		err_flags |= MP_FAIL_PATH;

	if (err_flags & MP_FAIL_PATH)
@@ -996,7 +1045,7 @@ void dm_pg_init_complete(struct dm_path *path, unsigned err_flags)
		bypass_pg(m, pg, 1);

	spin_lock_irqsave(&m->lock, flags);
	if (err_flags) {
	if (err_flags & ~MP_RETRY) {
		m->current_pgpath = NULL;
		m->current_pg = NULL;
	} else if (!m->pg_init_required)
@@ -1148,11 +1197,15 @@ static int multipath_status(struct dm_target *ti, status_type_t type,

	/* Features */
	if (type == STATUSTYPE_INFO)
		DMEMIT("1 %u ", m->queue_size);
	else if (m->queue_if_no_path)
		DMEMIT("1 queue_if_no_path ");
	else
		DMEMIT("0 ");
		DMEMIT("2 %u %u ", m->queue_size, m->pg_init_count);
	else {
		DMEMIT("%u ", m->queue_if_no_path +
			      (m->pg_init_retries > 0) * 2);
		if (m->queue_if_no_path)
			DMEMIT("queue_if_no_path ");
		if (m->pg_init_retries)
			DMEMIT("pg_init_retries %u ", m->pg_init_retries);
	}

	if (hwh->type && hwh->type->status)
		sz += hwh->type->status(hwh, type, result + sz, maxlen - sz);