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

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
...@@ -58,5 +58,6 @@ unsigned dm_scsi_err_handler(struct hw_handler *hwh, struct bio *bio); ...@@ -58,5 +58,6 @@ unsigned dm_scsi_err_handler(struct hw_handler *hwh, struct bio *bio);
#define MP_FAIL_PATH 1 #define MP_FAIL_PATH 1
#define MP_BYPASS_PG 2 #define MP_BYPASS_PG 2
#define MP_ERROR_IO 4 /* Don't retry this I/O */ #define MP_ERROR_IO 4 /* Don't retry this I/O */
#define MP_RETRY 8
#endif #endif
...@@ -75,6 +75,8 @@ struct multipath { ...@@ -75,6 +75,8 @@ struct multipath {
unsigned queue_io; /* Must we queue all I/O? */ unsigned queue_io; /* Must we queue all I/O? */
unsigned queue_if_no_path; /* Queue I/O if last path fails? */ unsigned queue_if_no_path; /* Queue I/O if last path fails? */
unsigned saved_queue_if_no_path;/* Saved state during suspension */ 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 work_struct process_queued_ios;
struct bio_list queued_ios; struct bio_list queued_ios;
...@@ -225,6 +227,8 @@ static void __switch_pg(struct multipath *m, struct pgpath *pgpath) ...@@ -225,6 +227,8 @@ static void __switch_pg(struct multipath *m, struct pgpath *pgpath)
m->pg_init_required = 0; m->pg_init_required = 0;
m->queue_io = 0; m->queue_io = 0;
} }
m->pg_init_count = 0;
} }
static int __choose_path_in_pg(struct multipath *m, struct priority_group *pg) 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) ...@@ -424,6 +428,7 @@ static void process_queued_ios(struct work_struct *work)
must_queue = 0; must_queue = 0;
if (m->pg_init_required && !m->pg_init_in_progress) { if (m->pg_init_required && !m->pg_init_in_progress) {
m->pg_init_count++;
m->pg_init_required = 0; m->pg_init_required = 0;
m->pg_init_in_progress = 1; m->pg_init_in_progress = 1;
init_required = 1; init_required = 1;
...@@ -689,9 +694,11 @@ static int parse_features(struct arg_set *as, struct multipath *m) ...@@ -689,9 +694,11 @@ static int parse_features(struct arg_set *as, struct multipath *m)
int r; int r;
unsigned argc; unsigned argc;
struct dm_target *ti = m->ti; struct dm_target *ti = m->ti;
const char *param_name;
static struct param _params[] = { 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); r = read_param(_params, shift(as), &argc, &ti->error);
...@@ -701,12 +708,28 @@ static int parse_features(struct arg_set *as, struct multipath *m) ...@@ -701,12 +708,28 @@ static int parse_features(struct arg_set *as, struct multipath *m)
if (!argc) if (!argc)
return 0; return 0;
if (!strnicmp(shift(as), MESG_STR("queue_if_no_path"))) do {
return queue_if_no_path(m, 1, 0); param_name = shift(as);
else { argc--;
ti->error = "Unrecognised multipath feature request";
return -EINVAL; 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, 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) ...@@ -975,6 +998,26 @@ static int bypass_pg_num(struct multipath *m, const char *pgstr, int bypassed)
return 0; 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 * 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) ...@@ -985,8 +1028,14 @@ void dm_pg_init_complete(struct dm_path *path, unsigned err_flags)
struct multipath *m = pg->m; struct multipath *m = pg->m;
unsigned long flags; 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; err_flags |= MP_FAIL_PATH;
if (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) ...@@ -996,7 +1045,7 @@ void dm_pg_init_complete(struct dm_path *path, unsigned err_flags)
bypass_pg(m, pg, 1); bypass_pg(m, pg, 1);
spin_lock_irqsave(&m->lock, flags); spin_lock_irqsave(&m->lock, flags);
if (err_flags) { if (err_flags & ~MP_RETRY) {
m->current_pgpath = NULL; m->current_pgpath = NULL;
m->current_pg = NULL; m->current_pg = NULL;
} else if (!m->pg_init_required) } else if (!m->pg_init_required)
...@@ -1148,11 +1197,15 @@ static int multipath_status(struct dm_target *ti, status_type_t type, ...@@ -1148,11 +1197,15 @@ static int multipath_status(struct dm_target *ti, status_type_t type,
/* Features */ /* Features */
if (type == STATUSTYPE_INFO) if (type == STATUSTYPE_INFO)
DMEMIT("1 %u ", m->queue_size); DMEMIT("2 %u %u ", m->queue_size, m->pg_init_count);
else if (m->queue_if_no_path) else {
DMEMIT("1 queue_if_no_path "); DMEMIT("%u ", m->queue_if_no_path +
else (m->pg_init_retries > 0) * 2);
DMEMIT("0 "); 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) if (hwh->type && hwh->type->status)
sz += hwh->type->status(hwh, type, result + sz, maxlen - sz); sz += hwh->type->status(hwh, type, result + sz, maxlen - sz);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment