diff options
author | Dave Wysochanski <dwysocha@redhat.com> | 2007-10-19 17:47:53 -0400 |
---|---|---|
committer | Alasdair G Kergon <agk@redhat.com> | 2007-10-19 21:01:18 -0400 |
commit | c9e45581ad530cc1ca4b5d4add44a5b625234ada (patch) | |
tree | 3d5ddc7762e9c3d3da78f8c4d98047f3f108dd98 /drivers/md/dm-mpath.c | |
parent | 636d5786c45414fd8e48f2a2325be072274fdba4 (diff) |
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: Dave Wysochanski <dwysocha@redhat.com>
Acked-by: Mike Christie <michaelc@cs.wisc.edu>
Acked-by: Chandra Seetharaman <sekharan@us.ibm.com>
Signed-off-by: Alasdair G Kergon <agk@redhat.com>
Diffstat (limited to 'drivers/md/dm-mpath.c')
-rw-r--r-- | drivers/md/dm-mpath.c | 81 |
1 files changed, 67 insertions, 14 deletions
diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c index 31056abca89d..dd5ad6310f54 100644 --- a/drivers/md/dm-mpath.c +++ b/drivers/md/dm-mpath.c | |||
@@ -75,6 +75,8 @@ struct multipath { | |||
75 | unsigned queue_io; /* Must we queue all I/O? */ | 75 | unsigned queue_io; /* Must we queue all I/O? */ |
76 | unsigned queue_if_no_path; /* Queue I/O if last path fails? */ | 76 | unsigned queue_if_no_path; /* Queue I/O if last path fails? */ |
77 | unsigned saved_queue_if_no_path;/* Saved state during suspension */ | 77 | unsigned saved_queue_if_no_path;/* Saved state during suspension */ |
78 | unsigned pg_init_retries; /* Number of times to retry pg_init */ | ||
79 | unsigned pg_init_count; /* Number of times pg_init called */ | ||
78 | 80 | ||
79 | struct work_struct process_queued_ios; | 81 | struct work_struct process_queued_ios; |
80 | struct bio_list queued_ios; | 82 | struct bio_list queued_ios; |
@@ -225,6 +227,8 @@ static void __switch_pg(struct multipath *m, struct pgpath *pgpath) | |||
225 | m->pg_init_required = 0; | 227 | m->pg_init_required = 0; |
226 | m->queue_io = 0; | 228 | m->queue_io = 0; |
227 | } | 229 | } |
230 | |||
231 | m->pg_init_count = 0; | ||
228 | } | 232 | } |
229 | 233 | ||
230 | static int __choose_path_in_pg(struct multipath *m, struct priority_group *pg) | 234 | 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 | must_queue = 0; | 428 | must_queue = 0; |
425 | 429 | ||
426 | if (m->pg_init_required && !m->pg_init_in_progress) { | 430 | if (m->pg_init_required && !m->pg_init_in_progress) { |
431 | m->pg_init_count++; | ||
427 | m->pg_init_required = 0; | 432 | m->pg_init_required = 0; |
428 | m->pg_init_in_progress = 1; | 433 | m->pg_init_in_progress = 1; |
429 | init_required = 1; | 434 | init_required = 1; |
@@ -689,9 +694,11 @@ static int parse_features(struct arg_set *as, struct multipath *m) | |||
689 | int r; | 694 | int r; |
690 | unsigned argc; | 695 | unsigned argc; |
691 | struct dm_target *ti = m->ti; | 696 | struct dm_target *ti = m->ti; |
697 | const char *param_name; | ||
692 | 698 | ||
693 | static struct param _params[] = { | 699 | static struct param _params[] = { |
694 | {0, 1, "invalid number of feature args"}, | 700 | {0, 3, "invalid number of feature args"}, |
701 | {1, 50, "pg_init_retries must be between 1 and 50"}, | ||
695 | }; | 702 | }; |
696 | 703 | ||
697 | r = read_param(_params, shift(as), &argc, &ti->error); | 704 | 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 | if (!argc) | 708 | if (!argc) |
702 | return 0; | 709 | return 0; |
703 | 710 | ||
704 | if (!strnicmp(shift(as), MESG_STR("queue_if_no_path"))) | 711 | do { |
705 | return queue_if_no_path(m, 1, 0); | 712 | param_name = shift(as); |
706 | else { | 713 | argc--; |
714 | |||
715 | if (!strnicmp(param_name, MESG_STR("queue_if_no_path"))) { | ||
716 | r = queue_if_no_path(m, 1, 0); | ||
717 | continue; | ||
718 | } | ||
719 | |||
720 | if (!strnicmp(param_name, MESG_STR("pg_init_retries")) && | ||
721 | (argc >= 1)) { | ||
722 | r = read_param(_params + 1, shift(as), | ||
723 | &m->pg_init_retries, &ti->error); | ||
724 | argc--; | ||
725 | continue; | ||
726 | } | ||
727 | |||
707 | ti->error = "Unrecognised multipath feature request"; | 728 | ti->error = "Unrecognised multipath feature request"; |
708 | return -EINVAL; | 729 | r = -EINVAL; |
709 | } | 730 | } while (argc && !r); |
731 | |||
732 | return r; | ||
710 | } | 733 | } |
711 | 734 | ||
712 | static int multipath_ctr(struct dm_target *ti, unsigned int argc, | 735 | static int multipath_ctr(struct dm_target *ti, unsigned int argc, |
@@ -976,6 +999,26 @@ static int bypass_pg_num(struct multipath *m, const char *pgstr, int bypassed) | |||
976 | } | 999 | } |
977 | 1000 | ||
978 | /* | 1001 | /* |
1002 | * Should we retry pg_init immediately? | ||
1003 | */ | ||
1004 | static int pg_init_limit_reached(struct multipath *m, struct pgpath *pgpath) | ||
1005 | { | ||
1006 | unsigned long flags; | ||
1007 | int limit_reached = 0; | ||
1008 | |||
1009 | spin_lock_irqsave(&m->lock, flags); | ||
1010 | |||
1011 | if (m->pg_init_count <= m->pg_init_retries) | ||
1012 | m->pg_init_required = 1; | ||
1013 | else | ||
1014 | limit_reached = 1; | ||
1015 | |||
1016 | spin_unlock_irqrestore(&m->lock, flags); | ||
1017 | |||
1018 | return limit_reached; | ||
1019 | } | ||
1020 | |||
1021 | /* | ||
979 | * pg_init must call this when it has completed its initialisation | 1022 | * pg_init must call this when it has completed its initialisation |
980 | */ | 1023 | */ |
981 | void dm_pg_init_complete(struct dm_path *path, unsigned err_flags) | 1024 | 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) | |||
985 | struct multipath *m = pg->m; | 1028 | struct multipath *m = pg->m; |
986 | unsigned long flags; | 1029 | unsigned long flags; |
987 | 1030 | ||
988 | /* We insist on failing the path if the PG is already bypassed. */ | 1031 | /* |
989 | if (err_flags && pg->bypassed) | 1032 | * If requested, retry pg_init until maximum number of retries exceeded. |
1033 | * If retry not requested and PG already bypassed, always fail the path. | ||
1034 | */ | ||
1035 | if (err_flags & MP_RETRY) { | ||
1036 | if (pg_init_limit_reached(m, pgpath)) | ||
1037 | err_flags |= MP_FAIL_PATH; | ||
1038 | } else if (err_flags && pg->bypassed) | ||
990 | err_flags |= MP_FAIL_PATH; | 1039 | err_flags |= MP_FAIL_PATH; |
991 | 1040 | ||
992 | if (err_flags & MP_FAIL_PATH) | 1041 | if (err_flags & MP_FAIL_PATH) |
@@ -996,7 +1045,7 @@ void dm_pg_init_complete(struct dm_path *path, unsigned err_flags) | |||
996 | bypass_pg(m, pg, 1); | 1045 | bypass_pg(m, pg, 1); |
997 | 1046 | ||
998 | spin_lock_irqsave(&m->lock, flags); | 1047 | spin_lock_irqsave(&m->lock, flags); |
999 | if (err_flags) { | 1048 | if (err_flags & ~MP_RETRY) { |
1000 | m->current_pgpath = NULL; | 1049 | m->current_pgpath = NULL; |
1001 | m->current_pg = NULL; | 1050 | m->current_pg = NULL; |
1002 | } else if (!m->pg_init_required) | 1051 | } else if (!m->pg_init_required) |
@@ -1148,11 +1197,15 @@ static int multipath_status(struct dm_target *ti, status_type_t type, | |||
1148 | 1197 | ||
1149 | /* Features */ | 1198 | /* Features */ |
1150 | if (type == STATUSTYPE_INFO) | 1199 | if (type == STATUSTYPE_INFO) |
1151 | DMEMIT("1 %u ", m->queue_size); | 1200 | DMEMIT("2 %u %u ", m->queue_size, m->pg_init_count); |
1152 | else if (m->queue_if_no_path) | 1201 | else { |
1153 | DMEMIT("1 queue_if_no_path "); | 1202 | DMEMIT("%u ", m->queue_if_no_path + |
1154 | else | 1203 | (m->pg_init_retries > 0) * 2); |
1155 | DMEMIT("0 "); | 1204 | if (m->queue_if_no_path) |
1205 | DMEMIT("queue_if_no_path "); | ||
1206 | if (m->pg_init_retries) | ||
1207 | DMEMIT("pg_init_retries %u ", m->pg_init_retries); | ||
1208 | } | ||
1156 | 1209 | ||
1157 | if (hwh->type && hwh->type->status) | 1210 | if (hwh->type && hwh->type->status) |
1158 | sz += hwh->type->status(hwh, type, result + sz, maxlen - sz); | 1211 | sz += hwh->type->status(hwh, type, result + sz, maxlen - sz); |