b05147c356
Change patch format to remove Git version * Patches 0001-0122 only have the patch format modified Update to the head of the upstream staging branch plus redhat patches * Patches 0123-0134 & 1036-0142 are from the upstream staging branch * Patches 0143-1046 have been submitted upstream * Patch 0156 is a Red Hat only patch. Red Hat udev rules set ID_SERIAL from 60-persistent-storage.rules instead of 55-scsi-sg3_id.rules. Multipath's parse_vpd_pg83() function needs to match the ID_SERIAL value from udev. Rename files * Previous patches 0123-0132 are now patches 1035 & 0147-0155
454 lines
16 KiB
Diff
454 lines
16 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Benjamin Marzinski <bmarzins@redhat.com>
|
|
Date: Wed, 24 Feb 2021 00:33:22 -0600
|
|
Subject: [PATCH] multipathd: add recheck_wwid option to verify the path wwid
|
|
|
|
There are cases where the wwid of a path changes due to LUN remapping
|
|
without triggering uevent for the changed path. Multipathd has no method
|
|
for trying to catch these cases, and corruption has resulted because of
|
|
it.
|
|
|
|
In order to have a better chance at catching these cases, multipath now
|
|
has a recheck_wwid option. If this is set to "yes", when a failed path
|
|
has become active again, multipathd will recheck its wwid. If multipathd
|
|
notices that a path's wwid has changed, it will remove and re-add the
|
|
path, just like the existing wwid checking code for change events does.
|
|
In cases where the no uevent occurs, both the udev database entry and
|
|
sysfs will have the old wwid, so the only way to get a current wwid is
|
|
to ask the device directly. Currently multipath only has code to
|
|
directly get the wwid for scsi devices, so this option only effects scsi
|
|
devices, and they must be configured to be able to use the uid_fallback
|
|
methods. To make sure both the sysfs and udev database values are
|
|
updated, multipathd triggers a both a rescan of the device and a udev
|
|
add event.
|
|
|
|
Co-developed-by: Chongyun Wu <wucy11@chinatelecom.cn>
|
|
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
|
|
Reviewed-by: Martin Wilck <mwilck@suse.com>Reviewed-by: Martin Wilck <mwilck@suse.com>
|
|
---
|
|
libmultipath/config.c | 2 +
|
|
libmultipath/config.h | 2 +
|
|
libmultipath/configure.c | 4 +-
|
|
libmultipath/configure.h | 2 +
|
|
libmultipath/defaults.h | 1 +
|
|
libmultipath/dict.c | 11 +++++
|
|
libmultipath/discovery.c | 7 ++-
|
|
libmultipath/discovery.h | 1 +
|
|
libmultipath/libmultipath.version | 6 +++
|
|
libmultipath/propsel.c | 21 +++++++++
|
|
libmultipath/propsel.h | 1 +
|
|
libmultipath/structs.h | 7 +++
|
|
multipath/multipath.conf.5 | 14 ++++++
|
|
multipathd/cli_handlers.c | 9 ++++
|
|
multipathd/main.c | 78 +++++++++++++++++++++++++++++++
|
|
multipathd/main.h | 2 +
|
|
16 files changed, 164 insertions(+), 4 deletions(-)
|
|
|
|
diff --git a/libmultipath/config.c b/libmultipath/config.c
|
|
index be310159..30046a17 100644
|
|
--- a/libmultipath/config.c
|
|
+++ b/libmultipath/config.c
|
|
@@ -436,6 +436,7 @@ merge_hwe (struct hwentry * dst, struct hwentry * src)
|
|
merge_num(max_sectors_kb);
|
|
merge_num(ghost_delay);
|
|
merge_num(all_tg_pt);
|
|
+ merge_num(recheck_wwid);
|
|
merge_num(vpd_vendor_id);
|
|
merge_num(san_path_err_threshold);
|
|
merge_num(san_path_err_forget_rate);
|
|
@@ -867,6 +868,7 @@ int _init_config (const char *file, struct config *conf)
|
|
conf->remove_retries = 0;
|
|
conf->ghost_delay = DEFAULT_GHOST_DELAY;
|
|
conf->all_tg_pt = DEFAULT_ALL_TG_PT;
|
|
+ conf->recheck_wwid = DEFAULT_RECHECK_WWID;
|
|
/*
|
|
* preload default hwtable
|
|
*/
|
|
diff --git a/libmultipath/config.h b/libmultipath/config.h
|
|
index 9ce37f16..933fe0d1 100644
|
|
--- a/libmultipath/config.h
|
|
+++ b/libmultipath/config.h
|
|
@@ -83,6 +83,7 @@ struct hwentry {
|
|
int ghost_delay;
|
|
int all_tg_pt;
|
|
int vpd_vendor_id;
|
|
+ int recheck_wwid;
|
|
char * bl_product;
|
|
};
|
|
|
|
@@ -187,6 +188,7 @@ struct config {
|
|
int marginal_pathgroups;
|
|
int skip_delegate;
|
|
unsigned int sequence_nr;
|
|
+ int recheck_wwid;
|
|
|
|
char * multipath_dir;
|
|
char * selector;
|
|
diff --git a/libmultipath/configure.c b/libmultipath/configure.c
|
|
index 598efe05..6ca1f4bb 100644
|
|
--- a/libmultipath/configure.c
|
|
+++ b/libmultipath/configure.c
|
|
@@ -527,8 +527,8 @@ get_udev_for_mpp(const struct multipath *mpp)
|
|
return udd;
|
|
}
|
|
|
|
-static void trigger_partitions_udev_change(struct udev_device *dev,
|
|
- const char *action, int len)
|
|
+void trigger_partitions_udev_change(struct udev_device *dev,
|
|
+ const char *action, int len)
|
|
{
|
|
struct udev_enumerate *part_enum;
|
|
struct udev_list_entry *item;
|
|
diff --git a/libmultipath/configure.h b/libmultipath/configure.h
|
|
index 6b23ccbb..70cf77a3 100644
|
|
--- a/libmultipath/configure.h
|
|
+++ b/libmultipath/configure.h
|
|
@@ -58,3 +58,5 @@ int get_refwwid (enum mpath_cmds cmd, const char *dev, enum devtypes dev_type,
|
|
vector pathvec, char **wwid);
|
|
struct udev_device *get_udev_device(const char *dev, enum devtypes dev_type);
|
|
void trigger_paths_udev_change(struct multipath *mpp, bool is_mpath);
|
|
+void trigger_partitions_udev_change(struct udev_device *dev, const char *action,
|
|
+ int len);
|
|
diff --git a/libmultipath/defaults.h b/libmultipath/defaults.h
|
|
index 947ba467..c27946c7 100644
|
|
--- a/libmultipath/defaults.h
|
|
+++ b/libmultipath/defaults.h
|
|
@@ -53,6 +53,7 @@
|
|
#define DEFAULT_FIND_MULTIPATHS_TIMEOUT -10
|
|
#define DEFAULT_UNKNOWN_FIND_MULTIPATHS_TIMEOUT 1
|
|
#define DEFAULT_ALL_TG_PT ALL_TG_PT_OFF
|
|
+#define DEFAULT_RECHECK_WWID RECHECK_WWID_OFF
|
|
/* Enable no foreign libraries by default */
|
|
#define DEFAULT_ENABLE_FOREIGN "NONE"
|
|
|
|
diff --git a/libmultipath/dict.c b/libmultipath/dict.c
|
|
index bab96146..dd08abf5 100644
|
|
--- a/libmultipath/dict.c
|
|
+++ b/libmultipath/dict.c
|
|
@@ -1401,6 +1401,14 @@ declare_hw_snprint(all_tg_pt, print_yes_no_undef)
|
|
declare_def_handler(marginal_pathgroups, set_yes_no)
|
|
declare_def_snprint(marginal_pathgroups, print_yes_no)
|
|
|
|
+declare_def_handler(recheck_wwid, set_yes_no_undef)
|
|
+declare_def_snprint_defint(recheck_wwid, print_yes_no_undef, DEFAULT_RECHECK_WWID)
|
|
+declare_ovr_handler(recheck_wwid, set_yes_no_undef)
|
|
+declare_ovr_snprint(recheck_wwid, print_yes_no_undef)
|
|
+declare_hw_handler(recheck_wwid, set_yes_no_undef)
|
|
+declare_hw_snprint(recheck_wwid, print_yes_no_undef)
|
|
+
|
|
+
|
|
static int
|
|
def_uxsock_timeout_handler(struct config *conf, vector strvec)
|
|
{
|
|
@@ -1819,6 +1827,7 @@ init_keywords(vector keywords)
|
|
install_keyword("enable_foreign", &def_enable_foreign_handler,
|
|
&snprint_def_enable_foreign);
|
|
install_keyword("marginal_pathgroups", &def_marginal_pathgroups_handler, &snprint_def_marginal_pathgroups);
|
|
+ install_keyword("recheck_wwid", &def_recheck_wwid_handler, &snprint_def_recheck_wwid);
|
|
__deprecated install_keyword("default_selector", &def_selector_handler, NULL);
|
|
__deprecated install_keyword("default_path_grouping_policy", &def_pgpolicy_handler, NULL);
|
|
__deprecated install_keyword("default_uid_attribute", &def_uid_attribute_handler, NULL);
|
|
@@ -1908,6 +1917,7 @@ init_keywords(vector keywords)
|
|
install_keyword("ghost_delay", &hw_ghost_delay_handler, &snprint_hw_ghost_delay);
|
|
install_keyword("all_tg_pt", &hw_all_tg_pt_handler, &snprint_hw_all_tg_pt);
|
|
install_keyword("vpd_vendor", &hw_vpd_vendor_handler, &snprint_hw_vpd_vendor);
|
|
+ install_keyword("recheck_wwid", &hw_recheck_wwid_handler, &snprint_hw_recheck_wwid);
|
|
install_sublevel_end();
|
|
|
|
install_keyword_root("overrides", &overrides_handler);
|
|
@@ -1949,6 +1959,7 @@ init_keywords(vector keywords)
|
|
install_keyword("max_sectors_kb", &ovr_max_sectors_kb_handler, &snprint_ovr_max_sectors_kb);
|
|
install_keyword("ghost_delay", &ovr_ghost_delay_handler, &snprint_ovr_ghost_delay);
|
|
install_keyword("all_tg_pt", &ovr_all_tg_pt_handler, &snprint_ovr_all_tg_pt);
|
|
+ install_keyword("recheck_wwid", &ovr_recheck_wwid_handler, &snprint_ovr_recheck_wwid);
|
|
|
|
install_keyword_root("multipaths", &multipaths_handler);
|
|
install_keyword_multi("multipath", &multipath_handler, NULL);
|
|
diff --git a/libmultipath/discovery.c b/libmultipath/discovery.c
|
|
index 40727fa3..f216a724 100644
|
|
--- a/libmultipath/discovery.c
|
|
+++ b/libmultipath/discovery.c
|
|
@@ -2127,7 +2127,7 @@ static ssize_t uid_fallback(struct path *pp, int path_state,
|
|
return len;
|
|
}
|
|
|
|
-static bool has_uid_fallback(struct path *pp)
|
|
+bool has_uid_fallback(struct path *pp)
|
|
{
|
|
/*
|
|
* Falling back to direct WWID determination is dangerous
|
|
@@ -2162,6 +2162,7 @@ get_uid (struct path * pp, int path_state, struct udev_device *udev,
|
|
conf = get_multipath_config();
|
|
pthread_cleanup_push(put_multipath_config, conf);
|
|
select_getuid(conf, pp);
|
|
+ select_recheck_wwid(conf, pp);
|
|
pthread_cleanup_pop(1);
|
|
}
|
|
|
|
@@ -2293,8 +2294,10 @@ int pathinfo(struct path *pp, struct config *conf, int mask)
|
|
|
|
if (mask & DI_BLACKLIST && mask & DI_SYSFS) {
|
|
/* uid_attribute is required for filter_property() */
|
|
- if (pp->udev && !pp->uid_attribute)
|
|
+ if (pp->udev && !pp->uid_attribute) {
|
|
select_getuid(conf, pp);
|
|
+ select_recheck_wwid(conf, pp);
|
|
+ }
|
|
|
|
if (filter_property(conf, pp->udev, 4, pp->uid_attribute) > 0 ||
|
|
filter_device(conf->blist_device, conf->elist_device,
|
|
diff --git a/libmultipath/discovery.h b/libmultipath/discovery.h
|
|
index d3193daf..a5446b4d 100644
|
|
--- a/libmultipath/discovery.h
|
|
+++ b/libmultipath/discovery.h
|
|
@@ -54,6 +54,7 @@ ssize_t sysfs_get_inquiry(struct udev_device *udev,
|
|
unsigned char *buff, size_t len);
|
|
int sysfs_get_asymmetric_access_state(struct path *pp,
|
|
char *buff, int buflen);
|
|
+bool has_uid_fallback(struct path *pp);
|
|
int get_uid(struct path * pp, int path_state, struct udev_device *udev,
|
|
int allow_fallback);
|
|
bool is_vpd_page_supported(int fd, int pg);
|
|
diff --git a/libmultipath/libmultipath.version b/libmultipath/libmultipath.version
|
|
index 2228f4ec..e9b4608f 100644
|
|
--- a/libmultipath/libmultipath.version
|
|
+++ b/libmultipath/libmultipath.version
|
|
@@ -280,3 +280,9 @@ LIBMULTIPATH_4.4.0 {
|
|
global:
|
|
get_next_string;
|
|
} LIBMULTIPATH_4.3.0;
|
|
+
|
|
+LIBMULITIPATH_4.5.0 {
|
|
+global:
|
|
+ get_vpd_sgio;
|
|
+ trigger_partitions_udev_change;
|
|
+} LIBMULTIPATH_4.4.0;
|
|
diff --git a/libmultipath/propsel.c b/libmultipath/propsel.c
|
|
index f771a830..b7b33791 100644
|
|
--- a/libmultipath/propsel.c
|
|
+++ b/libmultipath/propsel.c
|
|
@@ -581,6 +581,27 @@ out:
|
|
return 0;
|
|
}
|
|
|
|
+/* must be called after select_getuid */
|
|
+int select_recheck_wwid(struct config *conf, struct path * pp)
|
|
+{
|
|
+ const char *origin;
|
|
+
|
|
+ pp_set_ovr(recheck_wwid);
|
|
+ pp_set_hwe(recheck_wwid);
|
|
+ pp_set_conf(recheck_wwid);
|
|
+ pp_set_default(recheck_wwid, DEFAULT_RECHECK_WWID);
|
|
+out:
|
|
+ if (pp->recheck_wwid == RECHECK_WWID_ON &&
|
|
+ (pp->bus != SYSFS_BUS_SCSI || pp->getuid != NULL ||
|
|
+ !has_uid_fallback(pp))) {
|
|
+ pp->recheck_wwid = RECHECK_WWID_OFF;
|
|
+ origin = "(setting: unsupported by device type/config)";
|
|
+ }
|
|
+ condlog(3, "%s: recheck_wwid = %i %s", pp->dev, pp->recheck_wwid,
|
|
+ origin);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
void
|
|
detect_prio(struct config *conf, struct path * pp)
|
|
{
|
|
diff --git a/libmultipath/propsel.h b/libmultipath/propsel.h
|
|
index a68bacf0..72a7e33c 100644
|
|
--- a/libmultipath/propsel.h
|
|
+++ b/libmultipath/propsel.h
|
|
@@ -7,6 +7,7 @@ int select_features (struct config *conf, struct multipath * mp);
|
|
int select_hwhandler (struct config *conf, struct multipath * mp);
|
|
int select_checker(struct config *conf, struct path *pp);
|
|
int select_getuid (struct config *conf, struct path * pp);
|
|
+int select_recheck_wwid(struct config *conf, struct path * pp);
|
|
int select_prio (struct config *conf, struct path * pp);
|
|
int select_find_multipaths_timeout(struct config *conf, struct path *pp);
|
|
int select_no_path_retry(struct config *conf, struct multipath *mp);
|
|
diff --git a/libmultipath/structs.h b/libmultipath/structs.h
|
|
index d6ff6762..c8447e56 100644
|
|
--- a/libmultipath/structs.h
|
|
+++ b/libmultipath/structs.h
|
|
@@ -242,6 +242,12 @@ enum eh_deadline_states {
|
|
EH_DEADLINE_ZERO = UOZ_ZERO,
|
|
};
|
|
|
|
+enum recheck_wwid_states {
|
|
+ RECHECK_WWID_UNDEF = YNU_UNDEF,
|
|
+ RECHECK_WWID_OFF = YNU_NO,
|
|
+ RECHECK_WWID_ON = YNU_YES,
|
|
+};
|
|
+
|
|
struct vpd_vendor_page {
|
|
int pg;
|
|
const char *name;
|
|
@@ -316,6 +322,7 @@ struct path {
|
|
int find_multipaths_timeout;
|
|
int marginal;
|
|
int vpd_vendor_id;
|
|
+ int recheck_wwid;
|
|
/* configlet pointers */
|
|
vector hwe;
|
|
struct gen_path generic_path;
|
|
diff --git a/multipath/multipath.conf.5 b/multipath/multipath.conf.5
|
|
index 8ef3a747..37030765 100644
|
|
--- a/multipath/multipath.conf.5
|
|
+++ b/multipath/multipath.conf.5
|
|
@@ -1273,6 +1273,20 @@ The default is: \fB\(dqNONE\(dq\fR
|
|
.RE
|
|
.
|
|
.
|
|
+.TP
|
|
+.B recheck_wwid
|
|
+If set to \fIyes\fR, when a failed path is restored, its wwid is rechecked. If
|
|
+the wwid has changed, the path is removed from the current multipath device,
|
|
+and re-added as a new path. Multipathd will also recheck a path's wwid if it is
|
|
+manually re-added. This option only works for SCSI devices that are configured
|
|
+to use the default uid_attribute, \fIID_SERIAL\fR, or sysfs for getting their
|
|
+wwid.
|
|
+.RS
|
|
+.TP
|
|
+The default is \fBno\fR
|
|
+.RE
|
|
+.
|
|
+.
|
|
|
|
.
|
|
.\" ----------------------------------------------------------------------------
|
|
diff --git a/multipathd/cli_handlers.c b/multipathd/cli_handlers.c
|
|
index 54635738..7f3e61f6 100644
|
|
--- a/multipathd/cli_handlers.c
|
|
+++ b/multipathd/cli_handlers.c
|
|
@@ -715,6 +715,15 @@ cli_add_path (void * v, char ** reply, int * len, void * data)
|
|
pp = find_path_by_dev(vecs->pathvec, param);
|
|
if (pp && pp->initialized != INIT_REMOVED) {
|
|
condlog(2, "%s: path already in pathvec", param);
|
|
+
|
|
+ if (pp->recheck_wwid == RECHECK_WWID_ON &&
|
|
+ check_path_wwid_change(pp)) {
|
|
+ condlog(0, "%s: wwid changed. Removing device",
|
|
+ pp->dev);
|
|
+ handle_path_wwid_change(pp, vecs);
|
|
+ return 1;
|
|
+ }
|
|
+
|
|
if (pp->mpp)
|
|
return 0;
|
|
} else if (pp) {
|
|
diff --git a/multipathd/main.c b/multipathd/main.c
|
|
index 19679848..637a53bf 100644
|
|
--- a/multipathd/main.c
|
|
+++ b/multipathd/main.c
|
|
@@ -823,6 +823,73 @@ ev_remove_map (char * devname, char * alias, int minor, struct vectors * vecs)
|
|
return flush_map(mpp, vecs, 0);
|
|
}
|
|
|
|
+static void
|
|
+rescan_path(struct udev_device *parent)
|
|
+{
|
|
+ while(parent) {
|
|
+ const char *subsys = udev_device_get_subsystem(parent);
|
|
+ if (subsys && !strncmp(subsys, "scsi", 4))
|
|
+ break;
|
|
+ parent = udev_device_get_parent(parent);
|
|
+ }
|
|
+ if (parent)
|
|
+ sysfs_attr_set_value(parent, "rescan", "1", strlen("1"));
|
|
+}
|
|
+
|
|
+void
|
|
+handle_path_wwid_change(struct path *pp, struct vectors *vecs)
|
|
+{
|
|
+ struct udev_device *udd;
|
|
+
|
|
+ if (!pp || !pp->udev)
|
|
+ return;
|
|
+
|
|
+ udd = udev_device_ref(pp->udev);
|
|
+ if (ev_remove_path(pp, vecs, 1) != 0 && pp->mpp) {
|
|
+ pp->dmstate = PSTATE_FAILED;
|
|
+ dm_fail_path(pp->mpp->alias, pp->dev_t);
|
|
+ }
|
|
+ rescan_path(udd);
|
|
+ sysfs_attr_set_value(udd, "uevent", "add", strlen("add"));
|
|
+ trigger_partitions_udev_change(udd, "add", strlen("add"));
|
|
+ udev_device_unref(udd);
|
|
+}
|
|
+
|
|
+bool
|
|
+check_path_wwid_change(struct path *pp)
|
|
+{
|
|
+ char wwid[WWID_SIZE];
|
|
+ int len = 0;
|
|
+ size_t i;
|
|
+
|
|
+ if (!strlen(pp->wwid))
|
|
+ return false;
|
|
+
|
|
+ /* Get the real fresh device wwid by sgio. sysfs still has old
|
|
+ * data, so only get_vpd_sgio will work to get the new wwid */
|
|
+ len = get_vpd_sgio(pp->fd, 0x83, 0, wwid, WWID_SIZE);
|
|
+
|
|
+ if (len <= 0) {
|
|
+ condlog(2, "%s: failed to check wwid by sgio: len = %d",
|
|
+ pp->dev, len);
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ /*Strip any trailing blanks */
|
|
+ for (i = strlen(pp->wwid); i > 0 && pp->wwid[i-1] == ' '; i--);
|
|
+ /* no-op */
|
|
+ pp->wwid[i] = '\0';
|
|
+ condlog(4, "%s: Got wwid %s by sgio", pp->dev, wwid);
|
|
+
|
|
+ if (strncmp(wwid, pp->wwid, WWID_SIZE)) {
|
|
+ condlog(0, "%s: wwid '%s' doesn't match wwid '%s' from device",
|
|
+ pp->dev, pp->wwid, wwid);
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ return false;
|
|
+}
|
|
+
|
|
static int
|
|
uev_add_path (struct uevent *uev, struct vectors * vecs, int need_do_map)
|
|
{
|
|
@@ -1296,6 +1363,7 @@ uev_update_path (struct uevent *uev, struct vectors * vecs)
|
|
condlog(0, "%s: path wwid changed from '%s' to '%s'",
|
|
uev->kernel, wwid, pp->wwid);
|
|
ev_remove_path(pp, vecs, 1);
|
|
+ rescan_path(uev->udev);
|
|
needs_reinit = 1;
|
|
goto out;
|
|
} else {
|
|
@@ -2197,6 +2265,16 @@ check_path (struct vectors * vecs, struct path * pp, unsigned int ticks)
|
|
return 0;
|
|
set_no_path_retry(pp->mpp);
|
|
|
|
+ if (pp->recheck_wwid == RECHECK_WWID_ON &&
|
|
+ (newstate == PATH_UP || newstate == PATH_GHOST) &&
|
|
+ ((pp->state != PATH_UP && pp->state != PATH_GHOST) ||
|
|
+ pp->dmstate == PSTATE_FAILED) &&
|
|
+ check_path_wwid_change(pp)) {
|
|
+ condlog(0, "%s: path wwid change detected. Removing", pp->dev);
|
|
+ handle_path_wwid_change(pp, vecs);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
if ((newstate == PATH_UP || newstate == PATH_GHOST) &&
|
|
(san_path_check_enabled(pp->mpp) ||
|
|
marginal_path_check_enabled(pp->mpp))) {
|
|
diff --git a/multipathd/main.h b/multipathd/main.h
|
|
index 5abbe97b..ddd953f9 100644
|
|
--- a/multipathd/main.h
|
|
+++ b/multipathd/main.h
|
|
@@ -50,4 +50,6 @@ int update_multipath (struct vectors *vecs, char *mapname, int reset);
|
|
int reload_and_sync_map(struct multipath *mpp, struct vectors *vecs,
|
|
int refresh);
|
|
|
|
+void handle_path_wwid_change(struct path *pp, struct vectors *vecs);
|
|
+bool check_path_wwid_change(struct path *pp);
|
|
#endif /* MAIN_H */
|