From 785c99753e3ab3f6383ec07c72708994f58037c1 Mon Sep 17 00:00:00 2001 From: CentOS Sources Date: Jul 14 2020 01:12:51 +0000 Subject: import device-mapper-multipath-0.8.4-3.el8 --- diff --git a/SOURCES/0021-libmultipath-remove-_blacklist_exceptions-functions.patch b/SOURCES/0021-libmultipath-remove-_blacklist_exceptions-functions.patch new file mode 100644 index 0000000..1177dd5 --- /dev/null +++ b/SOURCES/0021-libmultipath-remove-_blacklist_exceptions-functions.patch @@ -0,0 +1,139 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Mon, 8 Jun 2020 14:27:51 -0500 +Subject: [PATCH] libmultipath: remove _blacklist_exceptions functions + +_blacklist_exceptions() and _blacklist_exceptions_device() are exactly +the same as _blacklist() and _blacklist_device(), so remove them, and +give the remaining functions to a more general name. + +Signed-off-by: Benjamin Marzinski +--- + libmultipath/blacklist.c | 62 ++++++++++------------------------------ + 1 file changed, 15 insertions(+), 47 deletions(-) + +diff --git a/libmultipath/blacklist.c b/libmultipath/blacklist.c +index d9691b17..04d3adb9 100644 +--- a/libmultipath/blacklist.c ++++ b/libmultipath/blacklist.c +@@ -101,21 +101,8 @@ int set_ble_device(vector blist, char * vendor, char * product, int origin) + return 0; + } + +-int +-_blacklist_exceptions (vector elist, const char * str) +-{ +- int i; +- struct blentry * ele; +- +- vector_foreach_slot (elist, ele, i) { +- if (!regexec(&ele->regex, str, 0, NULL, 0)) +- return 1; +- } +- return 0; +-} +- +-int +-_blacklist (vector blist, const char * str) ++static int ++match_reglist (vector blist, const char * str) + { + int i; + struct blentry * ble; +@@ -127,28 +114,9 @@ _blacklist (vector blist, const char * str) + return 0; + } + +-int +-_blacklist_exceptions_device(const struct _vector *elist, const char * vendor, +- const char * product) +-{ +- int i; +- struct blentry_device * ble; +- +- vector_foreach_slot (elist, ble, i) { +- if (!ble->vendor && !ble->product) +- continue; +- if ((!ble->vendor || +- !regexec(&ble->vendor_reg, vendor, 0, NULL, 0)) && +- (!ble->product || +- !regexec(&ble->product_reg, product, 0, NULL, 0))) +- return 1; +- } +- return 0; +-} +- +-int +-_blacklist_device (const struct _vector *blist, const char * vendor, +- const char * product) ++static int ++match_reglist_device (const struct _vector *blist, const char * vendor, ++ const char * product) + { + int i; + struct blentry_device * ble; +@@ -294,9 +262,9 @@ filter_device (vector blist, vector elist, char * vendor, char * product, + int r = MATCH_NOTHING; + + if (vendor && product) { +- if (_blacklist_exceptions_device(elist, vendor, product)) ++ if (match_reglist_device(elist, vendor, product)) + r = MATCH_DEVICE_BLIST_EXCEPT; +- else if (_blacklist_device(blist, vendor, product)) ++ else if (match_reglist_device(blist, vendor, product)) + r = MATCH_DEVICE_BLIST; + } + +@@ -310,9 +278,9 @@ filter_devnode (vector blist, vector elist, char * dev) + int r = MATCH_NOTHING; + + if (dev) { +- if (_blacklist_exceptions(elist, dev)) ++ if (match_reglist(elist, dev)) + r = MATCH_DEVNODE_BLIST_EXCEPT; +- else if (_blacklist(blist, dev)) ++ else if (match_reglist(blist, dev)) + r = MATCH_DEVNODE_BLIST; + } + +@@ -326,9 +294,9 @@ filter_wwid (vector blist, vector elist, char * wwid, char * dev) + int r = MATCH_NOTHING; + + if (wwid) { +- if (_blacklist_exceptions(elist, wwid)) ++ if (match_reglist(elist, wwid)) + r = MATCH_WWID_BLIST_EXCEPT; +- else if (_blacklist(blist, wwid)) ++ else if (match_reglist(blist, wwid)) + r = MATCH_WWID_BLIST; + } + +@@ -345,9 +313,9 @@ filter_protocol(vector blist, vector elist, struct path * pp) + if (pp) { + snprint_path_protocol(buf, sizeof(buf), pp); + +- if (_blacklist_exceptions(elist, buf)) ++ if (match_reglist(elist, buf)) + r = MATCH_PROTOCOL_BLIST_EXCEPT; +- else if (_blacklist(blist, buf)) ++ else if (match_reglist(blist, buf)) + r = MATCH_PROTOCOL_BLIST; + } + +@@ -417,11 +385,11 @@ filter_property(struct config *conf, struct udev_device *udev, int lvl, + if (check_missing_prop && !strcmp(env, uid_attribute)) + uid_attr_seen = true; + +- if (_blacklist_exceptions(conf->elist_property, env)) { ++ if (match_reglist(conf->elist_property, env)) { + r = MATCH_PROPERTY_BLIST_EXCEPT; + break; + } +- if (_blacklist(conf->blist_property, env)) { ++ if (match_reglist(conf->blist_property, env)) { + r = MATCH_PROPERTY_BLIST; + break; + } +-- +2.17.2 + diff --git a/SOURCES/0022-libmultipath-fix-parser-issue-with-comments-in-strin.patch b/SOURCES/0022-libmultipath-fix-parser-issue-with-comments-in-strin.patch new file mode 100644 index 0000000..898826c --- /dev/null +++ b/SOURCES/0022-libmultipath-fix-parser-issue-with-comments-in-strin.patch @@ -0,0 +1,95 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Mon, 8 Jun 2020 20:23:56 -0500 +Subject: [PATCH] libmultipath: fix parser issue with comments in strings + +If a quoted string starts with '#' or '!', the parser will stop +parsing the line, thinking that it's a comment. It should only +be checking for comments outside of quoted strings. Fixed this and +added unit tests to verify it. + +Signed-off-by: Benjamin Marzinski +--- + libmultipath/parser.c | 4 +++- + tests/parser.c | 42 ++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 45 insertions(+), 1 deletion(-) + +diff --git a/libmultipath/parser.c b/libmultipath/parser.c +index a184511b..a7285a35 100644 +--- a/libmultipath/parser.c ++++ b/libmultipath/parser.c +@@ -300,8 +300,10 @@ alloc_strvec(char *string) + (isspace((int) *cp) || !isascii((int) *cp))) + && *cp != '\0') + cp++; +- if (*cp == '\0' || *cp == '!' || *cp == '#') ++ if (*cp == '\0' || ++ (!in_string && (*cp == '!' || *cp == '#'))) { + return strvec; ++ } + } + out: + vector_free(strvec); +diff --git a/tests/parser.c b/tests/parser.c +index 29859dac..5772391e 100644 +--- a/tests/parser.c ++++ b/tests/parser.c +@@ -440,6 +440,46 @@ static void test18(void **state) + free_strvec(v); + } + ++static void test19(void **state) ++{ ++#define QUOTED19 "!value" ++ vector v = alloc_strvec("key \"" QUOTED19 "\""); ++ char *val; ++ ++ assert_int_equal(VECTOR_SIZE(v), 4); ++ assert_string_equal(VECTOR_SLOT(v, 0), "key"); ++ assert_true(is_quote(VECTOR_SLOT(v, 1))); ++ assert_string_equal(VECTOR_SLOT(v, 2), QUOTED19); ++ assert_true(is_quote(VECTOR_SLOT(v, 3))); ++ assert_int_equal(validate_config_strvec(v, test_file), 0); ++ ++ val = set_value(v); ++ assert_string_equal(val, QUOTED19); ++ ++ free(val); ++ free_strvec(v); ++} ++ ++static void test20(void **state) ++{ ++#define QUOTED20 "#value" ++ vector v = alloc_strvec("key \"" QUOTED20 "\""); ++ char *val; ++ ++ assert_int_equal(VECTOR_SIZE(v), 4); ++ assert_string_equal(VECTOR_SLOT(v, 0), "key"); ++ assert_true(is_quote(VECTOR_SLOT(v, 1))); ++ assert_string_equal(VECTOR_SLOT(v, 2), QUOTED20); ++ assert_true(is_quote(VECTOR_SLOT(v, 3))); ++ assert_int_equal(validate_config_strvec(v, test_file), 0); ++ ++ val = set_value(v); ++ assert_string_equal(val, QUOTED20); ++ ++ free(val); ++ free_strvec(v); ++} ++ + int test_config_parser(void) + { + const struct CMUnitTest tests[] = { +@@ -461,6 +501,8 @@ int test_config_parser(void) + cmocka_unit_test(test16), + cmocka_unit_test(test17), + cmocka_unit_test(test18), ++ cmocka_unit_test(test19), ++ cmocka_unit_test(test20), + }; + return cmocka_run_group_tests(tests, setup, teardown); + } +-- +2.17.2 + diff --git a/SOURCES/0023-libmultipath-invert-regexes-that-start-with-exclamat.patch b/SOURCES/0023-libmultipath-invert-regexes-that-start-with-exclamat.patch new file mode 100644 index 0000000..575e771 --- /dev/null +++ b/SOURCES/0023-libmultipath-invert-regexes-that-start-with-exclamat.patch @@ -0,0 +1,435 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Mon, 8 Jun 2020 13:40:16 -0500 +Subject: [PATCH] libmultipath: invert regexes that start with exclamation + point + +The number of devices that multipath needs to blacklist keeps growing, +and the udev rules already have + +KERNEL!="sd*|dasd*|nvme*", GOTO="end_mpath" + +so they only work correctly with these device types. Instead of +individually blacklisting every type of device that can't be +multipathed, multipath's default blacklist should work like the udev +rule, and blacklist all devices that aren't scsi, dasd, or nvme. +Unfortunately, the c regex library doesn't support negative lookahead. +Instead, multipath should treat "!" at the beginning of +blacklist/exceptions regexes as inverse matching the rest of the regex. +If users need to match a literal '!' as the first character of their +regex, they can use "\!" instead. This allows multipath to change the +default devnode blacklist regex to "!^(sd[a-z]|dasd[a-z]|nvme[0-9])". + +Extra tests have been added to the blacklist unit tests to verify the +inverse matching code and the new default blacklist. + +Signed-off-by: Benjamin Marzinski +--- + libmultipath/blacklist.c | 41 +++++++++----- + libmultipath/blacklist.h | 3 + + multipath/multipath.conf.5 | 17 ++++-- + tests/blacklist.c | 110 +++++++++++++++++++++++++++++++++++++ + tests/test-lib.c | 2 +- + 5 files changed, 155 insertions(+), 18 deletions(-) + +diff --git a/libmultipath/blacklist.c b/libmultipath/blacklist.c +index 04d3adb9..0c58aa32 100644 +--- a/libmultipath/blacklist.c ++++ b/libmultipath/blacklist.c +@@ -15,9 +15,24 @@ + #include "structs_vec.h" + #include "print.h" + ++char *check_invert(char *str, bool *invert) ++{ ++ if (str[0] == '!') { ++ *invert = true; ++ return str + 1; ++ } ++ if (str[0] == '\\' && str[1] == '!') { ++ *invert = false; ++ return str + 1; ++ } ++ *invert = false; ++ return str; ++} ++ + int store_ble(vector blist, char * str, int origin) + { + struct blentry * ble; ++ char *regex_str; + + if (!str) + return 0; +@@ -30,7 +45,8 @@ int store_ble(vector blist, char * str, int origin) + if (!ble) + goto out; + +- if (regcomp(&ble->regex, str, REG_EXTENDED|REG_NOSUB)) ++ regex_str = check_invert(str, &ble->invert); ++ if (regcomp(&ble->regex, regex_str, REG_EXTENDED|REG_NOSUB)) + goto out1; + + if (!vector_alloc_slot(blist)) +@@ -66,6 +82,7 @@ int alloc_ble_device(vector blist) + int set_ble_device(vector blist, char * vendor, char * product, int origin) + { + struct blentry_device * ble; ++ char *regex_str; + + if (!blist) + return 1; +@@ -76,7 +93,8 @@ int set_ble_device(vector blist, char * vendor, char * product, int origin) + return 1; + + if (vendor) { +- if (regcomp(&ble->vendor_reg, vendor, ++ regex_str = check_invert(vendor, &ble->vendor_invert); ++ if (regcomp(&ble->vendor_reg, regex_str, + REG_EXTENDED|REG_NOSUB)) { + FREE(vendor); + if (product) +@@ -86,7 +104,8 @@ int set_ble_device(vector blist, char * vendor, char * product, int origin) + ble->vendor = vendor; + } + if (product) { +- if (regcomp(&ble->product_reg, product, ++ regex_str = check_invert(product, &ble->product_invert); ++ if (regcomp(&ble->product_reg, regex_str, + REG_EXTENDED|REG_NOSUB)) { + FREE(product); + if (vendor) { +@@ -108,7 +127,7 @@ match_reglist (vector blist, const char * str) + struct blentry * ble; + + vector_foreach_slot (blist, ble, i) { +- if (!regexec(&ble->regex, str, 0, NULL, 0)) ++ if (!!regexec(&ble->regex, str, 0, NULL, 0) == ble->invert) + return 1; + } + return 0; +@@ -125,9 +144,11 @@ match_reglist_device (const struct _vector *blist, const char * vendor, + if (!ble->vendor && !ble->product) + continue; + if ((!ble->vendor || +- !regexec(&ble->vendor_reg, vendor, 0, NULL, 0)) && ++ !!regexec(&ble->vendor_reg, vendor, 0, NULL, 0) == ++ ble->vendor_invert) && + (!ble->product || +- !regexec(&ble->product_reg, product, 0, NULL, 0))) ++ !!regexec(&ble->product_reg, product, 0, NULL, 0) == ++ ble->product_invert)) + return 1; + } + return 0; +@@ -160,13 +181,7 @@ setup_default_blist (struct config * conf) + char * str; + int i; + +- str = STRDUP("^(ram|zram|raw|loop|fd|md|dm-|sr|scd|st|dcssblk)[0-9]"); +- if (!str) +- return 1; +- if (store_ble(conf->blist_devnode, str, ORIGIN_DEFAULT)) +- return 1; +- +- str = STRDUP("^(td|hd|vd)[a-z]"); ++ str = STRDUP("!^(sd[a-z]|dasd[a-z]|nvme[0-9])"); + if (!str) + return 1; + if (store_ble(conf->blist_devnode, str, ORIGIN_DEFAULT)) +diff --git a/libmultipath/blacklist.h b/libmultipath/blacklist.h +index 2d721f60..4305857d 100644 +--- a/libmultipath/blacklist.h ++++ b/libmultipath/blacklist.h +@@ -20,6 +20,7 @@ + struct blentry { + char * str; + regex_t regex; ++ bool invert; + int origin; + }; + +@@ -28,6 +29,8 @@ struct blentry_device { + char * product; + regex_t vendor_reg; + regex_t product_reg; ++ bool vendor_invert; ++ bool product_invert; + int origin; + }; + +diff --git a/multipath/multipath.conf.5 b/multipath/multipath.conf.5 +index 3455b1cc..6dc26f10 100644 +--- a/multipath/multipath.conf.5 ++++ b/multipath/multipath.conf.5 +@@ -1248,6 +1248,16 @@ being handled by multipath-tools. + .LP + . + . ++In the \fIblacklist\fR and \fIblacklist_exceptions\fR sections, starting a ++quoted value with an exclamation mark \fB"!"\fR will invert the matching ++of the rest of the regular expression. For instance, \fB"!^sd[a-z]"\fR will ++match all values that do not start with \fB"sd[a-z]"\fR. The exclamation mark ++can be escaped \fB"\\!"\fR to match a literal \fB!\fR at the start of a ++regular expression. \fBNote:\fR The exclamation mark must be inside quotes, ++otherwise it will be treated as starting a comment. ++.LP ++. ++. + The \fIblacklist_exceptions\fR section is used to revert the actions of the + \fIblacklist\fR section. This allows one to selectively include ("whitelist") devices which + would normally be excluded via the \fIblacklist\fR section. A common usage is +@@ -1264,10 +1274,9 @@ unless explicitly stated. + Regular expression matching the device nodes to be excluded/included. + .RS + .PP +-The default \fIblacklist\fR consists of the regular expressions +-"^(ram|zram|raw|loop|fd|md|dm-|sr|scd|st|dcssblk)[0-9]" and +-"^(td|hd|vd)[a-z]". This causes virtual devices, non-disk devices, and some other +-device types to be excluded from multipath handling by default. ++The default \fIblacklist\fR consists of the regular expression ++\fB"!^(sd[a-z]|dasd[a-z]|nvme[0-9])"\fR. This causes all device types other ++than scsi, dasd, and nvme to be excluded from multipath handling by default. + .RE + .TP + .B wwid +diff --git a/tests/blacklist.c b/tests/blacklist.c +index cc8a9a4a..d20e97af 100644 +--- a/tests/blacklist.c ++++ b/tests/blacklist.c +@@ -60,20 +60,46 @@ __wrap_udev_list_entry_get_name(struct udev_list_entry *list_entry) + return *(const char **)list_entry; + } + ++vector elist_property_default; ++vector blist_devnode_default; + vector blist_devnode_sdb; ++vector blist_devnode_sdb_inv; + vector blist_all; + vector blist_device_foo_bar; ++vector blist_device_foo_inv_bar; ++vector blist_device_foo_bar_inv; + vector blist_device_all; + vector blist_wwid_xyzzy; ++vector blist_wwid_xyzzy_inv; + vector blist_protocol_fcp; ++vector blist_protocol_fcp_inv; + vector blist_property_wwn; ++vector blist_property_wwn_inv; + + static int setup(void **state) + { ++ struct config conf; ++ ++ memset(&conf, 0, sizeof(conf)); ++ conf.blist_devnode = vector_alloc(); ++ if (!conf.blist_devnode) ++ return -1; ++ conf.elist_property = vector_alloc(); ++ if (!conf.elist_property) ++ return -1; ++ if (setup_default_blist(&conf) != 0) ++ return -1; ++ elist_property_default = conf.elist_property; ++ blist_devnode_default = conf.blist_devnode; ++ + blist_devnode_sdb = vector_alloc(); + if (!blist_devnode_sdb || + store_ble(blist_devnode_sdb, strdup("sdb"), ORIGIN_CONFIG)) + return -1; ++ blist_devnode_sdb_inv = vector_alloc(); ++ if (!blist_devnode_sdb_inv || ++ store_ble(blist_devnode_sdb_inv, strdup("!sdb"), ORIGIN_CONFIG)) ++ return -1; + + blist_all = vector_alloc(); + if (!blist_all || store_ble(blist_all, strdup(".*"), ORIGIN_CONFIG)) +@@ -84,6 +110,18 @@ static int setup(void **state) + set_ble_device(blist_device_foo_bar, strdup("foo"), strdup("bar"), + ORIGIN_CONFIG)) + return -1; ++ blist_device_foo_inv_bar = vector_alloc(); ++ if (!blist_device_foo_inv_bar || ++ alloc_ble_device(blist_device_foo_inv_bar) || ++ set_ble_device(blist_device_foo_inv_bar, strdup("!foo"), ++ strdup("bar"), ORIGIN_CONFIG)) ++ return -1; ++ blist_device_foo_bar_inv = vector_alloc(); ++ if (!blist_device_foo_bar_inv || ++ alloc_ble_device(blist_device_foo_bar_inv) || ++ set_ble_device(blist_device_foo_bar_inv, strdup("foo"), ++ strdup("!bar"), ORIGIN_CONFIG)) ++ return -1; + + blist_device_all = vector_alloc(); + if (!blist_device_all || alloc_ble_device(blist_device_all) || +@@ -95,29 +133,50 @@ static int setup(void **state) + if (!blist_wwid_xyzzy || + store_ble(blist_wwid_xyzzy, strdup("xyzzy"), ORIGIN_CONFIG)) + return -1; ++ blist_wwid_xyzzy_inv = vector_alloc(); ++ if (!blist_wwid_xyzzy_inv || ++ store_ble(blist_wwid_xyzzy_inv, strdup("!xyzzy"), ORIGIN_CONFIG)) ++ return -1; + + blist_protocol_fcp = vector_alloc(); + if (!blist_protocol_fcp || + store_ble(blist_protocol_fcp, strdup("scsi:fcp"), ORIGIN_CONFIG)) + return -1; ++ blist_protocol_fcp_inv = vector_alloc(); ++ if (!blist_protocol_fcp_inv || ++ store_ble(blist_protocol_fcp_inv, strdup("!scsi:fcp"), ++ ORIGIN_CONFIG)) ++ return -1; + + blist_property_wwn = vector_alloc(); + if (!blist_property_wwn || + store_ble(blist_property_wwn, strdup("ID_WWN"), ORIGIN_CONFIG)) + return -1; ++ blist_property_wwn_inv = vector_alloc(); ++ if (!blist_property_wwn_inv || ++ store_ble(blist_property_wwn_inv, strdup("!ID_WWN"), ORIGIN_CONFIG)) ++ return -1; + + return 0; + } + + static int teardown(void **state) + { ++ free_blacklist(elist_property_default); ++ free_blacklist(blist_devnode_default); + free_blacklist(blist_devnode_sdb); ++ free_blacklist(blist_devnode_sdb_inv); + free_blacklist(blist_all); + free_blacklist_device(blist_device_foo_bar); ++ free_blacklist_device(blist_device_foo_inv_bar); ++ free_blacklist_device(blist_device_foo_bar_inv); + free_blacklist_device(blist_device_all); + free_blacklist(blist_wwid_xyzzy); ++ free_blacklist(blist_wwid_xyzzy_inv); + free_blacklist(blist_protocol_fcp); ++ free_blacklist(blist_protocol_fcp_inv); + free_blacklist(blist_property_wwn); ++ free_blacklist(blist_property_wwn_inv); + return 0; + } + +@@ -141,6 +200,11 @@ static void test_devnode_blacklist(void **state) + expect_condlog(3, "sdb: device node name blacklisted\n"); + assert_int_equal(filter_devnode(blist_devnode_sdb, NULL, "sdb"), + MATCH_DEVNODE_BLIST); ++ assert_int_equal(filter_devnode(blist_devnode_sdb_inv, NULL, "sdb"), ++ MATCH_NOTHING); ++ expect_condlog(3, "sdc: device node name blacklisted\n"); ++ assert_int_equal(filter_devnode(blist_devnode_sdb_inv, NULL, "sdc"), ++ MATCH_DEVNODE_BLIST); + } + + static void test_devnode_whitelist(void **state) +@@ -159,12 +223,39 @@ static void test_devnode_missing(void **state) + MATCH_NOTHING); + } + ++static void test_devnode_default(void **state) ++{ ++ assert_int_equal(filter_devnode(blist_devnode_default, NULL, "sdaa"), ++ MATCH_NOTHING); ++ assert_int_equal(filter_devnode(blist_devnode_default, NULL, "nvme0n1"), ++ MATCH_NOTHING); ++ assert_int_equal(filter_devnode(blist_devnode_default, NULL, "dasda"), ++ MATCH_NOTHING); ++ expect_condlog(3, "hda: device node name blacklisted\n"); ++ assert_int_equal(filter_devnode(blist_devnode_default, NULL, "hda"), ++ MATCH_DEVNODE_BLIST); ++} ++ + static void test_device_blacklist(void **state) + { + expect_condlog(3, "sdb: (foo:bar) vendor/product blacklisted\n"); + assert_int_equal(filter_device(blist_device_foo_bar, NULL, "foo", + "bar", "sdb"), + MATCH_DEVICE_BLIST); ++ assert_int_equal(filter_device(blist_device_foo_inv_bar, NULL, "foo", ++ "bar", "sdb"), ++ MATCH_NOTHING); ++ assert_int_equal(filter_device(blist_device_foo_bar_inv, NULL, "foo", ++ "bar", "sdb"), ++ MATCH_NOTHING); ++ expect_condlog(3, "sdb: (baz:bar) vendor/product blacklisted\n"); ++ assert_int_equal(filter_device(blist_device_foo_inv_bar, NULL, "baz", ++ "bar", "sdb"), ++ MATCH_DEVICE_BLIST); ++ expect_condlog(3, "sdb: (foo:baz) vendor/product blacklisted\n"); ++ assert_int_equal(filter_device(blist_device_foo_bar_inv, NULL, "foo", ++ "baz", "sdb"), ++ MATCH_DEVICE_BLIST); + } + + static void test_device_whitelist(void **state) +@@ -191,6 +282,11 @@ static void test_wwid_blacklist(void **state) + expect_condlog(3, "sdb: wwid xyzzy blacklisted\n"); + assert_int_equal(filter_wwid(blist_wwid_xyzzy, NULL, "xyzzy", "sdb"), + MATCH_WWID_BLIST); ++ assert_int_equal(filter_wwid(blist_wwid_xyzzy_inv, NULL, "xyzzy", ++ "sdb"), MATCH_NOTHING); ++ expect_condlog(3, "sdb: wwid plugh blacklisted\n"); ++ assert_int_equal(filter_wwid(blist_wwid_xyzzy_inv, NULL, "plugh", ++ "sdb"), MATCH_WWID_BLIST); + } + + static void test_wwid_whitelist(void **state) +@@ -218,6 +314,12 @@ static void test_protocol_blacklist(void **state) + expect_condlog(3, "sdb: protocol scsi:fcp blacklisted\n"); + assert_int_equal(filter_protocol(blist_protocol_fcp, NULL, &pp), + MATCH_PROTOCOL_BLIST); ++ assert_int_equal(filter_protocol(blist_protocol_fcp_inv, NULL, &pp), ++ MATCH_NOTHING); ++ pp.sg_id.proto_id = SCSI_PROTOCOL_ATA; ++ expect_condlog(3, "sdb: protocol scsi:ata blacklisted\n"); ++ assert_int_equal(filter_protocol(blist_protocol_fcp_inv, NULL, &pp), ++ MATCH_PROTOCOL_BLIST); + } + + static void test_protocol_whitelist(void **state) +@@ -245,10 +347,17 @@ static void test_protocol_missing(void **state) + static void test_property_blacklist(void **state) + { + static struct udev_device udev = { "sdb", { "ID_FOO", "ID_WWN", "ID_BAR", NULL } }; ++ static struct udev_device udev_inv = { "sdb", { "ID_WWN", NULL } }; + conf.blist_property = blist_property_wwn; + expect_condlog(3, "sdb: udev property ID_WWN blacklisted\n"); + assert_int_equal(filter_property(&conf, &udev, 3, "ID_SERIAL"), + MATCH_PROPERTY_BLIST); ++ conf.blist_property = blist_property_wwn_inv; ++ expect_condlog(3, "sdb: udev property ID_FOO blacklisted\n"); ++ assert_int_equal(filter_property(&conf, &udev, 3, "ID_SERIAL"), ++ MATCH_PROPERTY_BLIST); ++ assert_int_equal(filter_property(&conf, &udev_inv, 3, "ID_SERIAL"), ++ MATCH_NOTHING); + } + + /* the property check works different in that you check all the property +@@ -482,6 +591,7 @@ int test_blacklist(void) + cmocka_unit_test(test_devnode_blacklist), + cmocka_unit_test(test_devnode_whitelist), + cmocka_unit_test(test_devnode_missing), ++ cmocka_unit_test(test_devnode_default), + cmocka_unit_test(test_device_blacklist), + cmocka_unit_test(test_device_whitelist), + cmocka_unit_test(test_device_missing), +diff --git a/tests/test-lib.c b/tests/test-lib.c +index 59275163..08ff2d8d 100644 +--- a/tests/test-lib.c ++++ b/tests/test-lib.c +@@ -15,7 +15,7 @@ + #include "test-lib.h" + + const int default_mask = (DI_SYSFS|DI_BLACKLIST|DI_WWID|DI_CHECKER|DI_PRIO); +-const char default_devnode[] = "sdTEST"; ++const char default_devnode[] = "sdxTEST"; + const char default_wwid[] = "TEST-WWID"; + /* default_wwid should be a substring of default_wwid_1! */ + const char default_wwid_1[] = "TEST-WWID-1"; +-- +2.17.2 + diff --git a/SOURCES/0024-libmultipath-make-dm_get_map-status-return-codes-sym.patch b/SOURCES/0024-libmultipath-make-dm_get_map-status-return-codes-sym.patch new file mode 100644 index 0000000..e5159bb --- /dev/null +++ b/SOURCES/0024-libmultipath-make-dm_get_map-status-return-codes-sym.patch @@ -0,0 +1,322 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Tue, 23 Jun 2020 22:17:31 -0500 +Subject: [PATCH] libmultipath: make dm_get_map/status return codes symbolic + +dm_get_map() and dm_get_status() now use symbolic return codes. They +also differentiate between failing to get information from device-mapper +and not finding the requested device. These symboilc return codes are +also used by update_multipath_* functions. + +Signed-off-by: Benjamin Marzinski +--- + libmultipath/devmapper.c | 51 +++++++++++++++++++++++++------------- + libmultipath/devmapper.h | 6 +++++ + libmultipath/structs_vec.c | 45 +++++++++++++++++++-------------- + multipathd/main.c | 12 ++++----- + 4 files changed, 72 insertions(+), 42 deletions(-) + +diff --git a/libmultipath/devmapper.c b/libmultipath/devmapper.c +index 13a1cf53..f6204e5f 100644 +--- a/libmultipath/devmapper.c ++++ b/libmultipath/devmapper.c +@@ -525,36 +525,43 @@ int dm_map_present(const char * str) + + int dm_get_map(const char *name, unsigned long long *size, char *outparams) + { +- int r = 1; ++ int r = DMP_ERR; + struct dm_task *dmt; + uint64_t start, length; + char *target_type = NULL; + char *params = NULL; + + if (!(dmt = libmp_dm_task_create(DM_DEVICE_TABLE))) +- return 1; ++ return r; + + if (!dm_task_set_name(dmt, name)) + goto out; + + dm_task_no_open_count(dmt); + +- if (!dm_task_run(dmt)) ++ errno = 0; ++ if (!dm_task_run(dmt)) { ++ if (dm_task_get_errno(dmt) == ENXIO) ++ r = DMP_NOT_FOUND; + goto out; ++ } + ++ r = DMP_NOT_FOUND; + /* Fetch 1st target */ +- dm_get_next_target(dmt, NULL, &start, &length, +- &target_type, ¶ms); ++ if (dm_get_next_target(dmt, NULL, &start, &length, ++ &target_type, ¶ms) != NULL) ++ /* more than one target */ ++ goto out; + + if (size) + *size = length; + + if (!outparams) { +- r = 0; ++ r = DMP_OK; + goto out; + } + if (snprintf(outparams, PARAMS_SIZE, "%s", params) <= PARAMS_SIZE) +- r = 0; ++ r = DMP_OK; + out: + dm_task_destroy(dmt); + return r; +@@ -628,35 +635,45 @@ is_mpath_part(const char *part_name, const char *map_name) + + int dm_get_status(const char *name, char *outstatus) + { +- int r = 1; ++ int r = DMP_ERR; + struct dm_task *dmt; + uint64_t start, length; + char *target_type = NULL; + char *status = NULL; + + if (!(dmt = libmp_dm_task_create(DM_DEVICE_STATUS))) +- return 1; ++ return r; + + if (!dm_task_set_name(dmt, name)) + goto out; + + dm_task_no_open_count(dmt); + +- if (!dm_task_run(dmt)) ++ errno = 0; ++ if (!dm_task_run(dmt)) { ++ if (dm_task_get_errno(dmt) == ENXIO) ++ r = DMP_NOT_FOUND; + goto out; ++ } + ++ r = DMP_NOT_FOUND; + /* Fetch 1st target */ +- dm_get_next_target(dmt, NULL, &start, &length, +- &target_type, &status); ++ if (dm_get_next_target(dmt, NULL, &start, &length, ++ &target_type, &status) != NULL) ++ goto out; ++ ++ if (!target_type || strcmp(target_type, TGT_MPATH) != 0) ++ goto out; ++ + if (!status) { + condlog(2, "get null status."); + goto out; + } + + if (snprintf(outstatus, PARAMS_SIZE, "%s", status) <= PARAMS_SIZE) +- r = 0; ++ r = DMP_OK; + out: +- if (r) ++ if (r != DMP_OK) + condlog(0, "%s: error getting map status string", name); + + dm_task_destroy(dmt); +@@ -866,7 +883,7 @@ int _dm_flush_map (const char * mapname, int need_sync, int deferred_remove, + return 1; + + if (need_suspend && +- !dm_get_map(mapname, &mapsize, params) && ++ dm_get_map(mapname, &mapsize, params) == DMP_OK && + strstr(params, "queue_if_no_path")) { + if (!dm_queue_if_no_path(mapname, 0)) + queue_if_no_path = 1; +@@ -1075,7 +1092,7 @@ struct multipath *dm_get_multipath(const char *name) + if (!mpp->alias) + goto out; + +- if (dm_get_map(name, &mpp->size, NULL)) ++ if (dm_get_map(name, &mpp->size, NULL) != DMP_OK) + goto out; + + dm_get_uuid(name, mpp->wwid, WWID_SIZE); +@@ -1259,7 +1276,7 @@ do_foreach_partmaps (const char * mapname, + /* + * and we can fetch the map table from the kernel + */ +- !dm_get_map(names->name, &size, ¶ms[0]) && ++ dm_get_map(names->name, &size, ¶ms[0]) == DMP_OK && + + /* + * and the table maps over the multipath map +diff --git a/libmultipath/devmapper.h b/libmultipath/devmapper.h +index 7557a86b..adb55000 100644 +--- a/libmultipath/devmapper.h ++++ b/libmultipath/devmapper.h +@@ -27,6 +27,12 @@ + #define UUID_PREFIX "mpath-" + #define UUID_PREFIX_LEN (sizeof(UUID_PREFIX) - 1) + ++enum { ++ DMP_ERR, ++ DMP_OK, ++ DMP_NOT_FOUND, ++}; ++ + void dm_init(int verbosity); + void libmp_dm_init(void); + void libmp_udev_set_sync_support(int on); +diff --git a/libmultipath/structs_vec.c b/libmultipath/structs_vec.c +index 077f2e42..8137ea21 100644 +--- a/libmultipath/structs_vec.c ++++ b/libmultipath/structs_vec.c +@@ -196,43 +196,47 @@ extract_hwe_from_path(struct multipath * mpp) + int + update_multipath_table (struct multipath *mpp, vector pathvec, int is_daemon) + { ++ int r = DMP_ERR; + char params[PARAMS_SIZE] = {0}; + + if (!mpp) +- return 1; ++ return r; + +- if (dm_get_map(mpp->alias, &mpp->size, params)) { +- condlog(3, "%s: cannot get map", mpp->alias); +- return 1; ++ r = dm_get_map(mpp->alias, &mpp->size, params); ++ if (r != DMP_OK) { ++ condlog(3, "%s: %s", mpp->alias, (r == DMP_ERR)? "error getting table" : "map not present"); ++ return r; + } + + if (disassemble_map(pathvec, params, mpp, is_daemon)) { + condlog(3, "%s: cannot disassemble map", mpp->alias); +- return 1; ++ return DMP_ERR; + } + +- return 0; ++ return DMP_OK; + } + + int + update_multipath_status (struct multipath *mpp) + { ++ int r = DMP_ERR; + char status[PARAMS_SIZE] = {0}; + + if (!mpp) +- return 1; ++ return r; + +- if (dm_get_status(mpp->alias, status)) { +- condlog(3, "%s: cannot get status", mpp->alias); +- return 1; ++ r = dm_get_status(mpp->alias, status); ++ if (r != DMP_OK) { ++ condlog(3, "%s: %s", mpp->alias, (r == DMP_ERR)? "error getting status" : "map not present"); ++ return r; + } + + if (disassemble_status(status, mpp)) { + condlog(3, "%s: cannot disassemble status", mpp->alias); +- return 1; ++ return DMP_ERR; + } + +- return 0; ++ return DMP_OK; + } + + void sync_paths(struct multipath *mpp, vector pathvec) +@@ -264,10 +268,10 @@ int + update_multipath_strings(struct multipath *mpp, vector pathvec, int is_daemon) + { + struct pathgroup *pgp; +- int i; ++ int i, r = DMP_ERR; + + if (!mpp) +- return 1; ++ return r; + + update_mpp_paths(mpp, pathvec); + condlog(4, "%s: %s", mpp->alias, __FUNCTION__); +@@ -276,18 +280,21 @@ update_multipath_strings(struct multipath *mpp, vector pathvec, int is_daemon) + free_pgvec(mpp->pg, KEEP_PATHS); + mpp->pg = NULL; + +- if (update_multipath_table(mpp, pathvec, is_daemon)) +- return 1; ++ r = update_multipath_table(mpp, pathvec, is_daemon); ++ if (r != DMP_OK) ++ return r; ++ + sync_paths(mpp, pathvec); + +- if (update_multipath_status(mpp)) +- return 1; ++ r = update_multipath_status(mpp); ++ if (r != DMP_OK) ++ return r; + + vector_foreach_slot(mpp->pg, pgp, i) + if (pgp->paths) + path_group_prio_update(pgp); + +- return 0; ++ return DMP_OK; + } + + static void enter_recovery_mode(struct multipath *mpp) +diff --git a/multipathd/main.c b/multipathd/main.c +index 6b7db2c0..e3427d3d 100644 +--- a/multipathd/main.c ++++ b/multipathd/main.c +@@ -414,7 +414,7 @@ int __setup_multipath(struct vectors *vecs, struct multipath *mpp, + goto out; + } + +- if (update_multipath_strings(mpp, vecs->pathvec, 1)) { ++ if (update_multipath_strings(mpp, vecs->pathvec, 1) != DMP_OK) { + condlog(0, "%s: failed to setup multipath", mpp->alias); + goto out; + } +@@ -553,9 +553,9 @@ add_map_without_path (struct vectors *vecs, const char *alias) + mpp->mpe = find_mpe(conf->mptable, mpp->wwid); + put_multipath_config(conf); + +- if (update_multipath_table(mpp, vecs->pathvec, 1)) ++ if (update_multipath_table(mpp, vecs->pathvec, 1) != DMP_OK) + goto out; +- if (update_multipath_status(mpp)) ++ if (update_multipath_status(mpp) != DMP_OK) + goto out; + + if (!vector_alloc_slot(vecs->mpvec)) +@@ -1346,8 +1346,8 @@ map_discovery (struct vectors * vecs) + return 1; + + vector_foreach_slot (vecs->mpvec, mpp, i) +- if (update_multipath_table(mpp, vecs->pathvec, 1) || +- update_multipath_status(mpp)) { ++ if (update_multipath_table(mpp, vecs->pathvec, 1) != DMP_OK || ++ update_multipath_status(mpp) != DMP_OK) { + remove_map(mpp, vecs, 1); + i--; + } +@@ -2087,7 +2087,7 @@ check_path (struct vectors * vecs, struct path * pp, unsigned int ticks) + /* + * Synchronize with kernel state + */ +- if (update_multipath_strings(pp->mpp, vecs->pathvec, 1)) { ++ if (update_multipath_strings(pp->mpp, vecs->pathvec, 1) != DMP_OK) { + condlog(1, "%s: Could not synchronize with kernel state", + pp->dev); + pp->dmstate = PSTATE_UNDEF; +-- +2.17.2 + diff --git a/SOURCES/0025-multipathd-fix-check_path-errors-with-removed-map.patch b/SOURCES/0025-multipathd-fix-check_path-errors-with-removed-map.patch new file mode 100644 index 0000000..1ea59c7 --- /dev/null +++ b/SOURCES/0025-multipathd-fix-check_path-errors-with-removed-map.patch @@ -0,0 +1,116 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Thu, 11 Jun 2020 15:41:18 -0500 +Subject: [PATCH] multipathd: fix check_path errors with removed map + +If a multipath device is removed during, or immediately before the call +to check_path(), multipathd can behave incorrectly. A missing multpath +device will cause update_multipath_strings() to fail, setting +pp->dmstate to PSTATE_UNDEF. If the path is up, this state will cause +reinstate_path() to be called, which will also fail. This will trigger +a reload, restoring the recently removed device. + +If update_multipath_strings() fails because there is no multipath +device, check_path should just quit, since the remove dmevent and uevent +are likely already queued up. Also, I don't see any reason to reload the +multipath device if reinstate fails. This code was added by +fac68d7a99ef17d496079538a5c6836acd7911ab, which clamined that reinstate +could fail if the path was disabled. Looking through the current kernel +code, I can't see any reason why a reinstate would fail, where a reload +would help. If the path was missing from the multipath device, +update_multipath_strings() would already catch that, and quit +check_path() early, which make more sense to me than reloading does. + +Signed-off-by: Benjamin Marzinski +--- + multipathd/main.c | 44 +++++++++++++++++++------------------------- + 1 file changed, 19 insertions(+), 25 deletions(-) + +diff --git a/multipathd/main.c b/multipathd/main.c +index e3427d3d..1d9ce7f7 100644 +--- a/multipathd/main.c ++++ b/multipathd/main.c +@@ -1611,22 +1611,18 @@ fail_path (struct path * pp, int del_active) + /* + * caller must have locked the path list before calling that function + */ +-static int ++static void + reinstate_path (struct path * pp) + { +- int ret = 0; +- + if (!pp->mpp) +- return 0; ++ return; + +- if (dm_reinstate_path(pp->mpp->alias, pp->dev_t)) { ++ if (dm_reinstate_path(pp->mpp->alias, pp->dev_t)) + condlog(0, "%s: reinstate failed", pp->dev_t); +- ret = 1; +- } else { ++ else { + condlog(2, "%s: reinstated", pp->dev_t); + update_queue_mode_add_path(pp->mpp); + } +- return ret; + } + + static void +@@ -2087,9 +2083,16 @@ check_path (struct vectors * vecs, struct path * pp, unsigned int ticks) + /* + * Synchronize with kernel state + */ +- if (update_multipath_strings(pp->mpp, vecs->pathvec, 1) != DMP_OK) { +- condlog(1, "%s: Could not synchronize with kernel state", +- pp->dev); ++ ret = update_multipath_strings(pp->mpp, vecs->pathvec, 1); ++ if (ret != DMP_OK) { ++ if (ret == DMP_NOT_FOUND) { ++ /* multipath device missing. Likely removed */ ++ condlog(1, "%s: multipath device '%s' not found", ++ pp->dev, pp->mpp->alias); ++ return 0; ++ } else ++ condlog(1, "%s: Couldn't synchronize with kernel state", ++ pp->dev); + pp->dmstate = PSTATE_UNDEF; + } + /* if update_multipath_strings orphaned the path, quit early */ +@@ -2179,12 +2182,8 @@ check_path (struct vectors * vecs, struct path * pp, unsigned int ticks) + /* + * reinstate this path + */ +- if (!disable_reinstate && reinstate_path(pp)) { +- condlog(3, "%s: reload map", pp->dev); +- ev_add_path(pp, vecs, 1); +- pp->tick = 1; +- return 0; +- } ++ if (!disable_reinstate) ++ reinstate_path(pp); + new_path_up = 1; + + if (oldchkrstate != PATH_UP && oldchkrstate != PATH_GHOST) +@@ -2200,15 +2199,10 @@ check_path (struct vectors * vecs, struct path * pp, unsigned int ticks) + else if (newstate == PATH_UP || newstate == PATH_GHOST) { + if ((pp->dmstate == PSTATE_FAILED || + pp->dmstate == PSTATE_UNDEF) && +- !disable_reinstate) { ++ !disable_reinstate) + /* Clear IO errors */ +- if (reinstate_path(pp)) { +- condlog(3, "%s: reload map", pp->dev); +- ev_add_path(pp, vecs, 1); +- pp->tick = 1; +- return 0; +- } +- } else { ++ reinstate_path(pp); ++ else { + LOG_MSG(4, verbosity, pp); + if (pp->checkint != max_checkint) { + /* +-- +2.17.2 + diff --git a/SOURCES/0026-libmultipath-make-dm_flush_maps-only-return-0-on-suc.patch b/SOURCES/0026-libmultipath-make-dm_flush_maps-only-return-0-on-suc.patch new file mode 100644 index 0000000..55381a3 --- /dev/null +++ b/SOURCES/0026-libmultipath-make-dm_flush_maps-only-return-0-on-suc.patch @@ -0,0 +1,45 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Wed, 17 Jun 2020 13:31:37 -0500 +Subject: [PATCH] libmultipath: make dm_flush_maps only return 0 on success + +dm_flush_maps() returned both 0 and 1 on error, depending on which part +of the function it was in, but the caller was always treating 0 as a +success. Make dm_flush_maps() always return 1 on error and 0 on success. + +Signed-off-by: Benjamin Marzinski +--- + libmultipath/devmapper.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/libmultipath/devmapper.c b/libmultipath/devmapper.c +index f6204e5f..cda83ce4 100644 +--- a/libmultipath/devmapper.c ++++ b/libmultipath/devmapper.c +@@ -953,13 +953,13 @@ dm_flush_map_nopaths(const char * mapname, int deferred_remove) + + int dm_flush_maps (int retries) + { +- int r = 0; ++ int r = 1; + struct dm_task *dmt; + struct dm_names *names; + unsigned next = 0; + + if (!(dmt = libmp_dm_task_create (DM_DEVICE_LIST))) +- return 0; ++ return r; + + dm_task_no_open_count(dmt); + +@@ -972,6 +972,7 @@ int dm_flush_maps (int retries) + if (!names->dev) + goto out; + ++ r = 0; + do { + r |= dm_suspend_and_flush_map(names->name, retries); + next = names->next; +-- +2.17.2 + diff --git a/SOURCES/0027-multipathd-add-del-maps-multipathd-command.patch b/SOURCES/0027-multipathd-add-del-maps-multipathd-command.patch new file mode 100644 index 0000000..2f535e2 --- /dev/null +++ b/SOURCES/0027-multipathd-add-del-maps-multipathd-command.patch @@ -0,0 +1,160 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Mon, 15 Jun 2020 17:00:54 -0500 +Subject: [PATCH] multipathd: add "del maps" multipathd command + +This will flush all multipath devices. + +Signed-off-by: Benjamin Marzinski +--- + libmultipath/devmapper.c | 7 +++++-- + libmultipath/devmapper.h | 2 +- + multipath/main.c | 2 +- + multipathd/cli.c | 1 + + multipathd/cli_handlers.c | 19 +++++++++++++++++++ + multipathd/cli_handlers.h | 1 + + multipathd/main.c | 3 ++- + multipathd/main.h | 1 + + 8 files changed, 31 insertions(+), 5 deletions(-) + +diff --git a/libmultipath/devmapper.c b/libmultipath/devmapper.c +index cda83ce4..7f98bf9d 100644 +--- a/libmultipath/devmapper.c ++++ b/libmultipath/devmapper.c +@@ -951,7 +951,7 @@ dm_flush_map_nopaths(const char * mapname, int deferred_remove) + + #endif + +-int dm_flush_maps (int retries) ++int dm_flush_maps (int need_suspend, int retries) + { + int r = 1; + struct dm_task *dmt; +@@ -974,7 +974,10 @@ int dm_flush_maps (int retries) + + r = 0; + do { +- r |= dm_suspend_and_flush_map(names->name, retries); ++ if (need_suspend) ++ r |= dm_suspend_and_flush_map(names->name, retries); ++ else ++ r |= dm_flush_map(names->name); + next = names->next; + names = (void *) names + next; + } while (next); +diff --git a/libmultipath/devmapper.h b/libmultipath/devmapper.h +index adb55000..7e8812ad 100644 +--- a/libmultipath/devmapper.h ++++ b/libmultipath/devmapper.h +@@ -55,7 +55,7 @@ int dm_flush_map_nopaths(const char * mapname, int deferred_remove); + #define dm_suspend_and_flush_map(mapname, retries) \ + _dm_flush_map(mapname, 1, 0, 1, retries) + int dm_cancel_deferred_remove(struct multipath *mpp); +-int dm_flush_maps (int retries); ++int dm_flush_maps (int need_suspend, int retries); + int dm_fail_path(const char * mapname, char * path); + int dm_reinstate_path(const char * mapname, char * path); + int dm_queue_if_no_path(const char *mapname, int enable); +diff --git a/multipath/main.c b/multipath/main.c +index 78822ee1..7ab3102f 100644 +--- a/multipath/main.c ++++ b/multipath/main.c +@@ -1127,7 +1127,7 @@ main (int argc, char *argv[]) + goto out; + } + else if (conf->remove == FLUSH_ALL) { +- r = dm_flush_maps(retries) ? RTVL_FAIL : RTVL_OK; ++ r = dm_flush_maps(1, retries) ? RTVL_FAIL : RTVL_OK; + goto out; + } + while ((r = configure(conf, cmd, dev_type, dev)) == RTVL_RETRY) +diff --git a/multipathd/cli.c b/multipathd/cli.c +index 800c0fbe..bdc9fb10 100644 +--- a/multipathd/cli.c ++++ b/multipathd/cli.c +@@ -568,6 +568,7 @@ cli_init (void) { + add_handler(DEL+PATH, NULL); + add_handler(ADD+MAP, NULL); + add_handler(DEL+MAP, NULL); ++ add_handler(DEL+MAPS, NULL); + add_handler(SWITCH+MAP+GROUP, NULL); + add_handler(RECONFIGURE, NULL); + add_handler(SUSPEND+MAP, NULL); +diff --git a/multipathd/cli_handlers.c b/multipathd/cli_handlers.c +index 31c3d9fd..782bb003 100644 +--- a/multipathd/cli_handlers.c ++++ b/multipathd/cli_handlers.c +@@ -852,6 +852,25 @@ cli_del_map (void * v, char ** reply, int * len, void * data) + return rc; + } + ++int ++cli_del_maps (void *v, char **reply, int *len, void *data) ++{ ++ struct vectors * vecs = (struct vectors *)data; ++ struct multipath *mpp; ++ int i, ret = 0; ++ ++ condlog(2, "remove maps (operator)"); ++ vector_foreach_slot(vecs->mpvec, mpp, i) { ++ if (flush_map(mpp, vecs, 0)) ++ ret++; ++ else ++ i--; ++ } ++ /* flush any multipath maps that aren't currently known by multipathd */ ++ ret |= dm_flush_maps(0, 0); ++ return ret; ++} ++ + int + cli_reload(void *v, char **reply, int *len, void *data) + { +diff --git a/multipathd/cli_handlers.h b/multipathd/cli_handlers.h +index 0f451064..6f57b429 100644 +--- a/multipathd/cli_handlers.h ++++ b/multipathd/cli_handlers.h +@@ -26,6 +26,7 @@ int cli_add_path (void * v, char ** reply, int * len, void * data); + int cli_del_path (void * v, char ** reply, int * len, void * data); + int cli_add_map (void * v, char ** reply, int * len, void * data); + int cli_del_map (void * v, char ** reply, int * len, void * data); ++int cli_del_maps (void * v, char ** reply, int * len, void * data); + int cli_switch_group(void * v, char ** reply, int * len, void * data); + int cli_reconfigure(void * v, char ** reply, int * len, void * data); + int cli_resize(void * v, char ** reply, int * len, void * data); +diff --git a/multipathd/main.c b/multipathd/main.c +index 1d9ce7f7..1d0579e9 100644 +--- a/multipathd/main.c ++++ b/multipathd/main.c +@@ -631,7 +631,7 @@ sync_maps_state(vector mpvec) + sync_map_state(mpp); + } + +-static int ++int + flush_map(struct multipath * mpp, struct vectors * vecs, int nopaths) + { + int r; +@@ -1551,6 +1551,7 @@ uxlsnrloop (void * ap) + set_handler_callback(DEL+PATH, cli_del_path); + set_handler_callback(ADD+MAP, cli_add_map); + set_handler_callback(DEL+MAP, cli_del_map); ++ set_handler_callback(DEL+MAPS, cli_del_maps); + set_handler_callback(SWITCH+MAP+GROUP, cli_switch_group); + set_unlocked_handler_callback(RECONFIGURE, cli_reconfigure); + set_handler_callback(SUSPEND+MAP, cli_suspend); +diff --git a/multipathd/main.h b/multipathd/main.h +index 7bb8463f..5dff17e5 100644 +--- a/multipathd/main.h ++++ b/multipathd/main.h +@@ -28,6 +28,7 @@ int ev_add_path (struct path *, struct vectors *, int); + int ev_remove_path (struct path *, struct vectors *, int); + int ev_add_map (char *, const char *, struct vectors *); + int ev_remove_map (char *, char *, int, struct vectors *); ++int flush_map(struct multipath *, struct vectors *, int); + int set_config_state(enum daemon_status); + void * mpath_alloc_prin_response(int prin_sa); + int prin_do_scsi_ioctl(char *, int rq_servact, struct prin_resp * resp, +-- +2.17.2 + diff --git a/SOURCES/0028-multipath-make-flushing-maps-work-like-other-command.patch b/SOURCES/0028-multipath-make-flushing-maps-work-like-other-command.patch new file mode 100644 index 0000000..1674230 --- /dev/null +++ b/SOURCES/0028-multipath-make-flushing-maps-work-like-other-command.patch @@ -0,0 +1,103 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Mon, 15 Jun 2020 23:54:29 -0500 +Subject: [PATCH] multipath: make flushing maps work like other commands + +The config structure doesn't need a special variable just for removes. +Multipath can just use the cmd variable, like it does for the other +commands. + +Signed-off-by: Benjamin Marzinski +--- + libmultipath/config.h | 3 ++- + libmultipath/configure.h | 3 --- + multipath/main.c | 20 ++++++++++---------- + 3 files changed, 12 insertions(+), 14 deletions(-) + +diff --git a/libmultipath/config.h b/libmultipath/config.h +index 3368d8c9..4042eba6 100644 +--- a/libmultipath/config.h ++++ b/libmultipath/config.h +@@ -39,6 +39,8 @@ enum mpath_cmds { + CMD_ADD_WWID, + CMD_USABLE_PATHS, + CMD_DUMP_CONFIG, ++ CMD_FLUSH_ONE, ++ CMD_FLUSH_ALL, + }; + + enum force_reload_types { +@@ -143,7 +145,6 @@ struct config { + unsigned int max_checkint; + bool use_watchdog; + int pgfailback; +- int remove; + int rr_weight; + int no_path_retry; + int user_friendly_names; +diff --git a/libmultipath/configure.h b/libmultipath/configure.h +index d7509000..0e33bf40 100644 +--- a/libmultipath/configure.h ++++ b/libmultipath/configure.h +@@ -45,9 +45,6 @@ enum { + CP_RETRY, + }; + +-#define FLUSH_ONE 1 +-#define FLUSH_ALL 2 +- + struct vectors; + + int setup_map (struct multipath * mpp, char * params, int params_size, +diff --git a/multipath/main.c b/multipath/main.c +index 7ab3102f..a2080029 100644 +--- a/multipath/main.c ++++ b/multipath/main.c +@@ -942,10 +942,10 @@ main (int argc, char *argv[]) + cmd = CMD_DRY_RUN; + break; + case 'f': +- conf->remove = FLUSH_ONE; ++ cmd = CMD_FLUSH_ONE; + break; + case 'F': +- conf->remove = FLUSH_ALL; ++ cmd = CMD_FLUSH_ALL; + break; + case 'l': + if (optarg && !strncmp(optarg, "l", 1)) +@@ -1084,6 +1084,10 @@ main (int argc, char *argv[]) + condlog(0, "the -w option requires a device"); + goto out; + } ++ if (cmd == CMD_FLUSH_ONE && dev_type != DEV_DEVMAP) { ++ condlog(0, "the -f option requires a map name to remove"); ++ goto out; ++ } + + switch(delegate_to_multipathd(cmd, dev, dev_type, conf)) { + case DELEGATE_OK: +@@ -1117,16 +1121,12 @@ main (int argc, char *argv[]) + } + if (retries < 0) + retries = conf->remove_retries; +- if (conf->remove == FLUSH_ONE) { +- if (dev_type == DEV_DEVMAP) { +- r = dm_suspend_and_flush_map(dev, retries) ? +- RTVL_FAIL : RTVL_OK; +- } else +- condlog(0, "must provide a map name to remove"); +- ++ if (cmd == CMD_FLUSH_ONE) { ++ r = dm_suspend_and_flush_map(dev, retries) ? ++ RTVL_FAIL : RTVL_OK; + goto out; + } +- else if (conf->remove == FLUSH_ALL) { ++ else if (cmd == CMD_FLUSH_ALL) { + r = dm_flush_maps(1, retries) ? RTVL_FAIL : RTVL_OK; + goto out; + } +-- +2.17.2 + diff --git a/SOURCES/0029-multipath-delegate-flushing-maps-to-multipathd.patch b/SOURCES/0029-multipath-delegate-flushing-maps-to-multipathd.patch new file mode 100644 index 0000000..f610b93 --- /dev/null +++ b/SOURCES/0029-multipath-delegate-flushing-maps-to-multipathd.patch @@ -0,0 +1,63 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Tue, 16 Jun 2020 16:25:34 -0500 +Subject: [PATCH] multipath: delegate flushing maps to multipathd + +Since there can be problems with removing maps outside of multipathd, +multipath should attempt to delegate this command to multipathd. +However, multipathd doesn't attempt to suspend the device, in order +to avoid potential hangs. If delegating to multipathd fails, multipath +should try the remove itself. + +Signed-off-by: Benjamin Marzinski +--- + multipath/main.c | 14 ++++++++++++++ + multipath/multipath.8 | 4 ++-- + 2 files changed, 16 insertions(+), 2 deletions(-) + +diff --git a/multipath/main.c b/multipath/main.c +index a2080029..612c6815 100644 +--- a/multipath/main.c ++++ b/multipath/main.c +@@ -828,6 +828,20 @@ int delegate_to_multipathd(enum mpath_cmds cmd, + if (cmd == CMD_CREATE && conf->force_reload == FORCE_RELOAD_YES) { + p += snprintf(p, n, "reconfigure"); + } ++ else if (cmd == CMD_FLUSH_ONE && dev && dev_type == DEV_DEVMAP) { ++ p += snprintf(p, n, "del map %s", dev); ++ /* multipathd doesn't try as hard, to avoid potentially ++ * hanging. If it fails, retry with the regular multipath ++ * command */ ++ r = NOT_DELEGATED; ++ } ++ else if (cmd == CMD_FLUSH_ALL) { ++ p += snprintf(p, n, "del maps"); ++ /* multipathd doesn't try as hard, to avoid potentially ++ * hanging. If it fails, retry with the regular multipath ++ * command */ ++ r = NOT_DELEGATED; ++ } + /* Add other translations here */ + + if (strlen(command) == 0) +diff --git a/multipath/multipath.8 b/multipath/multipath.8 +index 8befc45a..47a33f9b 100644 +--- a/multipath/multipath.8 ++++ b/multipath/multipath.8 +@@ -125,11 +125,11 @@ the system. + Other operation modes are chosen by using one of the following command line switches: + .TP + .B \-f +-Flush (remove) a multipath device map specified as parameter, if unused. ++Flush (remove) a multipath device map specified as parameter, if unused. This operation is delegated to the multipathd daemon if it's running. + . + .TP + .B \-F +-Flush (remove) all unused multipath device maps. ++Flush (remove) all unused multipath device maps. This operation is delegated to the multipathd daemon if it's running. + . + .TP + .B \-l +-- +2.17.2 + diff --git a/SOURCES/0030-multipath-add-option-to-skip-multipathd-delegation.patch b/SOURCES/0030-multipath-add-option-to-skip-multipathd-delegation.patch new file mode 100644 index 0000000..141a565 --- /dev/null +++ b/SOURCES/0030-multipath-add-option-to-skip-multipathd-delegation.patch @@ -0,0 +1,62 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Tue, 16 Jun 2020 17:36:33 -0500 +Subject: [PATCH] multipath: add option to skip multipathd delegation + +Add the -D option to allow users to skip delegating commands to +multipathd. + +Signed-off-by: Benjamin Marzinski +--- + libmultipath/config.h | 1 + + multipath/main.c | 8 +++++++- + 2 files changed, 8 insertions(+), 1 deletion(-) + +diff --git a/libmultipath/config.h b/libmultipath/config.h +index 4042eba6..160867cd 100644 +--- a/libmultipath/config.h ++++ b/libmultipath/config.h +@@ -191,6 +191,7 @@ struct config { + int ghost_delay; + int find_multipaths_timeout; + int marginal_pathgroups; ++ int skip_delegate; + unsigned int version[3]; + unsigned int sequence_nr; + +diff --git a/multipath/main.c b/multipath/main.c +index 612c6815..3c3d2398 100644 +--- a/multipath/main.c ++++ b/multipath/main.c +@@ -825,6 +825,9 @@ int delegate_to_multipathd(enum mpath_cmds cmd, + *p = '\0'; + n = sizeof(command); + ++ if (conf->skip_delegate) ++ return NOT_DELEGATED; ++ + if (cmd == CMD_CREATE && conf->force_reload == FORCE_RELOAD_YES) { + p += snprintf(p, n, "reconfigure"); + } +@@ -923,7 +926,7 @@ main (int argc, char *argv[]) + multipath_conf = conf; + conf->retrigger_tries = 0; + conf->force_sync = 1; +- while ((arg = getopt(argc, argv, ":aAdcChl::FfM:v:p:b:BrR:itTquUwW")) != EOF ) { ++ while ((arg = getopt(argc, argv, ":aAdDcChl::FfM:v:p:b:BrR:itTquUwW")) != EOF ) { + switch(arg) { + case 1: printf("optarg : %s\n",optarg); + break; +@@ -955,6 +958,9 @@ main (int argc, char *argv[]) + if (cmd == CMD_CREATE) + cmd = CMD_DRY_RUN; + break; ++ case 'D': ++ conf->skip_delegate = 1; ++ break; + case 'f': + cmd = CMD_FLUSH_ONE; + break; +-- +2.17.2 + diff --git a/SOURCES/0031-libmultipath-fix-sysfs-dev_loss_tmo-parsing.patch b/SOURCES/0031-libmultipath-fix-sysfs-dev_loss_tmo-parsing.patch new file mode 100644 index 0000000..6954d3c --- /dev/null +++ b/SOURCES/0031-libmultipath-fix-sysfs-dev_loss_tmo-parsing.patch @@ -0,0 +1,43 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Thu, 25 Jun 2020 20:46:08 -0500 +Subject: [PATCH] libmultipath: fix sysfs dev_loss_tmo parsing + +dev_loss_tmo is a u32 value. However the kernel sysfs code prints it as +a signed integer. This means that if dev_loss_tmo is above INT_MAX, the +sysfs value will be a negative number. Parsing this was causing +sysfs_set_rport_tmo() to fail. + +Signed-off-by: Martin Wilck +Signed-off-by: Benjamin Marzinski +--- + libmultipath/discovery.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/libmultipath/discovery.c b/libmultipath/discovery.c +index ffec5162..83a41a4a 100644 +--- a/libmultipath/discovery.c ++++ b/libmultipath/discovery.c +@@ -583,7 +583,7 @@ sysfs_set_rport_tmo(struct multipath *mpp, struct path *pp) + struct udev_device *rport_dev = NULL; + char value[16], *eptr; + char rport_id[32]; +- unsigned long long tmo = 0; ++ unsigned int tmo; + int ret; + + sprintf(rport_id, "rport-%d:%d-%d", +@@ -607,8 +607,8 @@ sysfs_set_rport_tmo(struct multipath *mpp, struct path *pp) + "error %d", rport_id, -ret); + goto out; + } +- tmo = strtoull(value, &eptr, 0); +- if (value == eptr || tmo == ULLONG_MAX) { ++ tmo = strtoul(value, &eptr, 0); ++ if (value == eptr) { + condlog(0, "%s: Cannot parse dev_loss_tmo " + "attribute '%s'", rport_id, value); + goto out; +-- +2.17.2 + diff --git a/SOURCES/0032-kpartx-read-devices-with-direct-IO.patch b/SOURCES/0032-kpartx-read-devices-with-direct-IO.patch new file mode 100644 index 0000000..16d8937 --- /dev/null +++ b/SOURCES/0032-kpartx-read-devices-with-direct-IO.patch @@ -0,0 +1,267 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Fri, 26 Jun 2020 20:06:24 -0500 +Subject: [PATCH] kpartx: read devices with direct IO + +If kpartx is used on top of shared storage, and a device has its +partition table changed on one machine, and then kpartx is run on +another, it may not see the new data, because the cache still contains +the old data, and there is nothing to tell the machine running kpartx to +invalidate it. To solve this, kpartx should read the devices using +direct io. + +One issue with how this code has been updated is that the original code +for getblock() always read 1024 bytes. The new code reads a logical +sector size chunk of the device, and returns a pointer to the 512 byte +sector that the caller asked for, within that (possibly larger) chunk. +This means that if the logical sector size is 512, then the code is now +only reading 512 bytes. Looking through the code for the various +partition types, I can't see a case where more than 512 bytes is needed +and getblock() is used. If anyone has a reason why this code should be +reading 1024 bytes at minmum, I can certainly change this. But when I +looked, I couldn't find a case where reading 512 bytes would cause a +problem. + +Signed-off-by: Benjamin Marzinski +--- + kpartx/dasd.c | 7 ++++--- + kpartx/gpt.c | 22 +++++++++---------- + kpartx/kpartx.c | 56 +++++++++++++++++++++++++++++++++++++++---------- + kpartx/kpartx.h | 2 ++ + 4 files changed, 61 insertions(+), 26 deletions(-) + +diff --git a/kpartx/dasd.c b/kpartx/dasd.c +index 14b9d3aa..f0398645 100644 +--- a/kpartx/dasd.c ++++ b/kpartx/dasd.c +@@ -22,6 +22,7 @@ + * along with this program. If not, see . + */ + ++#define _GNU_SOURCE + #include + #include + #include +@@ -117,13 +118,13 @@ read_dasd_pt(int fd, __attribute__((unused)) struct slice all, + + sprintf(pathname, "/dev/.kpartx-node-%u-%u", + (unsigned int)major(dev), (unsigned int)minor(dev)); +- if ((fd_dasd = open(pathname, O_RDONLY)) == -1) { ++ if ((fd_dasd = open(pathname, O_RDONLY | O_DIRECT)) == -1) { + /* Devicenode does not exist. Try to create one */ + if (mknod(pathname, 0600 | S_IFBLK, dev) == -1) { + /* Couldn't create a device node */ + return -1; + } +- fd_dasd = open(pathname, O_RDONLY); ++ fd_dasd = open(pathname, O_RDONLY | O_DIRECT); + /* + * The file will vanish when the last process (we) + * has ceased to access it. +@@ -175,7 +176,7 @@ read_dasd_pt(int fd, __attribute__((unused)) struct slice all, + * Get volume label, extract name and type. + */ + +- if (!(data = (unsigned char *)malloc(blocksize))) ++ if (aligned_malloc((void **)&data, blocksize, NULL)) + goto out; + + +diff --git a/kpartx/gpt.c b/kpartx/gpt.c +index 785b34ea..f7fefb70 100644 +--- a/kpartx/gpt.c ++++ b/kpartx/gpt.c +@@ -243,8 +243,7 @@ alloc_read_gpt_entries(int fd, gpt_header * gpt) + + if (!count) return NULL; + +- pte = (gpt_entry *)malloc(count); +- if (!pte) ++ if (aligned_malloc((void **)&pte, get_sector_size(fd), &count)) + return NULL; + memset(pte, 0, count); + +@@ -269,12 +268,11 @@ static gpt_header * + alloc_read_gpt_header(int fd, uint64_t lba) + { + gpt_header *gpt; +- gpt = (gpt_header *) +- malloc(sizeof (gpt_header)); +- if (!gpt) ++ size_t size = sizeof (gpt_header); ++ if (aligned_malloc((void **)&gpt, get_sector_size(fd), &size)) + return NULL; +- memset(gpt, 0, sizeof (*gpt)); +- if (!read_lba(fd, lba, gpt, sizeof (gpt_header))) { ++ memset(gpt, 0, size); ++ if (!read_lba(fd, lba, gpt, size)) { + free(gpt); + return NULL; + } +@@ -498,6 +496,7 @@ find_valid_gpt(int fd, gpt_header ** gpt, gpt_entry ** ptes) + gpt_header *pgpt = NULL, *agpt = NULL; + gpt_entry *pptes = NULL, *aptes = NULL; + legacy_mbr *legacymbr = NULL; ++ size_t size = sizeof(legacy_mbr); + uint64_t lastlba; + if (!gpt || !ptes) + return 0; +@@ -526,11 +525,10 @@ find_valid_gpt(int fd, gpt_header ** gpt, gpt_entry ** ptes) + } + + /* This will be added to the EFI Spec. per Intel after v1.02. */ +- legacymbr = malloc(sizeof (*legacymbr)); +- if (legacymbr) { +- memset(legacymbr, 0, sizeof (*legacymbr)); +- read_lba(fd, 0, (uint8_t *) legacymbr, +- sizeof (*legacymbr)); ++ if (aligned_malloc((void **)&legacymbr, get_sector_size(fd), ++ &size) == 0) { ++ memset(legacymbr, 0, size); ++ read_lba(fd, 0, (uint8_t *) legacymbr, size); + good_pmbr = is_pmbr_valid(legacymbr); + free(legacymbr); + legacymbr=NULL; +diff --git a/kpartx/kpartx.c b/kpartx/kpartx.c +index d3620c5c..c24ad6d9 100644 +--- a/kpartx/kpartx.c ++++ b/kpartx/kpartx.c +@@ -19,6 +19,7 @@ + * cva, 2002-10-26 + */ + ++#define _GNU_SOURCE + #include + #include + #include +@@ -41,7 +42,6 @@ + + #define SIZE(a) (sizeof(a)/sizeof((a)[0])) + +-#define READ_SIZE 1024 + #define MAXTYPES 64 + #define MAXSLICES 256 + #define DM_TARGET "linear" +@@ -388,7 +388,7 @@ main(int argc, char **argv){ + set_delimiter(mapname, delim); + } + +- fd = open(device, O_RDONLY); ++ fd = open(device, O_RDONLY | O_DIRECT); + + if (fd == -1) { + perror(device); +@@ -690,9 +690,9 @@ xmalloc (size_t size) { + */ + + static int +-sseek(int fd, unsigned int secnr) { ++sseek(int fd, unsigned int secnr, int secsz) { + off64_t in, out; +- in = ((off64_t) secnr << 9); ++ in = ((off64_t) secnr * secsz); + out = 1; + + if ((out = lseek64(fd, in, SEEK_SET)) != in) +@@ -703,6 +703,31 @@ sseek(int fd, unsigned int secnr) { + return 0; + } + ++int ++aligned_malloc(void **mem_p, size_t align, size_t *size_p) ++{ ++ static size_t pgsize = 0; ++ size_t size; ++ int err; ++ ++ if (!mem_p || !align || (size_p && !*size_p)) ++ return EINVAL; ++ ++ if (!pgsize) ++ pgsize = getpagesize(); ++ ++ if (size_p) ++ size = ((*size_p + align - 1) / align) * align; ++ else ++ size = pgsize; ++ ++ err = posix_memalign(mem_p, pgsize, size); ++ if (!err && size_p) ++ *size_p = size; ++ return err; ++} ++ ++/* always in sector size blocks */ + static + struct block { + unsigned int secnr; +@@ -710,30 +735,39 @@ struct block { + struct block *next; + } *blockhead; + ++/* blknr is always in 512 byte blocks */ + char * +-getblock (int fd, unsigned int secnr) { ++getblock (int fd, unsigned int blknr) { ++ unsigned int secsz = get_sector_size(fd); ++ unsigned int blks_per_sec = secsz / 512; ++ unsigned int secnr = blknr / blks_per_sec; ++ unsigned int blk_off = (blknr % blks_per_sec) * 512; + struct block *bp; + + for (bp = blockhead; bp; bp = bp->next) + + if (bp->secnr == secnr) +- return bp->block; ++ return bp->block + blk_off; + +- if (sseek(fd, secnr)) ++ if (sseek(fd, secnr, secsz)) + return NULL; + + bp = xmalloc(sizeof(struct block)); + bp->secnr = secnr; + bp->next = blockhead; + blockhead = bp; +- bp->block = (char *) xmalloc(READ_SIZE); ++ if (aligned_malloc((void **)&bp->block, secsz, NULL)) { ++ fprintf(stderr, "aligned_malloc failed\n"); ++ exit(1); ++ } + +- if (read(fd, bp->block, READ_SIZE) != READ_SIZE) { ++ if (read(fd, bp->block, secsz) != secsz) { + fprintf(stderr, "read error, sector %d\n", secnr); +- bp->block = NULL; ++ blockhead = bp->next; ++ return NULL; + } + +- return bp->block; ++ return bp->block + blk_off; + } + + int +diff --git a/kpartx/kpartx.h b/kpartx/kpartx.h +index 67edeb82..727632c1 100644 +--- a/kpartx/kpartx.h ++++ b/kpartx/kpartx.h +@@ -1,6 +1,7 @@ + #ifndef _KPARTX_H + #define _KPARTX_H + ++#include + #include + #include + +@@ -61,6 +62,7 @@ extern ptreader read_mac_pt; + extern ptreader read_sun_pt; + extern ptreader read_ps3_pt; + ++int aligned_malloc(void **mem_p, size_t align, size_t *size_p); + char *getblock(int fd, unsigned int secnr); + + static inline unsigned int +-- +2.17.2 + diff --git a/SOURCES/0033-kpartx-handle-alternate-bsd-disklabel-location.patch b/SOURCES/0033-kpartx-handle-alternate-bsd-disklabel-location.patch new file mode 100644 index 0000000..556ea24 --- /dev/null +++ b/SOURCES/0033-kpartx-handle-alternate-bsd-disklabel-location.patch @@ -0,0 +1,52 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Tue, 30 Jun 2020 10:49:59 -0500 +Subject: [PATCH] kpartx: handle alternate bsd disklabel location + +bsd disk labels can either be at the start of the second sector, or 64 +bytes into the first sector, but kpartx only handled the first case. +However the second case is what parted creates, and what the linux +kernel partition code expects. kpartx should handle both cases. + +Signed-off-by: Benjamin Marzinski +--- + kpartx/bsd.c | 16 ++++++++++++++-- + 1 file changed, 14 insertions(+), 2 deletions(-) + +diff --git a/kpartx/bsd.c b/kpartx/bsd.c +index 0e661fbc..950b0f92 100644 +--- a/kpartx/bsd.c ++++ b/kpartx/bsd.c +@@ -1,6 +1,7 @@ + #include "kpartx.h" + #include + ++#define BSD_LABEL_OFFSET 64 + #define BSD_DISKMAGIC (0x82564557UL) /* The disk magic number */ + #define XBSD_MAXPARTITIONS 16 + #define BSD_FS_UNUSED 0 +@@ -60,8 +61,19 @@ read_bsd_pt(int fd, struct slice all, struct slice *sp, unsigned int ns) { + return -1; + + l = (struct bsd_disklabel *) bp; +- if (l->d_magic != BSD_DISKMAGIC) +- return -1; ++ if (l->d_magic != BSD_DISKMAGIC) { ++ /* ++ * BSD disklabels can also start 64 bytes offset from the ++ * start of the first sector ++ */ ++ bp = getblock(fd, offset); ++ if (bp == NULL) ++ return -1; ++ ++ l = (struct bsd_disklabel *)(bp + 64); ++ if (l->d_magic != BSD_DISKMAGIC) ++ return -1; ++ } + + max_partitions = 16; + if (l->d_npartitions < max_partitions) +-- +2.17.2 + diff --git a/SOURCES/0034-libmultipath-fix-checker-detection-for-nvme-devices.patch b/SOURCES/0034-libmultipath-fix-checker-detection-for-nvme-devices.patch new file mode 100644 index 0000000..ac5a32c --- /dev/null +++ b/SOURCES/0034-libmultipath-fix-checker-detection-for-nvme-devices.patch @@ -0,0 +1,61 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Tue, 30 Jun 2020 13:59:13 -0500 +Subject: [PATCH] libmultipath: fix checker detection for nvme devices + +In order to fix hwhandler autodetection, commit 8794a776 made +detect_alua() differentiate between failures to detect whether alua was +supported, and successfully detecting that it was not supported. +However, this causes nvme devices to get the TUR checker assigned to +them. This is because there is nothing in detect_alua() to make it only +work on scsi devices, and select_checker wasn't updated to handle +detect_alua() failing without setting pp->tpgs to TPGS_NONE. + +detect_alua() should automatically set pp->tpgs to TPGS_NONE and exit on +non-scsi devices. Also, select_checker() should not assume that a +devices is ALUA, simply because if failed to detect if alua was +supported. + +Fixes: 8794a776 "libmultipath: fix ALUA autodetection when paths are + down" +Signed-off-by: Benjamin Marzinski +--- + libmultipath/discovery.c | 6 ++++++ + libmultipath/propsel.c | 4 +++- + 2 files changed, 9 insertions(+), 1 deletion(-) + +diff --git a/libmultipath/discovery.c b/libmultipath/discovery.c +index 83a41a4a..aa5942c3 100644 +--- a/libmultipath/discovery.c ++++ b/libmultipath/discovery.c +@@ -887,6 +887,12 @@ detect_alua(struct path * pp) + int tpgs; + unsigned int timeout; + ++ ++ if (pp->bus != SYSFS_BUS_SCSI) { ++ pp->tpgs = TPGS_NONE; ++ return; ++ } ++ + if (sysfs_get_timeout(pp, &timeout) <= 0) + timeout = DEF_TIMEOUT; + +diff --git a/libmultipath/propsel.c b/libmultipath/propsel.c +index 897e48ca..d362beb4 100644 +--- a/libmultipath/propsel.c ++++ b/libmultipath/propsel.c +@@ -521,7 +521,9 @@ int select_checker(struct config *conf, struct path *pp) + if (check_rdac(pp)) { + ckr_name = RDAC; + goto out; +- } else if (path_get_tpgs(pp) != TPGS_NONE) { ++ } ++ path_get_tpgs(pp); ++ if (pp->tpgs != TPGS_NONE && pp->tpgs != TPGS_UNDEF) { + ckr_name = TUR; + goto out; + } +-- +2.17.2 + diff --git a/SOURCES/0035-Makefile.inc-trim-extra-information-from-systemd-ver.patch b/SOURCES/0035-Makefile.inc-trim-extra-information-from-systemd-ver.patch new file mode 100644 index 0000000..27ce5fe --- /dev/null +++ b/SOURCES/0035-Makefile.inc-trim-extra-information-from-systemd-ver.patch @@ -0,0 +1,30 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Mon, 6 Jul 2020 13:21:12 -0500 +Subject: [PATCH] Makefile.inc: trim extra information from systemd version + +Some systemd versions print extra information in the +"pkg-config --modversion" output, which confuses make. Trim this +off. + +Signed-off-by: Benjamin Marzinski +--- + Makefile.inc | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/Makefile.inc b/Makefile.inc +index c2abd301..220009e0 100644 +--- a/Makefile.inc ++++ b/Makefile.inc +@@ -37,7 +37,7 @@ endif + + ifndef SYSTEMD + ifeq ($(shell pkg-config --modversion libsystemd >/dev/null 2>&1 && echo 1), 1) +- SYSTEMD = $(shell pkg-config --modversion libsystemd) ++ SYSTEMD = $(shell pkg-config --modversion libsystemd | awk '{print $$1}') + else + ifeq ($(shell systemctl --version >/dev/null 2>&1 && echo 1), 1) + SYSTEMD = $(shell systemctl --version 2> /dev/null | \ +-- +2.17.2 + diff --git a/SOURCES/0036-kpartx-fix-Wsign-compare-error.patch b/SOURCES/0036-kpartx-fix-Wsign-compare-error.patch new file mode 100644 index 0000000..ffaa477 --- /dev/null +++ b/SOURCES/0036-kpartx-fix-Wsign-compare-error.patch @@ -0,0 +1,26 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Mon, 6 Jul 2020 17:28:46 -0500 +Subject: [PATCH] kpartx: fix -Wsign-compare error + +Signed-off-by: Benjamin Marzinski +--- + kpartx/kpartx.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/kpartx/kpartx.c b/kpartx/kpartx.c +index c24ad6d9..653ce0c8 100644 +--- a/kpartx/kpartx.c ++++ b/kpartx/kpartx.c +@@ -738,7 +738,7 @@ struct block { + /* blknr is always in 512 byte blocks */ + char * + getblock (int fd, unsigned int blknr) { +- unsigned int secsz = get_sector_size(fd); ++ int secsz = get_sector_size(fd); + unsigned int blks_per_sec = secsz / 512; + unsigned int secnr = blknr / blks_per_sec; + unsigned int blk_off = (blknr % blks_per_sec) * 512; +-- +2.17.2 + diff --git a/SPECS/device-mapper-multipath.spec b/SPECS/device-mapper-multipath.spec index 8163dc2..a2c64cf 100644 --- a/SPECS/device-mapper-multipath.spec +++ b/SPECS/device-mapper-multipath.spec @@ -1,7 +1,7 @@ Summary: Tools to manage multipath devices using device-mapper Name: device-mapper-multipath Version: 0.8.4 -Release: 1%{?dist} +Release: 3%{?dist} License: GPLv2 Group: System Environment/Base URL: http://christophe.varoqui.free.fr/ @@ -31,6 +31,22 @@ Patch00017: 0017-RH-warn-on-invalid-regex-instead-of-failing.patch Patch00018: 0018-RH-reset-default-find_mutipaths-value-to-off.patch Patch00019: 0019-RH-Fix-nvme-compilation-warning.patch Patch00020: 0020-RH-attempt-to-get-ANA-info-via-sysfs-first.patch +Patch00021: 0021-libmultipath-remove-_blacklist_exceptions-functions.patch +Patch00022: 0022-libmultipath-fix-parser-issue-with-comments-in-strin.patch +Patch00023: 0023-libmultipath-invert-regexes-that-start-with-exclamat.patch +Patch00024: 0024-libmultipath-make-dm_get_map-status-return-codes-sym.patch +Patch00025: 0025-multipathd-fix-check_path-errors-with-removed-map.patch +Patch00026: 0026-libmultipath-make-dm_flush_maps-only-return-0-on-suc.patch +Patch00027: 0027-multipathd-add-del-maps-multipathd-command.patch +Patch00028: 0028-multipath-make-flushing-maps-work-like-other-command.patch +Patch00029: 0029-multipath-delegate-flushing-maps-to-multipathd.patch +Patch00030: 0030-multipath-add-option-to-skip-multipathd-delegation.patch +Patch00031: 0031-libmultipath-fix-sysfs-dev_loss_tmo-parsing.patch +Patch00032: 0032-kpartx-read-devices-with-direct-IO.patch +Patch00033: 0033-kpartx-handle-alternate-bsd-disklabel-location.patch +Patch00034: 0034-libmultipath-fix-checker-detection-for-nvme-devices.patch +Patch00035: 0035-Makefile.inc-trim-extra-information-from-systemd-ver.patch +Patch00036: 0036-kpartx-fix-Wsign-compare-error.patch # runtime Requires: %{name}-libs = %{version}-%{release} @@ -232,6 +248,36 @@ fi %{_pkgconfdir}/libdmmp.pc %changelog +* Mon Jul 6 2020 Benjamin Marzinski 0.8.4-3 +- Add 0024-libmultipath-make-dm_get_map-status-return-codes-sym.patch +- Add 0025-multipathd-fix-check_path-errors-with-removed-map.patch +- Add 0026-libmultipath-make-dm_flush_maps-only-return-0-on-suc.patch +- Add 0027-multipathd-add-del-maps-multipathd-command.patch +- Add 0028-multipath-make-flushing-maps-work-like-other-command.patch +- Add 0029-multipath-delegate-flushing-maps-to-multipathd.patch +- Add 0030-multipath-add-option-to-skip-multipathd-delegation.patch + * The above 7 patches fix bz #1845875. Multipath now attempts to + delegate device removal to multipathd, and multipathd handles + external device removal better. +- Add 0031-libmultipath-fix-sysfs-dev_loss_tmo-parsing.patch + * Fixes bz #1852843 +- Add 0032-kpartx-read-devices-with-direct-IO.patch + * Fixes bz #1814858 +- Add 0033-kpartx-handle-alternate-bsd-disklabel-location.patch +- Add 0034-libmultipath-fix-checker-detection-for-nvme-devices.patch + * fixes bz #1845915 +- Add 0035-Makefile.inc-trim-extra-information-from-systemd-ver.patch +- Add 0036-kpartx-fix-Wsign-compare-error.patch +- Resolves: bz #1814858, #1845875, #1845915, #1852843 + +* Tue Jun 9 2020 Benjamin Marzinski 0.8.4-2 +- Add 0021-libmultipath-remove-_blacklist_exceptions-functions.patch +- Add 0022-libmultipath-fix-parser-issue-with-comments-in-strin.patch +- Add 0023-libmultipath-invert-regexes-that-start-with-exclamat.patch + * This commit also changes the default devnode blacklist to + blacklist all devices except scsi, dasd, and nvme. +- Resolves: bz #1828180 + * Wed May 27 2020 Benjamin Marzinski 0.8.4-1 - Update Source to upstream version 0.8.4 * This version includes the fixes for bz #1768894 & #1821214 @@ -471,7 +517,7 @@ fi - Rename files * Previous patches 0007-0014 are now patches 0008-0015 -* Tue Apr 02 2018 Björn Esser - 0.7.6-1.git1cb704b +* Mon Apr 02 2018 Benjamin Marzinski - 0.7.6-1.git1cb704b - Update Source to the latest upstream commit * Previous patches 0001-0014 are included in this commit * Previous patches 0015-0022 are now patches 0007-0014