From e1f667e2ee8eeb1259e5c5724c8d31d1871386cb Mon Sep 17 00:00:00 2001 From: DongHun Kwak Date: Fri, 14 Jan 2022 13:50:18 +0900 Subject: [PATCH] Imported Upstream version 0.7.8 --- Makefile.inc | 28 +- README.alua | 4 + kpartx/dasd.c | 5 +- kpartx/del-part-nodes.rules | 2 +- kpartx/devmapper.c | 25 +- kpartx/gpt.c | 10 - kpartx/gpt.h | 19 - kpartx/kpartx.c | 2 +- kpartx/lopart.c | 30 + kpartx/mac.h | 2 +- kpartx/test-kpartx | 2 +- libdmmp/docs/doc-preclean.pl | 0 libdmmp/docs/kernel-doc | 691 ++++--- libdmmp/docs/split-man.pl | 0 libmpathcmd/Makefile | 2 +- libmpathcmd/mpath_cmd.h | 2 +- libmpathpersist/mpath_persist.c | 45 +- libmpathpersist/mpath_persistent_reserve_in.3 | 2 +- .../mpath_persistent_reserve_out.3 | 2 +- libmpathpersist/mpath_pr_ioctl.c | 75 +- libmpathpersist/mpath_updatepr.c | 11 +- libmpathpersist/mpathpr.h | 3 +- libmultipath/Makefile | 2 +- libmultipath/blacklist.c | 302 ++- libmultipath/blacklist.h | 7 +- libmultipath/checkers.c | 22 - libmultipath/checkers.h | 6 - libmultipath/checkers/Makefile | 7 - libmultipath/checkers/cciss_tur.c | 5 - libmultipath/checkers/directio.c | 5 - libmultipath/checkers/emc_clariion.c | 5 - libmultipath/checkers/hp_sw.c | 5 - libmultipath/checkers/rbd.c | 653 ------ libmultipath/checkers/rdac.c | 5 - libmultipath/checkers/readsector0.c | 5 - libmultipath/checkers/tur.c | 173 +- libmultipath/config.c | 231 ++- libmultipath/config.h | 11 +- libmultipath/configure.c | 7 +- libmultipath/defaults.c | 2 + libmultipath/defaults.h | 3 + libmultipath/devmapper.c | 6 +- libmultipath/dict.c | 122 +- libmultipath/dict.h | 3 +- libmultipath/discovery.c | 202 +- libmultipath/dm-generic.c | 4 +- libmultipath/dm-generic.h | 4 +- libmultipath/file.h | 2 + libmultipath/foreign.c | 4 +- libmultipath/foreign.h | 4 +- libmultipath/foreign/nvme.c | 167 +- libmultipath/generic.c | 4 +- libmultipath/generic.h | 4 +- libmultipath/hwtable.c | 149 +- libmultipath/parser.c | 12 +- libmultipath/print.c | 210 +- libmultipath/print.h | 13 +- libmultipath/prio.c | 8 +- libmultipath/prio.h | 8 +- libmultipath/prioritizers/alua.c | 5 +- libmultipath/prioritizers/alua_rtpg.c | 107 +- libmultipath/prioritizers/path_latency.c | 3 +- libmultipath/prkey.c | 27 +- libmultipath/prkey.h | 6 +- libmultipath/propsel.c | 80 +- libmultipath/propsel.h | 1 + libmultipath/structs.c | 28 +- libmultipath/structs.h | 37 +- libmultipath/structs_vec.c | 19 + libmultipath/structs_vec.h | 1 + libmultipath/sysfs.c | 5 +- libmultipath/uevent.c | 6 + libmultipath/unaligned.h | 16 + libmultipath/util.c | 20 +- libmultipath/util.h | 1 + libmultipath/vector.c | 12 + libmultipath/vector.h | 1 + libmultipath/version.h | 4 +- mpathpersist/main.c | 14 +- mpathpersist/main.h | 1 + mpathpersist/mpathpersist.8 | 4 + multipath/main.c | 136 +- multipath/multipath.8 | 8 +- multipath/multipath.conf.5 | 229 ++- multipathd/cli.c | 2 + multipathd/cli.h | 2 + multipathd/cli_handlers.c | 108 +- multipathd/cli_handlers.h | 1 + multipathd/dmevents.c | 14 +- multipathd/main.c | 86 +- multipathd/main.h | 6 +- multipathd/multipathd.8 | 5 + multipathd/uxlsnr.c | 5 +- tests/Makefile | 40 +- tests/blacklist.c | 512 +++++ tests/dmevents.c | 2 +- tests/hwtable.c | 1776 +++++++++++++++++ tests/parser.c | 2 +- tests/test-lib.c | 365 ++++ tests/test-lib.h | 68 + tests/uevent.c | 2 +- tests/unaligned.c | 96 + tests/util.c | 2 +- third-party/valgrind/valgrind.h | 269 +-- 104 files changed, 5236 insertions(+), 2242 deletions(-) mode change 100644 => 100755 libdmmp/docs/doc-preclean.pl mode change 100644 => 100755 libdmmp/docs/split-man.pl delete mode 100644 libmultipath/checkers/rbd.c create mode 100644 tests/blacklist.c create mode 100644 tests/hwtable.c create mode 100644 tests/test-lib.c create mode 100644 tests/test-lib.h create mode 100644 tests/unaligned.c diff --git a/Makefile.inc b/Makefile.inc index 57a1835..a83f02c 100644 --- a/Makefile.inc +++ b/Makefile.inc @@ -78,7 +78,7 @@ INSTALL_PROGRAM = install # Test if the C compiler supports the option. # Evaluates to "option" if yes, and "fallback" otherwise. TEST_CC_OPTION = $(shell \ - if echo 'int main(void){return 0;}' | $(CC) -o /dev/null -c "$(1)" -xc - &>/dev/null; \ + if echo 'int main(void){return 0;}' | $(CC) -o /dev/null -c "$(1)" -xc - >/dev/null 2>&1; \ then \ echo "$(1)"; \ else \ @@ -86,11 +86,12 @@ TEST_CC_OPTION = $(shell \ fi) STACKPROT := $(call TEST_CC_OPTION,-fstack-protector-strong,-fstack-protector) +ERROR_DISCARDED_QUALIFIERS := $(call TEST_CC_OPTION,-Werror=discarded-qualifiers,) OPTFLAGS = -O2 -g -pipe -Wall -Wextra -Wformat=2 -Werror=implicit-int \ -Werror=implicit-function-declaration -Werror=format-security \ -Wno-sign-compare -Wno-unused-parameter -Wno-clobbered \ - -Werror=cast-qual -Werror=discarded-qualifiers \ + -Werror=cast-qual $(ERROR_DISCARDED_QUALIFIERS) \ -Wp,-D_FORTIFY_SOURCE=2 $(STACKPROT) \ --param=ssp-buffer-size=4 @@ -103,21 +104,20 @@ LDFLAGS = -Wl,-z,relro -Wl,-z,now BIN_LDFLAGS = -pie # Check whether a function with name $1 has been declared in header file $2. -check_func = \ - $(shell \ +check_func = $(shell \ if grep -Eq "^[^[:blank:]]+[[:blank:]]+$1[[:blank:]]*(.*)*" "$2"; then \ - found=1; \ - status="yes"; \ - else \ - found=0; \ - status="no"; \ - fi; \ - echo 1>&2 "Checking for $1 in $2 ... $$status"; \ - echo "$$found" \ - ) + found=1; \ + status="yes"; \ + else \ + found=0; \ + status="no"; \ + fi; \ + echo 1>&2 "Checking for $1 in $2 ... $$status"; \ + echo "$$found" \ + ) # Checker whether a file with name $1 exists -check_file = $(shell \ +check_file = $(shell \ if [ -f "$1" ]; then \ found=1; \ status="yes"; \ diff --git a/README.alua b/README.alua index e39debd..340ccba 100644 --- a/README.alua +++ b/README.alua @@ -15,3 +15,7 @@ To enable ALUA, the following options should be changed: - LSI/Engenio/NetApp RDAC class, as NetApp SANtricity E/EF Series and OEM arrays: "Select operating system:" should be changed to "Linux DM-MP (Kernel 3.10 or later)". + +- NetApp ONTAP: + To check ALUA state: "igroup show -v ", and to enable ALUA: + "igroup set alua yes". diff --git a/kpartx/dasd.c b/kpartx/dasd.c index e418d5a..94ae81b 100644 --- a/kpartx/dasd.c +++ b/kpartx/dasd.c @@ -129,7 +129,7 @@ read_dasd_pt(int fd, struct slice all, struct slice *sp, int ns) */ unlink(pathname); } - if (!fd_dasd) { + if (fd_dasd < 0) { /* Couldn't open the device */ return -1; } @@ -157,7 +157,8 @@ read_dasd_pt(int fd, struct slice all, struct slice *sp, int ns) geo.heads = 15; geo.sectors = recs_per_track(blocksize); - cyl = disksize / (blocksize * geo.heads * geo.sectors); + cyl = disksize / ((uint64_t)blocksize * geo.heads * + geo.sectors); if (cyl < LV_COMPAT_CYL) geo.cylinders = cyl; else diff --git a/kpartx/del-part-nodes.rules b/kpartx/del-part-nodes.rules index 17bc505..0ceecf5 100644 --- a/kpartx/del-part-nodes.rules +++ b/kpartx/del-part-nodes.rules @@ -10,7 +10,7 @@ # or create an udev rule file that sets ENV{DONT_DEL_PART_NODES}="1". SUBSYSTEM!="block", GOTO="end_del_part_nodes" -KERNEL!="sd*|dasd*|rbd*", GOTO="end_del_part_nodes" +KERNEL!="sd*|dasd*", GOTO="end_del_part_nodes" ACTION!="add|change", GOTO="end_del_part_nodes" ENV{DEVTYPE}=="partition", GOTO="end_del_part_nodes" diff --git a/kpartx/devmapper.c b/kpartx/devmapper.c index 8f68a24..8db1eb5 100644 --- a/kpartx/devmapper.c +++ b/kpartx/devmapper.c @@ -11,7 +11,6 @@ #include #include "devmapper.h" -#define FREE_CONST(p) do { free((void*)(long)p); p = NULL; } while(0) #define _UUID_PREFIX "part" #define UUID_PREFIX _UUID_PREFIX "%d-" #define _UUID_PREFIX_LEN (sizeof(_UUID_PREFIX) - 1) @@ -252,10 +251,11 @@ out: return r; } -static const char *dm_find_uuid(const char *uuid) +static char *dm_find_uuid(const char *uuid) { struct dm_task *dmt; - const char *name = NULL, *tmp; + char *name = NULL; + const char *tmp; if ((dmt = dm_task_create(DM_DEVICE_INFO)) == NULL) return NULL; @@ -365,7 +365,7 @@ dm_devn (const char * mapname, int *major, int *minor) struct dm_info info; if (!(dmt = dm_task_create(DM_DEVICE_INFO))) - return 0; + return 1; if (!dm_task_set_name(dmt, mapname)) goto out; @@ -642,7 +642,7 @@ int dm_find_part(const char *parent, const char *delim, int part, { int r; char params[PARAMS_SIZE]; - const char *tmp; + char *tmp; char *uuid; int major, minor; char dev_t[32]; @@ -663,7 +663,7 @@ int dm_find_part(const char *parent, const char *delim, int part, tmp = dm_find_uuid(uuid); if (tmp == NULL) - return r; + goto out; /* Sanity check on partition, see dm_foreach_partmaps */ if (dm_type(tmp, "linear") != 1) @@ -689,14 +689,15 @@ int dm_find_part(const char *parent, const char *delim, int part, tmp, uuid, name); r = dm_rename(tmp, name); - if (r == 0) { - free(uuid); - if (verbose) - fprintf(stderr, "renaming %s->%s failed\n", tmp, name); - } else + if (r == 1) { *part_uuid = uuid; + return 1; + } + if (verbose) + fprintf(stderr, "renaming %s->%s failed\n", tmp, name); out: - FREE_CONST(tmp); + free(uuid); + free(tmp); return r; } diff --git a/kpartx/gpt.c b/kpartx/gpt.c index 6ef20f9..e31611a 100644 --- a/kpartx/gpt.c +++ b/kpartx/gpt.c @@ -348,16 +348,6 @@ is_gpt_valid(int fd, uint64_t lba, return 0; } - - /* Check that sizeof_partition_entry has the correct value */ - if (__le32_to_cpu((*gpt)->sizeof_partition_entry) != sizeof(gpt_entry)) { - // printf("GUID partition entry size check failed.\n"); - free(*gpt); - *gpt = NULL; - return 0; - } - - if (!(*ptes = alloc_read_gpt_entries(fd, *gpt))) { free(*gpt); *gpt = NULL; diff --git a/kpartx/gpt.h b/kpartx/gpt.h index 66ce8f1..7bb54b7 100644 --- a/kpartx/gpt.h +++ b/kpartx/gpt.h @@ -109,22 +109,3 @@ int read_gpt_pt (int fd, struct slice all, struct slice *sp, int ns); #endif - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * Emacs will notice this stuff at the end of the file and automatically - * adjust the settings for this buffer only. This must remain at the end - * of the file. - * --------------------------------------------------------------------------- - * Local variables: - * c-indent-level: 4 - * c-brace-imaginary-offset: 0 - * c-brace-offset: -4 - * c-argdecl-indent: 4 - * c-label-offset: -4 - * c-continued-statement-offset: 4 - * c-continued-brace-offset: 0 - * indent-tabs-mode: nil - * tab-width: 8 - * End: - */ diff --git a/kpartx/kpartx.c b/kpartx/kpartx.c index 442b6bd..d4fb53b 100644 --- a/kpartx/kpartx.c +++ b/kpartx/kpartx.c @@ -341,7 +341,7 @@ main(int argc, char **argv){ if (!loopdev) { loopdev = find_unused_loop_device(); - if (set_loop(loopdev, device, 0, &ro)) { + if (set_loop(loopdev, rpath, 0, &ro)) { fprintf(stderr, "can't set up loop\n"); exit (1); } diff --git a/kpartx/lopart.c b/kpartx/lopart.c index 69c1eda..9b65255 100644 --- a/kpartx/lopart.c +++ b/kpartx/lopart.c @@ -69,6 +69,8 @@ char *find_loop_by_file(const char *filename) struct loop_info loopinfo; const char VIRT_BLOCK[] = "/sys/devices/virtual/block"; char path[PATH_MAX]; + char bf_path[PATH_MAX]; + char backing_file[PATH_MAX]; dir = opendir(VIRT_BLOCK); if (!dir) @@ -122,6 +124,34 @@ char *find_loop_by_file(const char *filename) found = realpath(path, NULL); break; } + + /* + * filename is a realpath, while loopinfo.lo_name may hold just the + * basename. If that's the case, try to match filename against the + * backing_file entry for this loop entry + */ + if (snprintf(bf_path, PATH_MAX, "%s/%s/loop/backing_file", VIRT_BLOCK, + dent->d_name) >= PATH_MAX) + continue; + + fd = open(bf_path, O_RDONLY); + if (fd < 0) + continue; + + bytes_read = read(fd, backing_file, sizeof(backing_file) - 1); + if (bytes_read <= 0) { + close(fd); + continue; + } + + close(fd); + + backing_file[bytes_read-1] = '\0'; + + if (0 == strcmp(filename, backing_file)) { + found = realpath(path, NULL); + break; + } } closedir(dir); return found; diff --git a/kpartx/mac.h b/kpartx/mac.h index a44cf38..55c3ec9 100644 --- a/kpartx/mac.h +++ b/kpartx/mac.h @@ -24,7 +24,7 @@ struct mac_driver_desc { uint16_t signature; /* expected to be MAC_DRIVER_MAGIC */ uint16_t block_size; uint32_t block_count; - /* ... more stuff */ + /* ... more stuff */ }; #endif diff --git a/kpartx/test-kpartx b/kpartx/test-kpartx index 9cee20f..d2001dc 100755 --- a/kpartx/test-kpartx +++ b/kpartx/test-kpartx @@ -131,7 +131,7 @@ step "create DM devices (spans)" # They also serve as DM devices to test partition removal on those. TABLE="\ -0 $((SIZE/SECTSIZ-OFFS)) linear $DEV1 $OFFS +0 $((SIZE/SECTSIZ-OFFS)) linear $DEV1 $OFFS $((SIZE/SECTSIZ-OFFS)) $((SIZE/SECTSIZ-OFFS)) linear $DEV2 $OFFS" SPAN1=kpt diff --git a/libdmmp/docs/doc-preclean.pl b/libdmmp/docs/doc-preclean.pl old mode 100644 new mode 100755 diff --git a/libdmmp/docs/kernel-doc b/libdmmp/docs/kernel-doc index fee8952..8f0f508 100755 --- a/libdmmp/docs/kernel-doc +++ b/libdmmp/docs/kernel-doc @@ -1,4 +1,5 @@ #!/usr/bin/env perl +# SPDX-License-Identifier: GPL-2.0 use warnings; use strict; @@ -328,13 +329,15 @@ my $lineprefix=""; use constant { STATE_NORMAL => 0, # normal code STATE_NAME => 1, # looking for function name - STATE_FIELD => 2, # scanning field start - STATE_PROTO => 3, # scanning prototype - STATE_DOCBLOCK => 4, # documentation block - STATE_INLINE => 5, # gathering documentation outside main block + STATE_BODY_MAYBE => 2, # body - or maybe more description + STATE_BODY => 3, # the body of the comment + STATE_PROTO => 4, # scanning prototype + STATE_DOCBLOCK => 5, # documentation block + STATE_INLINE => 6, # gathering documentation outside main block }; my $state; my $in_doc_sect; +my $leading_space; # Inline documentation state use constant { @@ -363,7 +366,7 @@ my $doc_sect = $doc_com . my $doc_content = $doc_com_body . '(.*)'; my $doc_block = $doc_com . 'DOC:\s*(.*)?'; my $doc_inline_start = '^\s*/\*\*\s*$'; -my $doc_inline_sect = '\s*\*\s*(@[\w\s]+):(.*)'; +my $doc_inline_sect = '\s*\*\s*(@\s*[\w][\w\.]*\s*):(.*)'; my $doc_inline_end = '^\s*\*/\s*$'; my $doc_inline_oneline = '^\s*/\*\*\s*(@[\w\s]+):\s*(.*)\s*\*/\s*$'; my $export_symbol = '^\s*EXPORT_SYMBOL(_GPL)?\s*\(\s*(\w+)\s*\)\s*;'; @@ -553,10 +556,9 @@ sub output_highlight { } if ($line eq ""){ if (! $output_preformatted) { - print $lineprefix, local_unescape($blankline); + print $lineprefix, $blankline; } } else { - $line =~ s/\\\\\\/\&/g; if ($output_mode eq "man" && substr($line, 0, 1) eq ".") { print "\\&$line"; } else { @@ -747,17 +749,73 @@ sub output_blockhead_rst(%) { } } -sub output_highlight_rst { - my $contents = join "\n",@_; - my $line; - - # undo the evil effects of xml_escape() earlier - $contents = xml_unescape($contents); - +# +# Apply the RST highlights to a sub-block of text. +# +sub highlight_block($) { + # The dohighlight kludge requires the text be called $contents + my $contents = shift; eval $dohighlight; die $@ if $@; + return $contents; +} - foreach $line (split "\n", $contents) { +# +# Regexes used only here. +# +my $sphinx_literal = '^[^.].*::$'; +my $sphinx_cblock = '^\.\.\ +code-block::'; + +sub output_highlight_rst { + my $input = join "\n",@_; + my $output = ""; + my $line; + my $in_literal = 0; + my $litprefix; + my $block = ""; + + foreach $line (split "\n",$input) { + # + # If we're in a literal block, see if we should drop out + # of it. Otherwise pass the line straight through unmunged. + # + if ($in_literal) { + if (! ($line =~ /^\s*$/)) { + # + # If this is the first non-blank line in a literal + # block we need to figure out what the proper indent is. + # + if ($litprefix eq "") { + $line =~ /^(\s*)/; + $litprefix = '^' . $1; + $output .= $line . "\n"; + } elsif (! ($line =~ /$litprefix/)) { + $in_literal = 0; + } else { + $output .= $line . "\n"; + } + } else { + $output .= $line . "\n"; + } + } + # + # Not in a literal block (or just dropped out) + # + if (! $in_literal) { + $block .= $line . "\n"; + if (($line =~ /$sphinx_literal/) || ($line =~ /$sphinx_cblock/)) { + $in_literal = 1; + $litprefix = ""; + $output .= highlight_block($block); + $block = "" + } + } + } + + if ($block) { + $output .= highlight_block($block); + } + foreach $line (split "\n", $output) { print $lineprefix . $line . "\n"; } } @@ -1004,7 +1062,7 @@ sub dump_struct($$) { my $x = shift; my $file = shift; - if ($x =~ /(struct|union)\s+(\w+)\s*{(.*)}/) { + if ($x =~ /(struct|union)\s+(\w+)\s*\{(.*)\}/) { my $decl_type = $1; $declaration_name = $2; my $members = $3; @@ -1062,7 +1120,7 @@ sub dump_struct($$) { # Handle bitmaps $arg =~ s/:\s*\d+\s*//g; # Handle arrays - $arg =~ s/\[\S+\]//g; + $arg =~ s/\[.*\]//g; # The type may have multiple words, # and multiple IDs can be defined, like: # const struct foo, *bar, foobar @@ -1090,20 +1148,20 @@ sub dump_struct($$) { } } } - $members =~ s/(struct|union)([^\{\};]+)\{([^\{\}]*)}([^\{\}\;]*)\;/$newmember/; + $members =~ s/(struct|union)([^\{\};]+)\{([^\{\}]*)\}([^\{\}\;]*)\;/$newmember/; } # Ignore other nested elements, like enums - $members =~ s/({[^\{\}]*})//g; + $members =~ s/(\{[^\{\}]*\})//g; create_parameterlist($members, ';', $file, $declaration_name); check_sections($file, $declaration_name, $decl_type, $sectcheck, $struct_actual); # Adjust declaration for better display - $declaration =~ s/([{;])/$1\n/g; - $declaration =~ s/}\s+;/};/g; + $declaration =~ s/([\{;])/$1\n/g; + $declaration =~ s/\}\s+;/};/g; # Better handle inlined enums - do {} while ($declaration =~ s/(enum\s+{[^}]+),([^\n])/$1,\n$2/); + do {} while ($declaration =~ s/(enum\s+\{[^\}]+),([^\n])/$1,\n$2/); my @def_args = split /\n/, $declaration; my $level = 1; @@ -1113,12 +1171,12 @@ sub dump_struct($$) { $clause =~ s/\s+$//; $clause =~ s/\s+/ /; next if (!$clause); - $level-- if ($clause =~ m/(})/ && $level > 1); + $level-- if ($clause =~ m/(\})/ && $level > 1); if (!($clause =~ m/^\s*#/)) { $declaration .= "\t" x $level; } $declaration .= "\t" . $clause . "\n"; - $level++ if ($clause =~ m/({)/ && !($clause =~m/}/)); + $level++ if ($clause =~ m/(\{)/ && !($clause =~m/\}/)); } output_declaration($declaration_name, 'struct', @@ -1186,7 +1244,7 @@ sub dump_enum($$) { # strip #define macros inside enums $x =~ s@#\s*((define|ifdef)\s+|endif)[^;]*;@@gos; - if ($x =~ /enum\s+(\w+)\s*{(.*)}/) { + if ($x =~ /enum\s+(\w+)\s*\{(.*)\}/) { $declaration_name = $1; my $members = $2; my %_members; @@ -1422,8 +1480,6 @@ sub push_parameter($$$$) { } } - $param = xml_escape($param); - # strip spaces from $param so that it is one continuous string # on @parameterlist; # this fixes a problem where check_sections() cannot find @@ -1522,6 +1578,7 @@ sub dump_function($$) { $prototype =~ s/__meminit +//; $prototype =~ s/__must_check +//; $prototype =~ s/__weak +//; + $prototype =~ s/__sched +//; my $define = $prototype =~ s/^#\s*define\s+//; #ak added $prototype =~ s/__attribute__\s*\(\( (?: @@ -1728,7 +1785,7 @@ sub process_proto_type($$) { } while (1) { - if ( $x =~ /([^{};]*)([{};])(.*)/ ) { + if ( $x =~ /([^\{\};]*)([\{\};])(.*)/ ) { if( length $prototype ) { $prototype .= " " } @@ -1748,47 +1805,6 @@ sub process_proto_type($$) { } } -# xml_escape: replace <, >, and & in the text stream; -# -# however, formatting controls that are generated internally/locally in the -# kernel-doc script are not escaped here; instead, they begin life like -# $blankline_html (4 of '\' followed by a mnemonic + ':'), then these strings -# are converted to their mnemonic-expected output, without the 4 * '\' & ':', -# just before actual output; (this is done by local_unescape()) -sub xml_escape($) { - my $text = shift; - if ($output_mode eq "man") { - return $text; - } - $text =~ s/\&/\\\\\\amp;/g; - $text =~ s/\/\\\\\\gt;/g; - return $text; -} - -# xml_unescape: reverse the effects of xml_escape -sub xml_unescape($) { - my $text = shift; - if ($output_mode eq "man") { - return $text; - } - $text =~ s/\\\\\\amp;/\&/g; - $text =~ s/\\\\\\lt;//g; - return $text; -} - -# convert local escape strings to html -# local escape strings look like: '\\\\menmonic:' (that's 4 backslashes) -sub local_unescape($) { - my $text = shift; - if ($output_mode eq "man") { - return $text; - } - $text =~ s/\\\\\\\\lt://g; - return $text; -} sub map_filename($) { my $file; @@ -1826,15 +1842,291 @@ sub process_export_file($) { close(IN); } -sub process_file($) { - my $file; +# +# Parsers for the various processing states. +# +# STATE_NORMAL: looking for the /** to begin everything. +# +sub process_normal() { + if (/$doc_start/o) { + $state = STATE_NAME; # next line is always the function name + $in_doc_sect = 0; + $declaration_start_line = $. + 1; + } +} + +# +# STATE_NAME: Looking for the "name - description" line +# +sub process_name($$) { + my $file = shift; my $identifier; - my $func; my $descr; - my $in_purpose = 0; + + if (/$doc_block/o) { + $state = STATE_DOCBLOCK; + $contents = ""; + $new_start_line = $. + 1; + + if ( $1 eq "" ) { + $section = $section_intro; + } else { + $section = $1; + } + } + elsif (/$doc_decl/o) { + $identifier = $1; + if (/\s*([\w\s]+?)(\(\))?\s*-/) { + $identifier = $1; + } + + $state = STATE_BODY; + # if there's no @param blocks need to set up default section + # here + $contents = ""; + $section = $section_default; + $new_start_line = $. + 1; + if (/-(.*)/) { + # strip leading/trailing/multiple spaces + $descr= $1; + $descr =~ s/^\s*//; + $descr =~ s/\s*$//; + $descr =~ s/\s+/ /g; + $declaration_purpose = $descr; + $state = STATE_BODY_MAYBE; + } else { + $declaration_purpose = ""; + } + + if (($declaration_purpose eq "") && $verbose) { + print STDERR "${file}:$.: warning: missing initial short description on line:\n"; + print STDERR $_; + ++$warnings; + } + + if ($identifier =~ m/^struct/) { + $decl_type = 'struct'; + } elsif ($identifier =~ m/^union/) { + $decl_type = 'union'; + } elsif ($identifier =~ m/^enum/) { + $decl_type = 'enum'; + } elsif ($identifier =~ m/^typedef/) { + $decl_type = 'typedef'; + } else { + $decl_type = 'function'; + } + + if ($verbose) { + print STDERR "${file}:$.: info: Scanning doc for $identifier\n"; + } + } else { + print STDERR "${file}:$.: warning: Cannot understand $_ on line $.", + " - I thought it was a doc line\n"; + ++$warnings; + $state = STATE_NORMAL; + } +} + + +# +# STATE_BODY and STATE_BODY_MAYBE: the bulk of a kerneldoc comment. +# +sub process_body($$) { + my $file = shift; + + if (/$doc_sect/i) { # case insensitive for supported section names + $newsection = $1; + $newcontents = $2; + + # map the supported section names to the canonical names + if ($newsection =~ m/^description$/i) { + $newsection = $section_default; + } elsif ($newsection =~ m/^context$/i) { + $newsection = $section_context; + } elsif ($newsection =~ m/^returns?$/i) { + $newsection = $section_return; + } elsif ($newsection =~ m/^\@return$/) { + # special: @return is a section, not a param description + $newsection = $section_return; + } + + if (($contents ne "") && ($contents ne "\n")) { + if (!$in_doc_sect && $verbose) { + print STDERR "${file}:$.: warning: contents before sections\n"; + ++$warnings; + } + dump_section($file, $section, $contents); + $section = $section_default; + } + + $in_doc_sect = 1; + $state = STATE_BODY; + $contents = $newcontents; + $new_start_line = $.; + while (substr($contents, 0, 1) eq " ") { + $contents = substr($contents, 1); + } + if ($contents ne "") { + $contents .= "\n"; + } + $section = $newsection; + $leading_space = undef; + } elsif (/$doc_end/) { + if (($contents ne "") && ($contents ne "\n")) { + dump_section($file, $section, $contents); + $section = $section_default; + $contents = ""; + } + # look for doc_com + + doc_end: + if ($_ =~ m'\s*\*\s*[a-zA-Z_0-9:\.]+\*/') { + print STDERR "${file}:$.: warning: suspicious ending line: $_"; + ++$warnings; + } + + $prototype = ""; + $state = STATE_PROTO; + $brcount = 0; + } elsif (/$doc_content/) { + # miguel-style comment kludge, look for blank lines after + # @parameter line to signify start of description + if ($1 eq "") { + if ($section =~ m/^@/ || $section eq $section_context) { + dump_section($file, $section, $contents); + $section = $section_default; + $contents = ""; + $new_start_line = $.; + } else { + $contents .= "\n"; + } + $state = STATE_BODY; + } elsif ($state == STATE_BODY_MAYBE) { + # Continued declaration purpose + chomp($declaration_purpose); + $declaration_purpose .= " " . $1; + $declaration_purpose =~ s/\s+/ /g; + } else { + my $cont = $1; + if ($section =~ m/^@/ || $section eq $section_context) { + if (!defined $leading_space) { + if ($cont =~ m/^(\s+)/) { + $leading_space = $1; + } else { + $leading_space = ""; + } + } + $cont =~ s/^$leading_space//; + } + $contents .= $cont . "\n"; + } + } else { + # i dont know - bad line? ignore. + print STDERR "${file}:$.: warning: bad line: $_"; + ++$warnings; + } +} + + +# +# STATE_PROTO: reading a function/whatever prototype. +# +sub process_proto($$) { + my $file = shift; + + if (/$doc_inline_oneline/) { + $section = $1; + $contents = $2; + if ($contents ne "") { + $contents .= "\n"; + dump_section($file, $section, $contents); + $section = $section_default; + $contents = ""; + } + } elsif (/$doc_inline_start/) { + $state = STATE_INLINE; + $inline_doc_state = STATE_INLINE_NAME; + } elsif ($decl_type eq 'function') { + process_proto_function($_, $file); + } else { + process_proto_type($_, $file); + } +} + +# +# STATE_DOCBLOCK: within a DOC: block. +# +sub process_docblock($$) { + my $file = shift; + + if (/$doc_end/) { + dump_doc_section($file, $section, $contents); + $section = $section_default; + $contents = ""; + $function = ""; + %parameterdescs = (); + %parametertypes = (); + @parameterlist = (); + %sections = (); + @sectionlist = (); + $prototype = ""; + $state = STATE_NORMAL; + } elsif (/$doc_content/) { + if ( $1 eq "" ) { + $contents .= $blankline; + } else { + $contents .= $1 . "\n"; + } + } +} + +# +# STATE_INLINE: docbook comments within a prototype. +# +sub process_inline($$) { + my $file = shift; + + # First line (state 1) needs to be a @parameter + if ($inline_doc_state == STATE_INLINE_NAME && /$doc_inline_sect/o) { + $section = $1; + $contents = $2; + $new_start_line = $.; + if ($contents ne "") { + while (substr($contents, 0, 1) eq " ") { + $contents = substr($contents, 1); + } + $contents .= "\n"; + } + $inline_doc_state = STATE_INLINE_TEXT; + # Documentation block end */ + } elsif (/$doc_inline_end/) { + if (($contents ne "") && ($contents ne "\n")) { + dump_section($file, $section, $contents); + $section = $section_default; + $contents = ""; + } + $state = STATE_PROTO; + $inline_doc_state = STATE_INLINE_NA; + # Regular text + } elsif (/$doc_content/) { + if ($inline_doc_state == STATE_INLINE_TEXT) { + $contents .= $1 . "\n"; + # nuke leading blank lines + if ($contents =~ /^\s*$/) { + $contents = ""; + } + } elsif ($inline_doc_state == STATE_INLINE_NAME) { + $inline_doc_state = STATE_INLINE_ERROR; + print STDERR "${file}:$.: warning: "; + print STDERR "Incorrect use of kernel-doc format: $_"; + ++$warnings; + } + } +} + + +sub process_file($) { + my $file; my $initial_section_counter = $section_counter; my ($orig_file) = @_; - my $leading_space; $file = map_filename($orig_file); @@ -1853,250 +2145,23 @@ sub process_file($) { } # Replace tabs by spaces while ($_ =~ s/\t+/' ' x (length($&) * 8 - length($`) % 8)/e) {}; + # Hand this line to the appropriate state handler if ($state == STATE_NORMAL) { - if (/$doc_start/o) { - $state = STATE_NAME; # next line is always the function name - $in_doc_sect = 0; - $declaration_start_line = $. + 1; - } - } elsif ($state == STATE_NAME) {# this line is the function name (always) - if (/$doc_block/o) { - $state = STATE_DOCBLOCK; - $contents = ""; - $new_start_line = $. + 1; - - if ( $1 eq "" ) { - $section = $section_intro; - } else { - $section = $1; - } - } - elsif (/$doc_decl/o) { - $identifier = $1; - if (/\s*([\w\s]+?)\s*-/) { - $identifier = $1; - } - - $state = STATE_FIELD; - # if there's no @param blocks need to set up default section - # here - $contents = ""; - $section = $section_default; - $new_start_line = $. + 1; - if (/-(.*)/) { - # strip leading/trailing/multiple spaces - $descr= $1; - $descr =~ s/^\s*//; - $descr =~ s/\s*$//; - $descr =~ s/\s+/ /g; - $declaration_purpose = xml_escape($descr); - $in_purpose = 1; - } else { - $declaration_purpose = ""; - } - - if (($declaration_purpose eq "") && $verbose) { - print STDERR "${file}:$.: warning: missing initial short description on line:\n"; - print STDERR $_; - ++$warnings; - } - - if ($identifier =~ m/^struct/) { - $decl_type = 'struct'; - } elsif ($identifier =~ m/^union/) { - $decl_type = 'union'; - } elsif ($identifier =~ m/^enum/) { - $decl_type = 'enum'; - } elsif ($identifier =~ m/^typedef/) { - $decl_type = 'typedef'; - } else { - $decl_type = 'function'; - } - - if ($verbose) { - print STDERR "${file}:$.: info: Scanning doc for $identifier\n"; - } - } else { - print STDERR "${file}:$.: warning: Cannot understand $_ on line $.", - " - I thought it was a doc line\n"; - ++$warnings; - $state = STATE_NORMAL; - } - } elsif ($state == STATE_FIELD) { # look for head: lines, and include content - if (/$doc_sect/i) { # case insensitive for supported section names - $newsection = $1; - $newcontents = $2; - - # map the supported section names to the canonical names - if ($newsection =~ m/^description$/i) { - $newsection = $section_default; - } elsif ($newsection =~ m/^context$/i) { - $newsection = $section_context; - } elsif ($newsection =~ m/^returns?$/i) { - $newsection = $section_return; - } elsif ($newsection =~ m/^\@return$/) { - # special: @return is a section, not a param description - $newsection = $section_return; - } - - if (($contents ne "") && ($contents ne "\n")) { - if (!$in_doc_sect && $verbose) { - print STDERR "${file}:$.: warning: contents before sections\n"; - ++$warnings; - } - dump_section($file, $section, xml_escape($contents)); - $section = $section_default; - } - - $in_doc_sect = 1; - $in_purpose = 0; - $contents = $newcontents; - $new_start_line = $.; - while (substr($contents, 0, 1) eq " ") { - $contents = substr($contents, 1); - } - if ($contents ne "") { - $contents .= "\n"; - } - $section = $newsection; - $leading_space = undef; - } elsif (/$doc_end/) { - if (($contents ne "") && ($contents ne "\n")) { - dump_section($file, $section, xml_escape($contents)); - $section = $section_default; - $contents = ""; - } - # look for doc_com + + doc_end: - if ($_ =~ m'\s*\*\s*[a-zA-Z_0-9:\.]+\*/') { - print STDERR "${file}:$.: warning: suspicious ending line: $_"; - ++$warnings; - } - - $prototype = ""; - $state = STATE_PROTO; - $brcount = 0; -# print STDERR "end of doc comment, looking for prototype\n"; - } elsif (/$doc_content/) { - # miguel-style comment kludge, look for blank lines after - # @parameter line to signify start of description - if ($1 eq "") { - if ($section =~ m/^@/ || $section eq $section_context) { - dump_section($file, $section, xml_escape($contents)); - $section = $section_default; - $contents = ""; - $new_start_line = $.; - } else { - $contents .= "\n"; - } - $in_purpose = 0; - } elsif ($in_purpose == 1) { - # Continued declaration purpose - chomp($declaration_purpose); - $declaration_purpose .= " " . xml_escape($1); - $declaration_purpose =~ s/\s+/ /g; - } else { - my $cont = $1; - if ($section =~ m/^@/ || $section eq $section_context) { - if (!defined $leading_space) { - if ($cont =~ m/^(\s+)/) { - $leading_space = $1; - } else { - $leading_space = ""; - } - } - - $cont =~ s/^$leading_space//; - } - $contents .= $cont . "\n"; - } - } else { - # i dont know - bad line? ignore. - print STDERR "${file}:$.: warning: bad line: $_"; - ++$warnings; - } + process_normal(); + } elsif ($state == STATE_NAME) { + process_name($file, $_); + } elsif ($state == STATE_BODY || $state == STATE_BODY_MAYBE) { + process_body($file, $_); } elsif ($state == STATE_INLINE) { # scanning for inline parameters - # First line (state 1) needs to be a @parameter - if ($inline_doc_state == STATE_INLINE_NAME && /$doc_inline_sect/o) { - $section = $1; - $contents = $2; - $new_start_line = $.; - if ($contents ne "") { - while (substr($contents, 0, 1) eq " ") { - $contents = substr($contents, 1); - } - $contents .= "\n"; - } - $inline_doc_state = STATE_INLINE_TEXT; - # Documentation block end */ - } elsif (/$doc_inline_end/) { - if (($contents ne "") && ($contents ne "\n")) { - dump_section($file, $section, xml_escape($contents)); - $section = $section_default; - $contents = ""; - } - $state = STATE_PROTO; - $inline_doc_state = STATE_INLINE_NA; - # Regular text - } elsif (/$doc_content/) { - if ($inline_doc_state == STATE_INLINE_TEXT) { - $contents .= $1 . "\n"; - # nuke leading blank lines - if ($contents =~ /^\s*$/) { - $contents = ""; - } - } elsif ($inline_doc_state == STATE_INLINE_NAME) { - $inline_doc_state = STATE_INLINE_ERROR; - print STDERR "${file}:$.: warning: "; - print STDERR "Incorrect use of kernel-doc format: $_"; - ++$warnings; - } - } - } elsif ($state == STATE_PROTO) { # scanning for function '{' (end of prototype) - if (/$doc_inline_oneline/) { - $section = $1; - $contents = $2; - if ($contents ne "") { - $contents .= "\n"; - dump_section($file, $section, xml_escape($contents)); - $section = $section_default; - $contents = ""; - } - } elsif (/$doc_inline_start/) { - $state = STATE_INLINE; - $inline_doc_state = STATE_INLINE_NAME; - } elsif ($decl_type eq 'function') { - process_proto_function($_, $file); - } else { - process_proto_type($_, $file); - } + process_inline($file, $_); + } elsif ($state == STATE_PROTO) { + process_proto($file, $_); } elsif ($state == STATE_DOCBLOCK) { - if (/$doc_end/) - { - dump_doc_section($file, $section, xml_escape($contents)); - $section = $section_default; - $contents = ""; - $function = ""; - %parameterdescs = (); - %parametertypes = (); - @parameterlist = (); - %sections = (); - @sectionlist = (); - $prototype = ""; - $state = STATE_NORMAL; - } - elsif (/$doc_content/) - { - if ( $1 eq "" ) - { - $contents .= $blankline; - } - else - { - $contents .= $1 . "\n"; - } - } + process_docblock($file, $_); } } + + # Make sure we got something interesting. if ($initial_section_counter == $section_counter) { if ($output_mode ne "none") { print STDERR "${file}:1: warning: no structured comments found\n"; diff --git a/libdmmp/docs/split-man.pl b/libdmmp/docs/split-man.pl old mode 100644 new mode 100755 diff --git a/libmpathcmd/Makefile b/libmpathcmd/Makefile index 53c0899..0f6b816 100644 --- a/libmpathcmd/Makefile +++ b/libmpathcmd/Makefile @@ -27,7 +27,7 @@ uninstall: $(RM) $(DESTDIR)$(includedir)/mpath_cmd.h clean: dep_clean - $(RM) core *.a *.o *.so *.so.* *.gz + $(RM) core *.a *.o *.so *.so.* *.gz include $(wildcard $(OBJS:.o=.d)) diff --git a/libmpathcmd/mpath_cmd.h b/libmpathcmd/mpath_cmd.h index aaa8da9..df9d938 100644 --- a/libmpathcmd/mpath_cmd.h +++ b/libmpathcmd/mpath_cmd.h @@ -20,7 +20,7 @@ #ifndef LIB_MPATH_CMD_H #define LIB_MPATH_CMD_H -#ifdef __cpluscplus +#ifdef __cplusplus extern "C" { #endif diff --git a/libmpathpersist/mpath_persist.c b/libmpathpersist/mpath_persist.c index 907a17c..4229a94 100644 --- a/libmpathpersist/mpath_persist.c +++ b/libmpathpersist/mpath_persist.c @@ -20,6 +20,7 @@ #include #include "propsel.h" #include "util.h" +#include "unaligned.h" #include "mpath_persist.h" #include "mpathpr.h" @@ -335,6 +336,7 @@ int mpath_persistent_reserve_out ( int fd, int rq_servact, int rq_scope, conf = get_multipath_config(); select_reservation_key(conf, mpp); + select_all_tg_pt(conf, mpp); put_multipath_config(conf); memcpy(&prkey, paramp->sa_key, 8); @@ -343,7 +345,8 @@ int mpath_persistent_reserve_out ( int fd, int rq_servact, int rq_scope, rq_servact == MPATH_PROUT_REG_SA) || rq_servact == MPATH_PROUT_REG_IGN_SA)) { memcpy(&mpp->reservation_key, paramp->sa_key, 8); - if (update_prkey(alias, get_be64(mpp->reservation_key))) { + if (update_prkey_flags(alias, get_be64(mpp->reservation_key), + paramp->sa_flags)) { condlog(0, "%s: failed to set prkey for multipathd.", alias); ret = MPATH_PR_DMMP_ERROR; @@ -456,7 +459,7 @@ int mpath_prout_reg(struct multipath *mpp,int rq_servact, int rq_scope, unsigned int rq_type, struct prout_param_descriptor * paramp, int noisy) { - int i, j; + int i, j, k; struct pathgroup *pgp = NULL; struct path *pp = NULL; int rollback = 0; @@ -464,11 +467,14 @@ int mpath_prout_reg(struct multipath *mpp,int rq_servact, int rq_scope, int rc; int count=0; int status = MPATH_PR_SUCCESS; + int all_tg_pt; uint64_t sa_key = 0; if (!mpp) return MPATH_PR_DMMP_ERROR; + all_tg_pt = (mpp->all_tg_pt == ALL_TG_PT_ON || + paramp->sa_flags & MPATH_F_ALL_TG_PT_MASK); active_pathcount = pathcount(mpp, PATH_UP) + pathcount(mpp, PATH_GHOST); if (active_pathcount == 0) { @@ -476,16 +482,14 @@ int mpath_prout_reg(struct multipath *mpp,int rq_servact, int rq_scope, return MPATH_PR_DMMP_ERROR; } - if ( paramp->sa_flags & MPATH_F_ALL_TG_PT_MASK ) { - condlog (1, "Warning: ALL_TG_PT is set. Configuration not supported"); - } - struct threadinfo thread[active_pathcount]; + int hosts[active_pathcount]; memset(thread, 0, sizeof(thread)); /* init thread parameter */ for (i =0; i< active_pathcount; i++){ + hosts[i] = -1; thread[i].param.rq_servact = rq_servact; thread[i].param.rq_scope = rq_scope; thread[i].param.rq_type = rq_type; @@ -514,6 +518,16 @@ int mpath_prout_reg(struct multipath *mpp,int rq_servact, int rq_scope, condlog (1, "%s: %s path not up. Skip.", mpp->wwid, pp->dev); continue; } + if (all_tg_pt && pp->sg_id.host_no != -1) { + for (k = 0; k < count; k++) { + if (pp->sg_id.host_no == hosts[k]) { + condlog(3, "%s: %s host %d matches skip.", pp->wwid, pp->dev, pp->sg_id.host_no); + break; + } + } + if (k < count) + continue; + } strncpy(thread[count].param.dev, pp->dev, FILE_NAME_SIZE - 1); @@ -531,10 +545,12 @@ int mpath_prout_reg(struct multipath *mpp,int rq_servact, int rq_scope, condlog (0, "%s: failed to create thread %d", mpp->wwid, rc); thread[count].param.status = MPATH_PR_THREAD_ERROR; } + else + hosts[count] = pp->sg_id.host_no; count = count + 1; } } - for( i=0; i < active_pathcount ; i++){ + for( i=0; i < count ; i++){ if (thread[i].param.status != MPATH_PR_THREAD_ERROR) { rc = pthread_join(thread[i].id, NULL); if (rc){ @@ -543,12 +559,7 @@ int mpath_prout_reg(struct multipath *mpp,int rq_servact, int rq_scope, } if (!rollback && (thread[i].param.status == MPATH_PR_RESERV_CONFLICT)){ rollback = 1; - sa_key = 0; - for (i = 0; i < 8; ++i){ - if (i > 0) - sa_key <<= 8; - sa_key |= paramp->sa_key[i]; - } + sa_key = get_unaligned_be64(¶mp->sa_key[0]); status = MPATH_PR_RESERV_CONFLICT ; } if (!rollback && (status == MPATH_PR_SUCCESS)){ @@ -557,7 +568,7 @@ int mpath_prout_reg(struct multipath *mpp,int rq_servact, int rq_scope, } if (rollback && ((rq_servact == MPATH_PROUT_REG_SA) && sa_key != 0 )){ condlog (3, "%s: ERROR: initiating pr out rollback", mpp->wwid); - for( i=0 ; i < active_pathcount ; i++){ + for( i=0 ; i < count ; i++){ if(thread[i].param.status == MPATH_PR_SUCCESS) { memcpy(&thread[i].param.paramp->key, &thread[i].param.paramp->sa_key, 8); memset(&thread[i].param.paramp->sa_key, 0, 8); @@ -571,7 +582,7 @@ int mpath_prout_reg(struct multipath *mpp,int rq_servact, int rq_scope, } else thread[i].param.status = MPATH_PR_SKIP; } - for(i=0; i < active_pathcount ; i++){ + for(i=0; i < count ; i++){ if (thread[i].param.status != MPATH_PR_SKIP && thread[i].param.status != MPATH_PR_THREAD_ERROR) { rc = pthread_join(thread[i].id, NULL); @@ -720,7 +731,7 @@ int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope, } } pthread_attr_destroy (&attr); - for (i = 0; i < active_pathcount; i++){ + for (i = 0; i < count; i++){ if (thread[i].param.status != MPATH_PR_THREAD_ERROR) { rc = pthread_join (thread[i].id, NULL); if (rc){ @@ -729,7 +740,7 @@ int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope, } } - for (i = 0; i < active_pathcount; i++){ + for (i = 0; i < count; i++){ /* check thread status here and return the status */ if (thread[i].param.status == MPATH_PR_RESERV_CONFLICT) diff --git a/libmpathpersist/mpath_persistent_reserve_in.3 b/libmpathpersist/mpath_persistent_reserve_in.3 index 5ed0779..4691bde 100644 --- a/libmpathpersist/mpath_persistent_reserve_in.3 +++ b/libmpathpersist/mpath_persistent_reserve_in.3 @@ -12,7 +12,7 @@ .SH NAME .\" ---------------------------------------------------------------------------- . -mpath_persistent_reserve_in +mpath_persistent_reserve_in \- send PRIN command to DM device . . .\" ---------------------------------------------------------------------------- diff --git a/libmpathpersist/mpath_persistent_reserve_out.3 b/libmpathpersist/mpath_persistent_reserve_out.3 index e11eb57..55b00b0 100644 --- a/libmpathpersist/mpath_persistent_reserve_out.3 +++ b/libmpathpersist/mpath_persistent_reserve_out.3 @@ -12,7 +12,7 @@ .SH NAME .\" ---------------------------------------------------------------------------- . -mpath_persistent_reserve_out +mpath_persistent_reserve_out \- send PROUT command to DM device . . .\" ---------------------------------------------------------------------------- diff --git a/libmpathpersist/mpath_pr_ioctl.c b/libmpathpersist/mpath_pr_ioctl.c index 6dd7403..a222b1e 100644 --- a/libmpathpersist/mpath_pr_ioctl.c +++ b/libmpathpersist/mpath_pr_ioctl.c @@ -31,8 +31,8 @@ void dumpHex(const char* str, int len, int no_ascii); int prout_do_scsi_ioctl( char * dev, int rq_servact, int rq_scope, unsigned int rq_type, struct prout_param_descriptor *paramp, int noisy); uint32_t format_transportids(struct prout_param_descriptor *paramp); -void mpath_reverse_uint32_byteorder(uint32_t *num); -void mpath_reverse_uint16_byteorder(uint16_t *num); +void convert_be32_to_cpu(uint32_t *num); +void convert_be16_to_cpu(uint16_t *num); void decode_transport_id(struct prin_fulldescr *fdesc, unsigned char * p, int length); int get_prin_length(int rq_servact); int mpath_isLittleEndian(void); @@ -52,7 +52,7 @@ int prout_do_scsi_ioctl(char * dev, int rq_servact, int rq_scope, int fd = -1; snprintf(devname, FILE_NAME_SIZE, "/dev/%s",dev); - fd = open(devname, O_WRONLY); + fd = open(devname, O_RDONLY); if(fd < 0){ condlog (1, "%s: unable to open device.", dev); return MPATH_PR_FILE_ERROR; @@ -183,23 +183,23 @@ uint32_t format_transportids(struct prout_param_descriptor *paramp) void mpath_format_readkeys( struct prin_resp *pr_buff, int len, int noisy) { - mpath_reverse_uint32_byteorder(&pr_buff->prin_descriptor.prin_readkeys.prgeneration); - mpath_reverse_uint32_byteorder(&pr_buff->prin_descriptor.prin_readkeys.additional_length); + convert_be32_to_cpu(&pr_buff->prin_descriptor.prin_readkeys.prgeneration); + convert_be32_to_cpu(&pr_buff->prin_descriptor.prin_readkeys.additional_length); } void mpath_format_readresv(struct prin_resp *pr_buff, int len, int noisy) { - mpath_reverse_uint32_byteorder(&pr_buff->prin_descriptor.prin_readkeys.prgeneration); - mpath_reverse_uint32_byteorder(&pr_buff->prin_descriptor.prin_readkeys.additional_length); + convert_be32_to_cpu(&pr_buff->prin_descriptor.prin_readkeys.prgeneration); + convert_be32_to_cpu(&pr_buff->prin_descriptor.prin_readkeys.additional_length); return; } void mpath_format_reportcapabilities(struct prin_resp *pr_buff, int len, int noisy) { - mpath_reverse_uint16_byteorder(&pr_buff->prin_descriptor.prin_readcap.length); - mpath_reverse_uint16_byteorder(&pr_buff->prin_descriptor.prin_readcap.pr_type_mask); + convert_be16_to_cpu(&pr_buff->prin_descriptor.prin_readcap.length); + convert_be16_to_cpu(&pr_buff->prin_descriptor.prin_readcap.pr_type_mask); return; } @@ -213,18 +213,12 @@ void mpath_format_readfullstatus(struct prin_resp *pr_buff, int len, int noisy) uint32_t additional_length; - mpath_reverse_uint32_byteorder(&pr_buff->prin_descriptor.prin_readfd.prgeneration); - mpath_reverse_uint32_byteorder(&pr_buff->prin_descriptor.prin_readfd.number_of_descriptor); - - if (0 == pr_buff->prin_descriptor.prin_readfd.number_of_descriptor) - { - return ; - } - + convert_be32_to_cpu(&pr_buff->prin_descriptor.prin_readfd.prgeneration); + convert_be32_to_cpu(&pr_buff->prin_descriptor.prin_readfd.number_of_descriptor); if (pr_buff->prin_descriptor.prin_readfd.number_of_descriptor == 0) { - condlog(3, "No registration or resrvation found."); + condlog(3, "No registration or reservation found."); return; } @@ -247,6 +241,13 @@ void mpath_format_readfullstatus(struct prin_resp *pr_buff, int len, int noisy) fdesc.rtpi = get_unaligned_be16(&p[18]); tid_len_len = get_unaligned_be32(&p[20]); + if (tid_len_len + 24 + k >= additional_length) { + condlog(0, + "%s: corrupt PRIN response: status descriptor end %d exceeds length %d", + __func__, tid_len_len + k + 24, + additional_length); + tid_len_len = additional_length - k - 24; + } if (tid_len_len > 0) decode_transport_id( &fdesc, &p[24], tid_len_len); @@ -278,6 +279,8 @@ decode_transport_id(struct prin_fulldescr *fdesc, unsigned char * p, int length) break; case MPATH_PROTOCOL_ID_ISCSI: num = get_unaligned_be16(&p[2]); + if (num >= sizeof(fdesc->trnptid.iscsi_name)) + num = sizeof(fdesc->trnptid.iscsi_name); memcpy(&fdesc->trnptid.iscsi_name, &p[4], num); jump = (((num + 4) < 24) ? 24 : num + 4); break; @@ -305,7 +308,7 @@ int prin_do_scsi_ioctl(char * dev, int rq_servact, struct prin_resp * resp, int {MPATH_PRIN_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0}; snprintf(devname, FILE_NAME_SIZE, "/dev/%s",dev); - fd = open(devname, O_WRONLY); + fd = open(devname, O_RDONLY); if(fd < 0){ condlog(0, "%s: Unable to open device ", dev); return MPATH_PR_FILE_ERROR; @@ -466,40 +469,14 @@ int mpath_translate_response (char * dev, struct sg_io_hdr io_hdr, return MPATH_PR_SUCCESS; } -int mpath_isLittleEndian(void) +void convert_be16_to_cpu(uint16_t *num) { - int num = 1; - if(*(char *)&num == 1) - { - condlog(4, "Little-Endian"); - } - else - { - condlog(4, "Big-Endian"); - } - return 0; -} - -void mpath_reverse_uint16_byteorder(uint16_t *num) -{ - uint16_t byte0, byte1; - - byte0 = (*num & 0x000000FF) >> 0 ; - byte1 = (*num & 0x0000FF00) >> 8 ; - - *num = ((byte0 << 8) | (byte1 << 0)); + *num = get_unaligned_be16(num); } -void mpath_reverse_uint32_byteorder(uint32_t *num) +void convert_be32_to_cpu(uint32_t *num) { - uint32_t byte0, byte1, byte2, byte3; - - byte0 = (*num & 0x000000FF) >> 0 ; - byte1 = (*num & 0x0000FF00) >> 8 ; - byte2 = (*num & 0x00FF0000) >> 16 ; - byte3 = (*num & 0xFF000000) >> 24 ; - - *num = ((byte0 << 24) | (byte1 << 16) | (byte2 << 8) | (byte3 << 0)); + *num = get_unaligned_be32(num); } void diff --git a/libmpathpersist/mpath_updatepr.c b/libmpathpersist/mpath_updatepr.c index 8063e90..0aca28e 100644 --- a/libmpathpersist/mpath_updatepr.c +++ b/libmpathpersist/mpath_updatepr.c @@ -1,7 +1,5 @@ #include #include -#include - #include #include #include @@ -11,6 +9,8 @@ #include #include #include +#include +#include #include "debug.h" #include "mpath_cmd.h" #include "uxsock.h" @@ -59,11 +59,14 @@ int update_prflag(char *mapname, int set) { return do_update_pr(mapname, (set)? "setprstatus" : "unsetprstatus"); } -int update_prkey(char *mapname, uint64_t prkey) { +int update_prkey_flags(char *mapname, uint64_t prkey, uint8_t sa_flags) { char str[256]; + char *flagstr = ""; + if (sa_flags & MPATH_F_APTPL_MASK) + flagstr = ":aptpl"; if (prkey) - sprintf(str, "setprkey key %" PRIx64, prkey); + sprintf(str, "setprkey key %" PRIx64 "%s", prkey, flagstr); else sprintf(str, "unsetprkey"); return do_update_pr(mapname, str); diff --git a/libmpathpersist/mpathpr.h b/libmpathpersist/mpathpr.h index 72feb60..5ea8cd6 100644 --- a/libmpathpersist/mpathpr.h +++ b/libmpathpersist/mpathpr.h @@ -46,7 +46,8 @@ int send_prout_activepath(char * dev, int rq_servact, int rq_scope, unsigned int rq_type, struct prout_param_descriptor * paramp, int noisy); int update_prflag(char *mapname, int set); -int update_prkey(char *mapname, uint64_t prkey); +int update_prkey_flags(char *mapname, uint64_t prkey, uint8_t sa_flags); +#define update_prkey(mapname, prkey) update_prkey_flags(mapname, prkey, 0) void * mpath_alloc_prin_response(int prin_sa); int update_map_pr(struct multipath *mpp); diff --git a/libmultipath/Makefile b/libmultipath/Makefile index f51786d..33f5269 100644 --- a/libmultipath/Makefile +++ b/libmultipath/Makefile @@ -7,7 +7,7 @@ SONAME = 0 DEVLIB = libmultipath.so LIBS = $(DEVLIB).$(SONAME) -CFLAGS += $(LIB_CFLAGS) -I$(mpathcmddir) +CFLAGS += $(LIB_CFLAGS) -I$(mpathcmddir) -I$(mpathpersistdir) LIBDEPS += -lpthread -ldl -ldevmapper -ludev -L$(mpathcmddir) -lmpathcmd -lurcu -laio diff --git a/libmultipath/blacklist.c b/libmultipath/blacklist.c index ee396e2..318ec03 100644 --- a/libmultipath/blacklist.c +++ b/libmultipath/blacklist.c @@ -12,6 +12,8 @@ #include "structs.h" #include "config.h" #include "blacklist.h" +#include "structs_vec.h" +#include "print.h" int store_ble(vector blist, char * str, int origin) { @@ -126,7 +128,8 @@ _blacklist (vector blist, const char * str) } int -_blacklist_exceptions_device(vector elist, char * vendor, char * product) +_blacklist_exceptions_device(const struct _vector *elist, const char * vendor, + const char * product) { int i; struct blentry_device * ble; @@ -144,7 +147,8 @@ _blacklist_exceptions_device(vector elist, char * vendor, char * product) } int -_blacklist_device (vector blist, char * vendor, char * product) +_blacklist_device (const struct _vector *blist, const char * vendor, + const char * product) { int i; struct blentry_device * ble; @@ -161,6 +165,25 @@ _blacklist_device (vector blist, char * vendor, char * product) return 0; } +static int +find_blacklist_device (const struct _vector *blist, const char * vendor, + const char * product) +{ + int i; + struct blentry_device * ble; + + vector_foreach_slot (blist, ble, i) { + if (((!vendor && !ble->vendor) || + (vendor && ble->vendor && + !strcmp(vendor, ble->vendor))) && + ((!product && !ble->product) || + (product && ble->product && + !strcmp(product, ble->product)))) + return 1; + } + return 0; +} + int setup_default_blist (struct config * conf) { @@ -189,8 +212,8 @@ setup_default_blist (struct config * conf) vector_foreach_slot (conf->hwtable, hwe, i) { if (hwe->bl_product) { - if (_blacklist_device(conf->blist_device, hwe->vendor, - hwe->bl_product)) + if (find_blacklist_device(conf->blist_device, + hwe->vendor, hwe->bl_product)) continue; if (alloc_ble_device(conf->blist_device)) return 1; @@ -219,12 +242,14 @@ setup_default_blist (struct config * conf) condlog(3, "%s: %s %s %s", dev, (M), wwid, (S)); \ else if (env) \ condlog(3, "%s: %s %s %s", dev, (M), env, (S)); \ + else if (protocol) \ + condlog(3, "%s: %s %s %s", dev, (M), protocol, (S)); \ else \ condlog(3, "%s: %s %s", dev, (M), (S)) void log_filter (const char *dev, char *vendor, char *product, char *wwid, - const char *env, int r) + const char *env, const char *protocol, int r) { /* * Try to sort from most likely to least. @@ -244,6 +269,9 @@ log_filter (const char *dev, char *vendor, char *product, char *wwid, case MATCH_PROPERTY_BLIST: LOG_BLIST("udev property", "blacklisted"); break; + case MATCH_PROTOCOL_BLIST: + LOG_BLIST("protocol", "blacklisted"); + break; case MATCH_DEVICE_BLIST_EXCEPT: LOG_BLIST("vendor/product", "whitelisted"); break; @@ -259,140 +287,145 @@ log_filter (const char *dev, char *vendor, char *product, char *wwid, case MATCH_PROPERTY_BLIST_MISSING: LOG_BLIST("blacklisted,", "udev property missing"); break; + case MATCH_PROTOCOL_BLIST_EXCEPT: + LOG_BLIST("protocol", "whitelisted"); + break; } } int -_filter_device (vector blist, vector elist, char * vendor, char * product) +filter_device (vector blist, vector elist, char * vendor, char * product, + char * dev) { - if (!vendor || !product) - return 0; - if (_blacklist_exceptions_device(elist, vendor, product)) - return MATCH_DEVICE_BLIST_EXCEPT; - if (_blacklist_device(blist, vendor, product)) - return MATCH_DEVICE_BLIST; - return 0; -} + int r = MATCH_NOTHING; -int -filter_device (vector blist, vector elist, char * vendor, char * product) -{ - int r = _filter_device(blist, elist, vendor, product); - log_filter(NULL, vendor, product, NULL, NULL, r); - return r; -} + if (vendor && product) { + if (_blacklist_exceptions_device(elist, vendor, product)) + r = MATCH_DEVICE_BLIST_EXCEPT; + else if (_blacklist_device(blist, vendor, product)) + r = MATCH_DEVICE_BLIST; + } -int -_filter_devnode (vector blist, vector elist, char * dev) -{ - if (!dev) - return 0; - if (_blacklist_exceptions(elist, dev)) - return MATCH_DEVNODE_BLIST_EXCEPT; - if (_blacklist(blist, dev)) - return MATCH_DEVNODE_BLIST; - return 0; + log_filter(dev, vendor, product, NULL, NULL, NULL, r); + return r; } int filter_devnode (vector blist, vector elist, char * dev) { - int r = _filter_devnode(blist, elist, dev); - log_filter(dev, NULL, NULL, NULL, NULL, r); + int r = MATCH_NOTHING; + + if (dev) { + if (_blacklist_exceptions(elist, dev)) + r = MATCH_DEVNODE_BLIST_EXCEPT; + else if (_blacklist(blist, dev)) + r = MATCH_DEVNODE_BLIST; + } + + log_filter(dev, NULL, NULL, NULL, NULL, NULL, r); return r; } int -_filter_wwid (vector blist, vector elist, char * wwid) +filter_wwid (vector blist, vector elist, char * wwid, char * dev) { - if (!wwid) - return 0; - if (_blacklist_exceptions(elist, wwid)) - return MATCH_WWID_BLIST_EXCEPT; - if (_blacklist(blist, wwid)) - return MATCH_WWID_BLIST; - return 0; + int r = MATCH_NOTHING; + + if (wwid) { + if (_blacklist_exceptions(elist, wwid)) + r = MATCH_WWID_BLIST_EXCEPT; + else if (_blacklist(blist, wwid)) + r = MATCH_WWID_BLIST; + } + + log_filter(dev, NULL, NULL, wwid, NULL, NULL, r); + return r; } int -filter_wwid (vector blist, vector elist, char * wwid, char * dev) +filter_protocol(vector blist, vector elist, struct path * pp) { - int r = _filter_wwid(blist, elist, wwid); - log_filter(dev, NULL, NULL, wwid, NULL, r); + char buf[PROTOCOL_BUF_SIZE]; + int r = MATCH_NOTHING; + + if (pp) { + snprint_path_protocol(buf, sizeof(buf), pp); + + if (_blacklist_exceptions(elist, buf)) + r = MATCH_PROTOCOL_BLIST_EXCEPT; + else if (_blacklist(blist, buf)) + r = MATCH_PROTOCOL_BLIST; + } + + log_filter(pp->dev, NULL, NULL, NULL, NULL, buf, r); return r; } int -_filter_path (struct config * conf, struct path * pp) +filter_path (struct config * conf, struct path * pp) { int r; r = filter_property(conf, pp->udev); if (r > 0) return r; - - r = _filter_devnode(conf->blist_devnode, conf->elist_devnode,pp->dev); + r = filter_devnode(conf->blist_devnode, conf->elist_devnode, pp->dev); if (r > 0) return r; - r = _filter_device(conf->blist_device, conf->elist_device, - pp->vendor_id, pp->product_id); + r = filter_device(conf->blist_device, conf->elist_device, + pp->vendor_id, pp->product_id, pp->dev); if (r > 0) return r; - r = _filter_wwid(conf->blist_wwid, conf->elist_wwid, pp->wwid); - return r; -} - -int -filter_path (struct config * conf, struct path * pp) -{ - int r=_filter_path(conf, pp); - log_filter(pp->dev, pp->vendor_id, pp->product_id, pp->wwid, NULL, r); + r = filter_protocol(conf->blist_protocol, conf->elist_protocol, pp); + if (r > 0) + return r; + r = filter_wwid(conf->blist_wwid, conf->elist_wwid, pp->wwid, pp->dev); return r; } -int -_filter_property (struct config *conf, const char *env) -{ - if (_blacklist_exceptions(conf->elist_property, env)) - return MATCH_PROPERTY_BLIST_EXCEPT; - if (_blacklist(conf->blist_property, env)) - return MATCH_PROPERTY_BLIST; - - return 0; -} - int filter_property(struct config * conf, struct udev_device * udev) { const char *devname = udev_device_get_sysname(udev); struct udev_list_entry *list_entry; - int r; - - if (!udev) - return 0; - - udev_list_entry_foreach(list_entry, + const char *env = NULL; + int r = MATCH_NOTHING; + + if (udev) { + /* + * This is the inverse of the 'normal' matching; + * the environment variable _has_ to match. + */ + r = MATCH_PROPERTY_BLIST_MISSING; + udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(udev)) { - const char *env; - - env = udev_list_entry_get_name(list_entry); - if (!env) - continue; - r = _filter_property(conf, env); - if (r) { - log_filter(devname, NULL, NULL, NULL, env, r); - return r; + env = udev_list_entry_get_name(list_entry); + if (!env) + continue; + if (_blacklist_exceptions(conf->elist_property, env)) { + r = MATCH_PROPERTY_BLIST_EXCEPT; + break; + } + if (_blacklist(conf->blist_property, env)) { + r = MATCH_PROPERTY_BLIST; + break; + } + env = NULL; } } - /* - * This is the inverse of the 'normal' matching; - * the environment variable _has_ to match. - */ - log_filter(devname, NULL, NULL, NULL, NULL, - MATCH_PROPERTY_BLIST_MISSING); - return MATCH_PROPERTY_BLIST_MISSING; + log_filter(devname, NULL, NULL, NULL, env, NULL, r); + return r; +} + +static void free_ble(struct blentry *ble) +{ + if (!ble) + return; + regfree(&ble->regex); + FREE(ble->str); + FREE(ble); } void @@ -405,15 +438,45 @@ free_blacklist (vector blist) return; vector_foreach_slot (blist, ble, i) { - if (ble) { - regfree(&ble->regex); - FREE(ble->str); - FREE(ble); - } + free_ble(ble); } vector_free(blist); } +void merge_blacklist(vector blist) +{ + struct blentry *bl1, *bl2; + int i, j; + + vector_foreach_slot(blist, bl1, i) { + j = i + 1; + vector_foreach_slot_after(blist, bl2, j) { + if (!bl1->str || !bl2->str || strcmp(bl1->str, bl2->str)) + continue; + condlog(3, "%s: duplicate blist entry section for %s", + __func__, bl1->str); + free_ble(bl2); + vector_del_slot(blist, j); + j--; + } + } +} + +static void free_ble_device(struct blentry_device *ble) +{ + if (ble) { + if (ble->vendor) { + regfree(&ble->vendor_reg); + FREE(ble->vendor); + } + if (ble->product) { + regfree(&ble->product_reg); + FREE(ble->product); + } + FREE(ble); + } +} + void free_blacklist_device (vector blist) { @@ -424,17 +487,42 @@ free_blacklist_device (vector blist) return; vector_foreach_slot (blist, ble, i) { - if (ble) { - if (ble->vendor) { - regfree(&ble->vendor_reg); - FREE(ble->vendor); - } - if (ble->product) { - regfree(&ble->product_reg); - FREE(ble->product); - } - FREE(ble); - } + free_ble_device(ble); } vector_free(blist); } + +void merge_blacklist_device(vector blist) +{ + struct blentry_device *bl1, *bl2; + int i, j; + + vector_foreach_slot(blist, bl1, i) { + if (!bl1->vendor && !bl1->product) { + free_ble_device(bl1); + vector_del_slot(blist, i); + i--; + } + } + + vector_foreach_slot(blist, bl1, i) { + j = i + 1; + vector_foreach_slot_after(blist, bl2, j) { + if ((!bl1->vendor && bl2->vendor) || + (bl1->vendor && !bl2->vendor) || + (bl1->vendor && bl2->vendor && + strcmp(bl1->vendor, bl2->vendor))) + continue; + if ((!bl1->product && bl2->product) || + (bl1->product && !bl2->product) || + (bl1->product && bl2->product && + strcmp(bl1->product, bl2->product))) + continue; + condlog(3, "%s: duplicate blist entry section for %s:%s", + __func__, bl1->vendor, bl1->product); + free_ble_device(bl2); + vector_del_slot(blist, j); + j--; + } + } +} diff --git a/libmultipath/blacklist.h b/libmultipath/blacklist.h index 443025d..18903b6 100644 --- a/libmultipath/blacklist.h +++ b/libmultipath/blacklist.h @@ -10,10 +10,12 @@ #define MATCH_DEVNODE_BLIST 3 #define MATCH_PROPERTY_BLIST 4 #define MATCH_PROPERTY_BLIST_MISSING 5 +#define MATCH_PROTOCOL_BLIST 6 #define MATCH_WWID_BLIST_EXCEPT -MATCH_WWID_BLIST #define MATCH_DEVICE_BLIST_EXCEPT -MATCH_DEVICE_BLIST #define MATCH_DEVNODE_BLIST_EXCEPT -MATCH_DEVNODE_BLIST #define MATCH_PROPERTY_BLIST_EXCEPT -MATCH_PROPERTY_BLIST +#define MATCH_PROTOCOL_BLIST_EXCEPT -MATCH_PROTOCOL_BLIST struct blentry { char * str; @@ -33,12 +35,15 @@ int setup_default_blist (struct config *); int alloc_ble_device (vector); int filter_devnode (vector, vector, char *); int filter_wwid (vector, vector, char *, char *); -int filter_device (vector, vector, char *, char *); +int filter_device (vector, vector, char *, char *, char *); int filter_path (struct config *, struct path *); int filter_property(struct config *, struct udev_device *); +int filter_protocol(vector, vector, struct path *); int store_ble (vector, char *, int); int set_ble_device (vector, char *, char *, int); void free_blacklist (vector); void free_blacklist_device (vector); +void merge_blacklist(vector); +void merge_blacklist_device(vector); #endif /* _BLACKLIST_H */ diff --git a/libmultipath/checkers.c b/libmultipath/checkers.c index 08cdfc3..0bacc86 100644 --- a/libmultipath/checkers.c +++ b/libmultipath/checkers.c @@ -141,13 +141,6 @@ struct checker * add_checker (char *multipath_dir, char * name) if (!c->free) goto out; - c->repair = (void (*)(struct checker *)) dlsym(c->handle, - "libcheck_repair"); - errstr = dlerror(); - if (errstr != NULL) - condlog(0, "A dynamic linking error occurred: (%s)", errstr); - if (!c->repair) - goto out; done: c->fd = -1; c->sync = 1; @@ -222,20 +215,6 @@ void checker_put (struct checker * dst) free_checker(src); } -void checker_repair (struct checker * c) -{ - if (!checker_selected(c)) - return; - - c->message[0] = '\0'; - if (c->disable) { - MSG(c, "checker disabled"); - return; - } - if (c->repair) - c->repair(c); -} - int checker_check (struct checker * c, int path_state) { int r; @@ -310,7 +289,6 @@ void checker_get (char *multipath_dir, struct checker * dst, char * name) dst->sync = src->sync; strncpy(dst->name, src->name, CHECKER_NAME_LEN); strncpy(dst->message, src->message, CHECKER_MSG_LEN); - dst->repair = src->repair; dst->check = src->check; dst->init = src->init; dst->free = src->free; diff --git a/libmultipath/checkers.h b/libmultipath/checkers.h index 52154ca..7b18a1a 100644 --- a/libmultipath/checkers.h +++ b/libmultipath/checkers.h @@ -86,7 +86,6 @@ enum path_check_state { #define READSECTOR0 "readsector0" #define CCISS_TUR "cciss_tur" #define NONE "none" -#define RBD "rbd" #define ASYNC_TIMEOUT_SEC 30 @@ -113,9 +112,6 @@ struct checker { multipath-wide. Use MALLOC if you want to stuff data in. */ int (*check)(struct checker *); - void (*repair)(struct checker *); /* called if check returns - PATH_DOWN to bring path into - usable state */ int (*init)(struct checker *); /* to allocate the context */ void (*free)(struct checker *); /* to free the context */ }; @@ -136,7 +132,6 @@ void checker_set_async (struct checker *); void checker_set_fd (struct checker *, int); void checker_enable (struct checker *); void checker_disable (struct checker *); -void checker_repair (struct checker *); int checker_check (struct checker *, int); int checker_selected (struct checker *); char * checker_name (struct checker *); @@ -148,6 +143,5 @@ void checker_get (char *, struct checker *, char *); int libcheck_check(struct checker *); int libcheck_init(struct checker *); void libcheck_free(struct checker *); -void libcheck_repair(struct checker *); #endif /* _CHECKERS_H */ diff --git a/libmultipath/checkers/Makefile b/libmultipath/checkers/Makefile index 87c15bd..02caea6 100644 --- a/libmultipath/checkers/Makefile +++ b/libmultipath/checkers/Makefile @@ -15,15 +15,8 @@ LIBS= \ libcheckhp_sw.so \ libcheckrdac.so -ifneq ($(call check_file,/usr/include/rados/librados.h),0) -LIBS += libcheckrbd.so -endif - all: $(LIBS) -libcheckrbd.so: rbd.o - $(CC) $(LDFLAGS) $(SHARED_FLAGS) -o $@ $^ -lrados -ludev - libcheckdirectio.so: libsg.o directio.o $(CC) $(LDFLAGS) $(SHARED_FLAGS) -o $@ $^ -laio diff --git a/libmultipath/checkers/cciss_tur.c b/libmultipath/checkers/cciss_tur.c index 436470c..1cab201 100644 --- a/libmultipath/checkers/cciss_tur.c +++ b/libmultipath/checkers/cciss_tur.c @@ -59,11 +59,6 @@ void libcheck_free (struct checker * c) return; } -void libcheck_repair (struct checker * c) -{ - return; -} - int libcheck_check(struct checker * c) { int rc; diff --git a/libmultipath/checkers/directio.c b/libmultipath/checkers/directio.c index ce60e4c..a80848d 100644 --- a/libmultipath/checkers/directio.c +++ b/libmultipath/checkers/directio.c @@ -118,11 +118,6 @@ void libcheck_free (struct checker * c) free(ct); } -void libcheck_repair (struct checker * c) -{ - return; -} - static int check_state(int fd, struct directio_context *ct, int sync, int timeout_secs) { diff --git a/libmultipath/checkers/emc_clariion.c b/libmultipath/checkers/emc_clariion.c index 9c1ffed..9115b1b 100644 --- a/libmultipath/checkers/emc_clariion.c +++ b/libmultipath/checkers/emc_clariion.c @@ -90,11 +90,6 @@ void libcheck_free (struct checker * c) free(c->context); } -void libcheck_repair (struct checker * c) -{ - return; -} - int libcheck_check (struct checker * c) { unsigned char sense_buffer[128] = { 0, }; diff --git a/libmultipath/checkers/hp_sw.c b/libmultipath/checkers/hp_sw.c index cee9aab..0ad34a6 100644 --- a/libmultipath/checkers/hp_sw.c +++ b/libmultipath/checkers/hp_sw.c @@ -45,11 +45,6 @@ void libcheck_free (struct checker * c) return; } -void libcheck_repair (struct checker * c) -{ - return; -} - static int do_inq(int sg_fd, int cmddt, int evpd, unsigned int pg_op, void *resp, int mx_resp_len, int noisy, unsigned int timeout) diff --git a/libmultipath/checkers/rbd.c b/libmultipath/checkers/rbd.c deleted file mode 100644 index 4ff54f4..0000000 --- a/libmultipath/checkers/rbd.c +++ /dev/null @@ -1,653 +0,0 @@ -/* - * Copyright (c) 2016 Red Hat - * Copyright (c) 2004 Christophe Varoqui - * - * Code based off of tur.c and ceph's krbd.cc - */ -#define _GNU_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "rados/librados.h" - -#include "structs.h" -#include "checkers.h" - -#include "../libmultipath/debug.h" -#include "../libmultipath/util.h" -#include "../libmultipath/time-util.h" -#include "../libmultipath/util.h" - -struct rbd_checker_context; -typedef int (thread_fn)(struct rbd_checker_context *ct, char *msg); - -#define RBD_MSG(msg, fmt, args...) snprintf(msg, CHECKER_MSG_LEN, fmt, ##args); - -#define RBD_FEATURE_EXCLUSIVE_LOCK (1 << 2) - -struct rbd_checker_context { - int rbd_bus_id; - char *client_addr; - char *config_info; - char *snap; - char *pool; - char *image; - char *username; - int remapped; - int blacklisted; - unsigned lock_on_read:1; - - rados_t cluster; - - int state; - int running; - time_t time; - thread_fn *fn; - pthread_t thread; - pthread_mutex_t lock; - pthread_cond_t active; - pthread_spinlock_t hldr_lock; - int holders; - char message[CHECKER_MSG_LEN]; -}; - -int libcheck_init(struct checker * c) -{ - struct rbd_checker_context *ct; - struct udev_device *block_dev; - struct udev_device *bus_dev; - struct udev *udev; - struct stat sb; - const char *block_name, *addr, *config_info, *features_str; - const char *image, *pool, *snap, *username; - uint64_t features = 0; - char sysfs_path[PATH_SIZE]; - int ret; - - ct = malloc(sizeof(struct rbd_checker_context)); - if (!ct) - return 1; - memset(ct, 0, sizeof(struct rbd_checker_context)); - ct->holders = 1; - pthread_cond_init_mono(&ct->active); - pthread_mutex_init(&ct->lock, NULL); - pthread_spin_init(&ct->hldr_lock, PTHREAD_PROCESS_PRIVATE); - c->context = ct; - - /* - * The rbd block layer sysfs device is not linked to the rbd bus - * device that we interact with, so figure that out now. - */ - if (fstat(c->fd, &sb) != 0) - goto free_ct; - - udev = udev_new(); - if (!udev) - goto free_ct; - - block_dev = udev_device_new_from_devnum(udev, 'b', sb.st_rdev); - if (!block_dev) - goto free_udev; - - block_name = udev_device_get_sysname(block_dev); - ret = sscanf(block_name, "rbd%d", &ct->rbd_bus_id); - - udev_device_unref(block_dev); - if (ret != 1) - goto free_udev; - - snprintf(sysfs_path, sizeof(sysfs_path), "/sys/bus/rbd/devices/%d", - ct->rbd_bus_id); - bus_dev = udev_device_new_from_syspath(udev, sysfs_path); - if (!bus_dev) - goto free_udev; - - addr = udev_device_get_sysattr_value(bus_dev, "client_addr"); - if (!addr) { - condlog(0, "rbd%d: Could not find client_addr in rbd sysfs. " - "Try updating kernel", ct->rbd_bus_id); - goto free_dev; - } - - ct->client_addr = strdup(addr); - if (!ct->client_addr) - goto free_dev; - - features_str = udev_device_get_sysattr_value(bus_dev, "features"); - if (!features_str) - goto free_addr; - features = strtoll(features_str, NULL, 16); - if (!(features & RBD_FEATURE_EXCLUSIVE_LOCK)) { - condlog(3, "rbd%d: Exclusive lock not set.", ct->rbd_bus_id); - goto free_addr; - } - - config_info = udev_device_get_sysattr_value(bus_dev, "config_info"); - if (!config_info) - goto free_addr; - - if (!strstr(config_info, "noshare")) { - condlog(3, "rbd%d: Only nonshared clients supported.", - ct->rbd_bus_id); - goto free_addr; - } - - if (strstr(config_info, "lock_on_read")) - ct->lock_on_read = 1; - - ct->config_info = strdup(config_info); - if (!ct->config_info) - goto free_addr; - - username = strstr(config_info, "name="); - if (username) { - char *end; - int len; - - username += 5; - end = strchr(username, ','); - if (!end) - goto free_info; - len = end - username; - - ct->username = malloc(len + 1); - if (!ct->username) - goto free_info; - strncpy(ct->username, username, len); - ct->username[len] = '\0'; - } - - image = udev_device_get_sysattr_value(bus_dev, "name"); - if (!image) - goto free_username; - - ct->image = strdup(image); - if (!ct->image) - goto free_username; - - pool = udev_device_get_sysattr_value(bus_dev, "pool"); - if (!pool) - goto free_image; - - ct->pool = strdup(pool); - if (!ct->pool) - goto free_image; - - snap = udev_device_get_sysattr_value(bus_dev, "current_snap"); - if (!snap) - goto free_pool; - - if (strcmp("-", snap)) { - ct->snap = strdup(snap); - if (!ct->snap) - goto free_pool; - } - - if (rados_create(&ct->cluster, NULL) < 0) { - condlog(0, "rbd%d: Could not create rados cluster", - ct->rbd_bus_id); - goto free_snap; - } - - if (rados_conf_read_file(ct->cluster, NULL) < 0) { - condlog(0, "rbd%d: Could not read rados conf", ct->rbd_bus_id); - goto shutdown_rados; - } - - ret = rados_connect(ct->cluster); - if (ret < 0) { - condlog(0, "rbd%d: Could not connect to rados cluster", - ct->rbd_bus_id); - goto shutdown_rados; - } - - udev_device_unref(bus_dev); - udev_unref(udev); - - condlog(3, "rbd%d checker init %s %s/%s@%s %s", ct->rbd_bus_id, - ct->client_addr, ct->pool, ct->image, ct->snap ? ct->snap : "-", - ct->username ? ct->username : "none"); - return 0; - -shutdown_rados: - rados_shutdown(ct->cluster); -free_snap: - if (ct->snap) - free(ct->snap); -free_pool: - free(ct->pool); -free_image: - free(ct->image); -free_username: - if (ct->username) - free(ct->username); -free_info: - free(ct->config_info); -free_addr: - free(ct->client_addr); -free_dev: - udev_device_unref(bus_dev); -free_udev: - udev_unref(udev); -free_ct: - free(ct); - return 1; -} - -static void cleanup_context(struct rbd_checker_context *ct) -{ - pthread_mutex_destroy(&ct->lock); - pthread_cond_destroy(&ct->active); - pthread_spin_destroy(&ct->hldr_lock); - - rados_shutdown(ct->cluster); - - if (ct->username) - free(ct->username); - if (ct->snap) - free(ct->snap); - free(ct->pool); - free(ct->image); - free(ct->config_info); - free(ct->client_addr); - free(ct); -} - -void libcheck_free(struct checker * c) -{ - if (c->context) { - struct rbd_checker_context *ct = c->context; - int holders; - pthread_t thread; - - pthread_spin_lock(&ct->hldr_lock); - ct->holders--; - holders = ct->holders; - thread = ct->thread; - pthread_spin_unlock(&ct->hldr_lock); - if (holders) - pthread_cancel(thread); - else - cleanup_context(ct); - c->context = NULL; - } -} - -static int rbd_is_blacklisted(struct rbd_checker_context *ct, char *msg) -{ - char *addr_tok, *start, *save; - const char *cmd[2]; - char *blklist, *stat; - size_t blklist_len, stat_len; - int ret; - char *end; - - cmd[0] = "{\"prefix\": \"osd blacklist ls\"}"; - cmd[1] = NULL; - - ret = rados_mon_command(ct->cluster, (const char **)cmd, 1, "", 0, - &blklist, &blklist_len, &stat, &stat_len); - if (ret < 0) { - RBD_MSG(msg, "checker failed: mon command failed %d", ret); - return ret; - } - - if (!blklist || !blklist_len) - goto free_bufs; - - /* - * parse list of addrs with the format - * ipv4:port/nonce date time\n - * or - * [ipv6]:port/nonce date time\n - */ - ret = 0; - for (start = blklist; ; start = NULL) { - addr_tok = strtok_r(start, "\n", &save); - if (!addr_tok || !strlen(addr_tok)) - break; - - end = strchr(addr_tok, ' '); - if (!end) { - RBD_MSG(msg, "checker failed: invalid blacklist %s", - addr_tok); - break; - } - *end = '\0'; - - if (!strcmp(addr_tok, ct->client_addr)) { - ct->blacklisted = 1; - RBD_MSG(msg, "%s is blacklisted", ct->client_addr); - ret = 1; - break; - } - } - -free_bufs: - rados_buffer_free(blklist); - rados_buffer_free(stat); - return ret; -} - -static int rbd_check(struct rbd_checker_context *ct, char *msg) -{ - if (ct->blacklisted || rbd_is_blacklisted(ct, msg) == 1) - return PATH_DOWN; - - RBD_MSG(msg, "checker reports path is up"); - /* - * Path may have issues, but the ceph cluster is at least - * accepting IO, so we can attempt to do IO. - * - * TODO: in future versions, we can run other tests to - * verify OSDs and networks. - */ - return PATH_UP; -} - -static int sysfs_write_rbd_bus(const char *which, const char *buf, - size_t buf_len) -{ - char sysfs_path[PATH_SIZE]; - int fd; - int r; - - /* we require newer kernels so single_major should always be there */ - snprintf(sysfs_path, sizeof(sysfs_path), - "/sys/bus/rbd/%s_single_major", which); - fd = open(sysfs_path, O_WRONLY); - if (fd < 0) - return -errno; - - r = safe_write(fd, buf, buf_len); - close(fd); - return r; -} - -static int rbd_remap(struct rbd_checker_context *ct) -{ - char *argv[11]; - pid_t pid; - int ret = 0, i = 0; - int status; - - pid = fork(); - switch (pid) { - case 0: - argv[i++] = "rbd"; - argv[i++] = "map"; - if (ct->lock_on_read) - argv[i++] = "-o noshare,lock_on_read"; - else - argv[i++] = "-o noshare"; - if (ct->username) { - argv[i++] = "--id"; - argv[i++] = ct->username; - } - argv[i++] = "--pool"; - argv[i++] = ct->pool; - if (ct->snap) { - argv[i++] = "--snap"; - argv[i++] = ct->snap; - } - argv[i++] = ct->image; - argv[i] = NULL; - - ret = execvp(argv[0], argv); - condlog(0, "rbd%d: Error executing rbd: %s", ct->rbd_bus_id, - strerror(errno)); - exit(-1); - case -1: - condlog(0, "rbd%d: fork failed: %s", ct->rbd_bus_id, - strerror(errno)); - return -1; - default: - ret = -1; - wait(&status); - if (WIFEXITED(status)) { - status = WEXITSTATUS(status); - if (status == 0) - ret = 0; - else - condlog(0, "rbd%d: failed with %d", - ct->rbd_bus_id, status); - } - } - - return ret; -} - -static int sysfs_write_rbd_remove(const char *buf, int buf_len) -{ - return sysfs_write_rbd_bus("remove", buf, buf_len); -} - -static int rbd_rm_blacklist(struct rbd_checker_context *ct) -{ - const char *cmd[2]; - char *stat, *cmd_str; - size_t stat_len; - int ret; - - ret = asprintf(&cmd_str, "{\"prefix\": \"osd blacklist\", \"blacklistop\": \"rm\", \"addr\": \"%s\"}", - ct->client_addr); - if (ret == -1) - return -ENOMEM; - - cmd[0] = cmd_str; - cmd[1] = NULL; - - ret = rados_mon_command(ct->cluster, (const char **)cmd, 1, "", 0, - NULL, NULL, &stat, &stat_len); - if (ret < 0) { - condlog(1, "rbd%d: repair failed to remove blacklist for %s %d", - ct->rbd_bus_id, ct->client_addr, ret); - goto free_cmd; - } - - condlog(1, "rbd%d: repair rm blacklist for %s", - ct->rbd_bus_id, ct->client_addr); - free(stat); -free_cmd: - free(cmd_str); - return ret; -} - -static int rbd_repair(struct rbd_checker_context *ct, char *msg) -{ - char del[17]; - int ret; - - if (!ct->blacklisted) - return PATH_UP; - - if (!ct->remapped) { - ret = rbd_remap(ct); - if (ret) { - RBD_MSG(msg, "repair failed to remap. Err %d", ret); - return PATH_DOWN; - } - } - ct->remapped = 1; - - snprintf(del, sizeof(del), "%d force", ct->rbd_bus_id); - ret = sysfs_write_rbd_remove(del, strlen(del) + 1); - if (ret) { - RBD_MSG(msg, "repair failed to clean up. Err %d", ret); - return PATH_DOWN; - } - - ret = rbd_rm_blacklist(ct); - if (ret) { - RBD_MSG(msg, "repair could not remove blacklist entry. Err %d", - ret); - return PATH_DOWN; - } - - ct->remapped = 0; - ct->blacklisted = 0; - - RBD_MSG(msg, "has been repaired"); - return PATH_UP; -} - -#define rbd_thread_cleanup_push(ct) pthread_cleanup_push(cleanup_func, ct) -#define rbd_thread_cleanup_pop(ct) pthread_cleanup_pop(1) - -static void cleanup_func(void *data) -{ - int holders; - struct rbd_checker_context *ct = data; - pthread_spin_lock(&ct->hldr_lock); - ct->holders--; - holders = ct->holders; - ct->thread = 0; - pthread_spin_unlock(&ct->hldr_lock); - if (!holders) - cleanup_context(ct); - rcu_unregister_thread(); -} - -static void *rbd_thread(void *ctx) -{ - struct rbd_checker_context *ct = ctx; - int state; - - /* This thread can be canceled, so setup clean up */ - rbd_thread_cleanup_push(ct) - rcu_register_thread(); - condlog(3, "rbd%d: thread starting up", ct->rbd_bus_id); - - ct->message[0] = '\0'; - - /* checker start up */ - pthread_mutex_lock(&ct->lock); - ct->state = PATH_PENDING; - pthread_mutex_unlock(&ct->lock); - - state = ct->fn(ct, ct->message); - - /* checker done */ - pthread_mutex_lock(&ct->lock); - ct->state = state; - pthread_cond_signal(&ct->active); - pthread_mutex_unlock(&ct->lock); - - condlog(3, "rbd%d: thead finished, state %s", ct->rbd_bus_id, - checker_state_name(state)); - rbd_thread_cleanup_pop(ct); - return ((void *)0); -} - -static void rbd_timeout(struct timespec *tsp) -{ - clock_gettime(CLOCK_MONOTONIC, tsp); - tsp->tv_nsec += 1000 * 1000; /* 1 millisecond */ - normalize_timespec(tsp); -} - -static int rbd_exec_fn(struct checker *c, thread_fn *fn) -{ - struct rbd_checker_context *ct = c->context; - struct timespec tsp; - pthread_attr_t attr; - int rbd_status, r; - - if (c->sync) - return fn(ct, c->message); - /* - * Async mode - */ - r = pthread_mutex_lock(&ct->lock); - if (r != 0) { - condlog(2, "rbd%d: mutex lock failed with %d", ct->rbd_bus_id, - r); - MSG(c, "rbd%d: thread failed to initialize", ct->rbd_bus_id); - return PATH_WILD; - } - - if (ct->running) { - /* Check if checker is still running */ - if (ct->thread) { - condlog(3, "rbd%d: thread not finished", - ct->rbd_bus_id); - rbd_status = PATH_PENDING; - } else { - /* checker done */ - ct->running = 0; - rbd_status = ct->state; - strncpy(c->message, ct->message, CHECKER_MSG_LEN); - c->message[CHECKER_MSG_LEN - 1] = '\0'; - } - pthread_mutex_unlock(&ct->lock); - } else { - /* Start new checker */ - ct->state = PATH_UNCHECKED; - ct->fn = fn; - pthread_spin_lock(&ct->hldr_lock); - ct->holders++; - pthread_spin_unlock(&ct->hldr_lock); - setup_thread_attr(&attr, 32 * 1024, 1); - r = pthread_create(&ct->thread, &attr, rbd_thread, ct); - if (r) { - pthread_mutex_unlock(&ct->lock); - ct->thread = 0; - ct->holders--; - condlog(3, "rbd%d failed to start rbd thread, using sync mode", - ct->rbd_bus_id); - return fn(ct, c->message); - } - pthread_attr_destroy(&attr); - rbd_timeout(&tsp); - r = pthread_cond_timedwait(&ct->active, &ct->lock, &tsp); - rbd_status = ct->state; - strncpy(c->message, ct->message,CHECKER_MSG_LEN); - c->message[CHECKER_MSG_LEN -1] = '\0'; - pthread_mutex_unlock(&ct->lock); - - if (ct->thread && - (rbd_status == PATH_PENDING || rbd_status == PATH_UNCHECKED)) { - condlog(3, "rbd%d: thread still running", - ct->rbd_bus_id); - ct->running = 1; - rbd_status = PATH_PENDING; - } - } - - return rbd_status; -} - -void libcheck_repair(struct checker * c) -{ - struct rbd_checker_context *ct = c->context; - - if (!ct || !ct->blacklisted) - return; - rbd_exec_fn(c, rbd_repair); -} - -int libcheck_check(struct checker * c) -{ - struct rbd_checker_context *ct = c->context; - - if (!ct) - return PATH_UNCHECKED; - - if (ct->blacklisted) - return PATH_DOWN; - - return rbd_exec_fn(c, rbd_check); -} diff --git a/libmultipath/checkers/rdac.c b/libmultipath/checkers/rdac.c index a643a4a..5104e4e 100644 --- a/libmultipath/checkers/rdac.c +++ b/libmultipath/checkers/rdac.c @@ -139,11 +139,6 @@ void libcheck_free (struct checker * c) return; } -void libcheck_repair (struct checker * c) -{ - return; -} - static int do_inq(int sg_fd, unsigned int pg_op, void *resp, int mx_resp_len, unsigned int timeout) diff --git a/libmultipath/checkers/readsector0.c b/libmultipath/checkers/readsector0.c index 8fccb46..1c2a868 100644 --- a/libmultipath/checkers/readsector0.c +++ b/libmultipath/checkers/readsector0.c @@ -23,11 +23,6 @@ void libcheck_free (struct checker * c) return; } -void libcheck_repair (struct checker * c) -{ - return; -} - int libcheck_check (struct checker * c) { unsigned char buf[4096]; diff --git a/libmultipath/checkers/tur.c b/libmultipath/checkers/tur.c index eb3348d..86c0cdc 100644 --- a/libmultipath/checkers/tur.c +++ b/libmultipath/checkers/tur.c @@ -39,34 +39,21 @@ struct tur_checker_context { dev_t devt; int state; - int running; + int running; /* uatomic access only */ int fd; unsigned int timeout; time_t time; pthread_t thread; pthread_mutex_t lock; pthread_cond_t active; - int holders; + int holders; /* uatomic access only */ char message[CHECKER_MSG_LEN]; }; -static const char *tur_devt(char *devt_buf, int size, - struct tur_checker_context *ct) -{ - dev_t devt; - - pthread_mutex_lock(&ct->lock); - devt = ct->devt; - pthread_mutex_unlock(&ct->lock); - - snprintf(devt_buf, size, "%d:%d", major(devt), minor(devt)); - return devt_buf; -} - int libcheck_init (struct checker * c) { struct tur_checker_context *ct; - pthread_mutexattr_t attr; + struct stat sb; ct = malloc(sizeof(struct tur_checker_context)); if (!ct) @@ -77,10 +64,9 @@ int libcheck_init (struct checker * c) ct->fd = -1; uatomic_set(&ct->holders, 1); pthread_cond_init_mono(&ct->active); - pthread_mutexattr_init(&attr); - pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); - pthread_mutex_init(&ct->lock, &attr); - pthread_mutexattr_destroy(&attr); + pthread_mutex_init(&ct->lock, NULL); + if (fstat(c->fd, &sb) == 0) + ct->devt = sb.st_rdev; c->context = ct; return 0; @@ -112,22 +98,8 @@ void libcheck_free (struct checker * c) return; } -void libcheck_repair (struct checker * c) -{ - return; -} - -#define TUR_MSG(fmt, args...) \ - do { \ - char msg[CHECKER_MSG_LEN]; \ - \ - snprintf(msg, sizeof(msg), fmt, ##args); \ - copy_message(cb_arg, msg); \ - } while (0) - static int -tur_check(int fd, unsigned int timeout, - void (*copy_message)(void *, const char *), void *cb_arg) +tur_check(int fd, unsigned int timeout, char *msg) { struct sg_io_hdr io_hdr; unsigned char turCmdBlk[TUR_CMD_LEN] = { 0x00, 0, 0, 0, 0, 0 }; @@ -146,7 +118,7 @@ retry: io_hdr.timeout = timeout * 1000; io_hdr.pack_id = 0; if (ioctl(fd, SG_IO, &io_hdr) < 0) { - TUR_MSG(MSG_TUR_DOWN); + snprintf(msg, CHECKER_MSG_LEN, MSG_TUR_DOWN); return PATH_DOWN; } if ((io_hdr.status & 0x7e) == 0x18) { @@ -154,7 +126,7 @@ retry: * SCSI-3 arrays might return * reservation conflict on TUR */ - TUR_MSG(MSG_TUR_UP); + snprintf(msg, CHECKER_MSG_LEN, MSG_TUR_UP); return PATH_UP; } if (io_hdr.info & SG_INFO_OK_MASK) { @@ -199,14 +171,14 @@ retry: * LOGICAL UNIT NOT ACCESSIBLE, * TARGET PORT IN STANDBY STATE */ - TUR_MSG(MSG_TUR_GHOST); + snprintf(msg, CHECKER_MSG_LEN, MSG_TUR_GHOST); return PATH_GHOST; } } - TUR_MSG(MSG_TUR_DOWN); + snprintf(msg, CHECKER_MSG_LEN, MSG_TUR_DOWN); return PATH_DOWN; } - TUR_MSG(MSG_TUR_UP); + snprintf(msg, CHECKER_MSG_LEN, MSG_TUR_UP); return PATH_UP; } @@ -224,45 +196,31 @@ static void cleanup_func(void *data) rcu_unregister_thread(); } -static void copy_msg_to_tcc(void *ct_p, const char *msg) -{ - struct tur_checker_context *ct = ct_p; - - pthread_mutex_lock(&ct->lock); - strlcpy(ct->message, msg, sizeof(ct->message)); - pthread_mutex_unlock(&ct->lock); -} - static void *tur_thread(void *ctx) { struct tur_checker_context *ct = ctx; int state, running; - char devt[32]; + char msg[CHECKER_MSG_LEN]; /* This thread can be canceled, so setup clean up */ tur_thread_cleanup_push(ct); rcu_register_thread(); - condlog(3, "%s: tur checker starting up", - tur_devt(devt, sizeof(devt), ct)); - - /* TUR checker start up */ - pthread_mutex_lock(&ct->lock); - ct->state = PATH_PENDING; - ct->message[0] = '\0'; - pthread_mutex_unlock(&ct->lock); + condlog(3, "%d:%d : tur checker starting up", major(ct->devt), + minor(ct->devt)); - state = tur_check(ct->fd, ct->timeout, copy_msg_to_tcc, ct->message); + state = tur_check(ct->fd, ct->timeout, msg); pthread_testcancel(); /* TUR checker done */ pthread_mutex_lock(&ct->lock); ct->state = state; + strlcpy(ct->message, msg, sizeof(ct->message)); pthread_cond_signal(&ct->active); pthread_mutex_unlock(&ct->lock); - condlog(3, "%s: tur checker finished, state %s", - tur_devt(devt, sizeof(devt), ct), checker_state_name(state)); + condlog(3, "%d:%d : tur checker finished, state %s", major(ct->devt), + minor(ct->devt), checker_state_name(state)); running = uatomic_xchg(&ct->running, 0); if (!running) @@ -299,76 +257,64 @@ static int tur_check_async_timeout(struct checker *c) return (now.tv_sec > ct->time); } -static void copy_msg_to_checker(void *c_p, const char *msg) -{ - struct checker *c = c_p; - - strlcpy(c->message, msg, sizeof(c->message)); -} - int libcheck_check(struct checker * c) { struct tur_checker_context *ct = c->context; struct timespec tsp; - struct stat sb; pthread_attr_t attr; int tur_status, r; - char devt[32]; if (!ct) return PATH_UNCHECKED; - if (fstat(c->fd, &sb) == 0) { - pthread_mutex_lock(&ct->lock); - ct->devt = sb.st_rdev; - pthread_mutex_unlock(&ct->lock); - } - if (c->sync) - return tur_check(c->fd, c->timeout, copy_msg_to_checker, c); + return tur_check(c->fd, c->timeout, c->message); /* * Async mode */ - r = pthread_mutex_lock(&ct->lock); - if (r != 0) { - condlog(2, "%s: tur mutex lock failed with %d", - tur_devt(devt, sizeof(devt), ct), r); - MSG(c, MSG_TUR_FAILED); - return PATH_WILD; - } - if (ct->thread) { if (tur_check_async_timeout(c)) { int running = uatomic_xchg(&ct->running, 0); - if (running) + if (running) { pthread_cancel(ct->thread); - condlog(3, "%s: tur checker timeout", - tur_devt(devt, sizeof(devt), ct)); + condlog(3, "%d:%d : tur checker timeout", + major(ct->devt), minor(ct->devt)); + MSG(c, MSG_TUR_TIMEOUT); + tur_status = PATH_TIMEOUT; + } else { + pthread_mutex_lock(&ct->lock); + tur_status = ct->state; + strlcpy(c->message, ct->message, + sizeof(c->message)); + pthread_mutex_unlock(&ct->lock); + } ct->thread = 0; - MSG(c, MSG_TUR_TIMEOUT); - tur_status = PATH_TIMEOUT; } else if (uatomic_read(&ct->running) != 0) { - condlog(3, "%s: tur checker not finished", - tur_devt(devt, sizeof(devt), ct)); + condlog(3, "%d:%d : tur checker not finished", + major(ct->devt), minor(ct->devt)); tur_status = PATH_PENDING; } else { /* TUR checker done */ ct->thread = 0; + pthread_mutex_lock(&ct->lock); tur_status = ct->state; strlcpy(c->message, ct->message, sizeof(c->message)); + pthread_mutex_unlock(&ct->lock); } - pthread_mutex_unlock(&ct->lock); } else { - if (uatomic_read(&ct->running) != 0) { - /* pthread cancel failed. continue in sync mode */ - pthread_mutex_unlock(&ct->lock); - condlog(3, "%s: tur thread not responding", - tur_devt(devt, sizeof(devt), ct)); - return PATH_TIMEOUT; + if (uatomic_read(&ct->holders) > 1) { + /* The thread has been cancelled but hasn't + * quilt. Fail back to synchronous mode */ + condlog(3, "%d:%d : tur checker failing back to sync", + major(ct->devt), minor(ct->devt)); + return tur_check(c->fd, c->timeout, c->message); } /* Start new TUR checker */ - ct->state = PATH_UNCHECKED; + pthread_mutex_lock(&ct->lock); + tur_status = ct->state = PATH_PENDING; + ct->message[0] = '\0'; + pthread_mutex_unlock(&ct->lock); ct->fd = c->fd; ct->timeout = c->timeout; uatomic_add(&ct->holders, 1); @@ -381,22 +327,23 @@ int libcheck_check(struct checker * c) uatomic_sub(&ct->holders, 1); uatomic_set(&ct->running, 0); ct->thread = 0; - pthread_mutex_unlock(&ct->lock); - condlog(3, "%s: failed to start tur thread, using" - " sync mode", tur_devt(devt, sizeof(devt), ct)); - return tur_check(c->fd, c->timeout, - copy_msg_to_checker, c); + condlog(3, "%d:%d : failed to start tur thread, using" + " sync mode", major(ct->devt), minor(ct->devt)); + return tur_check(c->fd, c->timeout, c->message); } tur_timeout(&tsp); - r = pthread_cond_timedwait(&ct->active, &ct->lock, &tsp); - tur_status = ct->state; - strlcpy(c->message, ct->message, sizeof(c->message)); + pthread_mutex_lock(&ct->lock); + if (ct->state == PATH_PENDING) + r = pthread_cond_timedwait(&ct->active, &ct->lock, + &tsp); + if (!r) { + tur_status = ct->state; + strlcpy(c->message, ct->message, sizeof(c->message)); + } pthread_mutex_unlock(&ct->lock); - if (uatomic_read(&ct->running) != 0 && - (tur_status == PATH_PENDING || tur_status == PATH_UNCHECKED)) { - condlog(3, "%s: tur checker still running", - tur_devt(devt, sizeof(devt), ct)); - tur_status = PATH_PENDING; + if (tur_status == PATH_PENDING) { + condlog(3, "%d:%d : tur checker still running", + major(ct->devt), minor(ct->devt)); } else { int running = uatomic_xchg(&ct->running, 0); if (running) diff --git a/libmultipath/config.c b/libmultipath/config.c index 085a3e1..0aef186 100644 --- a/libmultipath/config.c +++ b/libmultipath/config.c @@ -28,7 +28,7 @@ #include "propsel.h" static int -hwe_strmatch (struct hwentry *hwe1, struct hwentry *hwe2) +hwe_strmatch (const struct hwentry *hwe1, const struct hwentry *hwe2) { if ((hwe2->vendor && !hwe1->vendor) || (hwe1->vendor && (!hwe2->vendor || @@ -49,7 +49,7 @@ hwe_strmatch (struct hwentry *hwe1, struct hwentry *hwe2) } static struct hwentry * -find_hwe_strmatch (vector hwtable, struct hwentry *hwe) +find_hwe_strmatch (const struct _vector *hwtable, const struct hwentry *hwe) { int i; struct hwentry *tmp, *ret = NULL; @@ -64,7 +64,8 @@ find_hwe_strmatch (vector hwtable, struct hwentry *hwe) } static int -hwe_regmatch (struct hwentry *hwe1, struct hwentry *hwe2) +hwe_regmatch (const struct hwentry *hwe1, const char *vendor, + const char *product, const char *revision) { regex_t vre, pre, rre; int retval = 1; @@ -81,13 +82,13 @@ hwe_regmatch (struct hwentry *hwe1, struct hwentry *hwe2) regcomp(&rre, hwe1->revision, REG_EXTENDED|REG_NOSUB)) goto out_pre; - if ((hwe2->vendor || hwe2->product || hwe2->revision) && - (!hwe1->vendor || !hwe2->vendor || - !regexec(&vre, hwe2->vendor, 0, NULL, 0)) && - (!hwe1->product || !hwe2->product || - !regexec(&pre, hwe2->product, 0, NULL, 0)) && - (!hwe1->revision || !hwe2->revision || - !regexec(&rre, hwe2->revision, 0, NULL, 0))) + if ((vendor || product || revision) && + (!hwe1->vendor || !vendor || + !regexec(&vre, vendor, 0, NULL, 0)) && + (!hwe1->product || !product || + !regexec(&pre, product, 0, NULL, 0)) && + (!hwe1->revision || !revision || + !regexec(&rre, revision, 0, NULL, 0))) retval = 0; if (hwe1->revision) @@ -102,28 +103,43 @@ out: return retval; } -struct hwentry * -find_hwe (vector hwtable, char * vendor, char * product, char * revision) +static void _log_match(const char *fn, const struct hwentry *h, + const char *vendor, const char *product, + const char *revision) { - int i; - struct hwentry hwe, *tmp, *ret = NULL; + condlog(4, "%s: found match /%s:%s:%s/ for '%s:%s:%s'", fn, + h->vendor, h->product, h->revision, + vendor, product, revision); +} +#define log_match(h, v, p, r) _log_match(__func__, (h), (v), (p), (r)) + +int +find_hwe (const struct _vector *hwtable, + const char * vendor, const char * product, const char * revision, + vector result) +{ + int i, n = 0; + struct hwentry *tmp; - hwe.vendor = vendor; - hwe.product = product; - hwe.revision = revision; /* - * Search backwards here. + * Search backwards here, and add forward. * User modified entries are attached at the end of * the list, so we have to check them first before * continuing to the generic entries */ + vector_reset(result); vector_foreach_slot_backwards (hwtable, tmp, i) { - if (hwe_regmatch(tmp, &hwe)) + if (hwe_regmatch(tmp, vendor, product, revision)) continue; - ret = tmp; - break; + if (vector_alloc_slot(result) != NULL) { + vector_set_slot(result, tmp); + n++; + } + log_match(tmp, vendor, product, revision); } - return ret; + condlog(n > 1 ? 3 : 4, "%s: found %d hwtable matches for %s:%s:%s", + __func__, n, vendor, product, revision); + return n; } struct mpentry *find_mpe(vector mptable, char *wwid) @@ -283,7 +299,7 @@ alloc_hwe (void) } static char * -set_param_str(char * str) +set_param_str(const char * str) { char * dst; int len; @@ -352,6 +368,7 @@ merge_hwe (struct hwentry * dst, struct hwentry * src) merge_num(skip_kpartx); merge_num(max_sectors_kb); merge_num(ghost_delay); + merge_num(all_tg_pt); snprintf(id, sizeof(id), "%s/%s", dst->vendor, dst->product); reconcile_features_with_options(id, &dst->features, @@ -360,6 +377,74 @@ merge_hwe (struct hwentry * dst, struct hwentry * src) return 0; } +static int +merge_mpe(struct mpentry *dst, struct mpentry *src) +{ + if (!dst || !src) + return 1; + + merge_str(alias); + merge_str(uid_attribute); + merge_str(getuid); + merge_str(selector); + merge_str(features); + merge_str(prio_name); + merge_str(prio_args); + + if (dst->prkey_source == PRKEY_SOURCE_NONE && + src->prkey_source != PRKEY_SOURCE_NONE) { + dst->prkey_source = src->prkey_source; + memcpy(&dst->reservation_key, &src->reservation_key, + sizeof(dst->reservation_key)); + } + + merge_num(pgpolicy); + merge_num(pgfailback); + merge_num(rr_weight); + merge_num(no_path_retry); + merge_num(minio); + merge_num(minio_rq); + merge_num(flush_on_last_del); + merge_num(attribute_flags); + merge_num(user_friendly_names); + merge_num(deferred_remove); + merge_num(delay_watch_checks); + merge_num(delay_wait_checks); + merge_num(marginal_path_err_sample_time); + merge_num(marginal_path_err_rate_threshold); + merge_num(marginal_path_err_recheck_gap_time); + merge_num(marginal_path_double_failed_time); + merge_num(skip_kpartx); + merge_num(max_sectors_kb); + merge_num(ghost_delay); + merge_num(uid); + merge_num(gid); + merge_num(mode); + + return 0; +} + +void merge_mptable(vector mptable) +{ + struct mpentry *mp1, *mp2; + int i, j; + + vector_foreach_slot(mptable, mp1, i) { + j = i + 1; + vector_foreach_slot_after(mptable, mp2, j) { + if (strcmp(mp1->wwid, mp2->wwid)) + continue; + condlog(1, "%s: duplicate multipath config section for %s", + __func__, mp1->wwid); + merge_mpe(mp2, mp1); + free_mpe(mp1); + vector_del_slot(mptable, i); + i--; + break; + } + } +} + int store_hwe (vector hwtable, struct hwentry * dhwe) { @@ -436,32 +521,33 @@ out: } static void -factorize_hwtable (vector hw, int n) +factorize_hwtable (vector hw, int n, const char *table_desc) { struct hwentry *hwe1, *hwe2; int i, j; restart: vector_foreach_slot(hw, hwe1, i) { - if (i == n) - break; - j = n; + /* drop invalid device configs */ + if (i >= n && (!hwe1->vendor || !hwe1->product)) { + condlog(0, "device config in %s missing vendor or product parameter", + table_desc); + vector_del_slot(hw, i--); + free_hwe(hwe1); + continue; + } + j = n > i + 1 ? n : i + 1; vector_foreach_slot_after(hw, hwe2, j) { - /* drop invalid device configs */ - if (!hwe2->vendor || !hwe2->product) { - condlog(0, "device config missing vendor or product parameter"); - vector_del_slot(hw, j--); - free_hwe(hwe2); - continue; - } - if (hwe_regmatch(hwe1, hwe2)) - continue; - /* dup */ - merge_hwe(hwe2, hwe1); if (hwe_strmatch(hwe2, hwe1) == 0) { + condlog(i >= n ? 1 : 3, + "%s: duplicate device section for %s:%s:%s in %s", + __func__, hwe1->vendor, hwe1->product, + hwe1->revision, table_desc); vector_del_slot(hw, i); + merge_hwe(hwe2, hwe1); free_hwe(hwe1); - n -= 1; + if (i < n) + n -= 1; /* * Play safe here; we have modified * the original vector so the outer @@ -537,11 +623,13 @@ free_config (struct config * conf) free_blacklist(conf->blist_devnode); free_blacklist(conf->blist_wwid); free_blacklist(conf->blist_property); + free_blacklist(conf->blist_protocol); free_blacklist_device(conf->blist_device); free_blacklist(conf->elist_devnode); free_blacklist(conf->elist_wwid); free_blacklist(conf->elist_property); + free_blacklist(conf->elist_protocol); free_blacklist_device(conf->elist_device); free_mptable(conf->mptable); @@ -551,6 +639,11 @@ free_config (struct config * conf) FREE(conf); } +static void free_namelist(void *nl) +{ + free(nl); +} + /* if multipath fails to process the config directory, it should continue, * with just a warning message */ static void @@ -574,7 +667,9 @@ process_config_dir(struct config *conf, vector keywords, char *dir) condlog(0, "couldn't open configuration dir '%s': %s", dir, strerror(errno)); return; - } + } else if (n == 0) + return; + pthread_cleanup_push(free_namelist, namelist); for (i = 0; i < n; i++) { if (!strstr(namelist[i]->d_name, ".conf")) continue; @@ -582,10 +677,10 @@ process_config_dir(struct config *conf, vector keywords, char *dir) snprintf(path, LINE_MAX, "%s/%s", dir, namelist[i]->d_name); path[LINE_MAX-1] = '\0'; process_file(conf, path); - if (VECTOR_SIZE(conf->hwtable) > old_hwtable_size) - factorize_hwtable(conf->hwtable, old_hwtable_size); - + factorize_hwtable(conf->hwtable, old_hwtable_size, + namelist[i]->d_name); } + pthread_cleanup_pop(1); } struct config * @@ -599,8 +694,7 @@ load_config (char * file) /* * internal defaults */ - if (!conf->verbosity) - conf->verbosity = DEFAULT_VERBOSITY; + conf->verbosity = DEFAULT_VERBOSITY; get_sys_max_fds(&conf->max_fds); conf->bindings_file = set_default(DEFAULT_BINDINGS_FILE); @@ -612,7 +706,8 @@ load_config (char * file) conf->checkint = DEFAULT_CHECKINT; conf->max_checkint = 0; conf->force_sync = DEFAULT_FORCE_SYNC; - conf->partition_delim = DEFAULT_PARTITION_DELIM; + conf->partition_delim = (default_partition_delim != NULL ? + strdup(default_partition_delim) : NULL); conf->processed_main_config = 0; conf->find_multipaths = DEFAULT_FIND_MULTIPATHS; conf->uxsock_timeout = DEFAULT_REPLY_TIMEOUT; @@ -622,19 +717,20 @@ load_config (char * file) conf->disable_changed_wwids = DEFAULT_DISABLE_CHANGED_WWIDS; conf->remove_retries = 0; conf->ghost_delay = DEFAULT_GHOST_DELAY; + conf->all_tg_pt = DEFAULT_ALL_TG_PT; /* * preload default hwtable */ - if (conf->hwtable == NULL) { - conf->hwtable = vector_alloc(); - - if (!conf->hwtable) + conf->hwtable = vector_alloc(); + if (!conf->hwtable) goto out; - } if (setup_default_hwtable(conf->hwtable)) goto out; +#ifdef CHECK_BUILTIN_HWTABLE + factorize_hwtable(conf->hwtable, 0, "builtin"); +#endif /* * read the config file */ @@ -648,14 +744,7 @@ load_config (char * file) condlog(0, "error parsing config file"); goto out; } - if (VECTOR_SIZE(conf->hwtable) > builtin_hwtable_size) { - /* - * remove duplica in hwtable. config file - * takes precedence over build-in hwtable - */ - factorize_hwtable(conf->hwtable, builtin_hwtable_size); - } - + factorize_hwtable(conf->hwtable, builtin_hwtable_size, file); } conf->processed_main_config = 1; @@ -693,6 +782,12 @@ load_config (char * file) if (!conf->blist_property) goto out; } + if (conf->blist_protocol == NULL) { + conf->blist_protocol = vector_alloc(); + + if (!conf->blist_protocol) + goto out; + } if (conf->elist_devnode == NULL) { conf->elist_devnode = vector_alloc(); @@ -720,6 +815,13 @@ load_config (char * file) if (!conf->elist_property) goto out; } + if (conf->elist_protocol == NULL) { + conf->elist_protocol = vector_alloc(); + + if (!conf->elist_protocol) + goto out; + } + if (setup_default_blist(conf)) goto out; @@ -728,6 +830,17 @@ load_config (char * file) if (!conf->mptable) goto out; } + + merge_mptable(conf->mptable); + merge_blacklist(conf->blist_devnode); + merge_blacklist(conf->blist_property); + merge_blacklist(conf->blist_wwid); + merge_blacklist_device(conf->blist_device); + merge_blacklist(conf->elist_devnode); + merge_blacklist(conf->elist_property); + merge_blacklist(conf->elist_wwid); + merge_blacklist_device(conf->elist_device); + if (conf->bindings_file == NULL) conf->bindings_file = set_default(DEFAULT_BINDINGS_FILE); diff --git a/libmultipath/config.h b/libmultipath/config.h index 6e69a37..7d0cd9a 100644 --- a/libmultipath/config.h +++ b/libmultipath/config.h @@ -37,6 +37,7 @@ enum mpath_cmds { CMD_RESET_WWIDS, CMD_ADD_WWID, CMD_USABLE_PATHS, + CMD_DUMP_CONFIG, }; enum force_reload_types { @@ -82,6 +83,7 @@ struct hwentry { int skip_kpartx; int max_sectors_kb; int ghost_delay; + int all_tg_pt; char * bl_product; }; @@ -97,6 +99,7 @@ struct mpentry { char * prio_args; int prkey_source; struct be64 reservation_key; + uint8_t sa_flags; int pgpolicy; int pgfailback; int rr_weight; @@ -194,7 +197,9 @@ struct config { char * partition_delim; char * config_dir; int prkey_source; + int all_tg_pt; struct be64 reservation_key; + uint8_t sa_flags; vector keywords; vector mptable; @@ -205,15 +210,19 @@ struct config { vector blist_wwid; vector blist_device; vector blist_property; + vector blist_protocol; vector elist_devnode; vector elist_wwid; vector elist_device; vector elist_property; + vector elist_protocol; }; extern struct udev * udev; -struct hwentry * find_hwe (vector hwtable, char * vendor, char * product, char *revision); +int find_hwe (const struct _vector *hwtable, + const char * vendor, const char * product, const char *revision, + vector result); struct mpentry * find_mpe (vector mptable, char * wwid); char * get_mpe_wwid (vector mptable, char * alias); diff --git a/libmultipath/configure.c b/libmultipath/configure.c index 5796683..09c3dcf 100644 --- a/libmultipath/configure.c +++ b/libmultipath/configure.c @@ -1030,7 +1030,7 @@ int coalesce_paths (struct vectors * vecs, vector newmp, char * refwwid, invalid = 1; pthread_cleanup_pop(1); if (invalid) { - orphan_path(pp1, "wwid blacklisted"); + orphan_path(pp1, "blacklisted"); continue; } @@ -1063,9 +1063,6 @@ int coalesce_paths (struct vectors * vecs, vector newmp, char * refwwid, continue; } - if (pp1->priority == PRIO_UNDEF) - mpp->action = ACT_REJECT; - if (!mpp->paths) { condlog(0, "%s: skip coalesce (no paths)", mpp->alias); remove_map(mpp, vecs, 0); @@ -1091,8 +1088,6 @@ int coalesce_paths (struct vectors * vecs, vector newmp, char * refwwid, mpp->size); mpp->action = ACT_REJECT; } - if (pp2->priority == PRIO_UNDEF) - mpp->action = ACT_REJECT; } verify_paths(mpp, vecs); diff --git a/libmultipath/defaults.c b/libmultipath/defaults.c index 7130e56..c20bb0d 100644 --- a/libmultipath/defaults.c +++ b/libmultipath/defaults.c @@ -6,6 +6,8 @@ #include "defaults.h" #include "memory.h" +const char * const default_partition_delim = DEFAULT_PARTITION_DELIM; + char * set_default (char * str) { diff --git a/libmultipath/defaults.h b/libmultipath/defaults.h index d7b87b4..7f3839f 100644 --- a/libmultipath/defaults.h +++ b/libmultipath/defaults.h @@ -36,6 +36,7 @@ #define DEFAULT_FLUSH FLUSH_DISABLED #define DEFAULT_USER_FRIENDLY_NAMES USER_FRIENDLY_NAMES_OFF #define DEFAULT_FORCE_SYNC 0 +#define UNSET_PARTITION_DELIM "/UNSET/" #define DEFAULT_PARTITION_DELIM NULL #define DEFAULT_SKIP_KPARTX SKIP_KPARTX_OFF #define DEFAULT_DISABLE_CHANGED_WWIDS 1 @@ -43,6 +44,7 @@ #define DEFAULT_GHOST_DELAY GHOST_DELAY_OFF #define DEFAULT_FIND_MULTIPATHS_TIMEOUT -10 #define DEFAULT_UNKNOWN_FIND_MULTIPATHS_TIMEOUT 1 +#define DEFAULT_ALL_TG_PT ALL_TG_PT_OFF #define DEFAULT_CHECKINT 5 #define MAX_CHECKINT(a) (a << 2) @@ -58,3 +60,4 @@ #define MULTIPATH_SHM_BASE "/dev/shm/multipath/" char * set_default (char * str); +extern const char *const default_partition_delim; diff --git a/libmultipath/devmapper.c b/libmultipath/devmapper.c index f2befad..8136d15 100644 --- a/libmultipath/devmapper.c +++ b/libmultipath/devmapper.c @@ -245,13 +245,13 @@ void libmp_dm_init(void) int verbosity; unsigned int version[3]; + if (dm_prereq(version)) + exit(1); conf = get_multipath_config(); verbosity = conf->verbosity; - memcpy(version, conf->version, sizeof(version)); + memcpy(conf->version, version, sizeof(version)); put_multipath_config(conf); dm_init(verbosity); - if (dm_prereq(version)) - exit(1); dm_udev_set_sync_support(libmp_dm_udev_sync); } diff --git a/libmultipath/dict.c b/libmultipath/dict.c index 4040611..bf4701e 100644 --- a/libmultipath/dict.c +++ b/libmultipath/dict.c @@ -22,6 +22,8 @@ #include "util.h" #include #include +#include +#include #include "mpath_cmd.h" #include "dict.h" @@ -31,7 +33,10 @@ set_int(vector strvec, void *ptr) int *int_ptr = (int *)ptr; char * buff; - buff = VECTOR_SLOT(strvec, 1); + buff = set_value(strvec); + if (!buff) + return 1; + *int_ptr = atoi(buff); return 0; @@ -109,9 +114,45 @@ print_nonzero (char *buff, int len, long v) static int print_str (char *buff, int len, const char *ptr) { - if (!ptr) + char *p; + char *last; + const char *q; + + if (!ptr || len <= 0) return 0; - return snprintf(buff, len, "\"%s\"", ptr); + + q = strchr(ptr, '"'); + if (q == NULL) + return snprintf(buff, len, "\"%s\"", ptr); + + last = buff + len - 1; + p = buff; + if (p >= last) + goto out; + *p++ = '"'; + if (p >= last) + goto out; + for (; q; q = strchr(ptr, '"')) { + if (q + 1 - ptr < last - p) + p = mempcpy(p, ptr, q + 1 - ptr); + else { + p = mempcpy(p, ptr, last - p); + goto out; + } + *p++ = '"'; + if (p >= last) + goto out; + ptr = q + 1; + } + p += strlcpy(p, ptr, last - p); + if (p >= last) + goto out; + *p++ = '"'; + *p = '\0'; + return p - buff; +out: + *p = '\0'; + return len; } static int @@ -237,8 +278,28 @@ declare_def_snprint(reassign_maps, print_yes_no) declare_def_handler(multipath_dir, set_str) declare_def_snprint(multipath_dir, print_str) -declare_def_handler(partition_delim, set_str) -declare_def_snprint(partition_delim, print_str) +static int def_partition_delim_handler(struct config *conf, vector strvec) +{ + int rc = set_str(strvec, &conf->partition_delim); + + if (rc != 0) + return rc; + + if (!strcmp(conf->partition_delim, UNSET_PARTITION_DELIM)) { + FREE(conf->partition_delim); + conf->partition_delim = NULL; + } + return 0; +} + +static int snprint_def_partition_delim(struct config *conf, char *buff, + int len, const void *data) +{ + if (default_partition_delim == NULL || conf->partition_delim != NULL) + return print_str(buff, len, conf->partition_delim); + else + return print_str(buff, len, UNSET_PARTITION_DELIM); +} static const char * const find_multipaths_optvals[] = { [FIND_MULTIPATHS_OFF] = "off", @@ -1012,10 +1073,12 @@ snprint_def_log_checker_err (struct config *conf, char * buff, int len, } static int -set_reservation_key(vector strvec, struct be64 *be64_ptr, int *source_ptr) +set_reservation_key(vector strvec, struct be64 *be64_ptr, uint8_t *flags_ptr, + int *source_ptr) { char *buff; uint64_t prkey; + uint8_t sa_flags; buff = set_value(strvec); if (!buff) @@ -1023,35 +1086,43 @@ set_reservation_key(vector strvec, struct be64 *be64_ptr, int *source_ptr) if (strcmp(buff, "file") == 0) { *source_ptr = PRKEY_SOURCE_FILE; + *flags_ptr = 0; put_be64(*be64_ptr, 0); FREE(buff); return 0; } - if (parse_prkey(buff, &prkey) != 0) { + if (parse_prkey_flags(buff, &prkey, &sa_flags) != 0) { FREE(buff); return 1; } *source_ptr = PRKEY_SOURCE_CONF; + *flags_ptr = sa_flags; put_be64(*be64_ptr, prkey); FREE(buff); return 0; } int -print_reservation_key(char * buff, int len, struct be64 key, int source) +print_reservation_key(char * buff, int len, struct be64 key, uint8_t flags, + int source) { + char *flagstr = ""; if (source == PRKEY_SOURCE_NONE) return 0; if (source == PRKEY_SOURCE_FILE) return snprintf(buff, len, "file"); - return snprintf(buff, len, "0x%" PRIx64, get_be64(key)); + if (flags & MPATH_F_APTPL_MASK) + flagstr = ":aptpl"; + return snprintf(buff, len, "0x%" PRIx64 "%s", get_be64(key), + flagstr); } static int def_reservation_key_handler(struct config *conf, vector strvec) { return set_reservation_key(strvec, &conf->reservation_key, + &conf->sa_flags, &conf->prkey_source); } @@ -1060,6 +1131,7 @@ snprint_def_reservation_key (struct config *conf, char * buff, int len, const void * data) { return print_reservation_key(buff, len, conf->reservation_key, + conf->sa_flags, conf->prkey_source); } @@ -1070,6 +1142,7 @@ mp_reservation_key_handler(struct config *conf, vector strvec) if (!mpe) return 1; return set_reservation_key(strvec, &mpe->reservation_key, + &mpe->sa_flags, &mpe->prkey_source); } @@ -1079,6 +1152,7 @@ snprint_mp_reservation_key (struct config *conf, char * buff, int len, { const struct mpentry * mpe = (const struct mpentry *)data; return print_reservation_key(buff, len, mpe->reservation_key, + mpe->sa_flags, mpe->prkey_source); } @@ -1115,7 +1189,8 @@ print_off_int_undef(char * buff, int len, long v) } declare_def_handler(delay_watch_checks, set_off_int_undef) -declare_def_snprint(delay_watch_checks, print_off_int_undef) +declare_def_snprint_defint(delay_watch_checks, print_off_int_undef, + DEFAULT_DELAY_CHECKS) declare_ovr_handler(delay_watch_checks, set_off_int_undef) declare_ovr_snprint(delay_watch_checks, print_off_int_undef) declare_hw_handler(delay_watch_checks, set_off_int_undef) @@ -1123,7 +1198,8 @@ declare_hw_snprint(delay_watch_checks, print_off_int_undef) declare_mp_handler(delay_watch_checks, set_off_int_undef) declare_mp_snprint(delay_watch_checks, print_off_int_undef) declare_def_handler(delay_wait_checks, set_off_int_undef) -declare_def_snprint(delay_wait_checks, print_off_int_undef) +declare_def_snprint_defint(delay_wait_checks, print_off_int_undef, + DEFAULT_DELAY_CHECKS) declare_ovr_handler(delay_wait_checks, set_off_int_undef) declare_ovr_snprint(delay_wait_checks, print_off_int_undef) declare_hw_handler(delay_wait_checks, set_off_int_undef) @@ -1176,6 +1252,13 @@ declare_hw_snprint(ghost_delay, print_off_int_undef) declare_mp_handler(ghost_delay, set_off_int_undef) declare_mp_snprint(ghost_delay, print_off_int_undef) +declare_def_handler(all_tg_pt, set_yes_no_undef) +declare_def_snprint_defint(all_tg_pt, print_yes_no_undef, DEFAULT_ALL_TG_PT) +declare_ovr_handler(all_tg_pt, set_yes_no_undef) +declare_ovr_snprint(all_tg_pt, print_yes_no_undef) +declare_hw_handler(all_tg_pt, set_yes_no_undef) +declare_hw_snprint(all_tg_pt, print_yes_no_undef) + static int def_uxsock_timeout_handler(struct config *conf, vector strvec) @@ -1211,9 +1294,12 @@ blacklist_handler(struct config *conf, vector strvec) conf->blist_device = vector_alloc(); if (!conf->blist_property) conf->blist_property = vector_alloc(); + if (!conf->blist_protocol) + conf->blist_protocol = vector_alloc(); if (!conf->blist_devnode || !conf->blist_wwid || - !conf->blist_device || !conf->blist_property) + !conf->blist_device || !conf->blist_property || + !conf->blist_protocol) return 1; return 0; @@ -1230,9 +1316,12 @@ blacklist_exceptions_handler(struct config *conf, vector strvec) conf->elist_device = vector_alloc(); if (!conf->elist_property) conf->elist_property = vector_alloc(); + if (!conf->elist_protocol) + conf->elist_protocol = vector_alloc(); if (!conf->elist_devnode || !conf->elist_wwid || - !conf->elist_device || !conf->elist_property) + !conf->elist_device || !conf->elist_property || + !conf->elist_protocol) return 1; return 0; @@ -1276,6 +1365,8 @@ declare_ble_handler(blist_wwid) declare_ble_handler(elist_wwid) declare_ble_handler(blist_property) declare_ble_handler(elist_property) +declare_ble_handler(blist_protocol) +declare_ble_handler(elist_protocol) static int snprint_def_uxsock_timeout(struct config *conf, char * buff, int len, @@ -1507,6 +1598,7 @@ init_keywords(vector keywords) install_keyword("prkeys_file", &def_prkeys_file_handler, &snprint_def_prkeys_file); install_keyword("log_checker_err", &def_log_checker_err_handler, &snprint_def_log_checker_err); install_keyword("reservation_key", &def_reservation_key_handler, &snprint_def_reservation_key); + install_keyword("all_tg_pt", &def_all_tg_pt_handler, &snprint_def_all_tg_pt); install_keyword("retain_attached_hw_handler", &def_retain_hwhandler_handler, &snprint_def_retain_hwhandler); install_keyword("detect_prio", &def_detect_prio_handler, &snprint_def_detect_prio); install_keyword("detect_checker", &def_detect_checker_handler, &snprint_def_detect_checker); @@ -1546,6 +1638,7 @@ init_keywords(vector keywords) install_keyword_multi("devnode", &ble_blist_devnode_handler, &snprint_ble_simple); install_keyword_multi("wwid", &ble_blist_wwid_handler, &snprint_ble_simple); install_keyword_multi("property", &ble_blist_property_handler, &snprint_ble_simple); + install_keyword_multi("protocol", &ble_blist_protocol_handler, &snprint_ble_simple); install_keyword_multi("device", &ble_device_handler, NULL); install_sublevel(); install_keyword("vendor", &ble_blist_device_vendor_handler, &snprint_bled_vendor); @@ -1555,6 +1648,7 @@ init_keywords(vector keywords) install_keyword_multi("devnode", &ble_elist_devnode_handler, &snprint_ble_simple); install_keyword_multi("wwid", &ble_elist_wwid_handler, &snprint_ble_simple); install_keyword_multi("property", &ble_elist_property_handler, &snprint_ble_simple); + install_keyword_multi("protocol", &ble_elist_protocol_handler, &snprint_ble_simple); install_keyword_multi("device", &ble_except_device_handler, NULL); install_sublevel(); install_keyword("vendor", &ble_elist_device_vendor_handler, &snprint_bled_vendor); @@ -1616,6 +1710,7 @@ init_keywords(vector keywords) install_keyword("skip_kpartx", &hw_skip_kpartx_handler, &snprint_hw_skip_kpartx); install_keyword("max_sectors_kb", &hw_max_sectors_kb_handler, &snprint_hw_max_sectors_kb); 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_sublevel_end(); install_keyword_root("overrides", &overrides_handler); @@ -1652,6 +1747,7 @@ init_keywords(vector keywords) install_keyword("skip_kpartx", &ovr_skip_kpartx_handler, &snprint_ovr_skip_kpartx); 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_root("multipaths", &multipaths_handler); install_keyword_multi("multipath", &multipath_handler, NULL); diff --git a/libmultipath/dict.h b/libmultipath/dict.h index 7564892..a40ac66 100644 --- a/libmultipath/dict.h +++ b/libmultipath/dict.h @@ -15,6 +15,7 @@ int print_pgpolicy(char *buff, int len, long v); int print_no_path_retry(char *buff, int len, long v); int print_fast_io_fail(char *buff, int len, long v); int print_dev_loss(char *buff, int len, unsigned long v); -int print_reservation_key(char * buff, int len, struct be64 key, int source); +int print_reservation_key(char * buff, int len, struct be64 key, uint8_t + flags, int source); int print_off_int_undef(char *buff, int len, long v); #endif /* _DICT_H */ diff --git a/libmultipath/discovery.c b/libmultipath/discovery.c index 1ef1dfa..b267f07 100644 --- a/libmultipath/discovery.c +++ b/libmultipath/discovery.c @@ -1116,17 +1116,21 @@ get_vpd_sgio (int fd, int pg, char * str, int maxlen) return -ENODATA; } buff_len = get_unaligned_be16(&buff[2]) + 4; - if (buff_len > 4096) + if (buff_len > 4096) { condlog(3, "vpd pg%02x page truncated", pg); - + buff_len = 4096; + } if (pg == 0x80) len = parse_vpd_pg80(buff, str, maxlen); else if (pg == 0x83) len = parse_vpd_pg83(buff, buff_len, str, maxlen); else if (pg == 0xc9 && maxlen >= 8) { - len = buff_len < 8 ? -ENODATA : - (buff_len <= maxlen ? buff_len : maxlen); - memcpy (str, buff, len); + if (buff_len < 8) + len = -ENODATA; + else { + len = (buff_len <= maxlen)? buff_len : maxlen; + memcpy (str, buff, len); + } } else len = -ENOSYS; @@ -1156,27 +1160,27 @@ scsi_sysfs_pathinfo (struct path * pp, vector hwtable) parent = udev_device_get_parent(parent); } if (!attr_path || pp->sg_id.host_no == -1) - return 1; + return PATHINFO_FAILED; if (sysfs_get_vendor(parent, pp->vendor_id, SCSI_VENDOR_SIZE) <= 0) - return 1; + return PATHINFO_FAILED;; condlog(3, "%s: vendor = %s", pp->dev, pp->vendor_id); if (sysfs_get_model(parent, pp->product_id, PATH_PRODUCT_SIZE) <= 0) - return 1; + return PATHINFO_FAILED;; condlog(3, "%s: product = %s", pp->dev, pp->product_id); if (sysfs_get_rev(parent, pp->rev, PATH_REV_SIZE) < 0) - return 1; + return PATHINFO_FAILED;; condlog(3, "%s: rev = %s", pp->dev, pp->rev); /* * set the hwe configlet pointer */ - pp->hwe = find_hwe(hwtable, pp->vendor_id, pp->product_id, pp->rev); + find_hwe(hwtable, pp->vendor_id, pp->product_id, pp->rev, pp->hwe); /* * host / bus / target / lun @@ -1192,12 +1196,12 @@ scsi_sysfs_pathinfo (struct path * pp, vector hwtable) * target node name */ if(sysfs_get_tgt_nodename(pp, pp->tgt_node_name)) - return 1; + return PATHINFO_FAILED; condlog(3, "%s: tgt_node_name = %s", pp->dev, pp->tgt_node_name); - return 0; + return PATHINFO_OK; } static int @@ -1209,17 +1213,17 @@ nvme_sysfs_pathinfo (struct path * pp, vector hwtable) attr_path = udev_device_get_sysname(pp->udev); if (!attr_path) - return 1; + return PATHINFO_FAILED; if (sscanf(attr_path, "nvme%dn%d", &pp->sg_id.host_no, &pp->sg_id.scsi_id) != 2) - return 1; + return PATHINFO_FAILED; parent = udev_device_get_parent_with_subsystem_devtype(pp->udev, "nvme", NULL); if (!parent) - return 1; + return PATHINFO_SKIPPED; attr = udev_device_get_sysattr_value(pp->udev, "nsid"); pp->sg_id.lun = attr ? atoi(attr) : 0; @@ -1240,24 +1244,9 @@ nvme_sysfs_pathinfo (struct path * pp, vector hwtable) condlog(3, "%s: serial = %s", pp->dev, pp->serial); condlog(3, "%s: rev = %s", pp->dev, pp->rev); - pp->hwe = find_hwe(hwtable, pp->vendor_id, pp->product_id, NULL); - - return 0; -} + find_hwe(hwtable, pp->vendor_id, pp->product_id, NULL, pp->hwe); -static int -rbd_sysfs_pathinfo (struct path * pp, vector hwtable) -{ - sprintf(pp->vendor_id, "Ceph"); - sprintf(pp->product_id, "RBD"); - - condlog(3, "%s: vendor = %s product = %s", pp->dev, pp->vendor_id, - pp->product_id); - /* - * set the hwe configlet pointer - */ - pp->hwe = find_hwe(hwtable, pp->vendor_id, pp->product_id, NULL); - return 0; + return PATHINFO_OK; } static int @@ -1275,14 +1264,14 @@ ccw_sysfs_pathinfo (struct path * pp, vector hwtable) parent = udev_device_get_parent(parent); } if (!parent) - return 1; + return PATHINFO_FAILED; sprintf(pp->vendor_id, "IBM"); condlog(3, "%s: vendor = %s", pp->dev, pp->vendor_id); if (sysfs_get_devtype(parent, attr_buff, FILE_NAME_SIZE) <= 0) - return 1; + return PATHINFO_FAILED; if (!strncmp(attr_buff, "3370", 4)) { sprintf(pp->product_id,"S/390 DASD FBA"); @@ -1297,7 +1286,7 @@ ccw_sysfs_pathinfo (struct path * pp, vector hwtable) /* * set the hwe configlet pointer */ - pp->hwe = find_hwe(hwtable, pp->vendor_id, pp->product_id, NULL); + find_hwe(hwtable, pp->vendor_id, pp->product_id, NULL, pp->hwe); /* * host / bus / target / lun @@ -1316,7 +1305,7 @@ ccw_sysfs_pathinfo (struct path * pp, vector hwtable) pp->sg_id.lun); } - return 0; + return PATHINFO_OK; } static int @@ -1340,27 +1329,27 @@ cciss_sysfs_pathinfo (struct path * pp, vector hwtable) parent = udev_device_get_parent(parent); } if (!attr_path || pp->sg_id.host_no == -1) - return 1; + return PATHINFO_FAILED; if (sysfs_get_vendor(parent, pp->vendor_id, SCSI_VENDOR_SIZE) <= 0) - return 1; + return PATHINFO_FAILED; condlog(3, "%s: vendor = %s", pp->dev, pp->vendor_id); if (sysfs_get_model(parent, pp->product_id, PATH_PRODUCT_SIZE) <= 0) - return 1; + return PATHINFO_FAILED; condlog(3, "%s: product = %s", pp->dev, pp->product_id); if (sysfs_get_rev(parent, pp->rev, PATH_REV_SIZE) <= 0) - return 1; + return PATHINFO_FAILED; condlog(3, "%s: rev = %s", pp->dev, pp->rev); /* * set the hwe configlet pointer */ - pp->hwe = find_hwe(hwtable, pp->vendor_id, pp->product_id, pp->rev); + find_hwe(hwtable, pp->vendor_id, pp->product_id, pp->rev, pp->hwe); /* * host / bus / target / lun @@ -1373,7 +1362,8 @@ cciss_sysfs_pathinfo (struct path * pp, vector hwtable) pp->sg_id.channel, pp->sg_id.scsi_id, pp->sg_id.lun); - return 0; + + return PATHINFO_OK; } static int @@ -1382,11 +1372,11 @@ common_sysfs_pathinfo (struct path * pp) dev_t devt; if (!pp) - return 1; + return PATHINFO_FAILED; if (!pp->udev) { condlog(4, "%s: udev not initialised", pp->dev); - return 1; + return PATHINFO_FAILED; } devt = udev_device_get_devnum(pp->udev); snprintf(pp->dev_t, BLK_DEV_SIZE, "%d:%d", major(devt), minor(devt)); @@ -1394,11 +1384,11 @@ common_sysfs_pathinfo (struct path * pp) condlog(3, "%s: dev_t = %s", pp->dev, pp->dev_t); if (sysfs_get_size(pp, &pp->size)) - return 1; + return PATHINFO_FAILED; condlog(3, "%s: size = %llu", pp->dev, pp->size); - return 0; + return PATHINFO_OK; } int @@ -1476,8 +1466,10 @@ path_offline (struct path * pp) int sysfs_pathinfo(struct path * pp, vector hwtable) { - if (common_sysfs_pathinfo(pp)) - return 1; + int r = common_sysfs_pathinfo(pp); + + if (r != PATHINFO_OK) + return r; pp->bus = SYSFS_BUS_UNDEF; if (!strncmp(pp->dev,"cciss",5)) @@ -1486,30 +1478,22 @@ sysfs_pathinfo(struct path * pp, vector hwtable) pp->bus = SYSFS_BUS_CCW; if (!strncmp(pp->dev,"sd", 2)) pp->bus = SYSFS_BUS_SCSI; - if (!strncmp(pp->dev,"rbd", 3)) - pp->bus = SYSFS_BUS_RBD; if (!strncmp(pp->dev,"nvme", 4)) pp->bus = SYSFS_BUS_NVME; - if (pp->bus == SYSFS_BUS_UNDEF) - return 0; - else if (pp->bus == SYSFS_BUS_SCSI) { - if (scsi_sysfs_pathinfo(pp, hwtable)) - return 1; - } else if (pp->bus == SYSFS_BUS_CCW) { - if (ccw_sysfs_pathinfo(pp, hwtable)) - return 1; - } else if (pp->bus == SYSFS_BUS_CCISS) { - if (cciss_sysfs_pathinfo(pp, hwtable)) - return 1; - } else if (pp->bus == SYSFS_BUS_RBD) { - if (rbd_sysfs_pathinfo(pp, hwtable)) - return 1; - } else if (pp->bus == SYSFS_BUS_NVME) { - if (nvme_sysfs_pathinfo(pp, hwtable)) - return 1; + switch (pp->bus) { + case SYSFS_BUS_SCSI: + return scsi_sysfs_pathinfo(pp, hwtable); + case SYSFS_BUS_CCW: + return ccw_sysfs_pathinfo(pp, hwtable); + case SYSFS_BUS_CCISS: + return cciss_sysfs_pathinfo(pp, hwtable); + case SYSFS_BUS_NVME: + return nvme_sysfs_pathinfo(pp, hwtable); + case SYSFS_BUS_UNDEF: + default: + return PATHINFO_OK; } - return 0; } static int @@ -1752,53 +1736,6 @@ get_udev_uid(struct path * pp, char *uid_attribute, struct udev_device *udev) return len; } -static int -get_rbd_uid(struct path * pp) -{ - struct udev_device *rbd_bus_dev; - int ret, rbd_bus_id; - const char *pool, *image, *snap; - char sysfs_path[PATH_SIZE]; - uint64_t snap_id, max_snap_id = -3; - - ret = sscanf(pp->dev, "rbd%d", &rbd_bus_id); - if (ret != 1) - return -EINVAL; - - snprintf(sysfs_path, sizeof(sysfs_path), "/sys/bus/rbd/devices/%d", - rbd_bus_id); - rbd_bus_dev = udev_device_new_from_syspath(udev, sysfs_path); - if (!rbd_bus_dev) - return -ENODEV; - - ret = -EINVAL; - pool = udev_device_get_sysattr_value(rbd_bus_dev, "pool_id"); - if (!pool) - goto free_dev; - - image = udev_device_get_sysattr_value(rbd_bus_dev, "image_id"); - if (!image) - goto free_dev; - - snap = udev_device_get_sysattr_value(rbd_bus_dev, "snap_id"); - if (!snap) - goto free_dev; - snap_id = strtoull(snap, NULL, 19); - if (snap_id >= max_snap_id) - ret = snprintf(pp->wwid, WWID_SIZE, "%s-%s", pool, image); - else - ret = snprintf(pp->wwid, WWID_SIZE, "%s-%s-%s", pool, - image, snap); - if (ret >= WWID_SIZE) { - condlog(0, "%s: wwid overflow", pp->dev); - ret = -EOVERFLOW; - } - -free_dev: - udev_device_unref(rbd_bus_dev); - return ret; -} - static int get_vpd_uid(struct path * pp) { @@ -1876,9 +1813,6 @@ get_uid (struct path * pp, int path_state, struct udev_device *udev) } else len = strlen(pp->wwid); origin = "callout"; - } else if (pp->bus == SYSFS_BUS_RBD) { - len = get_rbd_uid(pp); - origin = "sysfs"; } else { if (udev && pp->uid_attribute) { @@ -1928,9 +1862,18 @@ int pathinfo(struct path *pp, struct config *conf, int mask) * limited by DI_BLACKLIST and occurs before this debug * message with the mask value. */ - if (pp->udev && (is_claimed_by_foreign(pp->udev) || - filter_property(conf, pp->udev) > 0)) - return PATHINFO_SKIPPED; + if (pp->udev) { + const char *hidden = + udev_device_get_sysattr_value(pp->udev, "hidden"); + + if (hidden && !strcmp(hidden, "1")) { + condlog(3, "%s: hidden", pp->dev); + return PATHINFO_SKIPPED; + } + if (is_claimed_by_foreign(pp->udev) || + filter_property(conf, pp->udev) > 0) + return PATHINFO_SKIPPED; + } if (filter_devnode(conf->blist_devnode, conf->elist_devnode, @@ -1952,14 +1895,19 @@ int pathinfo(struct path *pp, struct config *conf, int mask) /* * fetch info available in sysfs */ - if (mask & DI_SYSFS && sysfs_pathinfo(pp, conf->hwtable)) - return PATHINFO_FAILED; + if (mask & DI_SYSFS) { + int rc = sysfs_pathinfo(pp, conf->hwtable); + + if (rc != PATHINFO_OK) + return rc; + } if (mask & DI_BLACKLIST && mask & DI_SYSFS) { if (filter_device(conf->blist_device, conf->elist_device, - pp->vendor_id, pp->product_id) > 0) { + pp->vendor_id, pp->product_id, pp->dev) > 0 || + filter_protocol(conf->blist_protocol, conf->elist_protocol, + pp) > 0) return PATHINFO_SKIPPED; - } } path_state = path_offline(pp); @@ -2056,9 +2004,9 @@ blank: /* * Recoverable error, for example faulty or offline path */ - memset(pp->wwid, 0, WWID_SIZE); pp->chkrstate = pp->state = PATH_DOWN; - pp->initialized = INIT_FAILED; + if (pp->initialized == INIT_FAILED) + memset(pp->wwid, 0, WWID_SIZE); return PATHINFO_OK; } diff --git a/libmultipath/dm-generic.c b/libmultipath/dm-generic.c index bdc9ca0..d752991 100644 --- a/libmultipath/dm-generic.c +++ b/libmultipath/dm-generic.c @@ -12,9 +12,7 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - USA. + along with this program. If not, see . */ #include diff --git a/libmultipath/dm-generic.h b/libmultipath/dm-generic.h index 5d59724..986429f 100644 --- a/libmultipath/dm-generic.h +++ b/libmultipath/dm-generic.h @@ -12,9 +12,7 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - USA. + along with this program. If not, see . */ #ifndef _DM_GENERIC_H #define _DM_GENERIC_H diff --git a/libmultipath/file.h b/libmultipath/file.h index 29520c7..3c75c90 100644 --- a/libmultipath/file.h +++ b/libmultipath/file.h @@ -5,6 +5,8 @@ #ifndef _FILE_H #define _FILE_H +#include + #define FILE_TIMEOUT 30 int ensure_directories_exist(const char *str, mode_t dir_mode); int open_file(const char *file, int *can_write, const char *header); diff --git a/libmultipath/foreign.c b/libmultipath/foreign.c index 7217184..80b399b 100644 --- a/libmultipath/foreign.c +++ b/libmultipath/foreign.c @@ -12,9 +12,7 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - USA. + along with this program. If not, see . */ #include diff --git a/libmultipath/foreign.h b/libmultipath/foreign.h index 973f368..697f12f 100644 --- a/libmultipath/foreign.h +++ b/libmultipath/foreign.h @@ -12,9 +12,7 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - USA. + along with this program. If not, see . */ #ifndef _FOREIGN_H #define _FOREIGN_H diff --git a/libmultipath/foreign/nvme.c b/libmultipath/foreign/nvme.c index 235f75d..8887a75 100644 --- a/libmultipath/foreign/nvme.c +++ b/libmultipath/foreign/nvme.c @@ -12,9 +12,7 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - USA. + along with this program. If not, see . */ #include @@ -28,6 +26,7 @@ #include #include #include +#include #include "vector.h" #include "generic.h" #include "foreign.h" @@ -287,10 +286,18 @@ static int snprint_nvme_path(const struct gen_path *gp, return 0; } +static int nvme_style(const struct gen_multipath* gm, + char *buf, int len, int verbosity) +{ + int n = snprintf(buf, len, "%%w [%%G]:%%d %%s"); + + return (n < len ? n : len - 1); +} + static const struct gen_multipath_ops nvme_map_ops = { .get_pathgroups = nvme_mp_get_pgs, .rel_pathgroups = nvme_mp_rel_pgs, - .style = generic_style, + .style = nvme_style, .snprint = snprint_nvme_map, }; @@ -444,17 +451,92 @@ _find_path_by_syspath(struct nvme_map *map, const char *syspath) return NULL; } -static int no_dotfiles(const struct dirent *di) +static void _udev_device_unref(void *p) +{ + udev_device_unref(p); +} + +static void _udev_enumerate_unref(void *p) +{ + udev_enumerate_unref(p); +} + +static int _dirent_controller(const struct dirent *di) +{ + static const char nvme_prefix[] = "nvme"; + const char *p; + +#ifdef _DIRENT_HAVE_D_TYPE + if (di->d_type != DT_LNK) + return 0; +#endif + if (strncmp(di->d_name, nvme_prefix, sizeof(nvme_prefix) - 1)) + return 0; + p = di->d_name + sizeof(nvme_prefix) - 1; + if (*p == '\0' || !isdigit(*p)) + return 0; + for (++p; *p != '\0'; ++p) + if (!isdigit(*p)) + return 0; + return 1; +} + +/* Find the block device for a given nvme controller */ +struct udev_device *get_ctrl_blkdev(const struct context *ctx, + struct udev_device *ctrl) { - return di->d_name[0] != '.'; + struct udev_list_entry *item; + struct udev_device *blkdev = NULL; + struct udev_enumerate *enm = udev_enumerate_new(ctx->udev); + + if (enm == NULL) + return NULL; + + pthread_cleanup_push(_udev_enumerate_unref, enm); + if (udev_enumerate_add_match_parent(enm, ctrl) < 0) + goto out; + if (udev_enumerate_add_match_subsystem(enm, "block")) + goto out; + + if (udev_enumerate_scan_devices(enm) < 0) { + condlog(1, "%s: %s: error enumerating devices", __func__, THIS); + goto out; + } + + for (item = udev_enumerate_get_list_entry(enm); + item != NULL; + item = udev_list_entry_get_next(item)) { + struct udev_device *tmp; + + tmp = udev_device_new_from_syspath(ctx->udev, + udev_list_entry_get_name(item)); + if (tmp == NULL) + continue; + if (!strcmp(udev_device_get_devtype(tmp), "disk")) { + blkdev = tmp; + break; + } else + udev_device_unref(tmp); + } + + if (blkdev == NULL) + condlog(1, "%s: %s: failed to get blockdev for %s", + __func__, THIS, udev_device_get_sysname(ctrl)); + else + condlog(5, "%s: %s: got %s", __func__, THIS, + udev_device_get_sysname(blkdev)); +out: + pthread_cleanup_pop(1); + return blkdev; } -static void _find_slaves(struct context *ctx, struct nvme_map *map) +static void _find_controllers(struct context *ctx, struct nvme_map *map) { - char pathbuf[PATH_MAX]; + char pathbuf[PATH_MAX], realbuf[PATH_MAX]; struct dirent **di = NULL; + struct udev_device *subsys; struct nvme_path *path; - int r, i; + int r, i, n; if (map == NULL || map->udev == NULL) return; @@ -462,33 +544,65 @@ static void _find_slaves(struct context *ctx, struct nvme_map *map) vector_foreach_slot(map->pathvec, path, i) path->seen = false; - snprintf(pathbuf, sizeof(pathbuf), - "%s/slaves", - udev_device_get_syspath(map->udev)); + subsys = udev_device_get_parent_with_subsystem_devtype(map->udev, + "nvme-subsystem", + NULL); + if (subsys == NULL) { + condlog(1, "%s: %s: BUG: no NVME subsys for %s", __func__, THIS, + udev_device_get_sysname(map->udev)); + return; + } - r = scandir(pathbuf, &di, no_dotfiles, alphasort); + n = snprintf(pathbuf, sizeof(pathbuf), "%s", + udev_device_get_syspath(subsys)); + r = scandir(pathbuf, &di, _dirent_controller, alphasort); if (r == 0) { - condlog(3, "%s: %s: no paths for %s", __func__, THIS, + condlog(3, "%s: %s: no controllers for %s", __func__, THIS, udev_device_get_sysname(map->udev)); return; } else if (r < 0) { - condlog(1, "%s: %s: error %d scanning paths of %s", __func__, - THIS, errno, udev_device_get_sysname(map->udev)); + condlog(1, "%s: %s: error %d scanning controllers of %s", + __func__, THIS, errno, + udev_device_get_sysname(map->udev)); return; } pthread_cleanup_push(free, di); for (i = 0; i < r; i++) { char *fn = di[i]->d_name; - struct udev_device *udev; + struct udev_device *ctrl, *udev; + + if (snprintf(pathbuf + n, sizeof(pathbuf) - n, "/%s", fn) + >= sizeof(pathbuf) - n) + continue; + if (realpath(pathbuf, realbuf) == NULL) { + condlog(3, "%s: %s: realpath: %s", __func__, THIS, + strerror(errno)); + continue; + } + condlog(4, "%s: %s: found %s", __func__, THIS, realbuf); + + ctrl = udev_device_new_from_syspath(ctx->udev, realbuf); + if (ctrl == NULL) { + condlog(1, "%s: %s: failed to get udev device for %s", + __func__, THIS, realbuf); + continue; + } + + pthread_cleanup_push(_udev_device_unref, ctrl); + udev = get_ctrl_blkdev(ctx, ctrl); + /* + * We give up the reference to the nvme device here and get + * it back from the child below. + * This way we don't need to worry about unreffing it. + */ + pthread_cleanup_pop(1); - if (snprintf(pathbuf, sizeof(pathbuf), "%s/slaves/%s", - udev_device_get_syspath(map->udev), fn) - >= sizeof(pathbuf)) + if (udev == NULL) continue; - path = _find_path_by_syspath(map, pathbuf); + path = _find_path_by_syspath(map, udev_device_get_syspath(udev)); if (path != NULL) { path->seen = true; condlog(4, "%s: %s already known", @@ -496,13 +610,6 @@ static void _find_slaves(struct context *ctx, struct nvme_map *map) continue; } - udev = udev_device_new_from_syspath(ctx->udev, pathbuf); - if (udev == NULL) { - condlog(1, "%s: %s: failed to get udev device for %s", - __func__, THIS, fn); - continue; - } - path = calloc(1, sizeof(*path)); if (path == NULL) continue; @@ -593,7 +700,7 @@ static int _add_map(struct context *ctx, struct udev_device *ud, return FOREIGN_ERR; } vector_set_slot(ctx->mpvec, map); - _find_slaves(ctx, map); + _find_controllers(ctx, map); return FOREIGN_CLAIMED; } @@ -690,7 +797,7 @@ void _check(struct context *ctx) vector_foreach_slot(ctx->mpvec, gm, i) { struct nvme_map *map = gen_mp_to_nvme(gm); - _find_slaves(ctx, map); + _find_controllers(ctx, map); } } diff --git a/libmultipath/generic.c b/libmultipath/generic.c index 6f7a2cd..0d1e632 100644 --- a/libmultipath/generic.c +++ b/libmultipath/generic.c @@ -12,9 +12,7 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - USA. + along with this program. If not, see . */ diff --git a/libmultipath/generic.h b/libmultipath/generic.h index 7f7fe66..6346ffe 100644 --- a/libmultipath/generic.h +++ b/libmultipath/generic.h @@ -12,9 +12,7 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - USA. + along with this program. If not, see . */ #ifndef _GENERIC_H #define _GENERIC_H diff --git a/libmultipath/hwtable.c b/libmultipath/hwtable.c index 88b4700..d3a8d9b 100644 --- a/libmultipath/hwtable.c +++ b/libmultipath/hwtable.c @@ -38,11 +38,10 @@ * * COMPANY_NAME * - * Maintainer : XXX - * Mail : XXX + * Maintainer: XXX */ { - /* If product-ID is different from marketing name add a comment */ + /* Product Name */ .vendor = "VENDOR", .product = "PRODUCT", .revision = "REVISION", @@ -78,14 +77,15 @@ #endif static struct hwentry default_hw[] = { - /* - * Generic NVMe - * - * Due to the parsing logic in find_hwe(), generic entries - * have to be put on top of this list, and more specific ones - * below. - */ + /* + * Generic NVMe devices + * + * Due to the parsing logic in find_hwe(), generic entries + * have to be put on top of this list, and more specific ones + * below. + */ { + /* Generic NVMe */ .vendor = "NVME", .product = ".*", .uid_attribute = "ID_WWN", @@ -95,10 +95,10 @@ static struct hwentry default_hw[] = { /* * Apple * - * Maintainer : Shyam Sundar - * Mail : g.shyamsundar@yahoo.co.in + * Maintainer: Shyam Sundar */ { + /* Xserve RAID */ .vendor = "APPLE", .product = "Xserve RAID", .pgpolicy = MULTIBUS, @@ -107,6 +107,7 @@ static struct hwentry default_hw[] = { * HPE */ { + /* 3PAR */ .vendor = "3PARdata", .product = "VV", .pgpolicy = GROUP_BY_PRIO, @@ -234,6 +235,7 @@ static struct hwentry default_hw[] = { }, /* SGI */ { + /* Total Performance 9100 */ .vendor = "SGI", .product = "TP9100", .pgpolicy = MULTIBUS, @@ -252,7 +254,7 @@ static struct hwentry default_hw[] = { .no_path_retry = 30, }, { - /* InfiniteStorage family */ + /* (RDAC) InfiniteStorage */ .vendor = "SGI", .product = "IS", .bl_product = "Universal Xport", @@ -265,7 +267,7 @@ static struct hwentry default_hw[] = { .no_path_retry = 30, }, { - /* (DDN) */ + /* (DDN) InfiniteStorage */ .vendor = "SGI", .product = "^DD[46]A-", .pgpolicy = GROUP_BY_PRIO, @@ -277,17 +279,20 @@ static struct hwentry default_hw[] = { * DataDirect Networks */ { + /* SAN DataDirector */ .vendor = "DDN", .product = "SAN DataDirector", .pgpolicy = MULTIBUS, }, { + /* EF3010 */ .vendor = "DDN", .product = "^EF3010", .pgpolicy = MULTIBUS, .no_path_retry = 30, }, { + /* EF3015 / S2A and SFA families */ .vendor = "DDN", .product = "^(EF3015|S2A|SFA)", .pgpolicy = GROUP_BY_PRIO, @@ -299,14 +304,14 @@ static struct hwentry default_hw[] = { * Dell EMC */ { - /* Symmetrix / DMX / VMAX */ + /* Symmetrix / DMX / VMAX / PowerMax */ .vendor = "EMC", .product = "SYMMETRIX", .pgpolicy = MULTIBUS, .no_path_retry = 6, }, { - /* DGC CLARiiON CX/AX / EMC VNX and Unity */ + /* DGC CLARiiON CX/AX / VNX and Unity */ .vendor = "^DGC", .product = "^(RAID|DISK|VRAID)", .bl_product = "LUNZ", @@ -326,16 +331,16 @@ static struct hwentry default_hw[] = { .no_path_retry = 5, }, { + /* XtremIO */ .vendor = "XtremIO", .product = "XtremApp", .pgpolicy = MULTIBUS, }, { /* - * Dell SC Series, formerly Compellent + * SC Series, formerly Compellent * - * Maintainer : Sean McGinnis - * Mail : sean_mcginnis@dell.com + * Maintainer: Sean McGinnis */ .vendor = "COMPELNT", .product = "Compellent Vol", @@ -359,11 +364,13 @@ static struct hwentry default_hw[] = { * Fujitsu */ { + /* CentricStor Virtual Tape */ .vendor = "FSC", .product = "CentricStor", .pgpolicy = GROUP_BY_SERIAL, }, { + /* ETERNUS family */ .vendor = "FUJITSU", .product = "ETERNUS_DX(H|L|M|400|8000)", .pgpolicy = GROUP_BY_PRIO, @@ -378,6 +385,7 @@ static struct hwentry default_hw[] = { .pgpolicy = MULTIBUS, }, { + /* ETERNUS 2000, 3000 and 4000 */ .vendor = "FUJITSU", .product = "E[234]000", .pgpolicy = GROUP_BY_PRIO, @@ -386,25 +394,25 @@ static struct hwentry default_hw[] = { .prio_name = PRIO_ALUA, }, { + /* ETERNUS 6000 and 8000 */ .vendor = "FUJITSU", .product = "E[68]000", .pgpolicy = MULTIBUS, .no_path_retry = 10, }, /* - * Hitachi + * Hitachi Vantara * - * Maintainer : Matthias Rudolph - * Mail : matthias.rudolph@hds.com + * Maintainer: Matthias Rudolph */ { - /* USP-V, HUS VM, VSP, VSP G1X00 and VSP GX00 families */ + /* USP-V, HUS VM, VSP, VSP G1X00 and VSP GX00 families / HP XP */ .vendor = "(HITACHI|HP)", .product = "^OPEN-", .pgpolicy = MULTIBUS, }, { - /* AMS 2000 and HUS 100 families */ + /* AMS other than AMS 2000 */ .vendor = "HITACHI", .product = "^DF", .no_path_retry = NO_PATH_RETRY_QUEUE, @@ -412,13 +420,19 @@ static struct hwentry default_hw[] = { .pgfailback = -FAILBACK_IMMEDIATE, .prio_name = PRIO_HDS, }, + { + /* AMS 2000 and HUS 100 families */ + .vendor = "HITACHI", + .product = "^DF600F", + .pgpolicy = MULTIBUS, + }, /* * IBM * - * Maintainer : Hannes Reinecke - * Mail : hare@suse.de + * Maintainer: Hannes Reinecke */ { + /* ProFibre 4000R */ .vendor = "IBM", .product = "ProFibre 4000R", .pgpolicy = MULTIBUS, @@ -567,7 +581,7 @@ static struct hwentry default_hw[] = { .no_path_retry = 30, }, { - /* Enterprise Storage Server / Shark family */ + /* Enterprise Storage Server(ESS) / Shark family */ .vendor = "IBM", .product = "^2105", .no_path_retry = NO_PATH_RETRY_QUEUE, @@ -599,20 +613,24 @@ static struct hwentry default_hw[] = { .prio_name = PRIO_ALUA, }, { + /* PAV DASD ECKD */ .vendor = "IBM", .product = "S/390 DASD ECKD", .bl_product = "S/390", .uid_attribute = "ID_UID", .no_path_retry = NO_PATH_RETRY_QUEUE, .pgpolicy = MULTIBUS, + .checker_name = DIRECTIO, }, { + /* PAV DASD FBA */ .vendor = "IBM", .product = "S/390 DASD FBA", .bl_product = "S/390", .uid_attribute = "ID_UID", .no_path_retry = NO_PATH_RETRY_QUEUE, .pgpolicy = MULTIBUS, + .checker_name = DIRECTIO, }, { /* Power RAID */ @@ -658,10 +676,10 @@ static struct hwentry default_hw[] = { /* * IBM Power Virtual SCSI Devices * - * Maintainer : Brian King - * Mail : brking@linux.vnet.ibm.com + * Maintainer: Brian King */ { + /* AIX VDASD */ .vendor = "AIX", .product = "VDASD", .pgpolicy = MULTIBUS, @@ -674,6 +692,7 @@ static struct hwentry default_hw[] = { .no_path_retry = (300 / DEFAULT_CHECKINT), }, { + /* AIX NVDISK */ .vendor = "AIX", .product = "NVDISK", .hwhandler = "1 alua", @@ -689,8 +708,7 @@ static struct hwentry default_hw[] = { /* * ONTAP family * - * Maintainer : Martin George - * Mail : marting@netapp.com + * Maintainer: Martin George */ .vendor = "NETAPP", .product = "LUN", @@ -706,8 +724,7 @@ static struct hwentry default_hw[] = { /* * SANtricity(RDAC) family * - * Maintainer : NetApp RDAC team - * Mail : ng-eseries-upstream-maintainers@netapp.com + * Maintainer: NetApp RDAC team */ .vendor = "(NETAPP|LSI|ENGENIO)", .product = "INF-01-00", @@ -724,8 +741,7 @@ static struct hwentry default_hw[] = { /* * SolidFir family * - * Maintainer : PJ Waskiewicz - * Mail : pj.waskiewicz@netapp.com + * Maintainer: PJ Waskiewicz */ .vendor = "SolidFir", .product = "SSD SAN", @@ -740,19 +756,16 @@ static struct hwentry default_hw[] = { */ .vendor = "NVME", .product = "^NetApp ONTAP Controller", - .uid_attribute = "ID_WWN", - .checker_name = NONE, .pgpolicy = MULTIBUS, .no_path_retry = NO_PATH_RETRY_QUEUE, - .retain_hwhandler = RETAIN_HWHANDLER_OFF, }, /* * Nexenta * - * Maintainer : Yacine Kheddache - * Mail : yacine@alyseo.com + * Maintainer: Yacine Kheddache */ { + /* COMSTAR */ .vendor = "NEXENTA", .product = "COMSTAR", .pgpolicy = GROUP_BY_SERIAL, @@ -776,10 +789,10 @@ static struct hwentry default_hw[] = { /* * Pillar Data / Oracle FS * - * Maintainer : Srinivasan Ramani - * Mail : srinivas.ramani@oracle.com + * Maintainer: Srinivasan Ramani */ { + /* Axiom */ .vendor = "^Pillar", .product = "^Axiom", .pgpolicy = GROUP_BY_PRIO, @@ -787,6 +800,7 @@ static struct hwentry default_hw[] = { .prio_name = PRIO_ALUA, }, { + /* FS */ .vendor = "^Oracle", .product = "^Oracle FS", .pgpolicy = GROUP_BY_PRIO, @@ -834,12 +848,13 @@ static struct hwentry default_hw[] = { .no_path_retry = 30, }, { - /* (Dot Hill) 3310, 3320, 3510 and 3511 */ + /* (Dot Hill) 3120, 3310, 3320, 3510 and 3511 */ .vendor = "SUN", .product = "StorEdge 3", .pgpolicy = MULTIBUS, }, { + /* 6580 and 6780 */ .vendor = "SUN", .product = "STK6580_6780", .bl_product = "Universal Xport", @@ -878,6 +893,7 @@ static struct hwentry default_hw[] = { .no_path_retry = 30, }, { + /* 6180 */ .vendor = "SUN", .product = "SUN_6180", .bl_product = "Universal Xport", @@ -889,6 +905,19 @@ static struct hwentry default_hw[] = { .pgfailback = -FAILBACK_IMMEDIATE, .no_path_retry = 30, }, + { + /* ArrayStorage */ + .vendor = "SUN", + .product = "ArrayStorage", + .bl_product = "Universal Xport", + .pgpolicy = GROUP_BY_PRIO, + .checker_name = RDAC, + .features = "2 pg_init_retries 50", + .hwhandler = "1 rdac", + .prio_name = PRIO_RDAC, + .pgfailback = -FAILBACK_IMMEDIATE, + .no_path_retry = 30, + }, { /* ZFS Storage Appliances */ .vendor = "SUN", @@ -901,16 +930,17 @@ static struct hwentry default_hw[] = { /* * Pivot3 * - * Maintainer : Bart Brooks - * Mail : bartb@pivot3.com + * Maintainer: Bart Brooks */ { + /* Raige */ .vendor = "PIVOT3", .product = "RAIGE VOLUME", .no_path_retry = NO_PATH_RETRY_QUEUE, .pgpolicy = MULTIBUS, }, { + /* NexGen / vSTAC */ .vendor = "(NexGen|Pivot3)", .product = "(TierStore|vSTAC)", .pgpolicy = GROUP_BY_PRIO, @@ -922,6 +952,7 @@ static struct hwentry default_hw[] = { * Intel */ { + /* Multi-Flex */ .vendor = "(Intel|INTEL)", .product = "Multi-Flex", .bl_product = "VTrak V-LUN", @@ -935,6 +966,7 @@ static struct hwentry default_hw[] = { * Linux-IO Target */ { + /* Linux-IO Target */ .vendor = "(LIO-ORG|SUSE)", .product = "RBD", .hwhandler = "1 alua", @@ -947,6 +979,7 @@ static struct hwentry default_hw[] = { * DataCore */ { + /* SANmelody */ .vendor = "DataCore", .product = "SANmelody", .pgpolicy = GROUP_BY_PRIO, @@ -967,6 +1000,7 @@ static struct hwentry default_hw[] = { * Pure Storage */ { + /* FlashArray */ .vendor = "PURE", .product = "FlashArray", .pgpolicy = MULTIBUS, @@ -981,22 +1015,11 @@ static struct hwentry default_hw[] = { .pgpolicy = GROUP_BY_PRIO, .prio_name = PRIO_ALUA, }, - /* - * Red Hat - * - * Maintainer: Mike Christie - * Mail: mchristi@redhat.com - */ - { - .vendor = "Ceph", - .product = "RBD", - .checker_name = RBD, - .deferred_remove = DEFERRED_REMOVE_ON, - }, /* * Kove */ { + /* XPD */ .vendor = "KOVE", .product = "XPD", .pgpolicy = MULTIBUS, @@ -1004,10 +1027,10 @@ static struct hwentry default_hw[] = { /* * Infinidat * - * Maintainer: Arnon Yaari - * Mail: arnony@infinidat.com + * Maintainer: Arnon Yaari */ { + /* InfiniBox */ .vendor = "NFINIDAT", .product = "InfiniBox", .pgpolicy = GROUP_BY_PRIO, @@ -1026,14 +1049,16 @@ static struct hwentry default_hw[] = { * Kaminario */ { + /* K2 */ .vendor = "KMNRIO", .product = "K2", .pgpolicy = MULTIBUS, }, /* - * Tegile Systems + * Western Digital (Tegile Systems) */ { + /* IntelliFlash */ .vendor = "TEGILE", .product = "(ZEBI-(FC|ISCSI)|INTELLIFLASH)", .hwhandler = "1 alua", @@ -1108,6 +1133,7 @@ static struct hwentry default_hw[] = { .no_path_retry = 30, }, { + /* 3000 / 6000 Series - ALUA mode */ .vendor = "VIOLIN", .product = "SAN ARRAY ALUA", .hwhandler = "1 alua", @@ -1127,6 +1153,7 @@ static struct hwentry default_hw[] = { * Promise Technology */ { + /* VTrak family */ .vendor = "Promise", .product = "VTrak", .bl_product = "VTrak V-LUN", @@ -1137,6 +1164,7 @@ static struct hwentry default_hw[] = { .no_path_retry = 30, }, { + /* Vess family */ .vendor = "Promise", .product = "Vess", .bl_product = "Vess V-LUN", @@ -1211,6 +1239,7 @@ static struct hwentry default_hw[] = { * EOL */ { + /* NULL */ .vendor = NULL, .product = NULL, }, diff --git a/libmultipath/parser.c b/libmultipath/parser.c index b8b7e0d..92ef7cf 100644 --- a/libmultipath/parser.c +++ b/libmultipath/parser.c @@ -79,12 +79,16 @@ _install_keyword(vector keywords, char *string, struct keyword *keyword; /* fetch last keyword */ - keyword = VECTOR_SLOT(keywords, VECTOR_SIZE(keywords) - 1); + keyword = VECTOR_LAST_SLOT(keywords); + if (!keyword) + return 1; /* position to last sub level */ - for (i = 0; i < sublevel; i++) - keyword = - VECTOR_SLOT(keyword->sub, VECTOR_SIZE(keyword->sub) - 1); + for (i = 0; i < sublevel; i++) { + keyword = VECTOR_LAST_SLOT(keyword->sub); + if (!keyword) + return 1; + } /* First sub level allocation */ if (!keyword->sub) diff --git a/libmultipath/print.c b/libmultipath/print.c index b1844c9..7b610b9 100644 --- a/libmultipath/print.c +++ b/libmultipath/print.c @@ -275,8 +275,6 @@ snprint_multipath_vpr (char * buff, size_t len, const struct multipath * mpp) int i, j; vector_foreach_slot(mpp->pg, pgp, i) { - if (!pgp) - continue; vector_foreach_slot(pgp->paths, pp, j) { if (strlen(pp->vendor_id) && strlen(pp->product_id)) return snprintf(buff, len, "%s,%s", @@ -295,8 +293,6 @@ snprint_multipath_vend (char * buff, size_t len, const struct multipath * mpp) int i, j; vector_foreach_slot(mpp->pg, pgp, i) { - if (!pgp) - continue; vector_foreach_slot(pgp->paths, pp, j) { if (strlen(pp->vendor_id)) return snprintf(buff, len, "%s", pp->vendor_id); @@ -313,8 +309,6 @@ snprint_multipath_prod (char * buff, size_t len, const struct multipath * mpp) int i, j; vector_foreach_slot(mpp->pg, pgp, i) { - if (!pgp) - continue; vector_foreach_slot(pgp->paths, pp, j) { if (strlen(pp->product_id)) return snprintf(buff, len, "%s", pp->product_id); @@ -331,8 +325,6 @@ snprint_multipath_rev (char * buff, size_t len, const struct multipath * mpp) int i, j; vector_foreach_slot(mpp->pg, pgp, i) { - if (!pgp) - continue; vector_foreach_slot(pgp->paths, pp, j) { if (strlen(pp->rev)) return snprintf(buff, len, "%s", pp->rev); @@ -638,6 +630,48 @@ snprint_path_failures(char * buff, size_t len, const struct path * pp) return snprint_int(buff, len, pp->failcount); } +/* if you add a protocol string bigger than "scsi:unspec" you must + * also change PROTOCOL_BUF_SIZE */ +int +snprint_path_protocol(char * buff, size_t len, const struct path * pp) +{ + switch (pp->bus) { + case SYSFS_BUS_SCSI: + switch (pp->sg_id.proto_id) { + case SCSI_PROTOCOL_FCP: + return snprintf(buff, len, "scsi:fcp"); + case SCSI_PROTOCOL_SPI: + return snprintf(buff, len, "scsi:spi"); + case SCSI_PROTOCOL_SSA: + return snprintf(buff, len, "scsi:ssa"); + case SCSI_PROTOCOL_SBP: + return snprintf(buff, len, "scsi:sbp"); + case SCSI_PROTOCOL_SRP: + return snprintf(buff, len, "scsi:srp"); + case SCSI_PROTOCOL_ISCSI: + return snprintf(buff, len, "scsi:iscsi"); + case SCSI_PROTOCOL_SAS: + return snprintf(buff, len, "scsi:sas"); + case SCSI_PROTOCOL_ADT: + return snprintf(buff, len, "scsi:adt"); + case SCSI_PROTOCOL_ATA: + return snprintf(buff, len, "scsi:ata"); + case SCSI_PROTOCOL_UNSPEC: + default: + return snprintf(buff, len, "scsi:unspec"); + } + case SYSFS_BUS_CCW: + return snprintf(buff, len, "ccw"); + case SYSFS_BUS_CCISS: + return snprintf(buff, len, "cciss"); + case SYSFS_BUS_NVME: + return snprintf(buff, len, "nvme"); + case SYSFS_BUS_UNDEF: + default: + return snprintf(buff, len, "undef"); + } +} + struct multipath_data mpd[] = { {'n', "name", 0, snprint_name}, {'w', "uuid", 0, snprint_multipath_uuid}, @@ -687,6 +721,7 @@ struct path_data pd[] = { {'a', "host adapter", 0, snprint_host_adapter}, {'G', "foreign", 0, snprint_path_foreign}, {'0', "failures", 0, snprint_path_failures}, + {'P', "protocol", 0, snprint_path_protocol}, {0, NULL, 0 , NULL} }; @@ -1342,7 +1377,8 @@ snprint_multipath_topology_json (char * buff, int len, const struct vectors * ve } static int -snprint_hwentry (struct config *conf, char * buff, int len, const struct hwentry * hwe) +snprint_hwentry (const struct config *conf, + char * buff, int len, const struct hwentry * hwe) { int i; int fwd = 0; @@ -1374,7 +1410,9 @@ snprint_hwentry (struct config *conf, char * buff, int len, const struct hwentry return fwd; } -int snprint_hwtable(struct config *conf, char *buff, int len, vector hwtable) +static int snprint_hwtable(const struct config *conf, + char *buff, int len, + const struct _vector *hwtable) { int fwd = 0; int i; @@ -1400,12 +1438,17 @@ int snprint_hwtable(struct config *conf, char *buff, int len, vector hwtable) } static int -snprint_mpentry (struct config *conf, char * buff, int len, const struct mpentry * mpe) +snprint_mpentry (const struct config *conf, char * buff, int len, + const struct mpentry * mpe, const struct _vector *mpvec) { int i; int fwd = 0; struct keyword * kw; struct keyword * rootkw; + struct multipath *mpp = NULL; + + if (mpvec != NULL && (mpp = find_mp_by_wwid(mpvec, mpe->wwid)) == NULL) + return 0; rootkw = find_keyword(conf->keywords, NULL, "multipath"); if (!rootkw) @@ -1420,13 +1463,23 @@ snprint_mpentry (struct config *conf, char * buff, int len, const struct mpentry if (fwd >= len) return len; } + /* + * This mpp doesn't have alias defined. Add the alias in a comment. + */ + if (mpp != NULL && strcmp(mpp->alias, mpp->wwid)) { + fwd += snprintf(buff + fwd, len - fwd, "\t\t# alias \"%s\"\n", + mpp->alias); + if (fwd >= len) + return len; + } fwd += snprintf(buff + fwd, len - fwd, "\t}\n"); if (fwd >= len) return len; return fwd; } -int snprint_mptable(struct config *conf, char *buff, int len, vector mptable) +static int snprint_mptable(const struct config *conf, + char *buff, int len, const struct _vector *mpvec) { int fwd = 0; int i; @@ -1440,19 +1493,51 @@ int snprint_mptable(struct config *conf, char *buff, int len, vector mptable) fwd += snprintf(buff + fwd, len - fwd, "multipaths {\n"); if (fwd >= len) return len; - vector_foreach_slot (mptable, mpe, i) { - fwd += snprint_mpentry(conf, buff + fwd, len - fwd, mpe); + vector_foreach_slot (conf->mptable, mpe, i) { + fwd += snprint_mpentry(conf, buff + fwd, len - fwd, mpe, mpvec); if (fwd >= len) return len; } + if (mpvec != NULL) { + struct multipath *mpp; + + vector_foreach_slot(mpvec, mpp, i) { + if (find_mpe(conf->mptable, mpp->wwid) != NULL) + continue; + + fwd += snprintf(buff + fwd, len - fwd, + "\tmultipath {\n"); + if (fwd >= len) + return len; + fwd += snprintf(buff + fwd, len - fwd, + "\t\twwid \"%s\"\n", mpp->wwid); + if (fwd >= len) + return len; + /* + * This mpp doesn't have alias defined in + * multipath.conf - otherwise find_mpe would have + * found it. Add the alias in a comment. + */ + if (strcmp(mpp->alias, mpp->wwid)) { + fwd += snprintf(buff + fwd, len - fwd, + "\t\t# alias \"%s\"\n", + mpp->alias); + if (fwd >= len) + return len; + } + fwd += snprintf(buff + fwd, len - fwd, "\t}\n"); + if (fwd >= len) + return len; + } + } fwd += snprintf(buff + fwd, len - fwd, "}\n"); if (fwd >= len) return len; return fwd; } -int snprint_overrides(struct config *conf, char * buff, int len, - const struct hwentry *overrides) +static int snprint_overrides(const struct config *conf, char * buff, int len, + const struct hwentry *overrides) { int fwd = 0; int i; @@ -1481,7 +1566,7 @@ out: return fwd; } -int snprint_defaults(struct config *conf, char *buff, int len) +static int snprint_defaults(const struct config *conf, char *buff, int len) { int fwd = 0; int i; @@ -1593,6 +1678,19 @@ int snprint_blacklist_report(struct config *conf, char *buff, int len) if (snprint_blacklist_group(buff, len, &fwd, &conf->elist_property) == 0) return len; + if ((len - fwd - threshold) <= 0) + return len; + fwd += snprintf(buff + fwd, len - fwd, "protocol rules:\n" + "- blacklist:\n"); + if (!snprint_blacklist_group(buff, len, &fwd, &conf->blist_protocol)) + return len; + + if ((len - fwd - threshold) <= 0) + return len; + fwd += snprintf(buff + fwd, len - fwd, "- exceptions:\n"); + if (snprint_blacklist_group(buff, len, &fwd, &conf->elist_protocol) == 0) + return len; + if ((len - fwd - threshold) <= 0) return len; fwd += snprintf(buff + fwd, len - fwd, "wwid rules:\n" @@ -1624,7 +1722,7 @@ int snprint_blacklist_report(struct config *conf, char *buff, int len) return fwd; } -int snprint_blacklist(struct config *conf, char *buff, int len) +static int snprint_blacklist(const struct config *conf, char *buff, int len) { int i; struct blentry * ble; @@ -1668,6 +1766,15 @@ int snprint_blacklist(struct config *conf, char *buff, int len) if (fwd >= len) return len; } + vector_foreach_slot (conf->blist_protocol, ble, i) { + kw = find_keyword(conf->keywords, rootkw->sub, "protocol"); + if (!kw) + return 0; + fwd += snprint_keyword(buff + fwd, len - fwd, "\t%k %v\n", + kw, ble); + if (fwd >= len) + return len; + } rootkw = find_keyword(conf->keywords, rootkw->sub, "device"); if (!rootkw) return 0; @@ -1700,7 +1807,8 @@ int snprint_blacklist(struct config *conf, char *buff, int len) return fwd; } -int snprint_blacklist_except(struct config *conf, char *buff, int len) +static int snprint_blacklist_except(const struct config *conf, + char *buff, int len) { int i; struct blentry * ele; @@ -1744,6 +1852,15 @@ int snprint_blacklist_except(struct config *conf, char *buff, int len) if (fwd >= len) return len; } + vector_foreach_slot (conf->elist_protocol, ele, i) { + kw = find_keyword(conf->keywords, rootkw->sub, "protocol"); + if (!kw) + return 0; + fwd += snprint_keyword(buff + fwd, len - fwd, "\t%k %v\n", + kw, ele); + if (fwd >= len) + return len; + } rootkw = find_keyword(conf->keywords, rootkw->sub, "device"); if (!rootkw) return 0; @@ -1776,6 +1893,61 @@ int snprint_blacklist_except(struct config *conf, char *buff, int len) return fwd; } +char *snprint_config(const struct config *conf, int *len, + const struct _vector *hwtable, const struct _vector *mpvec) +{ + char *reply; + /* built-in config is >20kB already */ + unsigned int maxlen = 32768; + + for (reply = NULL; maxlen <= UINT_MAX/2; maxlen *= 2) { + char *c, *tmp = reply; + + reply = REALLOC(reply, maxlen); + if (!reply) { + if (tmp) + free(tmp); + return NULL; + } + + c = reply + snprint_defaults(conf, reply, maxlen); + if ((c - reply) == maxlen) + continue; + + c += snprint_blacklist(conf, c, reply + maxlen - c); + if ((c - reply) == maxlen) + continue; + + c += snprint_blacklist_except(conf, c, reply + maxlen - c); + if ((c - reply) == maxlen) + continue; + + c += snprint_hwtable(conf, c, reply + maxlen - c, + hwtable ? hwtable : conf->hwtable); + if ((c - reply) == maxlen) + continue; + + c += snprint_overrides(conf, c, reply + maxlen - c, + conf->overrides); + if ((c - reply) == maxlen) + continue; + + if (VECTOR_SIZE(conf->mptable) > 0 || + (mpvec != NULL && VECTOR_SIZE(mpvec) > 0)) + c += snprint_mptable(conf, c, reply + maxlen - c, + mpvec); + + if ((c - reply) < maxlen) { + if (len) + *len = c - reply; + return reply; + } + } + + free(reply); + return NULL; +} + int snprint_status(char *buff, int len, const struct vectors *vecs) { int fwd = 0; diff --git a/libmultipath/print.h b/libmultipath/print.h index 7ba6438..e2fb865 100644 --- a/libmultipath/print.h +++ b/libmultipath/print.h @@ -113,29 +113,28 @@ int _snprint_multipath (const struct gen_multipath *, char *, int, const char *, int); #define snprint_multipath(buf, len, fmt, mp, v) \ _snprint_multipath(dm_multipath_to_gen(mp), buf, len, fmt, v) -int _snprint_multipath_topology (const struct gen_multipath *, char *, int, +int _snprint_multipath_topology (const struct gen_multipath *, char *, int, int verbosity); #define snprint_multipath_topology(buf, len, mpp, v) \ _snprint_multipath_topology (dm_multipath_to_gen(mpp), buf, len, v) int snprint_multipath_topology_json (char * buff, int len, const struct vectors * vecs); +char *snprint_config(const struct config *conf, int *len, + const struct _vector *hwtable, + const struct _vector *mpvec); int snprint_multipath_map_json (char * buff, int len, const struct multipath * mpp, int last); -int snprint_defaults (struct config *, char *, int); -int snprint_blacklist (struct config *, char *, int); -int snprint_blacklist_except (struct config *, char *, int); int snprint_blacklist_report (struct config *, char *, int); int snprint_wildcards (char *, int); int snprint_status (char *, int, const struct vectors *); int snprint_devices (struct config *, char *, int, const struct vectors *); -int snprint_hwtable (struct config *, char *, int, const vector); -int snprint_mptable (struct config *, char *, int, const vector); -int snprint_overrides (struct config *, char *, int, const struct hwentry *); int snprint_path_serial (char *, size_t, const struct path *); int snprint_host_wwnn (char *, size_t, const struct path *); int snprint_host_wwpn (char *, size_t, const struct path *); int snprint_tgt_wwnn (char *, size_t, const struct path *); int snprint_tgt_wwpn (char *, size_t, const struct path *); +#define PROTOCOL_BUF_SIZE sizeof("scsi:unspec") +int snprint_path_protocol(char *, size_t, const struct path *); void _print_multipath_topology (const struct gen_multipath * gmp, int verbosity); diff --git a/libmultipath/prio.c b/libmultipath/prio.c index 7fce921..17acfd0 100644 --- a/libmultipath/prio.c +++ b/libmultipath/prio.c @@ -81,7 +81,7 @@ static struct prio * prio_lookup (char * name) return NULL; } -int prio_set_args (struct prio * p, char * args) +int prio_set_args (struct prio * p, const char * args) { return snprintf(p->args, PRIO_ARGS_LEN, "%s", args); } @@ -130,19 +130,19 @@ int prio_getprio (struct prio * p, struct path * pp, unsigned int timeout) return p->getprio(pp, p->args, timeout); } -int prio_selected (struct prio * p) +int prio_selected (const struct prio * p) { if (!p) return 0; return (p->getprio) ? 1 : 0; } -char * prio_name (struct prio * p) +const char * prio_name (const struct prio * p) { return p->name; } -char * prio_args (struct prio * p) +const char * prio_args (const struct prio * p) { return p->args; } diff --git a/libmultipath/prio.h b/libmultipath/prio.h index c97fe39..aa587cc 100644 --- a/libmultipath/prio.h +++ b/libmultipath/prio.h @@ -60,10 +60,10 @@ struct prio * add_prio (char *, char *); int prio_getprio (struct prio *, struct path *, unsigned int); void prio_get (char *, struct prio *, char *, char *); void prio_put (struct prio *); -int prio_selected (struct prio *); -char * prio_name (struct prio *); -char * prio_args (struct prio *); -int prio_set_args (struct prio *, char *); +int prio_selected (const struct prio *); +const char * prio_name (const struct prio *); +const char * prio_args (const struct prio *); +int prio_set_args (struct prio *, const char *); /* The only function exported by prioritizer dynamic libraries (.so) */ int getprio(struct path *, char *, unsigned int); diff --git a/libmultipath/prioritizers/alua.c b/libmultipath/prioritizers/alua.c index 03d0a0e..b24e2d4 100644 --- a/libmultipath/prioritizers/alua.c +++ b/libmultipath/prioritizers/alua.c @@ -67,8 +67,11 @@ get_alua_info(struct path * pp, unsigned int timeout) } condlog(3, "%s: reported target port group is %i", pp->dev, tpg); rc = get_asymmetric_access_state(pp->fd, tpg, timeout); - if (rc < 0) + if (rc < 0) { + condlog(2, "%s: get_asymmetric_access_state returned %d", + __func__, rc); return -ALUA_PRIO_GETAAS_FAILED; + } condlog(3, "%s: aas = %02x [%s]%s", pp->dev, rc, aas_print_string(rc), (rc & 0x80) ? " [preferred]" : ""); diff --git a/libmultipath/prioritizers/alua_rtpg.c b/libmultipath/prioritizers/alua_rtpg.c index e431502..811ce7a 100644 --- a/libmultipath/prioritizers/alua_rtpg.c +++ b/libmultipath/prioritizers/alua_rtpg.c @@ -19,6 +19,7 @@ #include #include #include +#include #define __user #include @@ -27,20 +28,14 @@ #include "../prio.h" #include "../discovery.h" #include "../unaligned.h" +#include "../debug.h" #include "alua_rtpg.h" #define SENSE_BUFF_LEN 32 #define SGIO_TIMEOUT 60000 -/* - * Macro used to print debug messaged. - */ -#if DEBUG > 0 #define PRINT_DEBUG(f, a...) \ - fprintf(stderr, "DEBUG: " f, ##a) -#else -#define PRINT_DEBUG(f, a...) -#endif + condlog(4, "alua: " f, ##a) /* * Optionally print the commands sent and the data received a hex dump. @@ -75,10 +70,20 @@ print_hex(unsigned char *p, unsigned long len) #define SCSI_COMMAND_TERMINATED 0x22 #define SG_ERR_DRIVER_SENSE 0x08 #define RECOVERED_ERROR 0x01 +#define NOT_READY 0x2 +#define UNIT_ATTENTION 0x6 + +enum scsi_disposition { + SCSI_GOOD = 0, + SCSI_ERROR, + SCSI_RETRY, +}; static int -scsi_error(struct sg_io_hdr *hdr) +scsi_error(struct sg_io_hdr *hdr, int opcode) { + int sense_key, asc, ascq; + /* Treat SG_ERR here to get rid of sg_err.[ch] */ hdr->status &= 0x7e; @@ -87,29 +92,44 @@ scsi_error(struct sg_io_hdr *hdr) (hdr->host_status == 0) && (hdr->driver_status == 0) ) { - return 0; + return SCSI_GOOD; } + sense_key = asc = ascq = -1; if ( (hdr->status == SCSI_CHECK_CONDITION) || (hdr->status == SCSI_COMMAND_TERMINATED) || ((hdr->driver_status & 0xf) == SG_ERR_DRIVER_SENSE) ) { if (hdr->sbp && (hdr->sb_len_wr > 2)) { - int sense_key; unsigned char * sense_buffer = hdr->sbp; - if (sense_buffer[0] & 0x2) + if (sense_buffer[0] & 0x2) { sense_key = sense_buffer[1] & 0xf; - else + if (hdr->sb_len_wr > 3) + asc = sense_buffer[2]; + if (hdr->sb_len_wr > 4) + ascq = sense_buffer[3]; + } else { sense_key = sense_buffer[2] & 0xf; + if (hdr->sb_len_wr > 13) + asc = sense_buffer[12]; + if (hdr->sb_len_wr > 14) + ascq = sense_buffer[13]; + } if (sense_key == RECOVERED_ERROR) - return 0; + return SCSI_GOOD; } } - return 1; + PRINT_DEBUG("alua: SCSI error for command %02x: status %02x, sense %02x/%02x/%02x", + opcode, hdr->status, sense_key, asc, ascq); + + if (sense_key == UNIT_ATTENTION || sense_key == NOT_READY) + return SCSI_RETRY; + else + return SCSI_ERROR; } /* @@ -122,7 +142,9 @@ do_inquiry(int fd, int evpd, unsigned int codepage, struct inquiry_command cmd; struct sg_io_hdr hdr; unsigned char sense[SENSE_BUFF_LEN]; + int rc, retry_count = 3; +retry: memset(&cmd, 0, sizeof(cmd)); cmd.op = OPERATION_CODE_INQUIRY; if (evpd) { @@ -144,12 +166,18 @@ do_inquiry(int fd, int evpd, unsigned int codepage, hdr.timeout = get_prio_timeout(timeout, SGIO_TIMEOUT); if (ioctl(fd, SG_IO, &hdr) < 0) { - PRINT_DEBUG("do_inquiry: IOCTL failed!\n"); + PRINT_DEBUG("do_inquiry: IOCTL failed!"); return -RTPG_INQUIRY_FAILED; } - if (scsi_error(&hdr)) { - PRINT_DEBUG("do_inquiry: SCSI error!\n"); + rc = scsi_error(&hdr, OPERATION_CODE_INQUIRY); + if (rc == SCSI_ERROR) { + PRINT_DEBUG("do_inquiry: SCSI error!"); + return -RTPG_INQUIRY_FAILED; + } else if (rc == SCSI_RETRY) { + if (--retry_count >= 0) + goto retry; + PRINT_DEBUG("do_inquiry: retries exhausted!"); return -RTPG_INQUIRY_FAILED; } PRINT_HEX((unsigned char *) resp, resplen); @@ -189,7 +217,7 @@ get_sysfs_pg83(struct path *pp, unsigned char *buff, int buflen) } if (!parent || sysfs_get_vpd(parent, 0x83, buff, buflen) <= 0) { - PRINT_DEBUG("failed to read sysfs vpd pg83\n"); + PRINT_DEBUG("failed to read sysfs vpd pg83"); return -1; } return 0; @@ -208,7 +236,7 @@ get_target_port_group(struct path * pp, unsigned int timeout) buf = (unsigned char *)malloc(buflen); if (!buf) { PRINT_DEBUG("malloc failed: could not allocate" - "%u bytes\n", buflen); + "%u bytes", buflen); return -RTPG_RTPG_FAILED; } @@ -230,7 +258,7 @@ get_target_port_group(struct path * pp, unsigned int timeout) buf = (unsigned char *)malloc(scsi_buflen); if (!buf) { PRINT_DEBUG("malloc failed: could not allocate" - "%u bytes\n", scsi_buflen); + "%u bytes", scsi_buflen); return -RTPG_RTPG_FAILED; } buflen = scsi_buflen; @@ -248,7 +276,7 @@ get_target_port_group(struct path * pp, unsigned int timeout) struct vpd83_tpg_dscr *p; if (rc != -RTPG_NO_TPG_IDENTIFIER) { PRINT_DEBUG("get_target_port_group: more " - "than one TPG identifier found!\n"); + "than one TPG identifier found!"); continue; } p = (struct vpd83_tpg_dscr *)dscr->data; @@ -258,7 +286,7 @@ get_target_port_group(struct path * pp, unsigned int timeout) if (rc == -RTPG_NO_TPG_IDENTIFIER) { PRINT_DEBUG("get_target_port_group: " - "no TPG identifier found!\n"); + "no TPG identifier found!"); } out: free(buf); @@ -271,7 +299,9 @@ do_rtpg(int fd, void* resp, long resplen, unsigned int timeout) struct rtpg_command cmd; struct sg_io_hdr hdr; unsigned char sense[SENSE_BUFF_LEN]; + int retry_count = 3, rc; +retry: memset(&cmd, 0, sizeof(cmd)); cmd.op = OPERATION_CODE_RTPG; rtpg_command_set_service_action(&cmd); @@ -289,11 +319,20 @@ do_rtpg(int fd, void* resp, long resplen, unsigned int timeout) hdr.sbp = sense; hdr.timeout = get_prio_timeout(timeout, SGIO_TIMEOUT); - if (ioctl(fd, SG_IO, &hdr) < 0) + if (ioctl(fd, SG_IO, &hdr) < 0) { + condlog(2, "%s: sg ioctl failed: %s", + __func__, strerror(errno)); return -RTPG_RTPG_FAILED; + } - if (scsi_error(&hdr)) { - PRINT_DEBUG("do_rtpg: SCSI error!\n"); + rc = scsi_error(&hdr, OPERATION_CODE_RTPG); + if (rc == SCSI_ERROR) { + PRINT_DEBUG("do_rtpg: SCSI error!"); + return -RTPG_RTPG_FAILED; + } else if (rc == SCSI_RETRY) { + if (--retry_count >= 0) + goto retry; + PRINT_DEBUG("do_rtpg: retries exhausted!"); return -RTPG_RTPG_FAILED; } PRINT_HEX(resp, resplen); @@ -315,13 +354,15 @@ get_asymmetric_access_state(int fd, unsigned int tpg, unsigned int timeout) buf = (unsigned char *)malloc(buflen); if (!buf) { PRINT_DEBUG ("malloc failed: could not allocate" - "%u bytes\n", buflen); + "%u bytes", buflen); return -RTPG_RTPG_FAILED; } memset(buf, 0, buflen); rc = do_rtpg(fd, buf, buflen, timeout); - if (rc < 0) + if (rc < 0) { + PRINT_DEBUG("%s: do_rtpg returned %d", __func__, rc); goto out; + } scsi_buflen = get_unaligned_be32(&buf[0]) + 4; if (scsi_buflen > UINT_MAX) scsi_buflen = UINT_MAX; @@ -329,8 +370,8 @@ get_asymmetric_access_state(int fd, unsigned int tpg, unsigned int timeout) free(buf); buf = (unsigned char *)malloc(scsi_buflen); if (!buf) { - PRINT_DEBUG ("malloc failed: could not allocate" - "%u bytes\n", scsi_buflen); + PRINT_DEBUG("malloc failed: could not allocate %" + PRIu64 " bytes", scsi_buflen); return -RTPG_RTPG_FAILED; } buflen = scsi_buflen; @@ -347,13 +388,15 @@ get_asymmetric_access_state(int fd, unsigned int tpg, unsigned int timeout) if (rc != -RTPG_TPG_NOT_FOUND) { PRINT_DEBUG("get_asymmetric_access_state: " "more than one entry with same port " - "group.\n"); + "group."); } else { - PRINT_DEBUG("pref=%i\n", dscr->b0); + condlog(5, "pref=%i", dscr->b0); rc = rtpg_tpg_dscr_get_aas(dscr); } } } + if (rc == -RTPG_TPG_NOT_FOUND) + condlog(2, "%s: port group %d not found", __func__, tpg); out: free(buf); return rc; diff --git a/libmultipath/prioritizers/path_latency.c b/libmultipath/prioritizers/path_latency.c index 765265c..eeee01e 100644 --- a/libmultipath/prioritizers/path_latency.c +++ b/libmultipath/prioritizers/path_latency.c @@ -237,7 +237,8 @@ int getprio(struct path *pp, char *args, unsigned int timeout) lg_maxavglatency = log(MAX_AVG_LATENCY) / lg_base; lg_minavglatency = log(MIN_AVG_LATENCY) / lg_base; - prepare_directio_read(pp->fd, &blksize, &buf, &restore_flags); + if (prepare_directio_read(pp->fd, &blksize, &buf, &restore_flags) < 0) + return PRIO_UNDEF; temp = io_num; while (temp-- > 0) { diff --git a/libmultipath/prkey.c b/libmultipath/prkey.c index 89b90ed..d645f81 100644 --- a/libmultipath/prkey.c +++ b/libmultipath/prkey.c @@ -11,6 +11,8 @@ #include #include #include +#include +#include #define PRKEY_READ 0 #define PRKEY_WRITE 1 @@ -108,7 +110,8 @@ static int do_prkey(int fd, char *wwid, char *keystr, int cmd) return 0; } -int get_prkey(struct config *conf, struct multipath *mpp, uint64_t *prkey) +int get_prkey(struct config *conf, struct multipath *mpp, uint64_t *prkey, + uint8_t *sa_flags) { int fd; int unused; @@ -124,6 +127,9 @@ int get_prkey(struct config *conf, struct multipath *mpp, uint64_t *prkey) ret = do_prkey(fd, mpp->wwid, keystr, PRKEY_READ); if (ret) goto out_file; + *sa_flags = 0; + if (strchr(keystr, 'X')) + *sa_flags = MPATH_F_APTPL_MASK; ret = !!parse_prkey(keystr, prkey); out_file: close(fd); @@ -131,7 +137,8 @@ out: return ret; } -int set_prkey(struct config *conf, struct multipath *mpp, uint64_t prkey) +int set_prkey(struct config *conf, struct multipath *mpp, uint64_t prkey, + uint8_t sa_flags) { int fd; int can_write = 1; @@ -141,6 +148,12 @@ int set_prkey(struct config *conf, struct multipath *mpp, uint64_t prkey) if (!strlen(mpp->wwid)) goto out; + if (sa_flags & ~MPATH_F_APTPL_MASK) { + condlog(0, "unsupported pr flags, 0x%x", + sa_flags & ~MPATH_F_APTPL_MASK); + sa_flags &= MPATH_F_APTPL_MASK; + } + fd = open_file(conf->prkeys_file, &can_write, PRKEYS_FILE_HEADER); if (fd < 0) goto out; @@ -149,7 +162,15 @@ int set_prkey(struct config *conf, struct multipath *mpp, uint64_t prkey) goto out_file; } if (prkey) { - snprintf(keystr, PRKEY_SIZE, "0x%016" PRIx64, prkey); + /* using the capitalization of the 'x' is a hack, but + * it's unlikely that mpath_persist will support more options + * since sg_persist doesn't, and this lets us keep the + * same file format as before instead of needing to change + * the format of the prkeys file */ + if (sa_flags) + snprintf(keystr, PRKEY_SIZE, "0X%016" PRIx64, prkey); + else + snprintf(keystr, PRKEY_SIZE, "0x%016" PRIx64, prkey); keystr[PRKEY_SIZE - 1] = '\0'; ret = do_prkey(fd, mpp->wwid, keystr, PRKEY_WRITE); } diff --git a/libmultipath/prkey.h b/libmultipath/prkey.h index 4028e70..6739191 100644 --- a/libmultipath/prkey.h +++ b/libmultipath/prkey.h @@ -13,7 +13,9 @@ "# prkey wwid\n" \ "#\n" -int set_prkey(struct config *conf, struct multipath *mpp, uint64_t prkey); -int get_prkey(struct config *conf, struct multipath *mpp, uint64_t *prkey); +int set_prkey(struct config *conf, struct multipath *mpp, uint64_t prkey, + uint8_t sa_flags); +int get_prkey(struct config *conf, struct multipath *mpp, uint64_t *prkey, + uint8_t *sa_flags); #endif /* _PRKEY_H */ diff --git a/libmultipath/propsel.c b/libmultipath/propsel.c index 627d366..fdb5953 100644 --- a/libmultipath/propsel.c +++ b/libmultipath/propsel.c @@ -44,6 +44,23 @@ do { \ } \ } while(0) +#define do_set_from_vec(type, var, src, dest, msg) \ +do { \ + type *_p; \ + int i; \ + \ + vector_foreach_slot(src, _p, i) { \ + if (_p->var) { \ + dest = _p->var; \ + origin = msg; \ + goto out; \ + } \ + } \ +} while (0) + +#define do_set_from_hwe(var, src, dest, msg) \ + do_set_from_vec(struct hwentry, var, src->hwe, dest, msg) + static const char default_origin[] = "(setting: multipath internal)"; static const char hwe_origin[] = "(setting: storage device configuration)"; @@ -67,7 +84,7 @@ do { \ #define mp_set_mpe(var) \ do_set(var, mp->mpe, mp->var, multipaths_origin) #define mp_set_hwe(var) \ -do_set(var, mp->hwe, mp->var, hwe_origin) +do_set_from_hwe(var, mp, mp->var, hwe_origin) #define mp_set_ovr(var) \ do_set(var, conf->overrides, mp->var, overrides_origin) #define mp_set_conf(var) \ @@ -78,7 +95,7 @@ do_default(mp->var, value) #define pp_set_mpe(var) \ do_set(var, mpe, pp->var, multipaths_origin) #define pp_set_hwe(var) \ -do_set(var, pp->hwe, pp->var, hwe_origin) +do_set_from_hwe(var, pp, pp->var, hwe_origin) #define pp_set_conf(var) \ do_set(var, conf, pp->var, conf_origin) #define pp_set_ovr(var) \ @@ -106,6 +123,7 @@ do { \ if (src && src->prkey_source != PRKEY_SOURCE_NONE) { \ mp->prkey_source = src->prkey_source; \ mp->reservation_key = src->reservation_key; \ + mp->sa_flags = src->sa_flags; \ origin = msg; \ goto out; \ } \ @@ -250,8 +268,8 @@ want_user_friendly_names(struct config *conf, struct multipath * mp) multipaths_origin); do_set(user_friendly_names, conf->overrides, user_friendly_names, overrides_origin); - do_set(user_friendly_names, mp->hwe, user_friendly_names, - hwe_origin); + do_set_from_hwe(user_friendly_names, mp, user_friendly_names, + hwe_origin); do_set(user_friendly_names, conf, user_friendly_names, conf_origin); do_default(user_friendly_names, DEFAULT_USER_FRIENDLY_NAMES); @@ -402,9 +420,11 @@ int select_hwhandler(struct config *conf, struct multipath *mp) bool all_tpgs = true; dh_state = &handler[2]; + + vector_foreach_slot(mp->paths, pp, i) + all_tpgs = all_tpgs && (pp->tpgs > 0); if (mp->retain_hwhandler != RETAIN_HWHANDLER_OFF) { vector_foreach_slot(mp->paths, pp, i) { - all_tpgs = all_tpgs && (pp->tpgs > 0); if (get_dh_state(pp, dh_state, sizeof(handler) - 2) > 0 && strcmp(dh_state, "detached")) { memcpy(handler, "1 ", 2); @@ -448,6 +468,8 @@ check_rdac(struct path * pp) int len; char buff[44]; + if (pp->bus != SYSFS_BUS_SCSI) + return 0; len = get_vpd_sgio(pp->fd, 0xC9, buff, 44); if (len <= 0) return 0; @@ -469,9 +491,9 @@ int select_checker(struct config *conf, struct path *pp) checker_name = TUR; goto out; } - } + } do_set(checker_name, conf->overrides, checker_name, overrides_origin); - do_set(checker_name, pp->hwe, checker_name, hwe_origin); + do_set_from_hwe(checker_name, pp, checker_name, hwe_origin); do_set(checker_name, conf, checker_name, conf_origin); do_default(checker_name, DEFAULT_CHECKER); out: @@ -545,6 +567,25 @@ do { \ } \ } while(0) +#define set_prio_from_vec(type, dir, src, msg, p) \ +do { \ + type *_p; \ + int i; \ + char *prio_name = NULL, *prio_args = NULL; \ + \ + vector_foreach_slot(src, _p, i) { \ + if (prio_name == NULL && _p->prio_name) \ + prio_name = _p->prio_name; \ + if (prio_args == NULL && _p->prio_args) \ + prio_args = _p->prio_args; \ + } \ + if (prio_name != NULL) { \ + prio_get(dir, p, prio_name, prio_args); \ + origin = msg; \ + goto out; \ + } \ +} while (0) + int select_prio(struct config *conf, struct path *pp) { const char *origin; @@ -561,7 +602,8 @@ int select_prio(struct config *conf, struct path *pp) mpe = find_mpe(conf->mptable, pp->wwid); set_prio(conf->multipath_dir, mpe, multipaths_origin); set_prio(conf->multipath_dir, conf->overrides, overrides_origin); - set_prio(conf->multipath_dir, pp->hwe, hwe_origin); + set_prio_from_vec(struct hwentry, conf->multipath_dir, + pp->hwe, hwe_origin, p); set_prio(conf->multipath_dir, conf, conf_origin); prio_get(conf->multipath_dir, p, DEFAULT_PRIO, DEFAULT_PRIO_ARGS); origin = default_origin; @@ -614,7 +656,7 @@ select_minio_rq (struct config *conf, struct multipath * mp) do_set(minio_rq, mp->mpe, mp->minio, multipaths_origin); do_set(minio_rq, conf->overrides, mp->minio, overrides_origin); - do_set(minio_rq, mp->hwe, mp->minio, hwe_origin); + do_set_from_hwe(minio_rq, mp, mp->minio, hwe_origin); do_set(minio_rq, conf, mp->minio, conf_origin); do_default(mp->minio, DEFAULT_MINIO_RQ); out: @@ -703,18 +745,19 @@ int select_reservation_key(struct config *conf, struct multipath *mp) do_prkey_set(mp->mpe, multipaths_origin); do_prkey_set(conf, conf_origin); put_be64(mp->reservation_key, 0); + mp->sa_flags = 0; mp->prkey_source = PRKEY_SOURCE_NONE; return 0; out: if (mp->prkey_source == PRKEY_SOURCE_FILE) { from_file = " (from prkeys file)"; - if (get_prkey(conf, mp, &prkey) != 0) + if (get_prkey(conf, mp, &prkey, &mp->sa_flags) != 0) put_be64(mp->reservation_key, 0); else put_be64(mp->reservation_key, prkey); } print_reservation_key(buff, PRKEY_SIZE, mp->reservation_key, - mp->prkey_source); + mp->sa_flags, mp->prkey_source); condlog(3, "%s: reservation_key = %s %s%s", mp->alias, buff, origin, from_file); return 0; @@ -978,3 +1021,18 @@ out: pp->dev, pp->find_multipaths_timeout, origin); return 0; } + +int select_all_tg_pt (struct config *conf, struct multipath * mp) +{ + const char *origin; + + mp_set_ovr(all_tg_pt); + mp_set_hwe(all_tg_pt); + mp_set_conf(all_tg_pt); + mp_set_default(all_tg_pt, DEFAULT_ALL_TG_PT); +out: + condlog(3, "%s: all_tg_pt = %s %s", mp->alias, + (mp->all_tg_pt == ALL_TG_PT_ON)? "yes" : "no", + origin); + return 0; +} diff --git a/libmultipath/propsel.h b/libmultipath/propsel.h index a022bee..ae99b92 100644 --- a/libmultipath/propsel.h +++ b/libmultipath/propsel.h @@ -34,3 +34,4 @@ int select_ghost_delay(struct config *conf, struct multipath * mp); void reconcile_features_with_options(const char *id, char **features, int* no_path_retry, int *retain_hwhandler); +int select_all_tg_pt (struct config *conf, struct multipath * mp); diff --git a/libmultipath/structs.c b/libmultipath/structs.c index 991095c..caa178a 100644 --- a/libmultipath/structs.c +++ b/libmultipath/structs.c @@ -102,6 +102,11 @@ alloc_path (void) pp->priority = PRIO_UNDEF; checker_clear(&pp->checker); dm_path_to_gen(pp)->ops = &dm_gen_path_ops; + pp->hwe = vector_alloc(); + if (pp->hwe == NULL) { + free(pp); + return NULL; + } } return pp; } @@ -125,6 +130,7 @@ free_path (struct path * pp) udev_device_unref(pp->udev); pp->udev = NULL; } + vector_free(pp->hwe); FREE(pp); } @@ -159,7 +165,7 @@ alloc_pathgroup (void) if (!pgp->paths) { FREE(pgp); - pgp = NULL; + return NULL; } dm_pathgroup_to_gen(pgp)->ops = &dm_gen_pathgroup_ops; @@ -353,7 +359,7 @@ store_adaptergroup(vector adapters, struct adapter_group * agp) } struct multipath * -find_mp_by_minor (vector mpvec, int minor) +find_mp_by_minor (const struct _vector *mpvec, int minor) { int i; struct multipath * mpp; @@ -372,7 +378,7 @@ find_mp_by_minor (vector mpvec, int minor) } struct multipath * -find_mp_by_wwid (vector mpvec, char * wwid) +find_mp_by_wwid (const struct _vector *mpvec, const char * wwid) { int i; struct multipath * mpp; @@ -388,7 +394,7 @@ find_mp_by_wwid (vector mpvec, char * wwid) } struct multipath * -find_mp_by_alias (vector mpvec, const char * alias) +find_mp_by_alias (const struct _vector *mpvec, const char * alias) { int i; int len; @@ -411,7 +417,7 @@ find_mp_by_alias (vector mpvec, const char * alias) } struct multipath * -find_mp_by_str (vector mpvec, char * str) +find_mp_by_str (const struct _vector *mpvec, const char * str) { int minor; @@ -422,7 +428,7 @@ find_mp_by_str (vector mpvec, char * str) } struct path * -find_path_by_dev (vector pathvec, const char * dev) +find_path_by_dev (const struct _vector *pathvec, const char * dev) { int i; struct path * pp; @@ -439,7 +445,7 @@ find_path_by_dev (vector pathvec, const char * dev) } struct path * -find_path_by_devt (vector pathvec, const char * dev_t) +find_path_by_devt (const struct _vector *pathvec, const char * dev_t) { int i; struct path * pp; @@ -455,7 +461,7 @@ find_path_by_devt (vector pathvec, const char * dev_t) return NULL; } -int pathcountgr(struct pathgroup *pgp, int state) +int pathcountgr(const struct pathgroup *pgp, int state) { struct path *pp; int count = 0; @@ -468,7 +474,7 @@ int pathcountgr(struct pathgroup *pgp, int state) return count; } -int pathcount(struct multipath *mpp, int state) +int pathcount(const struct multipath *mpp, int state) { struct pathgroup *pgp; int count = 0; @@ -481,7 +487,7 @@ int pathcount(struct multipath *mpp, int state) return count; } -int pathcmp(struct pathgroup *pgp, struct pathgroup *cpgp) +int pathcmp(const struct pathgroup *pgp, const struct pathgroup *cpgp) { int i, j; struct path *pp, *cpp; @@ -501,7 +507,7 @@ int pathcmp(struct pathgroup *pgp, struct pathgroup *cpgp) } struct path * -first_path (struct multipath * mpp) +first_path (const struct multipath * mpp) { struct pathgroup * pgp; if (!mpp->pg) diff --git a/libmultipath/structs.h b/libmultipath/structs.h index eb6a178..0a2623a 100644 --- a/libmultipath/structs.h +++ b/libmultipath/structs.h @@ -24,7 +24,6 @@ #define SCSI_VENDOR_SIZE 9 #define SCSI_PRODUCT_SIZE 17 -#define SCSI_REV_SIZE 5 #define SCSI_STATE_SIZE 19 #define NVME_MODEL_SIZE 41 #define NVME_REV_SIZE 9 @@ -59,10 +58,8 @@ enum failback_mode { enum sysfs_buses { SYSFS_BUS_UNDEF, SYSFS_BUS_SCSI, - SYSFS_BUS_IDE, SYSFS_BUS_CCW, SYSFS_BUS_CCISS, - SYSFS_BUS_RBD, SYSFS_BUS_NVME, }; @@ -122,9 +119,9 @@ enum find_multipaths_states { FIND_MULTIPATHS_UNDEF = YNU_UNDEF, FIND_MULTIPATHS_OFF = YNU_NO, FIND_MULTIPATHS_ON = _FIND_MULTIPATHS_F, - FIND_MULTIPATHS_STRICT = _FIND_MULTIPATHS_F|_FIND_MULTIPATHS_N, FIND_MULTIPATHS_GREEDY = _FIND_MULTIPATHS_I, FIND_MULTIPATHS_SMART = _FIND_MULTIPATHS_F|_FIND_MULTIPATHS_I, + FIND_MULTIPATHS_STRICT = _FIND_MULTIPATHS_F|_FIND_MULTIPATHS_N, __FIND_MULTIPATHS_LAST, }; @@ -217,6 +214,12 @@ enum prkey_sources { PRKEY_SOURCE_FILE, }; +enum all_tg_pt_states { + ALL_TG_PT_UNDEF = YNU_UNDEF, + ALL_TG_PT_OFF = YNU_NO, + ALL_TG_PT_ON = YNU_YES, +}; + struct sg_id { int host_no; int channel; @@ -283,7 +286,7 @@ struct path { int io_err_pathfail_starttime; int find_multipaths_timeout; /* configlet pointers */ - struct hwentry * hwe; + vector hwe; struct gen_path generic_path; }; @@ -342,7 +345,7 @@ struct multipath { char * features; char * hwhandler; struct mpentry * mpe; - struct hwentry * hwe; + vector hwe; /* threads */ pthread_t waiter; @@ -361,7 +364,9 @@ struct multipath { /* persistent management data*/ int prkey_source; struct be64 reservation_key; + uint8_t sa_flags; unsigned char prflag; + int all_tg_pt; struct gen_multipath generic_mp; }; @@ -413,18 +418,18 @@ int store_path (vector pathvec, struct path * pp); int store_pathgroup (vector pgvec, struct pathgroup * pgp); int add_pathgroup(struct multipath*, struct pathgroup *); -struct multipath * find_mp_by_alias (vector mp, const char * alias); -struct multipath * find_mp_by_wwid (vector mp, char * wwid); -struct multipath * find_mp_by_str (vector mp, char * wwid); -struct multipath * find_mp_by_minor (vector mp, int minor); +struct multipath * find_mp_by_alias (const struct _vector *mp, const char *alias); +struct multipath * find_mp_by_wwid (const struct _vector *mp, const char *wwid); +struct multipath * find_mp_by_str (const struct _vector *mp, const char *wwid); +struct multipath * find_mp_by_minor (const struct _vector *mp, int minor); -struct path * find_path_by_devt (vector pathvec, const char * devt); -struct path * find_path_by_dev (vector pathvec, const char * dev); -struct path * first_path (struct multipath * mpp); +struct path * find_path_by_devt (const struct _vector *pathvec, const char *devt); +struct path * find_path_by_dev (const struct _vector *pathvec, const char *dev); +struct path * first_path (const struct multipath *mpp); -int pathcountgr (struct pathgroup *, int); -int pathcount (struct multipath *, int); -int pathcmp (struct pathgroup *, struct pathgroup *); +int pathcountgr (const struct pathgroup *, int); +int pathcount (const struct multipath *, int); +int pathcmp (const struct pathgroup *, const struct pathgroup *); int add_feature (char **, const char *); int remove_feature (char **, const char *); diff --git a/libmultipath/structs_vec.c b/libmultipath/structs_vec.c index 38f0438..f87d69d 100644 --- a/libmultipath/structs_vec.c +++ b/libmultipath/structs_vec.c @@ -445,3 +445,22 @@ void update_queue_mode_add_path(struct multipath *mpp) } condlog(2, "%s: remaining active paths: %d", mpp->alias, mpp->nr_active); } + +vector get_used_hwes(const struct _vector *pathvec) +{ + int i, j; + struct path *pp; + struct hwentry *hwe; + vector v = vector_alloc(); + + if (v == NULL) + return NULL; + + vector_foreach_slot(pathvec, pp, i) { + vector_foreach_slot_backwards(pp->hwe, hwe, j) { + vector_find_or_add_slot(v, hwe); + } + } + + return v; +} diff --git a/libmultipath/structs_vec.h b/libmultipath/structs_vec.h index 4220ea3..f7777aa 100644 --- a/libmultipath/structs_vec.h +++ b/libmultipath/structs_vec.h @@ -38,5 +38,6 @@ void update_queue_mode_add_path(struct multipath *mpp); int update_multipath_table (struct multipath *mpp, vector pathvec, int is_daemon); int update_multipath_status (struct multipath *mpp); +vector get_used_hwes(const struct _vector *pathvec); #endif /* _STRUCTS_VEC_H */ diff --git a/libmultipath/sysfs.c b/libmultipath/sysfs.c index ee72e6a..b7dacaa 100644 --- a/libmultipath/sysfs.c +++ b/libmultipath/sysfs.c @@ -28,6 +28,7 @@ #include #include #include +#include #include "checkers.h" #include "vector.h" @@ -238,7 +239,7 @@ sysfs_get_size (struct path *pp, unsigned long long * size) int sysfs_check_holders(char * check_devt, char * new_devt) { unsigned int major, new_minor, table_minor; - char path[PATH_SIZE], check_dev[PATH_SIZE]; + char path[PATH_MAX], check_dev[PATH_SIZE]; char * table_name; DIR *dirfd; struct dirent *holder; @@ -255,7 +256,7 @@ int sysfs_check_holders(char * check_devt, char * new_devt) condlog(3, "%s: checking holder", check_dev); - snprintf(path, PATH_SIZE, "/sys/block/%s/holders", check_dev); + snprintf(path, sizeof(path), "/sys/block/%s/holders", check_dev); dirfd = opendir(path); if (dirfd == NULL) { condlog(3, "%s: failed to open directory %s (%d)", diff --git a/libmultipath/uevent.c b/libmultipath/uevent.c index fd8ca35..5f910e6 100644 --- a/libmultipath/uevent.c +++ b/libmultipath/uevent.c @@ -729,6 +729,12 @@ struct uevent *uevent_from_udev_device(struct udev_device *dev) if (i == HOTPLUG_NUM_ENVP - 1) break; } + if (!uev->devpath || ! uev->action) { + udev_device_unref(dev); + condlog(1, "uevent missing necessary fields"); + FREE(uev); + return NULL; + } uev->udev = dev; uev->envp[i] = NULL; diff --git a/libmultipath/unaligned.h b/libmultipath/unaligned.h index 14ec8b2..68c0774 100644 --- a/libmultipath/unaligned.h +++ b/libmultipath/unaligned.h @@ -17,6 +17,14 @@ static inline uint32_t get_unaligned_be32(void *ptr) return p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3]; } +static inline uint64_t get_unaligned_be64(void *ptr) +{ + uint32_t low = get_unaligned_be32(ptr + 4); + uint64_t high = get_unaligned_be32(ptr); + + return high << 32 | low; +} + static inline void put_unaligned_be16(uint16_t val, void *ptr) { uint8_t *p = ptr; @@ -35,4 +43,12 @@ static inline void put_unaligned_be32(uint32_t val, void *ptr) p[3] = val; } +static inline void put_unaligned_be64(uint64_t val, void *ptr) +{ + uint8_t *p = ptr; + + put_unaligned_be32(val >> 32, p); + put_unaligned_be32(val, p + 4); +} + #endif /* _UNALIGNED_H_ */ diff --git a/libmultipath/util.c b/libmultipath/util.c index 7251ad0..347af5b 100644 --- a/libmultipath/util.c +++ b/libmultipath/util.c @@ -10,6 +10,8 @@ #include #include #include +#include +#include #include "util.h" #include "debug.h" @@ -329,7 +331,7 @@ setup_thread_attr(pthread_attr_t *attr, size_t stacksize, int detached) int systemd_service_enabled_in(const char *dev, const char *prefix) { - char path[PATH_SIZE], file[PATH_SIZE], service[PATH_SIZE]; + char path[PATH_SIZE], file[PATH_MAX], service[PATH_SIZE]; DIR *dirfd; struct dirent *d; int found = 0; @@ -356,7 +358,7 @@ int systemd_service_enabled_in(const char *dev, const char *prefix) p = d->d_name + strlen(d->d_name) - 6; if (strcmp(p, ".wants")) continue; - snprintf(file, PATH_SIZE, "%s/%s/%s", + snprintf(file, sizeof(file), "%s/%s/%s", path, d->d_name, service); if (stat(file, &stbuf) == 0) { condlog(3, "%s: found %s", dev, file); @@ -435,6 +437,20 @@ int parse_prkey(char *ptr, uint64_t *prkey) return 0; } +int parse_prkey_flags(char *ptr, uint64_t *prkey, uint8_t *flags) +{ + char *flagstr; + + flagstr = strchr(ptr, ':'); + *flags = 0; + if (flagstr) { + *flagstr++ = '\0'; + if (strlen(flagstr) == 5 && strcmp(flagstr, "aptpl") == 0) + *flags = MPATH_F_APTPL_MASK; + } + return parse_prkey(ptr, prkey); +} + int safe_write(int fd, const void *buf, size_t count) { while (count > 0) { diff --git a/libmultipath/util.h b/libmultipath/util.h index a3ab894..56cec76 100644 --- a/libmultipath/util.h +++ b/libmultipath/util.h @@ -19,6 +19,7 @@ void setup_thread_attr(pthread_attr_t *attr, size_t stacksize, int detached); int systemd_service_enabled(const char *dev); int get_linux_version_code(void); int parse_prkey(char *ptr, uint64_t *prkey); +int parse_prkey_flags(char *ptr, uint64_t *prkey, uint8_t *flags); int safe_write(int fd, const void *buf, size_t count); #define KERNEL_VERSION(maj, min, ptc) ((((maj) * 256) + (min)) * 256 + (ptc)) diff --git a/libmultipath/vector.c b/libmultipath/vector.c index f741ae0..501cf4c 100644 --- a/libmultipath/vector.c +++ b/libmultipath/vector.c @@ -196,3 +196,15 @@ vector_set_slot(vector v, void *value) i = VECTOR_SIZE(v) - 1; v->slot[i] = value; } + +int vector_find_or_add_slot(vector v, void *value) +{ + int n = find_slot(v, value); + + if (n >= 0) + return n; + if (vector_alloc_slot(v) == NULL) + return -1; + vector_set_slot(v, value); + return VECTOR_SIZE(v) - 1; +} diff --git a/libmultipath/vector.h b/libmultipath/vector.h index b9450ac..41d2b89 100644 --- a/libmultipath/vector.h +++ b/libmultipath/vector.h @@ -82,6 +82,7 @@ extern void vector_set_slot(vector v, void *value); extern void vector_del_slot(vector v, int slot); extern void *vector_insert_slot(vector v, int slot, void *value); int find_slot(vector v, void * addr); +int vector_find_or_add_slot(vector v, void *value); extern void vector_repack(vector v); extern void vector_dump(vector v); extern void dump_strvec(vector strvec); diff --git a/libmultipath/version.h b/libmultipath/version.h index 25dad1d..10ebbda 100644 --- a/libmultipath/version.h +++ b/libmultipath/version.h @@ -20,8 +20,8 @@ #ifndef _VERSION_H #define _VERSION_H -#define VERSION_CODE 0x000707 -#define DATE_CODE 0x050b12 +#define VERSION_CODE 0x000708 +#define DATE_CODE 0x0a0a12 #define PROG "multipath-tools" diff --git a/mpathpersist/main.c b/mpathpersist/main.c index 5b37f3a..10cba45 100644 --- a/mpathpersist/main.c +++ b/mpathpersist/main.c @@ -118,7 +118,7 @@ int main (int argc, char * argv[]) { int option_index = 0; - c = getopt_long (argc, argv, "v:Cd:hHioZK:S:PAT:skrGILcRX:l:", + c = getopt_long (argc, argv, "v:Cd:hHioYZK:S:PAT:skrGILcRX:l:", long_options, &option_index); if (c == -1) break; @@ -158,6 +158,10 @@ int main (int argc, char * argv[]) prout_flag = 1; break; + case 'Y': + param_alltgpt = 1; + ++num_prout_param; + break; case 'Z': param_aptpl = 1; ++num_prout_param; @@ -380,7 +384,7 @@ int main (int argc, char * argv[]) } /* open device */ - if ((fd = open (device_name, O_WRONLY)) < 0) + if ((fd = open (device_name, O_RDONLY)) < 0) { fprintf (stderr, "%s: error opening file (rw) fd=%d\n", device_name, fd); @@ -443,9 +447,9 @@ int main (int argc, char * argv[]) } if (param_alltgpt) - paramp->sa_flags |= 0x4; + paramp->sa_flags |= MPATH_F_ALL_TG_PT_MASK; if (param_aptpl) - paramp->sa_flags |= 0x1; + paramp->sa_flags |= MPATH_F_APTPL_MASK; if (num_transport) { @@ -698,8 +702,10 @@ static void usage(void) " --hex|-H output response in hex\n" " --in|-i request PR In command \n" " --out|-o request PR Out command\n" + " --param-alltgpt|-Y PR Out parameter 'ALL_TG_PT\n" " --param-aptpl|-Z PR Out parameter 'APTPL'\n" " --read-keys|-k PR In: Read Keys\n" + " --param-rk=RK|-K RK PR Out parameter reservation key\n" " --param-sark=SARK|-S SARK PR Out parameter service " "action\n" " reservation key (SARK is in " diff --git a/mpathpersist/main.h b/mpathpersist/main.h index 5c0e089..beb8a21 100644 --- a/mpathpersist/main.h +++ b/mpathpersist/main.h @@ -6,6 +6,7 @@ static struct option long_options[] = { {"hex", 0, NULL, 'H'}, {"in", 0, NULL, 'i'}, {"out", 0, NULL, 'o'}, + {"param-alltgpt", 0, NULL, 'Y'}, {"param-aptpl", 0, NULL, 'Z'}, {"param-rk", 1, NULL, 'K'}, {"param-sark", 1, NULL, 'S'}, diff --git a/mpathpersist/mpathpersist.8 b/mpathpersist/mpathpersist.8 index a8982e6..885491d 100644 --- a/mpathpersist/mpathpersist.8 +++ b/mpathpersist/mpathpersist.8 @@ -87,6 +87,10 @@ Request PR In command. Request PR Out command. . .TP +.B \--param-alltgpt|\-Y +PR Out parameter 'ALL_TG_PT'. +. +.TP .B \--param-aptpl|\-Z PR Out parameter 'APTPL'. . diff --git a/multipath/main.c b/multipath/main.c index c69e996..d5aad95 100644 --- a/multipath/main.c +++ b/multipath/main.c @@ -80,6 +80,19 @@ void put_multipath_config(void *arg) /* Noop for now */ } +static int +dump_config (struct config *conf, vector hwes, vector mpvec) +{ + char * reply = snprint_config(conf, NULL, hwes, mpvec); + + if (reply != NULL) { + printf("%s", reply); + FREE(reply); + return 0; + } else + return 1; +} + void rcu_register_thread_memb(void) {} void rcu_unregister_thread_memb(void) {} @@ -112,7 +125,7 @@ usage (char * progname) fprintf (stderr, " %s [-a|-c|-w|-W] [-d] [-r] [-i] [-v lvl] [-p pol] [-b fil] [-q] [dev]\n", progname); fprintf (stderr, " %s -l|-ll|-f [-v lvl] [-b fil] [-R num] [dev]\n", progname); fprintf (stderr, " %s -F [-v lvl] [-R num]\n", progname); - fprintf (stderr, " %s -t\n", progname); + fprintf (stderr, " %s [-t|-T]\n", progname); fprintf (stderr, " %s -h\n", progname); fprintf (stderr, "\n" @@ -128,6 +141,7 @@ usage (char * progname) " -q allow queue_if_no_path when multipathd is not running\n" " -d dry run, do not create or update devmaps\n" " -t display the currently used multipathd configuration\n" + " -T display the multipathd configuration without builtin defaults\n" " -r force devmap reload\n" " -i ignore wwids file\n" " -B treat the bindings file as read only\n" @@ -468,7 +482,7 @@ static int print_cmd_valid(int k, const vector pathvec, struct timespec until; struct path *pp; - if (k < 0 || k >= sizeof(vals)) + if (k < 0 || k >= (sizeof(vals) / sizeof(int))) return 1; if (k == 2) { @@ -482,10 +496,8 @@ static int print_cmd_valid(int k, const vector pathvec, pp, pp->find_multipaths_timeout, &until); if (wait != FIND_MULTIPATHS_WAITING) k = 1; - } else if (pathvec != NULL) { - pp = VECTOR_SLOT(pathvec, 0); + } else if (pathvec != NULL && (pp = VECTOR_SLOT(pathvec, 0))) wait = find_multipaths_check_timeout(pp, 0, &until); - } if (wait == FIND_MULTIPATHS_WAITING) printf("FIND_MULTIPATHS_WAIT_UNTIL=\"%ld.%06ld\"\n", until.tv_sec, until.tv_nsec/1000); @@ -673,6 +685,13 @@ configure (struct config *conf, enum mpath_cmds cmd, filter_pathvec(pathvec, refwwid); + if (cmd == CMD_DUMP_CONFIG) { + vector hwes = get_used_hwes(pathvec); + + dump_config(conf, hwes, curmp); + vector_free(hwes); + goto out; + } if (cmd == CMD_VALID_PATH) { struct path *pp; @@ -752,68 +771,6 @@ out: return r; } -static int -dump_config (struct config *conf) -{ - char * c, * tmp = NULL; - char * reply; - unsigned int maxlen = 256; - int again = 1; - - reply = MALLOC(maxlen); - - while (again) { - if (!reply) { - if (tmp) - free(tmp); - return 1; - } - c = tmp = reply; - c += snprint_defaults(conf, c, reply + maxlen - c); - again = ((c - reply) == maxlen); - if (again) { - reply = REALLOC(reply, maxlen *= 2); - continue; - } - c += snprint_blacklist(conf, c, reply + maxlen - c); - again = ((c - reply) == maxlen); - if (again) { - reply = REALLOC(reply, maxlen *= 2); - continue; - } - c += snprint_blacklist_except(conf, c, reply + maxlen - c); - again = ((c - reply) == maxlen); - if (again) { - reply = REALLOC(reply, maxlen *= 2); - continue; - } - c += snprint_hwtable(conf, c, reply + maxlen - c, conf->hwtable); - again = ((c - reply) == maxlen); - if (again) { - reply = REALLOC(reply, maxlen *= 2); - continue; - } - c += snprint_overrides(conf, c, reply + maxlen - c, - conf->overrides); - again = ((c - reply) == maxlen); - if (again) { - reply = REALLOC(reply, maxlen *= 2); - continue; - } - if (VECTOR_SIZE(conf->mptable) > 0) { - c += snprint_mptable(conf, c, reply + maxlen - c, - conf->mptable); - again = ((c - reply) == maxlen); - if (again) - reply = REALLOC(reply, maxlen *= 2); - } - } - - printf("%s", reply); - FREE(reply); - return 0; -} - static int get_dev_type(char *dev) { struct stat buf; @@ -839,16 +796,18 @@ get_dev_type(char *dev) { * * It is safer to use equivalent multipathd client commands instead. */ +enum { + DELEGATE_OK = 0, + DELEGATE_ERROR = -1, + NOT_DELEGATED = 1, +}; + int delegate_to_multipathd(enum mpath_cmds cmd, const char *dev, enum devtypes dev_type, const struct config *conf) { int fd; char command[1024], *p, *reply = NULL; - int n, r = 0; - - fd = mpath_connect(); - if (fd == -1) - return 0; + int n, r = DELEGATE_ERROR; p = command; *p = '\0'; @@ -861,17 +820,21 @@ int delegate_to_multipathd(enum mpath_cmds cmd, const char *dev, if (strlen(command) == 0) /* No command found, no need to delegate */ - return 0; - else if (p >= command + sizeof(command)) { + return NOT_DELEGATED; + + fd = mpath_connect(); + if (fd == -1) + return NOT_DELEGATED; + + if (p >= command + sizeof(command)) { condlog(0, "internal error - command buffer overflow"); - r = -1; goto out; } condlog(3, "delegating command to multipathd"); - r = mpath_process_cmd(fd, command, &reply, conf->uxsock_timeout); - if (r == -1) { + if (mpath_process_cmd(fd, command, &reply, conf->uxsock_timeout) + == -1) { condlog(1, "error in multipath command %s: %s", command, strerror(errno)); goto out; @@ -879,13 +842,11 @@ int delegate_to_multipathd(enum mpath_cmds cmd, const char *dev, if (reply != NULL && *reply != '\0' && strcmp(reply, "ok\n")) printf("%s", reply); - r = 1; + r = DELEGATE_OK; out: FREE(reply); close(fd); - if (r < 0) - exit(1); return r; } @@ -909,7 +870,7 @@ main (int argc, char *argv[]) exit(1); multipath_conf = conf; conf->retrigger_tries = 0; - while ((arg = getopt(argc, argv, ":adcChl::FfM:v:p:b:BrR:itquUwW")) != EOF ) { + while ((arg = getopt(argc, argv, ":adcChl::FfM:v:p:b:BrR:itTquUwW")) != EOF ) { switch(arg) { case 1: printf("optarg : %s\n",optarg); break; @@ -974,8 +935,11 @@ main (int argc, char *argv[]) conf->find_multipaths |= _FIND_MULTIPATHS_I; break; case 't': - r = dump_config(conf); + r = dump_config(conf, NULL, NULL); goto out_free_config; + case 'T': + cmd = CMD_DUMP_CONFIG; + break; case 'h': usage(argv[0]); exit(0); @@ -1089,8 +1053,14 @@ main (int argc, char *argv[]) goto out; } - if (delegate_to_multipathd(cmd, dev, dev_type, conf)) + switch(delegate_to_multipathd(cmd, dev, dev_type, conf)) { + case DELEGATE_OK: exit(0); + case DELEGATE_ERROR: + exit(1); + case NOT_DELEGATED: + break; + } if (cmd == CMD_RESET_WWIDS) { struct multipath * mpp; diff --git a/multipath/multipath.8 b/multipath/multipath.8 index 914a8cb..b5e5292 100644 --- a/multipath/multipath.8 +++ b/multipath/multipath.8 @@ -25,7 +25,7 @@ multipath \- Device mapper target autoconfig. .RB [\| \-b\ \c .IR bindings_file \|] .RB [\| \-d \|] -.RB [\| \-h | \-l | \-ll | \-f | \-t | \-F | \-B | \-c | \-C | \-q | \-r | \-i | \-a | \-u | \-U | \-w | \-W \|] +.RB [\| \-h | \-l | \-ll | \-f | \-t | \-T | \-F | \-B | \-c | \-C | \-q | \-r | \-i | \-a | \-u | \-U | \-w | \-W \|] .RB [\| \-p\ \c .IR failover | multibus | group_by_serial | group_by_prio | group_by_node_name \|] .RB [\| \-R\ \c @@ -89,6 +89,12 @@ Flush all unused multipath device maps. Display the currently used multipathd configuration. . .TP +.B \-T +Display the currently used multipathd configuration, limiting the output to +those devices actually present in the system. This can be used a template for +creating \fImultipath.conf\fR. +. +.TP .B \-r Force devmap reload. . diff --git a/multipath/multipath.conf.5 b/multipath/multipath.conf.5 index f689795..6333366 100644 --- a/multipath/multipath.conf.5 +++ b/multipath/multipath.conf.5 @@ -8,7 +8,7 @@ .\" .\" ---------------------------------------------------------------------------- . -.TH MULTIPATH.CONF 5 2017-08-18 Linux +.TH MULTIPATH.CONF 5 2018-05-21 Linux . . .\" ---------------------------------------------------------------------------- @@ -85,6 +85,16 @@ the indentation shown in the above example is helpful for human readers but not mandatory. .LP . +.LP +.B Note on regular expressions: +The \fImultipath.conf\fR syntax allows many attribute values to be specified as POSIX +Extended Regular Expressions (see \fBregex\fR(7)). These regular expressions +are \fBcase sensitive\fR and \fBnot anchored\fR, thus the expression "bar" matches "barbie", +"rhabarber", and "wunderbar", but not "Barbie". To avoid unwanted substring +matches, standard regular expression syntax using the special characters "^" and "$" can be used. +. +.LP +. The following \fIsection\fP keywords are recognized: .TP 17 .B defaults @@ -104,10 +114,12 @@ multipath topology discovery, despite being listed in the .B multipaths This section defines the multipath topologies. They are indexed by a \fIWorld Wide Identifier\fR(WWID). For details on the WWID generation -see section \fIWWID generation\fR below. +see section \fIWWID generation\fR below. Attributes set in this section take +precedence over all others. .TP .B devices -This section defines the device-specific settings. +This section defines the device-specific settings. Devices are identified by +vendor, product, and revision. .TP .B overrides This section defines values for attributes that should override the @@ -309,7 +321,7 @@ Active/Standby mode exclusively. .TP .I hds (Hardware-dependent) -Generate the path priority for Hitachi AMS 2000 and HUS 100 families of arrays. +Generate the path priority for Hitachi AMS families of arrays other than AMS 2000. .TP .I random Generate a random priority between 1 and 10. @@ -482,9 +494,6 @@ Check the path state for HP/COMPAQ Smart Array(CCISS) controllers. .I none Do not check the device, fallback to use the values retrieved from sysfs .TP -.I rbd -Check if the path is in the Ceph blacklist and remap the path if it is. -.TP The default is: \fBtur\fR .RE . @@ -729,20 +738,36 @@ This is the service action reservation key used by mpathpersist. It must be set for all multipath devices using persistent reservations, and it must be the same as the RESERVATION KEY field of the PERSISTENT RESERVE OUT parameter list which contains an 8-byte value provided by the application client to the -device server to identify the I_T nexus. +device server to identify the I_T nexus. If the \fI--param-aptpl\fR option is +used when registering the key with mpathpersist, \fB:aptpl\fR must be appended +to the end of the reservation key. .RS .PP Alternatively, this can be set to \fBfile\fR, which will store the RESERVATION KEY registered by mpathpersist in the \fIprkeys_file\fR. multipathd will then use this key to register additional paths as they appear. When the registration is removed, the RESERVATION KEY is removed from the -\fIprkeys_file\fR. +\fIprkeys_file\fR. The prkeys file will automatically keep track of whether +the key was registered with \fI--param-aptpl\fR. .TP The default is: \fB\fR .RE . . .TP +.B all_tg_pt +Set the 'all targets ports' flag when registering keys with mpathpersist. Some +arrays automatically set and clear registration keys on all target ports from a +host, instead of per target port per host. The ALL_TG_PT flag must be set to +successfully use mpathpersist on these arrays. Setting this option is identical +to calling mpathpersist with \fI--param-alltgpt\fR +.RS +.TP +The default is: \fBno\fR +.RE +. +. +.TP .B retain_attached_hw_handler (Obsolete for kernels >= 4.3) If set to .I yes @@ -835,10 +860,18 @@ The default is: \fBno\fR . .TP .B partition_delimiter -If this value is not set, when multipath renames a device, it will act just -like the kpartx default does, only adding a \fI"p"\fR to names ending in a -number. If this parameter is set, multipath will act like kpartx does with -the \fI-p\fR option is used, and always add delimiter. +This parameter controls how multipath chooses the names of partition devices +of multipath maps if a multipath map is renamed (e.g. if a map alias is added +or changed). If this parameter is set to a string other than "/UNSET/" (even +the empty string), multipath inserts that string between device name and +partition number to construct the partition device name. +Otherwise (i.e. if this parameter is unset or has the value "/UNSET/"), +the behavior depends on the map name: if it ends in a digit, a \fI"p"\fR is +inserted between name and partition number; otherwise, the partition number is +simply appended. +Distributions may use a non-null default value for this option; in this case, +the user must set it to "/UNSET/" to obtain the original \fB\fR +behavior. Use \fImultipath -T\fR to check the current settings. .RS .TP The default is: \fB\fR @@ -900,7 +933,7 @@ The error rate threshold as a permillage (1/1000). One of the four parameters of supporting path check based on accounting IO error such as intermittent error. Refer to \fImarginal_path_err_sample_time\fR. If the rate of IO errors on a particular path is greater than this parameter, then the path will not -reinstate for \fImarginal_path_err_rate_threshold\fR seconds unless there is +reinstate for \fImarginal_path_err_recheck_gap_time\fR seconds unless there is only one active path. .RS .TP @@ -1114,100 +1147,124 @@ The default is \fBno\fR . . .\" ---------------------------------------------------------------------------- -.SH "blacklist section" +.SH "blacklist and blacklist_exceptions sections" .\" ---------------------------------------------------------------------------- . -The \fIblacklist\fR section is used to exclude specific device from inclusion in -the multipath topology. It is most commonly used to exclude local disks or LUNs -for the array controller. +The \fIblacklist\fR section is used to exclude specific devices from +the multipath topology. It is most commonly used to exclude local disks or +non-disk devices (such as LUNs for the storage array controller) from +being handled by multipath-tools. .LP . . -The following keywords are recognized: +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 +to blacklist "everything" using a catch-all regular expression, and create +specific blacklist_exceptions entries for those devices that should be handled +by multipath-tools. +.LP +. +. +The following keywords are recognized in both sections. The defaults are empty +unless explicitly stated. .TP 17 .B devnode -Regular expression of the device nodes to be excluded. +Regular expression matching the device nodes to be excluded/included. .RS -.TP -The default is: \fB^(ram|raw|loop|fd|md|dm-|sr|scd|st|dcssblk)[0-9]\fR and \fB^(td|hd|vd)[a-z]\fR +.PP +The default \fIblacklist\fR consists of the regular expressions +"^(ram|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. .RE .TP .B wwid -The \fIWorld Wide Identification\fR of a device. -.TP -.B property -Regular expression of the udev property to be excluded. +Regular expression for the \fIWorld Wide Identifier\fR of a device to be excluded/included. +. .TP .B device Subsection for the device description. This subsection recognizes the .B vendor and .B product -keywords. For a full description of these keywords please see the +keywords. Both are regular expressions. For a full description of these keywords please see the \fIdevices\fR section description. -. -. -.\" ---------------------------------------------------------------------------- -.SH "blacklist_exceptions section" -.\" ---------------------------------------------------------------------------- -. -The \fIblacklist_exceptions\fR section is used to revert the actions of the -\fIblacklist\fR section. For example to include specific device in the -multipath topology. This allows one to selectively include devices which -would normally be excluded via the \fIblacklist\fR section. -.LP -. -. -The following keywords are recognized: -.TP 17 -.B devnode -Regular expression of the device nodes to be whitelisted. -.TP -.B wwid -The \fIWorld Wide Identification\fR of a device. .TP .B property -Regular expression of the udev property to be whitelisted. +Regular expression for an udev property. All +devices that have matching udev properties will be excluded/included. +The handling of the \fIproperty\fR keyword is special, +because devices \fBmust\fR have at least one whitelisted udev property; +otherwise they're treated as blacklisted, and the message +"\fIblacklisted, udev property missing\fR" is displayed in the logs. +. .RS -.TP -The default is: \fB(SCSI_IDENT_|ID_WWN)\fR +.PP +The default \fIblacklist exception\fR is: \fB(SCSI_IDENT_|ID_WWN)\fR, causing +well-behaved SCSI devices and devices that provide a WWN (World Wide Number) +to be included, and all others to be excluded. .RE .TP -.B device -Subsection for the device description. This subsection recognizes the -.B vendor -and -.B product -keywords. For a full description of these keywords please see the \fIdevices\fR -section description. +.B protocol +Regular expression for the protocol of a device to be excluded/included. +.RS +.PP +The protocol strings that multipath recognizes are \fIscsi:fcp\fR, +\fIscsi:spi\fR, \fIscsi:ssa\fR, \fIscsi:sbp\fR, \fIscsi:srp\fR, +\fIscsi:iscsi\fR, \fIscsi:sas\fR, \fIscsi:adt\fR, \fIscsi:ata\fR, +\fIscsi:unspec\fR, \fIccw\fR, \fIcciss\fR, \fInvme\fR, and \fIundef\fR. +The protocol that a path is using can be viewed by running +\fBmultipathd show paths format "%d %P"\fR +.RE .LP -The \fIproperty\fR blacklist and whitelist handling is different from the usual -handling in the sense that the whitelist \fIhas\fR to be set, otherwise the -device will be blacklisted. In these cases the message \fIblacklisted, udev -property missing\fR will be displayed. +For every device, these 5 blacklist criteria are evaluated in the the order +"property, dev\%node, device, protocol, wwid". If a device turns out to be +blacklisted by any criterion, it's excluded from handling by multipathd, and +the later criteria aren't evaluated any more. For each +criterion, the whitelist takes precedence over the blacklist if a device +matches both. +.LP +.B +Note: +Besides the blacklist and whitelist, other configuration options such as +\fIfind_multipaths\fR have an impact on +whether or not a given device is handled by multipath-tools. . . .\" ---------------------------------------------------------------------------- .SH "multipaths section" .\" ---------------------------------------------------------------------------- . +The \fImultipaths\fR section allows setting attributes of multipath maps. The +attributes that are set via the multipaths section (see list below) take +precedence over all other configuration settings, including those from the +\fIoverrides\fR section. +.LP The only recognized attribute for the \fImultipaths\fR section is the -\fImultipath\fR subsection. +\fImultipath\fR subsection. If there are multiple \fImultipath\fR subsections +matching a given WWID, the contents of these sections are merged, and settings +from later entries take precedence. .LP . . The \fImultipath\fR subsection recognizes the following attributes: .TP 17 .B wwid -(Mandatory) Index of the container. +(Mandatory) World Wide Identifier. Detected multipath maps are matched agains this attribute. +Note that, unlike the \fIwwid\fR attribute in the \fIblacklist\fR section, +this is \fBnot\fR a regular expression or a substring; WWIDs must match +exactly inside the multipaths section. .TP .B alias -Symbolic name for the multipath map. +Symbolic name for the multipath map. This takes precedence over a an entry for +the same WWID in the \fIbindings_file\fR. .LP . . The following attributes are optional; if not set the default values -are taken from the \fIdefaults\fR or \fIdevices\fR section: +are taken from the \fIoverrides\fR, \fIdevices\fR, or \fIdefaults\fR +section: .sp 1 .PD .1v .RS @@ -1266,26 +1323,46 @@ are taken from the \fIdefaults\fR or \fIdevices\fR section: .SH "devices section" .\" ---------------------------------------------------------------------------- . +\fImultipath-tools\fR have a built-in device table with reasonable defaults +for more than 100 known multipath-capable storage devices. The devices section +can be used to override these settings. If there are multiple matches for a +given device, the attributes of all matching entries are applied to it. +If an attribute is specified in several matching device subsections, +later entries take precedence. Thus, entries in files under \fIconfig_dir\fR (in +reverse alphabetical order) have the highest precedence, followed by entries +in \fImultipath.conf\fR; the built-in hardware table has the lowest +precedence. Inside a configuration file, later entries have higher precedence +than earlier ones. +.LP The only recognized attribute for the \fIdevices\fR section is the \fIdevice\fR -subsection. +subsection. Devices detected in the system are matched against the device entries +using the \fBvendor\fR, \fBproduct\fR, and \fBrevision\fR fields, which are +all POSIX Extended regular expressions (see \fBregex\fR(7)). +.LP +The vendor, product, and revision fields that multipath or multipathd detect for +devices in a system depend on the device type. For SCSI devices, they correspond to the +respective fields of the SCSI INQUIRY page. In general, the command '\fImultipathd show +paths format "%d %s"\fR' command can be used to see the detected properties +for all devices in the system. .LP -. . The \fIdevice\fR subsection recognizes the following attributes: -.TP -vendor, product, revision and product_blacklist are POSIX Extended regex. .TP 17 .B vendor -(Mandatory) Vendor identifier. +(Mandatory) Regular expression to match the vendor name. .TP .B product -(Mandatory) Product identifier. +(Mandatory) Regular expression to match the product name. .TP .B revision -Revision identfier. +Regular expression to match the product revision. If not specified, any +revision matches. .TP .B product_blacklist -Product strings to blacklist for this vendor. +Products with the given \fBvendor\fR matching this string are +blacklisted. This is equivalent to a \fBdevice\fR entry in the \fIblacklist\fR +section with the \fIvendor\fR attribute set to this entry's \fIvendor\fR, and +the \fIproduct\fR attribute set to the value of \fIproduct_blacklist\fR. .TP .B alias_prefix The user_friendly_names prefix to use for this @@ -1475,8 +1552,12 @@ Multipath uses a \fIWorld Wide Identification\fR (WWID) to determine which paths belong to the same device. Each path presenting the same WWID is assumed to point to the same device. .LP -The WWID is generated by three methods (in the order of preference): +The WWID is generated by four methods (in the order of preference): .TP 17 +.B uid_attrs +The WWID is derived from udev attributes by matching the device node name. See +description of \fIuid_attrs\fR in the defaults section above. +.TP .B getuid_callout Use the specified external program; cf \fIgetuid_callout\fR above. Care should be taken when using this method; the external program diff --git a/multipathd/cli.c b/multipathd/cli.c index d5ee4ff..a75afe3 100644 --- a/multipathd/cli.c +++ b/multipathd/cli.c @@ -212,6 +212,7 @@ load_keys (void) r += add_key(keys, "setprkey", SETPRKEY, 0); r += add_key(keys, "unsetprkey", UNSETPRKEY, 0); r += add_key(keys, "key", KEY, 1); + r += add_key(keys, "local", LOCAL, 0); if (r) { @@ -549,6 +550,7 @@ cli_init (void) { add_handler(LIST+MAP+FMT, NULL); add_handler(LIST+MAP+RAW+FMT, NULL); add_handler(LIST+CONFIG, NULL); + add_handler(LIST+CONFIG+LOCAL, NULL); add_handler(LIST+BLACKLIST, NULL); add_handler(LIST+DEVICES, NULL); add_handler(LIST+WILDCARDS, NULL); diff --git a/multipathd/cli.h b/multipathd/cli.h index 021f25b..7cc7e4b 100644 --- a/multipathd/cli.h +++ b/multipathd/cli.h @@ -44,6 +44,7 @@ enum { __SETPRKEY, __UNSETPRKEY, __KEY, + __LOCAL, }; #define LIST (1 << __LIST) @@ -87,6 +88,7 @@ enum { #define SETPRKEY (1ULL << __SETPRKEY) #define UNSETPRKEY (1ULL << __UNSETPRKEY) #define KEY (1ULL << __KEY) +#define LOCAL (1ULL << __LOCAL) #define INITIAL_REPLY_LEN 1200 diff --git a/multipathd/cli_handlers.c b/multipathd/cli_handlers.c index ba50fb8..bb16472 100644 --- a/multipathd/cli_handlers.c +++ b/multipathd/cli_handlers.c @@ -21,6 +21,7 @@ #include "sysfs.h" #include #include +#include #include "util.h" #include "prkey.h" #include "propsel.h" @@ -245,65 +246,20 @@ show_map_json (char ** r, int * len, struct multipath * mpp, return 0; } -int -show_config (char ** r, int * len) +static int +show_config (char ** r, int * len, const struct _vector *hwtable, + const struct _vector *mpvec) { - char * c; - char * reply; - unsigned int maxlen = INITIAL_REPLY_LEN; - int again = 1; struct config *conf; - int fail = 0; - - c = reply = MALLOC(maxlen); + char *reply; conf = get_multipath_config(); pthread_cleanup_push(put_multipath_config, conf); - while (again) { - if (!reply) { - fail = 1; - break; - } - c = reply; - c += snprint_defaults(conf, c, reply + maxlen - c); - again = ((c - reply) == maxlen); - REALLOC_REPLY(reply, again, maxlen); - if (again) - continue; - c += snprint_blacklist(conf, c, reply + maxlen - c); - again = ((c - reply) == maxlen); - REALLOC_REPLY(reply, again, maxlen); - if (again) - continue; - c += snprint_blacklist_except(conf, c, reply + maxlen - c); - again = ((c - reply) == maxlen); - REALLOC_REPLY(reply, again, maxlen); - if (again) - continue; - c += snprint_hwtable(conf, c, reply + maxlen - c, - conf->hwtable); - again = ((c - reply) == maxlen); - REALLOC_REPLY(reply, again, maxlen); - if (again) - continue; - c += snprint_overrides(conf, c, reply + maxlen - c, - conf->overrides); - again = ((c - reply) == maxlen); - REALLOC_REPLY(reply, again, maxlen); - if (again) - continue; - if (VECTOR_SIZE(conf->mptable) > 0) { - c += snprint_mptable(conf, c, reply + maxlen - c, - conf->mptable); - again = ((c - reply) == maxlen); - REALLOC_REPLY(reply, again, maxlen); - } - } + reply = snprint_config(conf, len, hwtable, mpvec); pthread_cleanup_pop(1); - if (fail) + if (reply == NULL) return 1; *r = reply; - *len = (int)(c - reply + 1); return 0; } @@ -323,7 +279,28 @@ cli_list_config (void * v, char ** reply, int * len, void * data) { condlog(3, "list config (operator)"); - return show_config(reply, len); + return show_config(reply, len, NULL, NULL); +} + +static void v_free(void *x) +{ + vector_free(x); +} + +int +cli_list_config_local (void * v, char ** reply, int * len, void * data) +{ + struct vectors * vecs = (struct vectors *)data; + vector hwes; + int ret; + + condlog(3, "list config local (operator)"); + + hwes = get_used_hwes(vecs->pathvec); + pthread_cleanup_push(v_free, hwes); + ret = show_config(reply, len, hwes, vecs->mpvec); + pthread_cleanup_pop(1); + return ret; } int @@ -819,8 +796,8 @@ cli_add_map (void * v, char ** reply, int * len, void * data) if (!alias && !count) { condlog(2, "%s: mapname not found for %d:%d", param, major, minor); - rc = get_refwwid(CMD_NONE, param, DEV_DEVMAP, - vecs->pathvec, &refwwid); + get_refwwid(CMD_NONE, param, DEV_DEVMAP, + vecs->pathvec, &refwwid); if (refwwid) { if (coalesce_paths(vecs, NULL, refwwid, FORCE_RELOAD_NONE, CMD_NONE)) @@ -904,7 +881,12 @@ int resize_map(struct multipath *mpp, unsigned long long size, mpp->size = size; update_mpp_paths(mpp, vecs->pathvec); - setup_map(mpp, params, PARAMS_SIZE, vecs); + if (setup_map(mpp, params, PARAMS_SIZE, vecs) != 0) { + condlog(0, "%s: failed to setup map for resize : %s", + mpp->alias, strerror(errno)); + mpp->size = orig_size; + return 1; + } mpp->action = ACT_RESIZE; mpp->force_udev_reload = 1; if (domap(mpp, params, 1) <= 0) { @@ -1463,6 +1445,7 @@ cli_getprkey(void * v, char ** reply, int * len, void * data) struct multipath * mpp; struct vectors * vecs = (struct vectors *)data; char *mapname = get_keyparam(v, MAP); + char *flagstr = ""; mapname = convert_dev(mapname, 0); condlog(3, "%s: get persistent reservation key (operator)", mapname); @@ -1471,15 +1454,17 @@ cli_getprkey(void * v, char ** reply, int * len, void * data) if (!mpp) return 1; - *reply = malloc(20); + *reply = malloc(26); if (!get_be64(mpp->reservation_key)) { sprintf(*reply, "none\n"); *len = strlen(*reply) + 1; return 0; } - snprintf(*reply, 20, "0x%" PRIx64 "\n", - get_be64(mpp->reservation_key)); + if (mpp->sa_flags & MPATH_F_APTPL_MASK) + flagstr = ":aptpl"; + snprintf(*reply, 26, "0x%" PRIx64 "%s\n", + get_be64(mpp->reservation_key), flagstr); (*reply)[19] = '\0'; *len = strlen(*reply) + 1; return 0; @@ -1503,7 +1488,7 @@ cli_unsetprkey(void * v, char ** reply, int * len, void * data) conf = get_multipath_config(); pthread_cleanup_push(put_multipath_config, conf); - ret = set_prkey(conf, mpp, 0); + ret = set_prkey(conf, mpp, 0, 0); pthread_cleanup_pop(1); return ret; @@ -1517,6 +1502,7 @@ cli_setprkey(void * v, char ** reply, int * len, void * data) char *mapname = get_keyparam(v, MAP); char *keyparam = get_keyparam(v, KEY); uint64_t prkey; + uint8_t flags; int ret; struct config *conf; @@ -1527,14 +1513,14 @@ cli_setprkey(void * v, char ** reply, int * len, void * data) if (!mpp) return 1; - if (parse_prkey(keyparam, &prkey) != 0) { + if (parse_prkey_flags(keyparam, &prkey, &flags) != 0) { condlog(0, "%s: invalid prkey : '%s'", mapname, keyparam); return 1; } conf = get_multipath_config(); pthread_cleanup_push(put_multipath_config, conf); - ret = set_prkey(conf, mpp, prkey); + ret = set_prkey(conf, mpp, prkey, flags); pthread_cleanup_pop(1); return ret; diff --git a/multipathd/cli_handlers.h b/multipathd/cli_handlers.h index 78a3a43..edbdf06 100644 --- a/multipathd/cli_handlers.h +++ b/multipathd/cli_handlers.h @@ -16,6 +16,7 @@ int cli_list_maps_topology (void * v, char ** reply, int * len, void * data); int cli_list_map_json (void * v, char ** reply, int * len, void * data); int cli_list_maps_json (void * v, char ** reply, int * len, void * data); int cli_list_config (void * v, char ** reply, int * len, void * data); +int cli_list_config_local (void * v, char ** reply, int * len, void * data); int cli_list_blacklist (void * v, char ** reply, int * len, void * data); int cli_list_devices (void * v, char ** reply, int * len, void * data); int cli_list_wildcards (void * v, char ** reply, int * len, void * data); diff --git a/multipathd/dmevents.c b/multipathd/dmevents.c index e98a974..31e64a7 100644 --- a/multipathd/dmevents.c +++ b/multipathd/dmevents.c @@ -50,16 +50,20 @@ struct dmevent_waiter { }; static struct dmevent_waiter *waiter; +/* + * DM_VERSION_MINOR hasn't been updated when DM_DEV_ARM_POLL + * was added in kernel 4.13. 4.37.0 (4.14) has it, safely. + */ +static const unsigned int DM_VERSION_FOR_ARM_POLL[] = {4, 37, 0}; int dmevent_poll_supported(void) { - unsigned int minv[3] = {4, 37, 0}; unsigned int v[3]; if (dm_drv_version(v)) return 0; - if (VERSION_GE(v, minv)) + if (VERSION_GE(v, DM_VERSION_FOR_ARM_POLL)) return 1; return 0; } @@ -120,9 +124,9 @@ static int arm_dm_event_poll(int fd) { struct dm_ioctl dmi; memset(&dmi, 0, sizeof(dmi)); - dmi.version[0] = DM_VERSION_MAJOR; - dmi.version[1] = DM_VERSION_MINOR; - dmi.version[2] = DM_VERSION_PATCHLEVEL; + dmi.version[0] = DM_VERSION_FOR_ARM_POLL[0]; + dmi.version[1] = DM_VERSION_FOR_ARM_POLL[1]; + dmi.version[2] = DM_VERSION_FOR_ARM_POLL[2]; /* This flag currently does nothing. It simply exists to * duplicate the behavior of libdevmapper */ dmi.flags = 0x4; diff --git a/multipathd/main.c b/multipathd/main.c index 0db88ee..af33239 100644 --- a/multipathd/main.c +++ b/multipathd/main.c @@ -429,7 +429,7 @@ int update_multipath (struct vectors *vecs, char *mapname, int reset) continue; if (pp->state != PATH_DOWN) { - struct config *conf = get_multipath_config(); + struct config *conf; int oldstate = pp->state; int checkint; @@ -743,7 +743,7 @@ uev_remove_map (struct uevent * uev, struct vectors * vecs) int minor; struct multipath *mpp; - condlog(2, "%s: remove map (uevent)", uev->kernel); + condlog(3, "%s: remove map (uevent)", uev->kernel); alias = uevent_get_dm_name(uev); if (!alias) { condlog(3, "%s: No DM_NAME in uevent, ignoring", uev->kernel); @@ -803,7 +803,7 @@ uev_add_path (struct uevent *uev, struct vectors * vecs, int need_do_map) int ret = 0, i; struct config *conf; - condlog(2, "%s: add path (uevent)", uev->kernel); + condlog(3, "%s: add path (uevent)", uev->kernel); if (strstr(uev->kernel, "..") != NULL) { /* * Don't allow relative device names in the pathvec @@ -911,7 +911,8 @@ ev_add_path (struct path * pp, struct vectors * vecs, int need_do_map) (pathcount(mpp, PATH_GHOST) > 0 && pp->tpgs != TPGS_IMPLICIT && mpp->ghost_delay_tick <= 0))) { /* if wait_for_udev is set and valid paths exist */ - condlog(2, "%s: delaying path addition until %s is fully initialized", pp->dev, mpp->alias); + condlog(3, "%s: delaying path addition until %s is fully initialized", + pp->dev, mpp->alias); mpp->wait_for_udev = 2; orphan_path(pp, "waiting for create to complete"); return 0; @@ -1038,7 +1039,7 @@ uev_remove_path (struct uevent *uev, struct vectors * vecs, int need_do_map) struct path *pp; int ret; - condlog(2, "%s: remove path (uevent)", uev->kernel); + condlog(3, "%s: remove path (uevent)", uev->kernel); delete_foreign(uev->udev); pthread_cleanup_push(cleanup_lock, &vecs->lock); @@ -1076,6 +1077,14 @@ ev_remove_path (struct path *pp, struct vectors * vecs, int need_do_map) mpp->alias); goto fail; } + + /* + * Make sure mpp->hwe doesn't point to freed memory + * We call extract_hwe_from_path() below to restore mpp->hwe + */ + if (mpp->hwe == pp->hwe) + mpp->hwe = NULL; + if ((i = find_slot(mpp->paths, (void *)pp)) != -1) vector_del_slot(mpp->paths, i); @@ -1088,7 +1097,7 @@ ev_remove_path (struct path *pp, struct vectors * vecs, int need_do_map) /* * flush_map will fail if the device is open */ - strncpy(alias, mpp->alias, WWID_SIZE); + strlcpy(alias, mpp->alias, WWID_SIZE); if (mpp->flush_on_last_del == FLUSH_ENABLED) { condlog(2, "%s Last path deleted, disabling queueing", mpp->alias); mpp->retry_tick = 0; @@ -1109,6 +1118,9 @@ ev_remove_path (struct path *pp, struct vectors * vecs, int need_do_map) */ } + if (mpp->hwe == NULL) + extract_hwe_from_path(mpp); + if (setup_map(mpp, params, PARAMS_SIZE, vecs)) { condlog(0, "%s: failed to setup map for" " removal of path %s", mpp->alias, pp->dev); @@ -1196,6 +1208,15 @@ uev_update_path (struct uevent *uev, struct vectors * vecs) struct multipath *mpp = pp->mpp; char wwid[WWID_SIZE]; + if (pp->initialized == INIT_REQUESTED_UDEV) { + needs_reinit = 1; + goto out; + } + /* Don't deal with other types of failed initialization + * now. check_path will handle it */ + if (!strlen(pp->wwid)) + goto out; + strcpy(wwid, pp->wwid); get_uid(pp, pp->state, uev->udev); @@ -1204,9 +1225,8 @@ uev_update_path (struct uevent *uev, struct vectors * vecs) uev->kernel, wwid, pp->wwid, (disable_changed_wwids ? "disallowing" : "continuing")); - if (disable_changed_wwids && - (strlen(wwid) || pp->wwid_changed)) { - strcpy(pp->wwid, wwid); + strcpy(pp->wwid, wwid); + if (disable_changed_wwids) { if (!pp->wwid_changed) { pp->wwid_changed = 1; pp->tick = 1; @@ -1214,11 +1234,9 @@ uev_update_path (struct uevent *uev, struct vectors * vecs) dm_fail_path(pp->mpp->alias, pp->dev_t); } goto out; - } else if (!disable_changed_wwids) - strcpy(pp->wwid, wwid); - else - pp->wwid_changed = 0; + } } else { + pp->wwid_changed = 0; udev_device_unref(pp->udev); pp->udev = udev_device_ref(uev->udev); conf = get_multipath_config(); @@ -1229,9 +1247,7 @@ uev_update_path (struct uevent *uev, struct vectors * vecs) pthread_cleanup_pop(1); } - if (pp->initialized == INIT_REQUESTED_UDEV) - needs_reinit = 1; - else if (mpp && ro >= 0) { + if (mpp && ro >= 0) { condlog(2, "%s: update path write_protect to '%d' (uevent)", uev->kernel, ro); if (mpp->wait_for_udev) @@ -1494,6 +1510,7 @@ uxlsnrloop (void * ap) set_handler_callback(LIST+MAP+FMT, cli_list_map_fmt); set_handler_callback(LIST+MAP+RAW+FMT, cli_list_map_fmt); set_handler_callback(LIST+MAP+JSON, cli_list_map_json); + set_handler_callback(LIST+CONFIG+LOCAL, cli_list_config_local); set_handler_callback(LIST+CONFIG, cli_list_config); set_handler_callback(LIST+BLACKLIST, cli_list_blacklist); set_handler_callback(LIST+DEVICES, cli_list_devices); @@ -1783,15 +1800,6 @@ int update_path_groups(struct multipath *mpp, struct vectors *vecs, int refresh) return 0; } -void repair_path(struct path * pp) -{ - if (pp->state != PATH_DOWN) - return; - - checker_repair(&pp->checker); - LOG_MSG(1, checker_message(&pp->checker)); -} - /* * Returns '1' if the path has been checked, '-1' if it was blacklisted * and '0' otherwise @@ -1972,18 +1980,17 @@ check_path (struct vectors * vecs, struct path * pp, int ticks) pp->mpp->failback_tick = 0; pp->mpp->stat_path_failures++; - repair_path(pp); return 1; } - if(newstate == PATH_UP || newstate == PATH_GHOST){ - if ( pp->mpp && pp->mpp->prflag ){ + if (newstate == PATH_UP || newstate == PATH_GHOST) { + if (pp->mpp->prflag) { /* * Check Persistent Reservation. */ - condlog(2, "%s: checking persistent reservation " - "registration", pp->dev); - mpath_pr_event_handle(pp); + condlog(2, "%s: checking persistent " + "reservation registration", pp->dev); + mpath_pr_event_handle(pp); } } @@ -2071,7 +2078,6 @@ check_path (struct vectors * vecs, struct path * pp, int ticks) } pp->state = newstate; - repair_path(pp); if (pp->mpp->wait_for_udev) return 1; @@ -2276,7 +2282,7 @@ configure (struct vectors * vecs) ret = path_discovery(vecs->pathvec, DI_ALL); if (ret < 0) { condlog(0, "configure failed at path discovery"); - return 1; + goto fail; } vector_foreach_slot (vecs->pathvec, pp, i){ @@ -2293,7 +2299,7 @@ configure (struct vectors * vecs) } if (map_discovery(vecs)) { condlog(0, "configure failed at map discovery"); - return 1; + goto fail; } /* @@ -2307,7 +2313,7 @@ configure (struct vectors * vecs) force_reload = FORCE_RELOAD_YES; if (ret) { condlog(0, "configure failed while coalescing paths"); - return 1; + goto fail; } /* @@ -2316,7 +2322,7 @@ configure (struct vectors * vecs) */ if (coalesce_maps(vecs, mpvec)) { condlog(0, "configure failed while coalescing maps"); - return 1; + goto fail; } dm_lib_release(); @@ -2352,6 +2358,10 @@ configure (struct vectors * vecs) i--; } return 0; + +fail: + vector_free(mpvec); + return 1; } int @@ -3100,6 +3110,7 @@ void * mpath_pr_event_handler_fn (void * pathp ) param= malloc(sizeof(struct prout_param_descriptor)); memset(param, 0 , sizeof(struct prout_param_descriptor)); + param->sa_flags = mpp->sa_flags; memcpy(param->sa_key, &mpp->reservation_key, 8); param->num_transportid = 0; @@ -3127,6 +3138,9 @@ int mpath_pr_event_handle(struct path *pp) pthread_attr_t attr; struct multipath * mpp; + if (pp->bus != SYSFS_BUS_SCSI) + return 0; + mpp = pp->mpp; if (!get_be64(mpp->reservation_key)) diff --git a/multipathd/main.h b/multipathd/main.h index af39558..8fd426b 100644 --- a/multipathd/main.h +++ b/multipathd/main.h @@ -29,11 +29,11 @@ int ev_remove_map (char *, char *, int, struct vectors *); 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, - int noisy); + int noisy); void dumpHex(const char * , int len, int no_ascii); int prout_do_scsi_ioctl(char * , int rq_servact, int rq_scope, - unsigned int rq_type, struct prout_param_descriptor *param, - int noisy); + unsigned int rq_type, + struct prout_param_descriptor *param, int noisy); int mpath_pr_event_handle(struct path *pp); void * mpath_pr_event_handler_fn (void * ); int update_map_pr(struct multipath *mpp); diff --git a/multipathd/multipathd.8 b/multipathd/multipathd.8 index e78ac9e..94c3f97 100644 --- a/multipathd/multipathd.8 +++ b/multipathd/multipathd.8 @@ -135,6 +135,11 @@ Show the currently used configuration, derived from default values and values specified within the configuration file \fI/etc/multipath.conf\fR. . .TP +.B list|show config local +Show the currently used configuration like \fIshow config\fR, but limiting +the devices section to those devices that are actually present in the system. +. +.TP .B list|show blacklist Show the currently used blacklist rules, derived from default values and values specified within the configuration file \fI/etc/multipath.conf\fR. diff --git a/multipathd/uxlsnr.c b/multipathd/uxlsnr.c index cdafd82..6f66666 100644 --- a/multipathd/uxlsnr.c +++ b/multipathd/uxlsnr.c @@ -178,7 +178,7 @@ void * uxsock_listen(uxsock_trigger_fn uxsock_trigger, void * trigger_data) if (ux_sock == -1) { condlog(1, "could not create uxsock: %d", errno); - return NULL; + exit_daemon(); } pthread_cleanup_push(uxsock_cleanup, (void *)ux_sock); @@ -187,7 +187,7 @@ void * uxsock_listen(uxsock_trigger_fn uxsock_trigger, void * trigger_data) polls = (struct pollfd *)MALLOC((MIN_POLLS + 1) * sizeof(struct pollfd)); if (!polls) { condlog(0, "uxsock: failed to allocate poll fds"); - return NULL; + exit_daemon(); } sigfillset(&mask); sigdelset(&mask, SIGINT); @@ -249,6 +249,7 @@ void * uxsock_listen(uxsock_trigger_fn uxsock_trigger, void * trigger_data) /* something went badly wrong! */ condlog(0, "uxsock: poll failed with %d", errno); + exit_daemon(); break; } diff --git a/tests/Makefile b/tests/Makefile index 1f36411..b37b502 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -3,30 +3,54 @@ include ../Makefile.inc CFLAGS += $(BIN_CFLAGS) -I$(multipathdir) -I$(mpathcmddir) LIBDEPS += -L$(multipathdir) -lmultipath -lcmocka -TESTS := uevent parser util dmevents +TESTS := uevent parser util dmevents hwtable blacklist unaligned .SILENT: $(TESTS:%=%.o) .PRECIOUS: $(TESTS:%=%-test) all: $(TESTS:%=%.out) -dmevents-test: dmevents.o ../multipathd/dmevents.c globals.c $(multipathdir)/libmultipath.so - @$(CC) -o $@ $< $(LDFLAGS) $(LIBDEPS) -lpthread -ldevmapper -lurcu -Wl,--wrap=open -Wl,--wrap=close -Wl,--wrap=dm_is_mpath -Wl,--wrap=dm_geteventnr -Wl,--wrap=ioctl -Wl,--wrap=libmp_dm_task_create -Wl,--wrap=dm_task_no_open_count -Wl,--wrap=dm_task_run -Wl,--wrap=dm_task_get_names -Wl,--wrap=dm_task_destroy -Wl,--wrap=poll -Wl,--wrap=remove_map_by_alias -Wl,--wrap=update_multipath - -%-test: %.o globals.c $(multipathdir)/libmultipath.so - @$(CC) -o $@ $< $(LDFLAGS) $(LIBDEPS) +# test-specific linker flags +# XYZ-test-TESTDEPS: test libraries containing __wrap_xyz functions +# XYZ-test_OBJDEPS: object files from libraries to link in explicitly +# That may be necessary if functions called from the object file are wrapped +# (wrapping works only for symbols which are undefined after processing a +# linker input file). +# XYZ-test_LIBDEPS: Additional libs to link for this test + +dmevents-test_LIBDEPS = -lpthread -ldevmapper -lurcu +hwtable-test_TESTDEPS := test-lib.o +hwtable-test_OBJDEPS := ../libmultipath/discovery.o ../libmultipath/blacklist.o \ + ../libmultipath/prio.o ../libmultipath/callout.o ../libmultipath/structs.o +hwtable-test_LIBDEPS := -ludev -lpthread -ldl +blacklist-test_OBJDEPS := ../libmultipath/blacklist.o +blacklist-test_LIBDEPS := -ludev %.out: %-test @echo == running $< == @LD_LIBRARY_PATH=$(multipathdir):$(mpathcmddir) ./$< >$@ +OBJS = $(TESTS:%=%.o) test-lib.o + clean: dep_clean - rm -f $(TESTS:%=%-test) $(TESTS:%=%.out) $(TESTS:%=%.o) + $(RM) $(TESTS:%=%-test) $(TESTS:%=%.out) $(OBJS) -OBJS = $(TESTS:%=%.o) .SECONDARY: $(OBJS) include $(wildcard $(OBJS:.o=.d)) dep_clean: $(RM) $(OBJS:.o=.d) + +%.o.wrap: %.c + @sed -n 's/^.*__wrap_\([a-zA-Z0-9_]*\).*$$/-Wl,--wrap=\1/p' $< | \ + sort -u | tr '\n' ' ' >$@ + +# COLON will get expanded during second expansion below +COLON:=: +.SECONDEXPANSION: +%-test: %.o %.o.wrap $$($$@_OBJDEPS) $$($$@_TESTDEPS) $$($$@_TESTDEPS$$(COLON).o=.o.wrap) \ + $(multipathdir)/libmultipath.so Makefile + $(CC) $(CFLAGS) -o $@ $(LDFLAGS) $< $($@_TESTDEPS) $($@_OBJDEPS) \ + $(LIBDEPS) $($@_LIBDEPS) \ + $(shell cat $<.wrap) $(foreach dep,$($@_TESTDEPS),$(shell cat $(dep).wrap)) diff --git a/tests/blacklist.c b/tests/blacklist.c new file mode 100644 index 0000000..a55c1c0 --- /dev/null +++ b/tests/blacklist.c @@ -0,0 +1,512 @@ +/* + * Copyright (c) 2018 Benjamin Marzinski, Redhat + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +#include +#include +#include +#include +#include "globals.c" +#include "blacklist.h" +#include "log.h" + +struct udev_device { + const char *sysname; + char *property_list[]; +}; + +const char * +__wrap_udev_device_get_sysname(struct udev_device *udev_device) +{ + assert_non_null(udev_device); + assert_non_null(udev_device->sysname); + return udev_device->sysname; +} + +struct udev_list_entry * +__wrap_udev_device_get_properties_list_entry(struct udev_device *udev_device) +{ + assert_non_null(udev_device); + if (!udev_device->property_list) + return NULL; + if (!*udev_device->property_list) + return NULL; + return (struct udev_list_entry *)udev_device->property_list; +} + +struct udev_list_entry * +__wrap_udev_list_entry_get_next(struct udev_list_entry *list_entry) +{ + assert_non_null(list_entry); + if (!*((char **)list_entry + 1)) + return NULL; + return (struct udev_list_entry *)(((char **)list_entry) + 1); +} + +const char * +__wrap_udev_list_entry_get_name(struct udev_list_entry *list_entry) +{ + return *(const char **)list_entry; +} + +void __wrap_dlog (int sink, int prio, const char * fmt, ...) +{ + char buff[MAX_MSG_SIZE]; + va_list ap; + + assert_int_equal(prio, mock_type(int)); + va_start(ap, fmt); + vsnprintf(buff, MAX_MSG_SIZE, fmt, ap); + va_end(ap); + assert_string_equal(buff, mock_ptr_type(char *)); +} + +void expect_condlog(int prio, char *string) +{ + will_return(__wrap_dlog, prio); + will_return(__wrap_dlog, string); +} + +vector blist_devnode_sdb; +vector blist_all; +vector blist_device_foo_bar; +vector blist_device_all; +vector blist_wwid_xyzzy; +vector blist_protocol_fcp; +vector blist_property_wwn; + +static int setup(void **state) +{ + blist_devnode_sdb = vector_alloc(); + if (!blist_devnode_sdb || + store_ble(blist_devnode_sdb, strdup("sdb"), ORIGIN_CONFIG)) + return -1; + + blist_all = vector_alloc(); + if (!blist_all || store_ble(blist_all, strdup(".*"), ORIGIN_CONFIG)) + return -1; + + blist_device_foo_bar = vector_alloc(); + if (!blist_device_foo_bar || alloc_ble_device(blist_device_foo_bar) || + set_ble_device(blist_device_foo_bar, strdup("foo"), strdup("bar"), + ORIGIN_CONFIG)) + return -1; + + blist_device_all = vector_alloc(); + if (!blist_device_all || alloc_ble_device(blist_device_all) || + set_ble_device(blist_device_all, strdup(".*"), strdup(".*"), + ORIGIN_CONFIG)) + return -1; + + blist_wwid_xyzzy = vector_alloc(); + if (!blist_wwid_xyzzy || + store_ble(blist_wwid_xyzzy, 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_property_wwn = vector_alloc(); + if (!blist_property_wwn || + store_ble(blist_property_wwn, strdup("ID_WWN"), ORIGIN_CONFIG)) + return -1; + + return 0; +} + +static int teardown(void **state) +{ + free_blacklist(blist_devnode_sdb); + free_blacklist(blist_all); + free_blacklist_device(blist_device_foo_bar); + free_blacklist_device(blist_device_all); + free_blacklist(blist_wwid_xyzzy); + free_blacklist(blist_protocol_fcp); + free_blacklist(blist_property_wwn); + return 0; +} + +static int reset_blists(void **state) +{ + conf.blist_devnode = NULL; + conf.blist_wwid = NULL; + conf.blist_property = NULL; + conf.blist_protocol = NULL; + conf.blist_device = NULL; + conf.elist_devnode = NULL; + conf.elist_wwid = NULL; + conf.elist_property = NULL; + conf.elist_protocol = NULL; + conf.elist_device = NULL; + return 0; +} + +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); +} + +static void test_devnode_whitelist(void **state) +{ + expect_condlog(3, "sdb: device node name whitelisted\n"); + assert_int_equal(filter_devnode(blist_all, blist_devnode_sdb, "sdb"), + MATCH_DEVNODE_BLIST_EXCEPT); + expect_condlog(3, "sdc: device node name blacklisted\n"); + assert_int_equal(filter_devnode(blist_all, blist_devnode_sdb, "sdc"), + MATCH_DEVNODE_BLIST); +} + +static void test_devnode_missing(void **state) +{ + assert_int_equal(filter_devnode(blist_devnode_sdb, NULL, "sdc"), + MATCH_NOTHING); +} + +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); +} + +static void test_device_whitelist(void **state) +{ + expect_condlog(3, "sdb: (foo:bar) vendor/product whitelisted\n"); + assert_int_equal(filter_device(blist_device_all, blist_device_foo_bar, + "foo", "bar", "sdb"), + MATCH_DEVICE_BLIST_EXCEPT); + expect_condlog(3, "sdb: (foo:baz) vendor/product blacklisted\n"); + assert_int_equal(filter_device(blist_device_all, blist_device_foo_bar, + "foo", "baz", "sdb"), + MATCH_DEVICE_BLIST); +} + +static void test_device_missing(void **state) +{ + assert_int_equal(filter_device(blist_device_foo_bar, NULL, "foo", + "baz", "sdb"), + MATCH_NOTHING); +} + +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); +} + +static void test_wwid_whitelist(void **state) +{ + expect_condlog(3, "sdb: wwid xyzzy whitelisted\n"); + assert_int_equal(filter_wwid(blist_all, blist_wwid_xyzzy, + "xyzzy", "sdb"), + MATCH_WWID_BLIST_EXCEPT); + expect_condlog(3, "sdb: wwid plugh blacklisted\n"); + assert_int_equal(filter_wwid(blist_all, blist_wwid_xyzzy, + "plugh", "sdb"), + MATCH_WWID_BLIST); +} + +static void test_wwid_missing(void **state) +{ + assert_int_equal(filter_wwid(blist_wwid_xyzzy, NULL, "plugh", "sdb"), + MATCH_NOTHING); +} + +static void test_protocol_blacklist(void **state) +{ + struct path pp = { .dev = "sdb", .bus = SYSFS_BUS_SCSI, + .sg_id.proto_id = SCSI_PROTOCOL_FCP }; + expect_condlog(3, "sdb: protocol scsi:fcp blacklisted\n"); + assert_int_equal(filter_protocol(blist_protocol_fcp, NULL, &pp), + MATCH_PROTOCOL_BLIST); +} + +static void test_protocol_whitelist(void **state) +{ + struct path pp1 = { .dev = "sdb", .bus = SYSFS_BUS_SCSI, + .sg_id.proto_id = SCSI_PROTOCOL_FCP }; + struct path pp2 = { .dev = "sdb", .bus = SYSFS_BUS_SCSI, + .sg_id.proto_id = SCSI_PROTOCOL_ISCSI }; + expect_condlog(3, "sdb: protocol scsi:fcp whitelisted\n"); + assert_int_equal(filter_protocol(blist_all, blist_protocol_fcp, &pp1), + MATCH_PROTOCOL_BLIST_EXCEPT); + expect_condlog(3, "sdb: protocol scsi:iscsi blacklisted\n"); + assert_int_equal(filter_protocol(blist_all, blist_protocol_fcp, &pp2), + MATCH_PROTOCOL_BLIST); +} + +static void test_protocol_missing(void **state) +{ + struct path pp = { .dev = "sdb", .bus = SYSFS_BUS_SCSI, + .sg_id.proto_id = SCSI_PROTOCOL_ISCSI }; + assert_int_equal(filter_protocol(blist_protocol_fcp, NULL, &pp), + MATCH_NOTHING); +} + +static void test_property_blacklist(void **state) +{ + static struct udev_device udev = { "sdb", { "ID_FOO", "ID_WWN", "ID_BAR", NULL } }; + conf.blist_property = blist_property_wwn; + expect_condlog(3, "sdb: udev property ID_WWN blacklisted\n"); + assert_int_equal(filter_property(&conf, &udev), MATCH_PROPERTY_BLIST); +} + +/* the property check works different in that you check all the property + * names, so setting a blacklist value will blacklist the device if any + * of the property on the blacklist are found before the property names + * in the whitelist. This might be worth changing. although it would + * force multipath to go through the properties twice */ +static void test_property_whitelist(void **state) +{ + static struct udev_device udev = { "sdb", { "ID_FOO", "ID_WWN", "ID_BAR", NULL } }; + conf.elist_property = blist_property_wwn; + expect_condlog(3, "sdb: udev property ID_WWN whitelisted\n"); + assert_int_equal(filter_property(&conf, &udev), + MATCH_PROPERTY_BLIST_EXCEPT); +} + +static void test_property_missing(void **state) +{ + static struct udev_device udev = { "sdb", { "ID_FOO", "ID_BAZ", "ID_BAR", NULL } }; + conf.blist_property = blist_property_wwn; + expect_condlog(3, "sdb: blacklisted, udev property missing\n"); + assert_int_equal(filter_property(&conf, &udev), + MATCH_PROPERTY_BLIST_MISSING); +} + +struct udev_device test_udev = { "sdb", { "ID_FOO", "ID_WWN", "ID_BAR", NULL } }; + +struct path test_pp = { .dev = "sdb", .bus = SYSFS_BUS_SCSI, .udev = &test_udev, + .sg_id.proto_id = SCSI_PROTOCOL_FCP, .vendor_id = "foo", + .product_id = "bar", .wwid = "xyzzy" }; + +static void test_filter_path_property(void **state) +{ + conf.blist_property = blist_property_wwn; + expect_condlog(3, "sdb: udev property ID_WWN blacklisted\n"); + assert_int_equal(filter_path(&conf, &test_pp), MATCH_PROPERTY_BLIST); +} + +static void test_filter_path_devnode(void **state) +{ + /* always must include property elist, to avoid "missing property" + * blacklisting */ + conf.elist_property = blist_property_wwn; + conf.blist_devnode = blist_devnode_sdb; + expect_condlog(3, "sdb: udev property ID_WWN whitelisted\n"); + expect_condlog(3, "sdb: device node name blacklisted\n"); + assert_int_equal(filter_path(&conf, &test_pp), MATCH_DEVNODE_BLIST); +} + +static void test_filter_path_device(void **state) +{ + /* always must include property elist, to avoid "missing property" + * blacklisting */ + conf.elist_property = blist_property_wwn; + conf.blist_device = blist_device_foo_bar; + expect_condlog(3, "sdb: udev property ID_WWN whitelisted\n"); + expect_condlog(3, "sdb: (foo:bar) vendor/product blacklisted\n"); + assert_int_equal(filter_path(&conf, &test_pp), MATCH_DEVICE_BLIST); +} + +static void test_filter_path_protocol(void **state) +{ + conf.elist_property = blist_property_wwn; + conf.blist_protocol = blist_protocol_fcp; + expect_condlog(3, "sdb: udev property ID_WWN whitelisted\n"); + expect_condlog(3, "sdb: protocol scsi:fcp blacklisted\n"); + assert_int_equal(filter_path(&conf, &test_pp), MATCH_PROTOCOL_BLIST); +} + +static void test_filter_path_wwid(void **state) +{ + conf.elist_property = blist_property_wwn; + conf.blist_wwid = blist_wwid_xyzzy; + expect_condlog(3, "sdb: udev property ID_WWN whitelisted\n"); + expect_condlog(3, "sdb: wwid xyzzy blacklisted\n"); + assert_int_equal(filter_path(&conf, &test_pp), MATCH_WWID_BLIST); +} + +struct udev_device miss_udev = { "sdb", { "ID_FOO", "ID_BAZ", "ID_BAR", NULL } }; + +struct path miss1_pp = { .dev = "sdc", .bus = SYSFS_BUS_SCSI, + .udev = &miss_udev, + .sg_id.proto_id = SCSI_PROTOCOL_ISCSI, + .vendor_id = "foo", .product_id = "baz", + .wwid = "plugh" }; + +struct path miss2_pp = { .dev = "sdc", .bus = SYSFS_BUS_SCSI, + .udev = &test_udev, + .sg_id.proto_id = SCSI_PROTOCOL_ISCSI, + .vendor_id = "foo", .product_id = "baz", + .wwid = "plugh" }; + +static void test_filter_path_missing1(void **state) +{ + conf.blist_property = blist_property_wwn; + conf.blist_devnode = blist_devnode_sdb; + conf.blist_device = blist_device_foo_bar; + conf.blist_protocol = blist_protocol_fcp; + conf.blist_wwid = blist_wwid_xyzzy; + expect_condlog(3, "sdb: blacklisted, udev property missing\n"); + assert_int_equal(filter_path(&conf, &miss1_pp), + MATCH_PROPERTY_BLIST_MISSING); +} + +/* This one matches the property whitelist, to test the other missing + * functions */ +static void test_filter_path_missing2(void **state) +{ + conf.elist_property = blist_property_wwn; + conf.blist_devnode = blist_devnode_sdb; + conf.blist_device = blist_device_foo_bar; + conf.blist_protocol = blist_protocol_fcp; + conf.blist_wwid = blist_wwid_xyzzy; + expect_condlog(3, "sdb: udev property ID_WWN whitelisted\n"); + assert_int_equal(filter_path(&conf, &miss2_pp), + MATCH_NOTHING); +} + +static void test_filter_path_whitelist(void **state) +{ + conf.elist_property = blist_property_wwn; + conf.elist_devnode = blist_devnode_sdb; + conf.elist_device = blist_device_foo_bar; + conf.elist_protocol = blist_protocol_fcp; + conf.elist_wwid = blist_wwid_xyzzy; + expect_condlog(3, "sdb: udev property ID_WWN whitelisted\n"); + expect_condlog(3, "sdb: device node name whitelisted\n"); + expect_condlog(3, "sdb: (foo:bar) vendor/product whitelisted\n"); + expect_condlog(3, "sdb: protocol scsi:fcp whitelisted\n"); + expect_condlog(3, "sdb: wwid xyzzy whitelisted\n"); + assert_int_equal(filter_path(&conf, &test_pp), + MATCH_WWID_BLIST_EXCEPT); +} + +static void test_filter_path_whitelist_property(void **state) +{ + conf.blist_property = blist_property_wwn; + conf.elist_devnode = blist_devnode_sdb; + conf.elist_device = blist_device_foo_bar; + conf.elist_protocol = blist_protocol_fcp; + conf.elist_wwid = blist_wwid_xyzzy; + expect_condlog(3, "sdb: udev property ID_WWN blacklisted\n"); + assert_int_equal(filter_path(&conf, &test_pp), MATCH_PROPERTY_BLIST); +} + +static void test_filter_path_whitelist_devnode(void **state) +{ + conf.elist_property = blist_property_wwn; + conf.blist_devnode = blist_devnode_sdb; + conf.elist_device = blist_device_foo_bar; + conf.elist_protocol = blist_protocol_fcp; + conf.elist_wwid = blist_wwid_xyzzy; + expect_condlog(3, "sdb: udev property ID_WWN whitelisted\n"); + expect_condlog(3, "sdb: device node name blacklisted\n"); + assert_int_equal(filter_path(&conf, &test_pp), MATCH_DEVNODE_BLIST); +} + +static void test_filter_path_whitelist_device(void **state) +{ + conf.elist_property = blist_property_wwn; + conf.elist_devnode = blist_devnode_sdb; + conf.blist_device = blist_device_foo_bar; + conf.elist_protocol = blist_protocol_fcp; + conf.elist_wwid = blist_wwid_xyzzy; + expect_condlog(3, "sdb: udev property ID_WWN whitelisted\n"); + expect_condlog(3, "sdb: device node name whitelisted\n"); + expect_condlog(3, "sdb: (foo:bar) vendor/product blacklisted\n"); + assert_int_equal(filter_path(&conf, &test_pp), MATCH_DEVICE_BLIST); +} + +static void test_filter_path_whitelist_protocol(void **state) +{ + conf.elist_property = blist_property_wwn; + conf.elist_devnode = blist_devnode_sdb; + conf.elist_device = blist_device_foo_bar; + conf.blist_protocol = blist_protocol_fcp; + conf.elist_wwid = blist_wwid_xyzzy; + expect_condlog(3, "sdb: udev property ID_WWN whitelisted\n"); + expect_condlog(3, "sdb: device node name whitelisted\n"); + expect_condlog(3, "sdb: (foo:bar) vendor/product whitelisted\n"); + expect_condlog(3, "sdb: protocol scsi:fcp blacklisted\n"); + assert_int_equal(filter_path(&conf, &test_pp), MATCH_PROTOCOL_BLIST); +} + +static void test_filter_path_whitelist_wwid(void **state) +{ + conf.elist_property = blist_property_wwn; + conf.elist_devnode = blist_devnode_sdb; + conf.elist_device = blist_device_foo_bar; + conf.elist_protocol = blist_protocol_fcp; + conf.blist_wwid = blist_wwid_xyzzy; + expect_condlog(3, "sdb: udev property ID_WWN whitelisted\n"); + expect_condlog(3, "sdb: device node name whitelisted\n"); + expect_condlog(3, "sdb: (foo:bar) vendor/product whitelisted\n"); + expect_condlog(3, "sdb: protocol scsi:fcp whitelisted\n"); + expect_condlog(3, "sdb: wwid xyzzy blacklisted\n"); + assert_int_equal(filter_path(&conf, &test_pp), MATCH_WWID_BLIST); +} + +#define test_and_reset(x) cmocka_unit_test_teardown((x), reset_blists) + +int test_blacklist(void) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_devnode_blacklist), + cmocka_unit_test(test_devnode_whitelist), + cmocka_unit_test(test_devnode_missing), + cmocka_unit_test(test_device_blacklist), + cmocka_unit_test(test_device_whitelist), + cmocka_unit_test(test_device_missing), + cmocka_unit_test(test_wwid_blacklist), + cmocka_unit_test(test_wwid_whitelist), + cmocka_unit_test(test_wwid_missing), + cmocka_unit_test(test_protocol_blacklist), + cmocka_unit_test(test_protocol_whitelist), + cmocka_unit_test(test_protocol_missing), + test_and_reset(test_property_blacklist), + test_and_reset(test_property_whitelist), + test_and_reset(test_property_missing), + test_and_reset(test_filter_path_property), + test_and_reset(test_filter_path_devnode), + test_and_reset(test_filter_path_device), + test_and_reset(test_filter_path_protocol), + test_and_reset(test_filter_path_wwid), + test_and_reset(test_filter_path_missing1), + test_and_reset(test_filter_path_missing2), + test_and_reset(test_filter_path_whitelist), + test_and_reset(test_filter_path_whitelist_property), + test_and_reset(test_filter_path_whitelist_devnode), + test_and_reset(test_filter_path_whitelist_device), + test_and_reset(test_filter_path_whitelist_protocol), + test_and_reset(test_filter_path_whitelist_wwid), + }; + return cmocka_run_group_tests(tests, setup, teardown); +} + +int main(void) +{ + int ret = 0; + ret += test_blacklist(); + return ret; +} diff --git a/tests/dmevents.c b/tests/dmevents.c index bba51dc..3399c81 100644 --- a/tests/dmevents.c +++ b/tests/dmevents.c @@ -12,7 +12,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software + * along with this program. If not, see . * */ diff --git a/tests/hwtable.c b/tests/hwtable.c new file mode 100644 index 0000000..9146ecc --- /dev/null +++ b/tests/hwtable.c @@ -0,0 +1,1776 @@ +/* Set BROKEN to 1 to treat broken behavior as success */ +#define BROKEN 1 +#define VERBOSITY 2 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "structs.h" +#include "structs_vec.h" +#include "config.h" +#include "debug.h" +#include "defaults.h" +#include "pgpolicies.h" +#include "test-lib.h" +#include "print.h" + +#define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0])) +#define N_CONF_FILES 2 + +static const char tmplate[] = "/tmp/hwtable-XXXXXX"; +/* pretend new dm, use minio_rq */ +static const unsigned int dm_tgt_version[3] = { 1, 1, 1 }; + +struct key_value { + const char *key; + const char *value; +}; + +struct hwt_state { + char *tmpname; + char *dirname; + FILE *config_file; + FILE *conf_dir_file[N_CONF_FILES]; + struct vectors *vecs; + void (*test)(const struct hwt_state *); + const char *test_name; +}; + +#define SET_TEST_FUNC(hwt, func) do { \ + hwt->test = func; \ + hwt->test_name = #func; \ + } while (0) + +static struct config *_conf; +struct udev *udev; +int logsink; + +struct config *get_multipath_config(void) +{ + return _conf; +} + +void put_multipath_config(void *arg) +{} + +void make_config_file_path(char *buf, int buflen, + const struct hwt_state *hwt, int i) +{ + static const char fn_template[] = "%s/test-%02d.conf"; + + if (i == -1) + /* main config file */ + snprintf(buf, buflen, fn_template, hwt->tmpname, 0); + else + snprintf(buf, buflen, fn_template, hwt->dirname, i); +} + +static void reset_vecs(struct vectors *vecs) +{ + remove_maps(vecs); + free_pathvec(vecs->pathvec, FREE_PATHS); + + vecs->pathvec = vector_alloc(); + assert_ptr_not_equal(vecs->pathvec, NULL); + vecs->mpvec = vector_alloc(); + assert_ptr_not_equal(vecs->mpvec, NULL); +} + +static void free_hwt(struct hwt_state *hwt) +{ + char buf[PATH_MAX]; + int i; + + if (hwt->config_file != NULL) + fclose(hwt->config_file); + for (i = 0; i < N_CONF_FILES; i++) { + if (hwt->conf_dir_file[i] != NULL) + fclose(hwt->conf_dir_file[i]); + } + + if (hwt->tmpname != NULL) { + make_config_file_path(buf, sizeof(buf), hwt, -1); + unlink(buf); + rmdir(hwt->tmpname); + free(hwt->tmpname); + } + + if (hwt->dirname != NULL) { + for (i = 0; i < N_CONF_FILES; i++) { + make_config_file_path(buf, sizeof(buf), hwt, i); + unlink(buf); + } + rmdir(hwt->dirname); + free(hwt->dirname); + } + + if (hwt->vecs != NULL) { + if (hwt->vecs->mpvec != NULL) + remove_maps(hwt->vecs); + if (hwt->vecs->pathvec != NULL) + free_pathvec(hwt->vecs->pathvec, FREE_PATHS); + pthread_mutex_destroy(&hwt->vecs->lock.mutex); + free(hwt->vecs); + } + free(hwt); +} + +static int setup(void **state) +{ + struct hwt_state *hwt; + char buf[PATH_MAX]; + int i; + + *state = NULL; + hwt = calloc(1, sizeof(*hwt)); + if (hwt == NULL) + return -1; + + snprintf(buf, sizeof(buf), "%s", tmplate); + if (mkdtemp(buf) == NULL) { + condlog(0, "mkdtemp: %s", strerror(errno)); + goto err; + } + hwt->tmpname = strdup(buf); + + snprintf(buf, sizeof(buf), "%s", tmplate); + if (mkdtemp(buf) == NULL) { + condlog(0, "mkdtemp (2): %s", strerror(errno)); + goto err; + } + hwt->dirname = strdup(buf); + + make_config_file_path(buf, sizeof(buf), hwt, -1); + hwt->config_file = fopen(buf, "w+"); + if (hwt->config_file == NULL) + goto err; + + for (i = 0; i < N_CONF_FILES; i++) { + make_config_file_path(buf, sizeof(buf), hwt, i); + hwt->conf_dir_file[i] = fopen(buf, "w+"); + if (hwt->conf_dir_file[i] == NULL) + goto err; + } + + hwt->vecs = calloc(1, sizeof(*hwt->vecs)); + if (hwt->vecs == NULL) + goto err; + pthread_mutex_init(&hwt->vecs->lock.mutex, NULL); + hwt->vecs->pathvec = vector_alloc(); + hwt->vecs->mpvec = vector_alloc(); + if (hwt->vecs->pathvec == NULL || hwt->vecs->mpvec == NULL) + goto err; + + *state = hwt; + return 0; + +err: + free_hwt(hwt); + return -1; +} + +static int teardown(void **state) +{ + if (state == NULL || *state == NULL) + return -1; + + free_hwt(*state); + *state = NULL; + + return 0; +} + +/* + * Helpers for creating the config file(s) + */ + +static void reset_config(FILE *ff) +{ + if (ff == NULL) + return; + rewind(ff); + if (ftruncate(fileno(ff), 0) == -1) + condlog(1, "ftruncate: %s", strerror(errno)); +} + +static void reset_configs(const struct hwt_state *hwt) +{ + int i; + + reset_config(hwt->config_file); + for (i = 0; i < N_CONF_FILES; i++) + reset_config(hwt->conf_dir_file[i]); +} + +static void write_key_values(FILE *ff, int nkv, const struct key_value *kv) +{ + int i; + + for (i = 0; i < nkv; i++) { + if (strchr(kv[i].value, ' ') == NULL && + strchr(kv[i].value, '\"') == NULL) + fprintf(ff, "\t%s %s\n", kv[i].key, kv[i].value); + else + fprintf(ff, "\t%s \"%s\"\n", kv[i].key, kv[i].value); + } +} + +static void begin_section(FILE *ff, const char *section) +{ + fprintf(ff, "%s {\n", section); +} + +static void end_section(FILE *ff) +{ + fprintf(ff, "}\n"); +} + +static void write_section(FILE *ff, const char *section, + int nkv, const struct key_value *kv) +{ + begin_section(ff, section); + write_key_values(ff, nkv, kv); + end_section(ff); +} + +static void write_defaults(const struct hwt_state *hwt) +{ + static const char bindings_name[] = "bindings"; + static struct key_value defaults[] = { + { "config_dir", NULL }, + { "bindings_file", NULL }, + { "detect_prio", "no" }, + { "detect_checker", "no" }, + }; + char buf[sizeof(tmplate) + sizeof(bindings_name)]; + + snprintf(buf, sizeof(buf), "%s/%s", hwt->tmpname, bindings_name); + defaults[0].value = hwt->dirname; + defaults[1].value = buf; + write_section(hwt->config_file, "defaults", + ARRAY_SIZE(defaults), defaults); +} + +static void begin_config(const struct hwt_state *hwt) +{ + reset_configs(hwt); + write_defaults(hwt); +} + +static void begin_section_all(const struct hwt_state *hwt, const char *section) +{ + int i; + + begin_section(hwt->config_file, section); + for (i = 0; i < N_CONF_FILES; i++) + begin_section(hwt->conf_dir_file[i], section); +} + +static void end_section_all(const struct hwt_state *hwt) +{ + int i; + + end_section(hwt->config_file); + for (i = 0; i < N_CONF_FILES; i++) + end_section(hwt->conf_dir_file[i]); +} + +static void finish_config(const struct hwt_state *hwt) +{ + int i; + + fflush(hwt->config_file); + for (i = 0; i < N_CONF_FILES; i++) { + fflush(hwt->conf_dir_file[i]); + } +} + +static void write_device(FILE *ff, int nkv, const struct key_value *kv) +{ + write_section(ff, "device", nkv, kv); +} + +/* + * Some macros to avoid boilerplace code + */ + +#define CHECK_STATE(state) ({ \ + assert_ptr_not_equal(state, NULL); \ + assert_ptr_not_equal(*(state), NULL); \ + *state; }) + +#define WRITE_EMPTY_CONF(hwt) do { \ + begin_config(hwt); \ + finish_config(hwt); \ + } while (0) + +#define WRITE_ONE_DEVICE(hwt, kv) do { \ + begin_config(hwt); \ + begin_section_all(hwt, "devices"); \ + write_device(hwt->config_file, ARRAY_SIZE(kv), kv); \ + end_section_all(hwt); \ + finish_config(hwt); \ + } while (0) + +#define WRITE_TWO_DEVICES(hwt, kv1, kv2) do { \ + begin_config(hwt); \ + begin_section_all(hwt, "devices"); \ + write_device(hwt->config_file, ARRAY_SIZE(kv1), kv1); \ + write_device(hwt->config_file, ARRAY_SIZE(kv2), kv2); \ + end_section_all(hwt); \ + finish_config(hwt); \ + } while (0) + +#define WRITE_TWO_DEVICES_W_DIR(hwt, kv1, kv2) do { \ + begin_config(hwt); \ + begin_section_all(hwt, "devices"); \ + write_device(hwt->config_file, ARRAY_SIZE(kv1), kv1); \ + write_device(hwt->conf_dir_file[0], \ + ARRAY_SIZE(kv2), kv2); \ + end_section_all(hwt); \ + finish_config(hwt); \ + } while (0) + +#define LOAD_CONFIG(hwt) ({ \ + char buf[PATH_MAX]; \ + struct config *__cf; \ + \ + make_config_file_path(buf, sizeof(buf), hwt, -1); \ + __cf = load_config(buf); \ + assert_ptr_not_equal(__cf, NULL); \ + assert_ptr_not_equal(__cf->hwtable, NULL); \ + __cf->verbosity = VERBOSITY; \ + memcpy(&__cf->version, dm_tgt_version, sizeof(__cf->version)); \ + __cf; }) + +#define FREE_CONFIG(conf) do { \ + free_config(conf); \ + conf = NULL; \ + } while (0) + +static void replace_config(const struct hwt_state *hwt, + const char *conf_str) +{ + FREE_CONFIG(_conf); + reset_configs(hwt); + fprintf(hwt->config_file, "%s", conf_str); + fflush(hwt->config_file); + _conf = LOAD_CONFIG(hwt); +} + +#define TEST_PROP(prop, val) do { \ + if (val == NULL) \ + assert_ptr_equal(prop, NULL); \ + else { \ + assert_ptr_not_equal(prop, NULL); \ + assert_string_equal(prop, val); \ + } \ + } while (0) + +#if BROKEN +#define TEST_PROP_BROKEN(name, prop, bad, good) do { \ + condlog(1, "%s: WARNING: Broken test for %s == \"%s\" on line %d, should be \"%s\"", \ + __func__, name, bad ? bad : "NULL", \ + __LINE__, good ? good : "NULL"); \ + TEST_PROP(prop, bad); \ + } while (0) +#else +#define TEST_PROP_BROKEN(name, prop, bad, good) TEST_PROP(prop, good) +#endif + +/* + * Some predefined key/value pairs + */ + +static const char _wwid[] = "wwid"; +static const char _vendor[] = "vendor"; +static const char _product[] = "product"; +static const char _prio[] = "prio"; +static const char _checker[] = "path_checker"; +static const char _getuid[] = "getuid_callout"; +static const char _uid_attr[] = "uid_attribute"; +static const char _bl_product[] = "product_blacklist"; +static const char _minio[] = "rr_min_io_rq"; +static const char _no_path_retry[] = "no_path_retry"; + +/* Device identifiers */ +static const struct key_value vnd_foo = { _vendor, "foo" }; +static const struct key_value prd_bar = { _product, "bar" }; +static const struct key_value prd_bam = { _product, "bam" }; +static const struct key_value prd_baq = { _product, "\"bar\"" }; +static const struct key_value prd_baqq = { _product, "\"\"bar\"\"" }; +static const struct key_value prd_barz = { _product, "barz" }; +static const struct key_value vnd_boo = { _vendor, "boo" }; +static const struct key_value prd_baz = { _product, "baz" }; +static const struct key_value wwid_test = { _wwid, default_wwid }; + +/* Regular expresssions */ +static const struct key_value vnd__oo = { _vendor, ".oo" }; +static const struct key_value vnd_t_oo = { _vendor, "^.oo" }; +static const struct key_value prd_ba_ = { _product, "ba." }; +static const struct key_value prd_ba_s = { _product, "(bar|baz|ba\\.)$" }; +/* Pathological cases, see below */ +static const struct key_value prd_barx = { _product, "ba[[rxy]" }; +static const struct key_value prd_bazy = { _product, "ba[zy]" }; +static const struct key_value prd_bazy1 = { _product, "ba(z|y)" }; + +/* Properties */ +static const struct key_value prio_emc = { _prio, "emc" }; +static const struct key_value prio_hds = { _prio, "hds" }; +static const struct key_value prio_rdac = { _prio, "rdac" }; +static const struct key_value chk_hp = { _checker, "hp_sw" }; +static const struct key_value gui_foo = { _getuid, "/tmp/foo" }; +static const struct key_value uid_baz = { _uid_attr, "BAZ_ATTR" }; +static const struct key_value bl_bar = { _bl_product, "bar" }; +static const struct key_value bl_baz = { _bl_product, "baz" }; +static const struct key_value bl_barx = { _bl_product, "ba[[rxy]" }; +static const struct key_value bl_bazy = { _bl_product, "ba[zy]" }; +static const struct key_value minio_99 = { _minio, "99" }; +static const struct key_value npr_37 = { _no_path_retry, "37" }; +static const struct key_value npr_queue = { _no_path_retry, "queue" }; + +/***** BEGIN TESTS SECTION *****/ + +/* + * Dump the configuration, subistitute the dumped configuration + * for the current one, and verify that the result is identical. + */ +static void replicate_config(const struct hwt_state *hwt, bool local) +{ + char *cfg1, *cfg2; + vector hwtable; + struct config *conf; + + condlog(1, "--- %s: replicating %s configuration", __func__, + local ? "local" : "full"); + + conf = get_multipath_config(); + if (!local) + /* "full" configuration */ + cfg1 = snprint_config(conf, NULL, NULL, NULL); + else { + /* "local" configuration */ + hwtable = get_used_hwes(hwt->vecs->pathvec); + cfg1 = snprint_config(conf, NULL, hwtable, hwt->vecs->mpvec); + } + + assert_non_null(cfg1); + put_multipath_config(conf); + + replace_config(hwt, cfg1); + + /* + * The local configuration adds multipath entries, and may move device + * entries for local devices to the end of the list. Identical config + * strings therefore can't be expected in the "local" case. + * That doesn't matter. The important thing is that, with the reloaded + * configuration, the test case still passes. + */ + if (local) { + free(cfg1); + return; + } + + conf = get_multipath_config(); + cfg2 = snprint_config(conf, NULL, NULL, NULL); + assert_non_null(cfg2); + put_multipath_config(conf); + +// #define DBG_CONFIG 1 +#ifdef DBG_CONFIG +#define DUMP_CFG_STR(x) do { \ + FILE *tmp = fopen("/tmp/hwtable-" #x ".txt", "w"); \ + fprintf(tmp, "%s", x); \ + fclose(tmp); \ + } while (0) + + DUMP_CFG_STR(cfg1); + DUMP_CFG_STR(cfg2); +#endif + + assert_int_equal(strlen(cfg2), strlen(cfg1)); + assert_string_equal(cfg2, cfg1); + free(cfg1); + free(cfg2); +} + +/* + * Run hwt->test three times; once with the constructed configuration, + * once after re-reading the full dumped configuration, and once with the + * dumped local configuration. + * + * Expected: test passes every time. + */ +static void test_driver(void **state) +{ + const struct hwt_state *hwt; + + hwt = CHECK_STATE(state); + _conf = LOAD_CONFIG(hwt); + hwt->test(hwt); + + replicate_config(hwt, false); + reset_vecs(hwt->vecs); + hwt->test(hwt); + + replicate_config(hwt, true); + reset_vecs(hwt->vecs); + hwt->test(hwt); + + reset_vecs(hwt->vecs); + FREE_CONFIG(_conf); +} + +/* + * Sanity check for the test itself, because defaults may be changed + * in libmultipath. + * + * Our checking for match or non-match relies on the defaults being + * different from what our device sections contain. + */ +static void test_sanity_globals(void **state) +{ + assert_string_not_equal(prio_emc.value, DEFAULT_PRIO); + assert_string_not_equal(prio_hds.value, DEFAULT_PRIO); + assert_string_not_equal(chk_hp.value, DEFAULT_CHECKER); + assert_int_not_equal(MULTIBUS, DEFAULT_PGPOLICY); + assert_int_not_equal(NO_PATH_RETRY_QUEUE, DEFAULT_NO_PATH_RETRY); + assert_int_not_equal(atoi(minio_99.value), DEFAULT_MINIO_RQ); + assert_int_not_equal(atoi(npr_37.value), DEFAULT_NO_PATH_RETRY); +} + +/* + * Regression test for internal hwtable. NVME is an example of two entries + * in the built-in hwtable, one if which matches a subset of the other. + */ +static void test_internal_nvme(const struct hwt_state *hwt) +{ + struct path *pp; + struct multipath *mp; + + /* + * Generic NVMe: expect defaults for pgpolicy and no_path_retry + */ + pp = mock_path("NVME", "NoName"); + mp = mock_multipath(pp); + assert_ptr_not_equal(mp, NULL); + TEST_PROP(pp->checker.name, NONE); + TEST_PROP(pp->uid_attribute, "ID_WWN"); + assert_int_equal(mp->pgpolicy, DEFAULT_PGPOLICY); + assert_int_equal(mp->no_path_retry, DEFAULT_NO_PATH_RETRY); + assert_int_equal(mp->retain_hwhandler, RETAIN_HWHANDLER_OFF); + + /* + * NetApp NVMe: expect special values for pgpolicy and no_path_retry + */ + pp = mock_path_wwid("NVME", "NetApp ONTAP Controller", + default_wwid_1); + mp = mock_multipath(pp); + assert_ptr_not_equal(mp, NULL); + TEST_PROP(pp->checker.name, NONE); + TEST_PROP(pp->uid_attribute, "ID_WWN"); + assert_int_equal(mp->pgpolicy, MULTIBUS); + assert_int_equal(mp->no_path_retry, NO_PATH_RETRY_QUEUE); + assert_int_equal(mp->retain_hwhandler, RETAIN_HWHANDLER_OFF); +} + +static int setup_internal_nvme(void **state) +{ + struct hwt_state *hwt = CHECK_STATE(state); + + WRITE_EMPTY_CONF(hwt); + SET_TEST_FUNC(hwt, test_internal_nvme); + + return 0; +} + +/* + * Device section with a simple entry qith double quotes ('foo:"bar"') + */ +static void test_quoted_hwe(const struct hwt_state *hwt) +{ + struct path *pp; + + /* foo:"bar" matches */ + pp = mock_path(vnd_foo.value, prd_baq.value); + TEST_PROP(prio_name(&pp->prio), prio_emc.value); + + /* foo:bar doesn't match */ + pp = mock_path(vnd_foo.value, prd_bar.value); + TEST_PROP(prio_name(&pp->prio), DEFAULT_PRIO); +} + +static int setup_quoted_hwe(void **state) +{ + struct hwt_state *hwt = CHECK_STATE(state); + const struct key_value kv[] = { vnd_foo, prd_baqq, prio_emc }; + + WRITE_ONE_DEVICE(hwt, kv); + SET_TEST_FUNC(hwt, test_quoted_hwe); + return 0; +} + +/* + * Device section with a single simple entry ("foo:bar") + */ +static void test_string_hwe(const struct hwt_state *hwt) +{ + struct path *pp; + + /* foo:bar matches */ + pp = mock_path(vnd_foo.value, prd_bar.value); + TEST_PROP(prio_name(&pp->prio), prio_emc.value); + + /* foo:baz doesn't match */ + pp = mock_path(vnd_foo.value, prd_baz.value); + TEST_PROP(prio_name(&pp->prio), DEFAULT_PRIO); + + /* boo:bar doesn't match */ + pp = mock_path(vnd_boo.value, prd_bar.value); + TEST_PROP(prio_name(&pp->prio), DEFAULT_PRIO); +} + +static int setup_string_hwe(void **state) +{ + struct hwt_state *hwt = CHECK_STATE(state); + const struct key_value kv[] = { vnd_foo, prd_bar, prio_emc }; + + WRITE_ONE_DEVICE(hwt, kv); + SET_TEST_FUNC(hwt, test_string_hwe); + return 0; +} + +/* + * Device section with a broken entry (no product) + * It should be ignored. + */ +static void test_broken_hwe(const struct hwt_state *hwt) +{ + struct path *pp; + + /* foo:bar doesn't match, as hwentry is ignored */ + pp = mock_path(vnd_foo.value, prd_bar.value); + TEST_PROP(prio_name(&pp->prio), DEFAULT_PRIO); + + /* boo:bar doesn't match */ + pp = mock_path(vnd_boo.value, prd_bar.value); + TEST_PROP(prio_name(&pp->prio), DEFAULT_PRIO); +} + +static int setup_broken_hwe(void **state) +{ + struct hwt_state *hwt = CHECK_STATE(state); + const struct key_value kv[] = { vnd_foo, prio_emc }; + + WRITE_ONE_DEVICE(hwt, kv); + SET_TEST_FUNC(hwt, test_broken_hwe); + return 0; +} + +/* + * Like test_broken_hwe, but in config_dir file. + */ +static int setup_broken_hwe_dir(void **state) +{ + struct hwt_state *hwt = CHECK_STATE(state); + const struct key_value kv[] = { vnd_foo, prio_emc }; + + begin_config(hwt); + begin_section_all(hwt, "devices"); + write_device(hwt->conf_dir_file[0], ARRAY_SIZE(kv), kv); + end_section_all(hwt); + finish_config(hwt); + hwt->test = test_broken_hwe; + hwt->test_name = "test_broken_hwe_dir"; + return 0; +} + +/* + * Device section with a single regex entry ("^.foo:(bar|baz|ba\.)$") + */ +static void test_regex_hwe(const struct hwt_state *hwt) +{ + struct path *pp; + + /* foo:bar matches */ + pp = mock_path(vnd_foo.value, prd_bar.value); + TEST_PROP(prio_name(&pp->prio), prio_emc.value); + + /* foo:baz matches */ + pp = mock_path(vnd_foo.value, prd_baz.value); + TEST_PROP(prio_name(&pp->prio), prio_emc.value); + + /* boo:baz matches */ + pp = mock_path(vnd_boo.value, prd_bar.value); + TEST_PROP(prio_name(&pp->prio), prio_emc.value); + + /* foo:BAR doesn't match */ + pp = mock_path(vnd_foo.value, "BAR"); + TEST_PROP(prio_name(&pp->prio), DEFAULT_PRIO); + + /* bboo:bar doesn't match */ + pp = mock_path("bboo", prd_bar.value); + TEST_PROP(prio_name(&pp->prio), DEFAULT_PRIO); +} + +static int setup_regex_hwe(void **state) +{ + struct hwt_state *hwt = CHECK_STATE(state); + const struct key_value kv[] = { vnd_t_oo, prd_ba_s, prio_emc }; + + WRITE_ONE_DEVICE(hwt, kv); + SET_TEST_FUNC(hwt, test_regex_hwe); + return 0; +} + +/* + * Two device entries, kv1 is a regex match ("^.foo:(bar|baz|ba\.)$"), + * kv2 a string match (foo:bar) which matches a subset of the regex. + * Both are added to the main config file. + * + * Expected: Devices matching both get properties from both, kv2 taking + * precedence. Devices matching kv1 only just get props from kv1. + */ +static void test_regex_string_hwe(const struct hwt_state *hwt) +{ + struct path *pp; + + /* foo:baz matches kv1 */ + pp = mock_path(vnd_foo.value, prd_baz.value); + TEST_PROP(prio_name(&pp->prio), prio_emc.value); + TEST_PROP(pp->getuid, NULL); + TEST_PROP(pp->checker.name, chk_hp.value); + + /* boo:baz matches kv1 */ + pp = mock_path(vnd_boo.value, prd_baz.value); + TEST_PROP(prio_name(&pp->prio), prio_emc.value); + TEST_PROP(pp->getuid, NULL); + TEST_PROP(pp->checker.name, chk_hp.value); + + /* .oo:ba. matches kv1 */ + pp = mock_path(vnd__oo.value, prd_ba_.value); + TEST_PROP(prio_name(&pp->prio), prio_emc.value); + TEST_PROP(pp->getuid, NULL); + TEST_PROP(pp->checker.name, chk_hp.value); + + /* .foo:(bar|baz|ba\.) doesn't match */ + pp = mock_path(vnd__oo.value, prd_ba_s.value); + TEST_PROP(prio_name(&pp->prio), DEFAULT_PRIO); + TEST_PROP(pp->getuid, NULL); + TEST_PROP(pp->checker.name, DEFAULT_CHECKER); + + /* foo:bar matches kv2 and kv1 */ + pp = mock_path_flags(vnd_foo.value, prd_bar.value, USE_GETUID); + TEST_PROP(prio_name(&pp->prio), prio_hds.value); + TEST_PROP(pp->getuid, gui_foo.value); + TEST_PROP(pp->checker.name, chk_hp.value); +} + +static int setup_regex_string_hwe(void **state) +{ + struct hwt_state *hwt = CHECK_STATE(state); + const struct key_value kv1[] = { vnd_t_oo, prd_ba_s, prio_emc, chk_hp }; + const struct key_value kv2[] = { vnd_foo, prd_bar, prio_hds, gui_foo }; + + WRITE_TWO_DEVICES(hwt, kv1, kv2); + SET_TEST_FUNC(hwt, test_regex_string_hwe); + return 0; +} + +/* + * Two device entries, kv1 is a regex match ("^.foo:(bar|baz|ba\.)$"), + * kv2 a string match (foo:bar) which matches a subset of the regex. + * kv1 is added to the main config file, kv2 to a config_dir file. + * This case is more important as you may think, because it's equivalent + * to kv1 being in the built-in hwtable and kv2 in multipath.conf. + * + * Expected: Devices matching kv2 (and thus, both) get properties + * from both, kv2 taking precedence. + * Devices matching kv1 only just get props from kv1. + */ +static void test_regex_string_hwe_dir(const struct hwt_state *hwt) +{ + struct path *pp; + + /* foo:baz matches kv1 */ + pp = mock_path(vnd_foo.value, prd_baz.value); + TEST_PROP(prio_name(&pp->prio), prio_emc.value); + TEST_PROP(pp->getuid, NULL); + TEST_PROP(pp->checker.name, chk_hp.value); + + /* boo:baz matches kv1 */ + pp = mock_path(vnd_boo.value, prd_baz.value); + TEST_PROP(prio_name(&pp->prio), prio_emc.value); + TEST_PROP(pp->getuid, NULL); + TEST_PROP(pp->checker.name, chk_hp.value); + + /* .oo:ba. matches kv1 */ + pp = mock_path(vnd__oo.value, prd_ba_.value); + TEST_PROP(prio_name(&pp->prio), prio_emc.value); + TEST_PROP(pp->getuid, NULL); + TEST_PROP(pp->checker.name, chk_hp.value); + + /* .oo:(bar|baz|ba\.)$ doesn't match */ + pp = mock_path(vnd__oo.value, prd_ba_s.value); + TEST_PROP(prio_name(&pp->prio), DEFAULT_PRIO); + TEST_PROP(pp->getuid, NULL); + TEST_PROP(pp->checker.name, DEFAULT_CHECKER); + + /* foo:bar matches kv2 */ + pp = mock_path_flags(vnd_foo.value, prd_bar.value, USE_GETUID); + /* Later match takes prio */ + TEST_PROP(prio_name(&pp->prio), prio_hds.value); + TEST_PROP(pp->getuid, gui_foo.value); + TEST_PROP(pp->checker.name, chk_hp.value); +} + +static int setup_regex_string_hwe_dir(void **state) +{ + const struct key_value kv1[] = { vnd_t_oo, prd_ba_s, prio_emc, chk_hp }; + const struct key_value kv2[] = { vnd_foo, prd_bar, prio_hds, gui_foo }; + struct hwt_state *hwt = CHECK_STATE(state); + + WRITE_TWO_DEVICES_W_DIR(hwt, kv1, kv2); + SET_TEST_FUNC(hwt, test_regex_string_hwe_dir); + return 0; +} + +/* + * Three device entries, kv1 is a regex match and kv2 and kv3 string + * matches, where kv3 is a substring of kv2. All in different config + * files. + * + * Expected: Devices matching kv3 get props from all, devices matching + * kv2 from kv2 and kv1, and devices matching kv1 only just from kv1. + */ +static void test_regex_2_strings_hwe_dir(const struct hwt_state *hwt) +{ + struct path *pp; + + /* foo:baz matches kv1 */ + pp = mock_path(vnd_foo.value, prd_baz.value); + TEST_PROP(prio_name(&pp->prio), prio_emc.value); + TEST_PROP(pp->getuid, NULL); + TEST_PROP(pp->uid_attribute, DEFAULT_UID_ATTRIBUTE); + TEST_PROP(pp->checker.name, chk_hp.value); + + /* boo:baz doesn't match */ + pp = mock_path(vnd_boo.value, prd_baz.value); + TEST_PROP(prio_name(&pp->prio), DEFAULT_PRIO); + TEST_PROP(pp->getuid, NULL); + TEST_PROP(pp->uid_attribute, DEFAULT_UID_ATTRIBUTE); + TEST_PROP(pp->checker.name, DEFAULT_CHECKER); + + /* foo:bar matches kv2 and kv1 */ + pp = mock_path(vnd_foo.value, prd_bar.value); + TEST_PROP(prio_name(&pp->prio), prio_hds.value); + TEST_PROP(pp->getuid, NULL); + TEST_PROP(pp->uid_attribute, uid_baz.value); + TEST_PROP(pp->checker.name, chk_hp.value); + + /* foo:barz matches kv3 and kv2 and kv1 */ + pp = mock_path_flags(vnd_foo.value, prd_barz.value, USE_GETUID); + TEST_PROP(prio_name(&pp->prio), prio_rdac.value); + TEST_PROP(pp->getuid, gui_foo.value); + TEST_PROP(pp->uid_attribute, NULL); + TEST_PROP(pp->checker.name, chk_hp.value); +} + +static int setup_regex_2_strings_hwe_dir(void **state) +{ + const struct key_value kv1[] = { vnd_foo, prd_ba_, prio_emc, chk_hp }; + const struct key_value kv2[] = { vnd_foo, prd_bar, prio_hds, uid_baz }; + const struct key_value kv3[] = { vnd_foo, prd_barz, + prio_rdac, gui_foo }; + struct hwt_state *hwt = CHECK_STATE(state); + + begin_config(hwt); + begin_section_all(hwt, "devices"); + write_device(hwt->config_file, ARRAY_SIZE(kv1), kv1); + write_device(hwt->conf_dir_file[0], ARRAY_SIZE(kv2), kv2); + write_device(hwt->conf_dir_file[1], ARRAY_SIZE(kv3), kv3); + end_section_all(hwt); + finish_config(hwt); + SET_TEST_FUNC(hwt, test_regex_2_strings_hwe_dir); + return 0; +} + +/* + * Like test_regex_string_hwe_dir, but the order of kv1 and kv2 is exchanged. + * + * Expected: Devices matching kv1 (and thus, both) get properties + * from both, kv1 taking precedence. + * Devices matching kv1 only just get props from kv1. + */ +static void test_string_regex_hwe_dir(const struct hwt_state *hwt) +{ + struct path *pp; + + /* foo:bar matches kv2 and kv1 */ + pp = mock_path_flags(vnd_foo.value, prd_bar.value, USE_GETUID); + TEST_PROP(prio_name(&pp->prio), prio_emc.value); + TEST_PROP(pp->getuid, gui_foo.value); + TEST_PROP(pp->checker.name, chk_hp.value); + + /* foo:baz matches kv1 */ + pp = mock_path(vnd_foo.value, prd_baz.value); + TEST_PROP(prio_name(&pp->prio), prio_emc.value); + TEST_PROP(pp->getuid, NULL); + TEST_PROP(pp->checker.name, chk_hp.value); + + /* boo:baz matches kv1 */ + pp = mock_path(vnd_boo.value, prd_baz.value); + TEST_PROP(prio_name(&pp->prio), prio_emc.value); + TEST_PROP(pp->getuid, NULL); + TEST_PROP(pp->checker.name, chk_hp.value); + + /* .oo:ba. matches kv1 */ + pp = mock_path(vnd__oo.value, prd_ba_.value); + TEST_PROP(prio_name(&pp->prio), prio_emc.value); + TEST_PROP(pp->getuid, NULL); + TEST_PROP(pp->checker.name, chk_hp.value); + + /* .oo:(bar|baz|ba\.)$ doesn't match */ + pp = mock_path(vnd__oo.value, prd_ba_s.value); + TEST_PROP(prio_name(&pp->prio), DEFAULT_PRIO); + TEST_PROP(pp->getuid, NULL); + TEST_PROP(pp->checker.name, DEFAULT_CHECKER); +} + +static int setup_string_regex_hwe_dir(void **state) +{ + const struct key_value kv1[] = { vnd_t_oo, prd_ba_s, prio_emc, chk_hp }; + const struct key_value kv2[] = { vnd_foo, prd_bar, prio_hds, gui_foo }; + struct hwt_state *hwt = CHECK_STATE(state); + + WRITE_TWO_DEVICES_W_DIR(hwt, kv2, kv1); + SET_TEST_FUNC(hwt, test_string_regex_hwe_dir); + return 0; +} + +/* + * Two identical device entries kv1 and kv2, trival regex ("string"). + * Both are added to the main config file. + * These entries are NOT merged. + * This could happen in a large multipath.conf file. + * + * Expected: matching devices get props from both, kv2 taking precedence. + */ +static void test_2_ident_strings_hwe(const struct hwt_state *hwt) +{ + struct path *pp; + + /* foo:baz doesn't match */ + pp = mock_path(vnd_foo.value, prd_baz.value); + TEST_PROP(prio_name(&pp->prio), DEFAULT_PRIO); + TEST_PROP(pp->getuid, NULL); + TEST_PROP(pp->checker.name, DEFAULT_CHECKER); + + /* foo:bar matches both */ + pp = mock_path_flags(vnd_foo.value, prd_bar.value, USE_GETUID); + TEST_PROP(prio_name(&pp->prio), prio_hds.value); + TEST_PROP(pp->getuid, gui_foo.value); + TEST_PROP(pp->checker.name, chk_hp.value); +} + +static int setup_2_ident_strings_hwe(void **state) +{ + const struct key_value kv1[] = { vnd_foo, prd_bar, prio_emc, chk_hp }; + const struct key_value kv2[] = { vnd_foo, prd_bar, prio_hds, gui_foo }; + struct hwt_state *hwt = CHECK_STATE(state); + + WRITE_TWO_DEVICES(hwt, kv1, kv2); + SET_TEST_FUNC(hwt, test_2_ident_strings_hwe); + return 0; +} + +/* + * Two identical device entries kv1 and kv2, trival regex ("string"). + * Both are added to an extra config file. + * This could happen in a large multipath.conf file. + * + * Expected: matching devices get props from both, kv2 taking precedence. + */ +static void test_2_ident_strings_both_dir(const struct hwt_state *hwt) +{ + struct path *pp; + + /* foo:baz doesn't match */ + pp = mock_path(vnd_foo.value, prd_baz.value); + TEST_PROP(prio_name(&pp->prio), DEFAULT_PRIO); + TEST_PROP(pp->getuid, NULL); + TEST_PROP(pp->checker.name, DEFAULT_CHECKER); + + /* foo:bar matches both */ + pp = mock_path_flags(vnd_foo.value, prd_bar.value, USE_GETUID); + TEST_PROP(prio_name(&pp->prio), prio_hds.value); + TEST_PROP(pp->getuid, gui_foo.value); + TEST_PROP(pp->checker.name, chk_hp.value); +} + +static int setup_2_ident_strings_both_dir(void **state) +{ + const struct key_value kv1[] = { vnd_foo, prd_bar, prio_emc, chk_hp }; + const struct key_value kv2[] = { vnd_foo, prd_bar, prio_hds, gui_foo }; + struct hwt_state *hwt = CHECK_STATE(state); + + begin_config(hwt); + begin_section_all(hwt, "devices"); + write_device(hwt->conf_dir_file[1], ARRAY_SIZE(kv1), kv1); + write_device(hwt->conf_dir_file[1], ARRAY_SIZE(kv2), kv2); + end_section_all(hwt); + finish_config(hwt); + SET_TEST_FUNC(hwt, test_2_ident_strings_both_dir); + return 0; +} + +/* + * Two identical device entries kv1 and kv2, trival regex ("string"). + * Both are added to an extra config file. + * An empty entry kv0 with the same string exists in the main config file. + * + * Expected: matching devices get props from both, kv2 taking precedence. + */ +static void test_2_ident_strings_both_dir_w_prev(const struct hwt_state *hwt) +{ + struct path *pp; + + /* foo:baz doesn't match */ + pp = mock_path(vnd_foo.value, prd_baz.value); + TEST_PROP(prio_name(&pp->prio), DEFAULT_PRIO); + TEST_PROP(pp->getuid, NULL); + TEST_PROP(pp->checker.name, DEFAULT_CHECKER); + + /* foo:bar matches both */ + pp = mock_path_flags(vnd_foo.value, prd_bar.value, USE_GETUID); + TEST_PROP(prio_name(&pp->prio), prio_hds.value); + TEST_PROP(pp->getuid, gui_foo.value); + TEST_PROP(pp->checker.name, chk_hp.value); +} + +static int setup_2_ident_strings_both_dir_w_prev(void **state) +{ + struct hwt_state *hwt = CHECK_STATE(state); + + const struct key_value kv0[] = { vnd_foo, prd_bar }; + const struct key_value kv1[] = { vnd_foo, prd_bar, prio_emc, chk_hp }; + const struct key_value kv2[] = { vnd_foo, prd_bar, prio_hds, gui_foo }; + + begin_config(hwt); + begin_section_all(hwt, "devices"); + write_device(hwt->config_file, ARRAY_SIZE(kv0), kv0); + write_device(hwt->conf_dir_file[1], ARRAY_SIZE(kv1), kv1); + write_device(hwt->conf_dir_file[1], ARRAY_SIZE(kv2), kv2); + end_section_all(hwt); + finish_config(hwt); + SET_TEST_FUNC(hwt, test_2_ident_strings_both_dir_w_prev); + return 0; +} + +/* + * Two identical device entries kv1 and kv2, trival regex ("string"). + * kv1 is added to the main config file, kv2 to a config_dir file. + * These entries are merged. + * This case is more important as you may think, because it's equivalent + * to kv1 being in the built-in hwtable and kv2 in multipath.conf. + * + * Expected: matching devices get props from both, kv2 taking precedence. + */ +static void test_2_ident_strings_hwe_dir(const struct hwt_state *hwt) +{ + struct path *pp; + + /* foo:baz doesn't match */ + pp = mock_path(vnd_foo.value, prd_baz.value); + TEST_PROP(prio_name(&pp->prio), DEFAULT_PRIO); + TEST_PROP(pp->getuid, NULL); + TEST_PROP(pp->checker.name, DEFAULT_CHECKER); + + /* foo:bar matches both */ + pp = mock_path_flags(vnd_foo.value, prd_bar.value, USE_GETUID); + TEST_PROP(prio_name(&pp->prio), prio_hds.value); + TEST_PROP(pp->getuid, gui_foo.value); + TEST_PROP(pp->checker.name, chk_hp.value); +} + +static int setup_2_ident_strings_hwe_dir(void **state) +{ + const struct key_value kv1[] = { vnd_foo, prd_bar, prio_emc, chk_hp }; + const struct key_value kv2[] = { vnd_foo, prd_bar, prio_hds, gui_foo }; + struct hwt_state *hwt = CHECK_STATE(state); + + WRITE_TWO_DEVICES_W_DIR(hwt, kv1, kv2); + SET_TEST_FUNC(hwt, test_2_ident_strings_hwe_dir); + return 0; +} + +/* + * Like test_2_ident_strings_hwe_dir, but this time the config_dir file + * contains an additional, empty entry (kv0). + * + * Expected: matching devices get props from kv1 and kv2, kv2 taking precedence. + */ +static void test_3_ident_strings_hwe_dir(const struct hwt_state *hwt) +{ + struct path *pp; + + /* foo:baz doesn't match */ + pp = mock_path(vnd_foo.value, prd_baz.value); + TEST_PROP(prio_name(&pp->prio), DEFAULT_PRIO); + TEST_PROP(pp->getuid, NULL); + TEST_PROP(pp->checker.name, DEFAULT_CHECKER); + + /* foo:bar matches both */ + pp = mock_path_flags(vnd_foo.value, prd_bar.value, USE_GETUID); + TEST_PROP(prio_name(&pp->prio), prio_hds.value); + TEST_PROP(pp->getuid, gui_foo.value); + TEST_PROP(pp->checker.name, chk_hp.value); +} + +static int setup_3_ident_strings_hwe_dir(void **state) +{ + const struct key_value kv0[] = { vnd_foo, prd_bar }; + const struct key_value kv1[] = { vnd_foo, prd_bar, prio_emc, chk_hp }; + const struct key_value kv2[] = { vnd_foo, prd_bar, prio_hds, gui_foo }; + struct hwt_state *hwt = CHECK_STATE(state); + + begin_config(hwt); + begin_section_all(hwt, "devices"); + write_device(hwt->config_file, ARRAY_SIZE(kv1), kv1); + write_device(hwt->conf_dir_file[1], ARRAY_SIZE(kv0), kv0); + write_device(hwt->conf_dir_file[1], ARRAY_SIZE(kv2), kv2); + end_section_all(hwt); + finish_config(hwt); + SET_TEST_FUNC(hwt, test_3_ident_strings_hwe_dir); + return 0; +} + +/* + * Two identical device entries kv1 and kv2, non-trival regex that matches + * itself (string ".oo" matches regex ".oo"). + * kv1 is added to the main config file, kv2 to a config_dir file. + * This case is more important as you may think, because it's equivalent + * to kv1 being in the built-in hwtable and kv2 in multipath.conf. + * + * Expected: matching devices get props from both, kv2 taking precedence. + */ +static void test_2_ident_self_matching_re_hwe_dir(const struct hwt_state *hwt) +{ + struct path *pp; + + /* foo:baz doesn't match */ + pp = mock_path(vnd_foo.value, prd_baz.value); + TEST_PROP(prio_name(&pp->prio), DEFAULT_PRIO); + TEST_PROP(pp->getuid, NULL); + TEST_PROP(pp->checker.name, DEFAULT_CHECKER); + + /* foo:bar matches both */ + pp = mock_path_flags(vnd_foo.value, prd_bar.value, USE_GETUID); + TEST_PROP(prio_name(&pp->prio), prio_hds.value); + TEST_PROP(pp->getuid, gui_foo.value); + TEST_PROP(pp->checker.name, chk_hp.value); +} + +static int setup_2_ident_self_matching_re_hwe_dir(void **state) +{ + const struct key_value kv1[] = { vnd__oo, prd_bar, prio_emc, chk_hp }; + const struct key_value kv2[] = { vnd__oo, prd_bar, prio_hds, gui_foo }; + struct hwt_state *hwt = CHECK_STATE(state); + + WRITE_TWO_DEVICES_W_DIR(hwt, kv1, kv2); + SET_TEST_FUNC(hwt, test_2_ident_self_matching_re_hwe_dir); + return 0; +} + +/* + * Two identical device entries kv1 and kv2, non-trival regex that matches + * itself (string ".oo" matches regex ".oo"). + * kv1 and kv2 are added to the main config file. + * + * Expected: matching devices get props from both, kv2 taking precedence. + */ +static void test_2_ident_self_matching_re_hwe(const struct hwt_state *hwt) +{ + struct path *pp; + + /* foo:baz doesn't match */ + pp = mock_path(vnd_foo.value, prd_baz.value); + TEST_PROP(prio_name(&pp->prio), DEFAULT_PRIO); + TEST_PROP(pp->getuid, NULL); + TEST_PROP(pp->checker.name, DEFAULT_CHECKER); + + /* foo:bar matches */ + pp = mock_path_flags(vnd_foo.value, prd_bar.value, USE_GETUID); + TEST_PROP(prio_name(&pp->prio), prio_hds.value); + TEST_PROP(pp->getuid, gui_foo.value); + TEST_PROP(pp->checker.name, chk_hp.value); +} + +static int setup_2_ident_self_matching_re_hwe(void **state) +{ + const struct key_value kv1[] = { vnd__oo, prd_bar, prio_emc, chk_hp }; + const struct key_value kv2[] = { vnd__oo, prd_bar, prio_hds, gui_foo }; + struct hwt_state *hwt = CHECK_STATE(state); + + WRITE_TWO_DEVICES(hwt, kv1, kv2); + SET_TEST_FUNC(hwt, test_2_ident_self_matching_re_hwe); + return 0; +} + +/* + * Two identical device entries kv1 and kv2, non-trival regex that doesn't + * match itself (string "^.oo" doesn't match regex "^.oo"). + * kv1 is added to the main config file, kv2 to a config_dir file. + * This case is more important as you may think, see above. + * + * Expected: matching devices get props from both, kv2 taking precedence. + */ +static void +test_2_ident_not_self_matching_re_hwe_dir(const struct hwt_state *hwt) +{ + struct path *pp; + + /* foo:baz doesn't match */ + pp = mock_path(vnd_foo.value, prd_baz.value); + TEST_PROP(prio_name(&pp->prio), DEFAULT_PRIO); + TEST_PROP(pp->getuid, NULL); + TEST_PROP(pp->checker.name, DEFAULT_CHECKER); + + /* foo:bar matches both */ + pp = mock_path_flags(vnd_foo.value, prd_bar.value, USE_GETUID); + TEST_PROP(prio_name(&pp->prio), prio_hds.value); + TEST_PROP(pp->getuid, gui_foo.value); + TEST_PROP(pp->checker.name, chk_hp.value); +} + +static int setup_2_ident_not_self_matching_re_hwe_dir(void **state) +{ + const struct key_value kv1[] = { vnd_t_oo, prd_bar, prio_emc, chk_hp }; + const struct key_value kv2[] = { vnd_t_oo, prd_bar, prio_hds, gui_foo }; + struct hwt_state *hwt = CHECK_STATE(state); + + WRITE_TWO_DEVICES_W_DIR(hwt, kv1, kv2); + SET_TEST_FUNC(hwt, test_2_ident_not_self_matching_re_hwe_dir); + return 0; +} + +/* + * Two different non-trivial regexes kv1, kv2. The 1st one matches the 2nd, but + * it doesn't match all possible strings matching the second. + * ("ba[zy]" matches regex "ba[[rxy]", but "baz" does not). + * + * Expected: Devices matching both regexes get properties from both, kv2 + * taking precedence. Devices matching just one regex get properties from + * that one regex only. + */ +static void test_2_matching_res_hwe_dir(const struct hwt_state *hwt) +{ + struct path *pp; + + /* foo:bar matches k1 only */ + pp = mock_path(vnd_foo.value, prd_bar.value); + TEST_PROP(prio_name(&pp->prio), prio_emc.value); + TEST_PROP(pp->getuid, NULL); + TEST_PROP(pp->checker.name, chk_hp.value); + + /* foo:bay matches k1 and k2 */ + pp = mock_path_flags(vnd_foo.value, "bay", USE_GETUID); + TEST_PROP(prio_name(&pp->prio), prio_hds.value); + TEST_PROP(pp->getuid, gui_foo.value); + TEST_PROP(pp->checker.name, chk_hp.value); + + /* foo:baz matches k2 only. */ + pp = mock_path_flags(vnd_foo.value, prd_baz.value, USE_GETUID); + TEST_PROP(prio_name(&pp->prio), prio_hds.value); + TEST_PROP(pp->getuid, gui_foo.value); + TEST_PROP(pp->checker.name, DEFAULT_CHECKER); +} + +static int setup_2_matching_res_hwe_dir(void **state) +{ + const struct key_value kv1[] = { vnd_foo, prd_barx, prio_emc, chk_hp }; + const struct key_value kv2[] = { vnd_foo, prd_bazy, prio_hds, gui_foo }; + struct hwt_state *hwt = CHECK_STATE(state); + + WRITE_TWO_DEVICES_W_DIR(hwt, kv1, kv2); + SET_TEST_FUNC(hwt, test_2_matching_res_hwe_dir); + return 0; +} + +/* + * Two different non-trivial regexes which match the same set of strings. + * But they don't match each other. + * "baz" matches both regex "ba[zy]" and "ba(z|y)" + * + * Expected: matching devices get properties from both, kv2 taking precedence. + */ +static void test_2_nonmatching_res_hwe_dir(const struct hwt_state *hwt) +{ + struct path *pp; + + /* foo:bar doesn't match */ + pp = mock_path(vnd_foo.value, prd_bar.value); + TEST_PROP(prio_name(&pp->prio), DEFAULT_PRIO); + TEST_PROP(pp->getuid, NULL); + TEST_PROP(pp->checker.name, DEFAULT_CHECKER); + + pp = mock_path_flags(vnd_foo.value, prd_baz.value, USE_GETUID); + TEST_PROP(prio_name(&pp->prio), prio_hds.value); + TEST_PROP(pp->getuid, gui_foo.value); + TEST_PROP(pp->checker.name, chk_hp.value); +} + +static int setup_2_nonmatching_res_hwe_dir(void **state) +{ + const struct key_value kv1[] = { vnd_foo, prd_bazy, prio_emc, chk_hp }; + const struct key_value kv2[] = { vnd_foo, prd_bazy1, + prio_hds, gui_foo }; + struct hwt_state *hwt = CHECK_STATE(state); + + WRITE_TWO_DEVICES_W_DIR(hwt, kv1, kv2); + SET_TEST_FUNC(hwt, test_2_nonmatching_res_hwe_dir); + return 0; +} + +/* + * Simple blacklist test. + * + * NOTE: test failures in blacklisting tests will manifest as cmocka errors + * "Could not get value to mock function XYZ", because pathinfo() takes + * different code paths for blacklisted devices. + */ +static void test_blacklist(const struct hwt_state *hwt) +{ + mock_path_flags(vnd_foo.value, prd_bar.value, BL_BY_DEVICE); + mock_path(vnd_foo.value, prd_baz.value); +} + +static int setup_blacklist(void **state) +{ + const struct key_value kv1[] = { vnd_foo, prd_bar }; + struct hwt_state *hwt = CHECK_STATE(state); + + begin_config(hwt); + begin_section_all(hwt, "blacklist"); + write_device(hwt->config_file, ARRAY_SIZE(kv1), kv1); + end_section_all(hwt); + finish_config(hwt); + SET_TEST_FUNC(hwt, test_blacklist); + return 0; +} + +/* + * Simple blacklist test with regex and exception + */ +static void test_blacklist_regex(const struct hwt_state *hwt) +{ + mock_path(vnd_foo.value, prd_bar.value); + mock_path_flags(vnd_foo.value, prd_baz.value, BL_BY_DEVICE); + mock_path(vnd_foo.value, prd_bam.value); +} + +static int setup_blacklist_regex(void **state) +{ + const struct key_value kv1[] = { vnd_foo, prd_ba_s }; + const struct key_value kv2[] = { vnd_foo, prd_bar }; + struct hwt_state *hwt = CHECK_STATE(state); + + hwt = CHECK_STATE(state); + begin_config(hwt); + begin_section_all(hwt, "blacklist"); + write_device(hwt->config_file, ARRAY_SIZE(kv1), kv1); + end_section_all(hwt); + begin_section_all(hwt, "blacklist_exceptions"); + write_device(hwt->conf_dir_file[0], ARRAY_SIZE(kv2), kv2); + end_section_all(hwt); + finish_config(hwt); + SET_TEST_FUNC(hwt, test_blacklist_regex); + return 0; +} + +/* + * Simple blacklist test with regex and exception + * config file order inverted wrt test_blacklist_regex + */ +static int setup_blacklist_regex_inv(void **state) +{ + const struct key_value kv1[] = { vnd_foo, prd_ba_s }; + const struct key_value kv2[] = { vnd_foo, prd_bar }; + struct hwt_state *hwt = CHECK_STATE(state); + + begin_config(hwt); + begin_section_all(hwt, "blacklist"); + write_device(hwt->conf_dir_file[0], ARRAY_SIZE(kv1), kv1); + end_section_all(hwt); + begin_section_all(hwt, "blacklist_exceptions"); + write_device(hwt->config_file, ARRAY_SIZE(kv2), kv2); + end_section_all(hwt); + finish_config(hwt); + SET_TEST_FUNC(hwt, test_blacklist_regex); + return 0; +} + +/* + * Simple blacklist test with regex and exception + * config file order inverted wrt test_blacklist_regex + */ +static void test_blacklist_regex_matching(const struct hwt_state *hwt) +{ + mock_path_flags(vnd_foo.value, prd_bar.value, BL_BY_DEVICE); + mock_path_flags(vnd_foo.value, prd_baz.value, BL_BY_DEVICE); + mock_path(vnd_foo.value, prd_bam.value); +} + +static int setup_blacklist_regex_matching(void **state) +{ + const struct key_value kv1[] = { vnd_foo, prd_barx }; + const struct key_value kv2[] = { vnd_foo, prd_bazy }; + struct hwt_state *hwt = CHECK_STATE(state); + + begin_config(hwt); + begin_section_all(hwt, "blacklist"); + write_device(hwt->config_file, ARRAY_SIZE(kv1), kv1); + write_device(hwt->conf_dir_file[0], ARRAY_SIZE(kv2), kv2); + end_section_all(hwt); + finish_config(hwt); + SET_TEST_FUNC(hwt, test_blacklist_regex_matching); + return 0; +} + +/* + * Test for blacklisting by WWID + * + * Note that default_wwid is a substring of default_wwid_1. Because + * matching is done by regex, both paths are blacklisted. + */ +static void test_blacklist_wwid(const struct hwt_state *hwt) +{ + mock_path_flags(vnd_foo.value, prd_bar.value, BL_BY_WWID); + mock_path_wwid_flags(vnd_foo.value, prd_baz.value, default_wwid_1, + BL_BY_WWID); +} + +static int setup_blacklist_wwid(void **state) +{ + const struct key_value kv[] = { wwid_test }; + struct hwt_state *hwt = CHECK_STATE(state); + + begin_config(hwt); + write_section(hwt->config_file, "blacklist", ARRAY_SIZE(kv), kv); + finish_config(hwt); + SET_TEST_FUNC(hwt, test_blacklist_wwid); + return 0; +} + +/* + * Test for blacklisting by WWID + * + * Here the blacklist contains only default_wwid_1. Thus the path + * with default_wwid is NOT blacklisted. + */ +static void test_blacklist_wwid_1(const struct hwt_state *hwt) +{ + mock_path(vnd_foo.value, prd_bar.value); + mock_path_wwid_flags(vnd_foo.value, prd_baz.value, default_wwid_1, + BL_BY_WWID); +} + +static int setup_blacklist_wwid_1(void **state) +{ + const struct key_value kv[] = { { _wwid, default_wwid_1 }, }; + struct hwt_state *hwt = CHECK_STATE(state); + + begin_config(hwt); + write_section(hwt->config_file, "blacklist", ARRAY_SIZE(kv), kv); + finish_config(hwt); + SET_TEST_FUNC(hwt, test_blacklist_wwid_1); + return 0; +} + +/* + * Test for product_blacklist. Two entries blacklisting each other. + * + * Expected: Both are blacklisted. + */ +static void test_product_blacklist(const struct hwt_state *hwt) +{ + mock_path_flags(vnd_foo.value, prd_baz.value, BL_BY_DEVICE); + mock_path_flags(vnd_foo.value, prd_bar.value, BL_BY_DEVICE); + mock_path(vnd_foo.value, prd_bam.value); +} + +static int setup_product_blacklist(void **state) +{ + const struct key_value kv1[] = { vnd_foo, prd_bar, bl_baz }; + const struct key_value kv2[] = { vnd_foo, prd_baz, bl_bar }; + struct hwt_state *hwt = CHECK_STATE(state); + + WRITE_TWO_DEVICES(hwt, kv1, kv2); + SET_TEST_FUNC(hwt, test_product_blacklist); + return 0; +} + +/* + * Test for product_blacklist. The second regex "matches" the first. + * This is a pathological example. + * + * Expected: "foo:bar", "foo:baz" are blacklisted. + */ +static void test_product_blacklist_matching(const struct hwt_state *hwt) +{ + mock_path_flags(vnd_foo.value, prd_bar.value, BL_BY_DEVICE); + mock_path_flags(vnd_foo.value, prd_baz.value, BL_BY_DEVICE); + mock_path(vnd_foo.value, prd_bam.value); +} + +static int setup_product_blacklist_matching(void **state) +{ + const struct key_value kv1[] = { vnd_foo, prd_bar, bl_barx }; + const struct key_value kv2[] = { vnd_foo, prd_baz, bl_bazy }; + struct hwt_state *hwt = CHECK_STATE(state); + + WRITE_TWO_DEVICES(hwt, kv1, kv2); + SET_TEST_FUNC(hwt, test_product_blacklist_matching); + return 0; +} + +/* + * Basic test for multipath-based configuration. + * + * Expected: properties, including pp->prio, are taken from multipath + * section. + */ +static void test_multipath_config(const struct hwt_state *hwt) +{ + struct path *pp; + struct multipath *mp; + + pp = mock_path(vnd_foo.value, prd_bar.value); + mp = mock_multipath(pp); + assert_ptr_not_equal(mp->mpe, NULL); + TEST_PROP(prio_name(&pp->prio), prio_rdac.value); + assert_int_equal(mp->minio, atoi(minio_99.value)); + TEST_PROP(pp->uid_attribute, uid_baz.value); + + /* test different wwid */ + pp = mock_path_wwid(vnd_foo.value, prd_bar.value, default_wwid_1); + mp = mock_multipath(pp); + // assert_ptr_equal(mp->mpe, NULL); + TEST_PROP(prio_name(&pp->prio), prio_emc.value); + assert_int_equal(mp->minio, DEFAULT_MINIO_RQ); + TEST_PROP(pp->uid_attribute, uid_baz.value); +} + +static int setup_multipath_config(void **state) +{ + struct hwt_state *hwt = CHECK_STATE(state); + const struct key_value kvm[] = { wwid_test, prio_rdac, minio_99 }; + const struct key_value kvp[] = { vnd_foo, prd_bar, prio_emc, uid_baz }; + + begin_config(hwt); + begin_section_all(hwt, "devices"); + write_section(hwt->conf_dir_file[0], "device", ARRAY_SIZE(kvp), kvp); + end_section_all(hwt); + begin_section_all(hwt, "multipaths"); + write_section(hwt->config_file, "multipath", ARRAY_SIZE(kvm), kvm); + end_section_all(hwt); + finish_config(hwt); + SET_TEST_FUNC(hwt, test_multipath_config); + return 0; +} + +/* + * Basic test for multipath-based configuration. Two sections for the same wwid. + * + * Expected: properties are taken from both multipath sections, later taking + * precedence + */ +static void test_multipath_config_2(const struct hwt_state *hwt) +{ + struct path *pp; + struct multipath *mp; + + pp = mock_path(vnd_foo.value, prd_bar.value); + mp = mock_multipath(pp); + assert_ptr_not_equal(mp, NULL); + assert_ptr_not_equal(mp->mpe, NULL); + TEST_PROP(prio_name(&pp->prio), prio_rdac.value); + assert_int_equal(mp->minio, atoi(minio_99.value)); + assert_int_equal(mp->no_path_retry, atoi(npr_37.value)); +} + +static int setup_multipath_config_2(void **state) +{ + const struct key_value kv1[] = { wwid_test, prio_rdac, npr_queue }; + const struct key_value kv2[] = { wwid_test, minio_99, npr_37 }; + struct hwt_state *hwt = CHECK_STATE(state); + + begin_config(hwt); + begin_section_all(hwt, "multipaths"); + write_section(hwt->config_file, "multipath", ARRAY_SIZE(kv1), kv1); + write_section(hwt->conf_dir_file[1], "multipath", ARRAY_SIZE(kv2), kv2); + end_section_all(hwt); + finish_config(hwt); + SET_TEST_FUNC(hwt, test_multipath_config_2); + return 0; +} + +/* + * Same as test_multipath_config_2, both entries in the same config file. + * + * Expected: properties are taken from both multipath sections. + */ +static void test_multipath_config_3(const struct hwt_state *hwt) +{ + struct path *pp; + struct multipath *mp; + + pp = mock_path(vnd_foo.value, prd_bar.value); + mp = mock_multipath(pp); + assert_ptr_not_equal(mp, NULL); + assert_ptr_not_equal(mp->mpe, NULL); + TEST_PROP(prio_name(&pp->prio), prio_rdac.value); + assert_int_equal(mp->minio, atoi(minio_99.value)); + assert_int_equal(mp->no_path_retry, atoi(npr_37.value)); +} + +static int setup_multipath_config_3(void **state) +{ + const struct key_value kv1[] = { wwid_test, prio_rdac, npr_queue }; + const struct key_value kv2[] = { wwid_test, minio_99, npr_37 }; + struct hwt_state *hwt = CHECK_STATE(state); + + begin_config(hwt); + begin_section_all(hwt, "multipaths"); + write_section(hwt->config_file, "multipath", ARRAY_SIZE(kv1), kv1); + write_section(hwt->config_file, "multipath", ARRAY_SIZE(kv2), kv2); + end_section_all(hwt); + finish_config(hwt); + SET_TEST_FUNC(hwt, test_multipath_config_3); + return 0; +} + +/* + * Test for device with "hidden" attribute + */ +static void test_hidden(const struct hwt_state *hwt) +{ + mock_path_flags("NVME", "NoName", DEV_HIDDEN|BL_MASK); +} + +static int setup_hidden(void **state) +{ + struct hwt_state *hwt = CHECK_STATE(state); + + WRITE_EMPTY_CONF(hwt); + SET_TEST_FUNC(hwt, test_hidden); + + return 0; +} + +/* + * Create wrapper functions around test_driver() to avoid that cmocka + * always uses the same test name. That makes it easier to read test results. + */ + +#define define_test(x) \ + static void run_##x(void **state) \ + { \ + return test_driver(state); \ + } + +define_test(string_hwe) +define_test(broken_hwe) +define_test(broken_hwe_dir) +define_test(quoted_hwe) +define_test(internal_nvme) +define_test(regex_hwe) +define_test(regex_string_hwe) +define_test(regex_string_hwe_dir) +define_test(regex_2_strings_hwe_dir) +define_test(string_regex_hwe_dir) +define_test(2_ident_strings_hwe) +define_test(2_ident_strings_both_dir) +define_test(2_ident_strings_both_dir_w_prev) +define_test(2_ident_strings_hwe_dir) +define_test(3_ident_strings_hwe_dir) +define_test(2_ident_self_matching_re_hwe_dir) +define_test(2_ident_self_matching_re_hwe) +define_test(2_ident_not_self_matching_re_hwe_dir) +define_test(2_matching_res_hwe_dir) +define_test(2_nonmatching_res_hwe_dir) +define_test(blacklist) +define_test(blacklist_wwid) +define_test(blacklist_wwid_1) +define_test(blacklist_regex) +define_test(blacklist_regex_inv) +define_test(blacklist_regex_matching) +define_test(product_blacklist) +define_test(product_blacklist_matching) +define_test(multipath_config) +define_test(multipath_config_2) +define_test(multipath_config_3) +define_test(hidden) + +#define test_entry(x) \ + cmocka_unit_test_setup(run_##x, setup_##x) + +static int test_hwtable(void) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_sanity_globals), + test_entry(internal_nvme), + test_entry(string_hwe), + test_entry(broken_hwe), + test_entry(broken_hwe_dir), + test_entry(quoted_hwe), + test_entry(regex_hwe), + test_entry(regex_string_hwe), + test_entry(regex_string_hwe_dir), + test_entry(regex_2_strings_hwe_dir), + test_entry(string_regex_hwe_dir), + test_entry(2_ident_strings_hwe), + test_entry(2_ident_strings_both_dir), + test_entry(2_ident_strings_both_dir_w_prev), + test_entry(2_ident_strings_hwe_dir), + test_entry(3_ident_strings_hwe_dir), + test_entry(2_ident_self_matching_re_hwe_dir), + test_entry(2_ident_self_matching_re_hwe), + test_entry(2_ident_not_self_matching_re_hwe_dir), + test_entry(2_matching_res_hwe_dir), + test_entry(2_nonmatching_res_hwe_dir), + test_entry(blacklist), + test_entry(blacklist_wwid), + test_entry(blacklist_wwid_1), + test_entry(blacklist_regex), + test_entry(blacklist_regex_inv), + test_entry(blacklist_regex_matching), + test_entry(product_blacklist), + test_entry(product_blacklist_matching), + test_entry(multipath_config), + test_entry(multipath_config_2), + test_entry(multipath_config_3), + test_entry(hidden), + }; + + return cmocka_run_group_tests(tests, setup, teardown); +} + +int main(void) +{ + int ret = 0; + + ret += test_hwtable(); + return ret; +} diff --git a/tests/parser.c b/tests/parser.c index a7e7598..29859da 100644 --- a/tests/parser.c +++ b/tests/parser.c @@ -12,7 +12,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software + * along with this program. If not, see . * */ diff --git a/tests/test-lib.c b/tests/test-lib.c new file mode 100644 index 0000000..5927516 --- /dev/null +++ b/tests/test-lib.c @@ -0,0 +1,365 @@ +#include +#include +#include +#include +#include +#include +#include "debug.h" +#include "util.h" +#include "vector.h" +#include "structs.h" +#include "structs_vec.h" +#include "config.h" +#include "discovery.h" +#include "propsel.h" +#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_wwid[] = "TEST-WWID"; +/* default_wwid should be a substring of default_wwid_1! */ +const char default_wwid_1[] = "TEST-WWID-1"; + +/* + * Helper wrappers for mock_path(). + * + * We need to make pathinfo() think it has detected a device with + * certain vendor/product/rev. This requires faking lots of udev + * and sysfs function responses. + * + * This requires hwtable-test_OBJDEPS = ../libmultipath/discovery.o + * in the Makefile in order to wrap calls from discovery.o. + * + * Note that functions that are called and defined in discovery.o can't + * be wrapped this way (e.g. sysfs_get_vendor), because symbols are + * resolved by the assembler before the linking stage. + */ + +int __real_open(const char *path, int flags, int mode); + +static const char _mocked_filename[] = "mocked_path"; +int __wrap_open(const char *path, int flags, int mode) +{ + condlog(4, "%s: %s", __func__, path); + + if (!strcmp(path, _mocked_filename)) + return 111; + return __real_open(path, flags, mode); +} + +int __wrap_execute_program(char *path, char *value, int len) +{ + char *val = mock_ptr_type(char *); + + condlog(5, "%s: %s", __func__, val); + strlcpy(value, val, len); + return 0; +} + +bool __wrap_is_claimed_by_foreign(struct udev_device *ud) +{ + condlog(5, "%s: %p", __func__, ud); + return false; +} + +struct udev_list_entry +*__wrap_udev_device_get_properties_list_entry(struct udev_device *ud) +{ + void *p = (void*)0x12345678; + condlog(5, "%s: %p", __func__, p); + + return p; +} + +struct udev_list_entry +*__wrap_udev_list_entry_get_next(struct udev_list_entry *udle) +{ + void *p = NULL; + condlog(5, "%s: %p", __func__, p); + + return p; +} + +const char *__wrap_udev_list_entry_get_name(struct udev_list_entry *udle) +{ + char *val = mock_ptr_type(char *); + + condlog(5, "%s: %s", __func__, val); + return val; +} + +struct udev_device *__wrap_udev_device_ref(struct udev_device *ud) +{ + return ud; +} + +struct udev_device *__wrap_udev_device_unref(struct udev_device *ud) +{ + return ud; +} + +char *__wrap_udev_device_get_subsystem(struct udev_device *ud) +{ + char *val = mock_ptr_type(char *); + + condlog(5, "%s: %s", __func__, val); + return val; +} + +char *__wrap_udev_device_get_sysname(struct udev_device *ud) +{ + char *val = mock_ptr_type(char *); + + condlog(5, "%s: %s", __func__, val); + return val; +} + +char *__wrap_udev_device_get_devnode(struct udev_device *ud) +{ + char *val = mock_ptr_type(char *); + + condlog(5, "%s: %s", __func__, val); + return val; +} + +dev_t __wrap_udev_device_get_devnum(struct udev_device *ud) +{ + condlog(5, "%s: %p", __func__, ud); + return makedev(17, 17); +} + +char *__wrap_udev_device_get_sysattr_value(struct udev_device *ud, + const char *attr) +{ + char *val = mock_ptr_type(char *); + + condlog(5, "%s: %s->%s", __func__, attr, val); + return val; +} + +char *__wrap_udev_device_get_property_value(struct udev_device *ud, + const char *attr) +{ + char *val = mock_ptr_type(char *); + + condlog(5, "%s: %s->%s", __func__, attr, val); + return val; +} + +int __wrap_sysfs_get_size(struct path *pp, unsigned long long *sz) +{ + *sz = 12345678UL; + return 0; +} + +void *__wrap_udev_device_get_parent_with_subsystem_devtype( + struct udev_device *ud, const char *subsys, char *type) +{ + /* return non-NULL for sysfs_get_tgt_nodename */ + return type; +} + +void *__wrap_udev_device_get_parent(struct udev_device *ud) +{ + char *val = mock_ptr_type(void *); + + condlog(5, "%s: %p", __func__, val); + return val; +} + +ssize_t __wrap_sysfs_attr_get_value(struct udev_device *dev, + const char *attr_name, + char *value, size_t sz) +{ + char *val = mock_ptr_type(char *); + + condlog(5, "%s: %s", __func__, val); + strlcpy(value, val, sz); + return strlen(value); +} + +int __wrap_checker_check(struct checker *c, int st) +{ + condlog(5, "%s: %d", __func__, st); + return st; +} + +int __wrap_prio_getprio(struct prio *p, struct path *pp, unsigned int tmo) +{ + int pr = 5; + + condlog(5, "%s: %d", __func__, pr); + return pr; +} + +struct mocked_path *fill_mocked_path(struct mocked_path *mp, + const char *vendor, const char *product, + const char *rev, const char *wwid, + const char *devnode, unsigned int flags) +{ + mp->vendor = (vendor ? vendor : "noname"); + mp->product = (product ? product : "noprod"); + mp->rev = (rev ? rev : "0"); + mp->wwid = (wwid ? wwid : default_wwid); + mp->devnode = (devnode ? devnode : default_devnode); + mp->flags = flags|NEED_SELECT_PRIO|NEED_FD; + return mp; +} + +struct mocked_path *mocked_path_from_path(struct mocked_path *mp, + const struct path *pp) +{ + mp->vendor = pp->vendor_id; + mp->product = pp->product_id; + mp->rev = pp->rev; + mp->wwid = pp->wwid; + mp->devnode = pp->dev; + mp->flags = (prio_selected(&pp->prio) ? 0 : NEED_SELECT_PRIO) | + (pp->fd < 0 ? NEED_FD : 0) | + (pp->getuid ? USE_GETUID : 0); + return mp; +} + +static void mock_sysfs_pathinfo(const struct mocked_path *mp) +{ + static const char hbtl[] = "4:0:3:1"; + + will_return(__wrap_udev_device_get_subsystem, "scsi"); + will_return(__wrap_udev_device_get_sysname, hbtl); + will_return(__wrap_udev_device_get_sysname, hbtl); + will_return(__wrap_udev_device_get_sysattr_value, mp->vendor); + will_return(__wrap_udev_device_get_sysname, hbtl); + will_return(__wrap_udev_device_get_sysattr_value, mp->product); + will_return(__wrap_udev_device_get_sysname, hbtl); + will_return(__wrap_udev_device_get_sysattr_value, mp->rev); + + /* sysfs_get_tgt_nodename */ + will_return(__wrap_udev_device_get_sysattr_value, NULL); + will_return(__wrap_udev_device_get_parent, NULL); + will_return(__wrap_udev_device_get_parent, NULL); + will_return(__wrap_udev_device_get_sysname, "nofibre"); + will_return(__wrap_udev_device_get_sysname, "noiscsi"); + will_return(__wrap_udev_device_get_parent, NULL); + will_return(__wrap_udev_device_get_sysname, "ata25"); +} + +/* + * Pretend we detected a SCSI device with given vendor/prod/rev + */ +void mock_pathinfo(int mask, const struct mocked_path *mp) +{ + if (mp->flags & DEV_HIDDEN) { + will_return(__wrap_udev_device_get_sysattr_value, "1"); + return; + } else + will_return(__wrap_udev_device_get_sysattr_value, "0"); + + /* filter_property */ + will_return(__wrap_udev_device_get_sysname, mp->devnode); + if (mp->flags & BL_BY_PROPERTY) { + will_return(__wrap_udev_list_entry_get_name, "BAZ"); + return; + } else + will_return(__wrap_udev_list_entry_get_name, + "SCSI_IDENT_LUN_NAA_EXT"); + if (mask & DI_SYSFS) + mock_sysfs_pathinfo(mp); + + if (mp->flags & BL_BY_DEVICE && + (mask & DI_BLACKLIST && mask & DI_SYSFS)) + return; + + /* path_offline */ + will_return(__wrap_udev_device_get_subsystem, "scsi"); + will_return(__wrap_sysfs_attr_get_value, "running"); + + if (mask & DI_NOIO) + return; + + /* fake open() in pathinfo() */ + if (mp->flags & NEED_FD) + will_return(__wrap_udev_device_get_devnode, _mocked_filename); + /* DI_SERIAL is unsupported */ + assert_false(mask & DI_SERIAL); + + if (mask & DI_WWID) { + if (mp->flags & USE_GETUID) + will_return(__wrap_execute_program, mp->wwid); + else + /* get_udev_uid() */ + will_return(__wrap_udev_device_get_property_value, + mp->wwid); + } + + if (mask & DI_CHECKER) { + /* get_state -> sysfs_get_timeout */ + will_return(__wrap_udev_device_get_subsystem, "scsi"); + will_return(__wrap_udev_device_get_sysattr_value, "180"); + } + + if (mask & DI_PRIO && mp->flags & NEED_SELECT_PRIO) { + + /* sysfs_get_timeout, again (!?) */ + will_return(__wrap_udev_device_get_subsystem, "scsi"); + will_return(__wrap_udev_device_get_sysattr_value, "180"); + + } +} + +void mock_store_pathinfo(int mask, const struct mocked_path *mp) +{ + will_return(__wrap_udev_device_get_sysname, mp->devnode); + mock_pathinfo(mask, mp); +} + +struct path *__mock_path(vector pathvec, + const char *vnd, const char *prd, + const char *rev, const char *wwid, + const char *dev, + unsigned int flags, int mask) +{ + struct mocked_path mop; + struct path *pp; + struct config *conf; + int r; + + fill_mocked_path(&mop, vnd, prd, rev, wwid, dev, flags); + mock_store_pathinfo(mask, &mop); + + conf = get_multipath_config(); + r = store_pathinfo(pathvec, conf, (void *)&mop, mask, &pp); + put_multipath_config(conf); + + if (flags & BL_MASK) { + assert_int_equal(r, PATHINFO_SKIPPED); + return NULL; + } + assert_int_equal(r, PATHINFO_OK); + assert_non_null(pp); + return pp; +} + + +struct multipath *__mock_multipath(struct vectors *vecs, struct path *pp) +{ + struct multipath *mp; + struct config *conf; + struct mocked_path mop; + + mocked_path_from_path(&mop, pp); + /* pathinfo() call in adopt_paths */ + mock_pathinfo(DI_CHECKER|DI_PRIO, &mop); + + mp = add_map_with_path(vecs, pp, 1); + assert_ptr_not_equal(mp, NULL); + + /* TBD: mock setup_map() ... */ + conf = get_multipath_config(); + select_pgpolicy(conf, mp); + select_no_path_retry(conf, mp); + select_retain_hwhandler(conf, mp); + select_minio(conf, mp); + put_multipath_config(conf); + + return mp; +} diff --git a/tests/test-lib.h b/tests/test-lib.h new file mode 100644 index 0000000..7643ab6 --- /dev/null +++ b/tests/test-lib.h @@ -0,0 +1,68 @@ +#ifndef __LIB_H +#define __LIB_H + +extern const int default_mask; +extern const char default_devnode[]; +extern const char default_wwid[]; +extern const char default_wwid_1[]; + +enum { + BL_BY_DEVNODE = (1 << 0), + BL_BY_DEVICE = (1 << 1), + BL_BY_WWID = (1 << 2), + BL_BY_PROPERTY = (1 << 3), + BL_MASK = BL_BY_DEVNODE|BL_BY_DEVICE|BL_BY_WWID|BL_BY_PROPERTY, + NEED_SELECT_PRIO = (1 << 8), + NEED_FD = (1 << 9), + USE_GETUID = (1 << 10), + DEV_HIDDEN = (1 << 11), +}; + +struct mocked_path { + const char *vendor; + const char *product; + const char *rev; + const char *wwid; + const char *devnode; + unsigned int flags; +}; + +struct mocked_path *fill_mocked_path(struct mocked_path *mp, + const char *vendor, + const char *product, + const char *rev, + const char *wwid, + const char *devnode, + unsigned int flags); + +struct mocked_path *mocked_path_from_path(struct mocked_path *mp, + const struct path *pp); + +void mock_pathinfo(int mask, const struct mocked_path *mp); +void mock_store_pathinfo(int mask, const struct mocked_path *mp); +struct path *__mock_path(vector pathvec, + const char *vnd, const char *prd, + const char *rev, const char *wwid, + const char *dev, + unsigned int flags, int mask); + +#define mock_path(v, p) \ + __mock_path(hwt->vecs->pathvec, (v), (p), "0", NULL, NULL, \ + 0, default_mask) +#define mock_path_flags(v, p, f) \ + __mock_path(hwt->vecs->pathvec, (v), (p), "0", NULL, NULL, \ + (f), default_mask) +#define mock_path_blacklisted(v, p) \ + __mock_path(hwt->vecs->pathvec, (v), (p), "0", NULL, NULL, \ + BL_BY_DEVICE, default_mask) +#define mock_path_wwid(v, p, w) \ + __mock_path(hwt->vecs->pathvec, (v), (p), "0", (w), NULL, \ + 0, default_mask) +#define mock_path_wwid_flags(v, p, w, f) \ + __mock_path(hwt->vecs->pathvec, (v), (p), "0", (w), \ + NULL, (f), default_mask) + +struct multipath *__mock_multipath(struct vectors *vecs, struct path *pp); +#define mock_multipath(pp) __mock_multipath(hwt->vecs, (pp)) + +#endif diff --git a/tests/uevent.c b/tests/uevent.c index acfcb14..b0d0bfd 100644 --- a/tests/uevent.c +++ b/tests/uevent.c @@ -12,7 +12,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software + * along with this program. If not, see . * */ diff --git a/tests/unaligned.c b/tests/unaligned.c new file mode 100644 index 0000000..7ece1de --- /dev/null +++ b/tests/unaligned.c @@ -0,0 +1,96 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "unaligned.h" + +#define SIZE 16 +static const char memory[8] = { + 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef +}; + +static const uint64_t intval64 = 0x0123456789abcdef; +static const uint32_t intval32 = 0x01234567; +static const uint16_t intval16 = 0x0123; + +#include "globals.c" + +static int setup(void **state) +{ + return posix_memalign(state, 16, 2 * SIZE); +} + +static int teardown(void **state) +{ + free(*state); + return 0; +} + + +#define make_test(bits, offset) \ + static void test_ ## bits ## _ ## offset(void **state) \ +{ \ + int len = bits/8; \ + uint8_t *c = *state; \ + uint8_t *p = *state + SIZE; \ + uint64_t u; \ + \ + assert_in_range(len, 1, SIZE); \ + assert_in_range(offset + len, 1, SIZE); \ + memset(c, 0, 2 * SIZE); \ + memcpy(c + offset, memory, len); \ + \ + u = get_unaligned_be##bits(c + offset); \ + assert_int_equal(u, intval##bits); \ + put_unaligned_be##bits(u, p + offset); \ + assert_memory_equal(c + offset, p + offset, len); \ +} + +make_test(16, 0); +make_test(16, 1); +make_test(32, 0); +make_test(32, 1); +make_test(32, 2); +make_test(32, 3); +make_test(64, 0); +make_test(64, 1); +make_test(64, 2); +make_test(64, 3); +make_test(64, 4); +make_test(64, 5); +make_test(64, 6); +make_test(64, 7); + +int test_unaligned(void) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_16_0), + cmocka_unit_test(test_16_1), + cmocka_unit_test(test_32_0), + cmocka_unit_test(test_32_1), + cmocka_unit_test(test_32_2), + cmocka_unit_test(test_32_3), + cmocka_unit_test(test_64_0), + cmocka_unit_test(test_64_1), + cmocka_unit_test(test_64_2), + cmocka_unit_test(test_64_3), + cmocka_unit_test(test_64_4), + cmocka_unit_test(test_64_5), + cmocka_unit_test(test_64_6), + cmocka_unit_test(test_64_7), + }; + return cmocka_run_group_tests(tests, setup, teardown); +} + +int main(void) +{ + int ret = 0; + + ret += test_unaligned(); + return ret; +} diff --git a/tests/util.c b/tests/util.c index 113b134..839effd 100644 --- a/tests/util.c +++ b/tests/util.c @@ -12,7 +12,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software + * along with this program. If not, see . * */ diff --git a/third-party/valgrind/valgrind.h b/third-party/valgrind/valgrind.h index 5aed0df..577c8f0 100644 --- a/third-party/valgrind/valgrind.h +++ b/third-party/valgrind/valgrind.h @@ -89,7 +89,7 @@ || (__VALGRIND_MAJOR__ == 3 && __VALGRIND_MINOR__ >= 6)) */ #define __VALGRIND_MAJOR__ 3 -#define __VALGRIND_MINOR__ 13 +#define __VALGRIND_MINOR__ 14 #include @@ -5687,15 +5687,17 @@ typedef "$7", "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", "$24", \ "$25", "$31" -/* These CALL_FN_ macros assume that on mips-linux, sizeof(unsigned - long) == 4. */ +/* These CALL_FN_ macros assume that on mips64-linux, + sizeof(long long) == 8. */ + +#define MIPS64_LONG2REG_CAST(x) ((long long)(long)x) #define CALL_FN_W_v(lval, orig) \ do { \ volatile OrigFn _orig = (orig); \ - volatile unsigned long _argvec[1]; \ - volatile unsigned long _res; \ - _argvec[0] = (unsigned long)_orig.nraddr; \ + volatile unsigned long long _argvec[1]; \ + volatile unsigned long long _res; \ + _argvec[0] = MIPS64_LONG2REG_CAST(_orig.nraddr); \ __asm__ volatile( \ "ld $25, 0(%1)\n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ @@ -5704,16 +5706,16 @@ typedef : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ - lval = (__typeof__(lval)) _res; \ + lval = (__typeof__(lval)) (long)_res; \ } while (0) #define CALL_FN_W_W(lval, orig, arg1) \ do { \ volatile OrigFn _orig = (orig); \ - volatile unsigned long _argvec[2]; \ - volatile unsigned long _res; \ - _argvec[0] = (unsigned long)_orig.nraddr; \ - _argvec[1] = (unsigned long)(arg1); \ + volatile unsigned long long _argvec[2]; \ + volatile unsigned long long _res; \ + _argvec[0] = MIPS64_LONG2REG_CAST(_orig.nraddr); \ + _argvec[1] = MIPS64_LONG2REG_CAST(arg1); \ __asm__ volatile( \ "ld $4, 8(%1)\n\t" /* arg1*/ \ "ld $25, 0(%1)\n\t" /* target->t9 */ \ @@ -5723,17 +5725,17 @@ typedef : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ - lval = (__typeof__(lval)) _res; \ + lval = (__typeof__(lval)) (long)_res; \ } while (0) #define CALL_FN_W_WW(lval, orig, arg1,arg2) \ do { \ volatile OrigFn _orig = (orig); \ - volatile unsigned long _argvec[3]; \ - volatile unsigned long _res; \ - _argvec[0] = (unsigned long)_orig.nraddr; \ - _argvec[1] = (unsigned long)(arg1); \ - _argvec[2] = (unsigned long)(arg2); \ + volatile unsigned long long _argvec[3]; \ + volatile unsigned long long _res; \ + _argvec[0] = _orig.nraddr; \ + _argvec[1] = MIPS64_LONG2REG_CAST(arg1); \ + _argvec[2] = MIPS64_LONG2REG_CAST(arg2); \ __asm__ volatile( \ "ld $4, 8(%1)\n\t" \ "ld $5, 16(%1)\n\t" \ @@ -5744,18 +5746,19 @@ typedef : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ - lval = (__typeof__(lval)) _res; \ + lval = (__typeof__(lval)) (long)_res; \ } while (0) + #define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ do { \ volatile OrigFn _orig = (orig); \ - volatile unsigned long _argvec[4]; \ - volatile unsigned long _res; \ - _argvec[0] = (unsigned long)_orig.nraddr; \ - _argvec[1] = (unsigned long)(arg1); \ - _argvec[2] = (unsigned long)(arg2); \ - _argvec[3] = (unsigned long)(arg3); \ + volatile unsigned long long _argvec[4]; \ + volatile unsigned long long _res; \ + _argvec[0] = _orig.nraddr; \ + _argvec[1] = MIPS64_LONG2REG_CAST(arg1); \ + _argvec[2] = MIPS64_LONG2REG_CAST(arg2); \ + _argvec[3] = MIPS64_LONG2REG_CAST(arg3); \ __asm__ volatile( \ "ld $4, 8(%1)\n\t" \ "ld $5, 16(%1)\n\t" \ @@ -5767,19 +5770,19 @@ typedef : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ - lval = (__typeof__(lval)) _res; \ + lval = (__typeof__(lval)) (long)_res; \ } while (0) #define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ do { \ volatile OrigFn _orig = (orig); \ - volatile unsigned long _argvec[5]; \ - volatile unsigned long _res; \ - _argvec[0] = (unsigned long)_orig.nraddr; \ - _argvec[1] = (unsigned long)(arg1); \ - _argvec[2] = (unsigned long)(arg2); \ - _argvec[3] = (unsigned long)(arg3); \ - _argvec[4] = (unsigned long)(arg4); \ + volatile unsigned long long _argvec[5]; \ + volatile unsigned long long _res; \ + _argvec[0] = MIPS64_LONG2REG_CAST(_orig.nraddr); \ + _argvec[1] = MIPS64_LONG2REG_CAST(arg1); \ + _argvec[2] = MIPS64_LONG2REG_CAST(arg2); \ + _argvec[3] = MIPS64_LONG2REG_CAST(arg3); \ + _argvec[4] = MIPS64_LONG2REG_CAST(arg4); \ __asm__ volatile( \ "ld $4, 8(%1)\n\t" \ "ld $5, 16(%1)\n\t" \ @@ -5792,20 +5795,20 @@ typedef : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ - lval = (__typeof__(lval)) _res; \ + lval = (__typeof__(lval)) (long)_res; \ } while (0) #define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ do { \ volatile OrigFn _orig = (orig); \ - volatile unsigned long _argvec[6]; \ - volatile unsigned long _res; \ - _argvec[0] = (unsigned long)_orig.nraddr; \ - _argvec[1] = (unsigned long)(arg1); \ - _argvec[2] = (unsigned long)(arg2); \ - _argvec[3] = (unsigned long)(arg3); \ - _argvec[4] = (unsigned long)(arg4); \ - _argvec[5] = (unsigned long)(arg5); \ + volatile unsigned long long _argvec[6]; \ + volatile unsigned long long _res; \ + _argvec[0] = MIPS64_LONG2REG_CAST(_orig.nraddr); \ + _argvec[1] = MIPS64_LONG2REG_CAST(arg1); \ + _argvec[2] = MIPS64_LONG2REG_CAST(arg2); \ + _argvec[3] = MIPS64_LONG2REG_CAST(arg3); \ + _argvec[4] = MIPS64_LONG2REG_CAST(arg4); \ + _argvec[5] = MIPS64_LONG2REG_CAST(arg5); \ __asm__ volatile( \ "ld $4, 8(%1)\n\t" \ "ld $5, 16(%1)\n\t" \ @@ -5819,21 +5822,21 @@ typedef : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ - lval = (__typeof__(lval)) _res; \ + lval = (__typeof__(lval)) (long)_res; \ } while (0) #define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ do { \ volatile OrigFn _orig = (orig); \ - volatile unsigned long _argvec[7]; \ - volatile unsigned long _res; \ - _argvec[0] = (unsigned long)_orig.nraddr; \ - _argvec[1] = (unsigned long)(arg1); \ - _argvec[2] = (unsigned long)(arg2); \ - _argvec[3] = (unsigned long)(arg3); \ - _argvec[4] = (unsigned long)(arg4); \ - _argvec[5] = (unsigned long)(arg5); \ - _argvec[6] = (unsigned long)(arg6); \ + volatile unsigned long long _argvec[7]; \ + volatile unsigned long long _res; \ + _argvec[0] = MIPS64_LONG2REG_CAST(_orig.nraddr); \ + _argvec[1] = MIPS64_LONG2REG_CAST(arg1); \ + _argvec[2] = MIPS64_LONG2REG_CAST(arg2); \ + _argvec[3] = MIPS64_LONG2REG_CAST(arg3); \ + _argvec[4] = MIPS64_LONG2REG_CAST(arg4); \ + _argvec[5] = MIPS64_LONG2REG_CAST(arg5); \ + _argvec[6] = MIPS64_LONG2REG_CAST(arg6); \ __asm__ volatile( \ "ld $4, 8(%1)\n\t" \ "ld $5, 16(%1)\n\t" \ @@ -5848,23 +5851,23 @@ typedef : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ - lval = (__typeof__(lval)) _res; \ + lval = (__typeof__(lval)) (long)_res; \ } while (0) #define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7) \ do { \ volatile OrigFn _orig = (orig); \ - volatile unsigned long _argvec[8]; \ - volatile unsigned long _res; \ - _argvec[0] = (unsigned long)_orig.nraddr; \ - _argvec[1] = (unsigned long)(arg1); \ - _argvec[2] = (unsigned long)(arg2); \ - _argvec[3] = (unsigned long)(arg3); \ - _argvec[4] = (unsigned long)(arg4); \ - _argvec[5] = (unsigned long)(arg5); \ - _argvec[6] = (unsigned long)(arg6); \ - _argvec[7] = (unsigned long)(arg7); \ + volatile unsigned long long _argvec[8]; \ + volatile unsigned long long _res; \ + _argvec[0] = MIPS64_LONG2REG_CAST(_orig.nraddr); \ + _argvec[1] = MIPS64_LONG2REG_CAST(arg1); \ + _argvec[2] = MIPS64_LONG2REG_CAST(arg2); \ + _argvec[3] = MIPS64_LONG2REG_CAST(arg3); \ + _argvec[4] = MIPS64_LONG2REG_CAST(arg4); \ + _argvec[5] = MIPS64_LONG2REG_CAST(arg5); \ + _argvec[6] = MIPS64_LONG2REG_CAST(arg6); \ + _argvec[7] = MIPS64_LONG2REG_CAST(arg7); \ __asm__ volatile( \ "ld $4, 8(%1)\n\t" \ "ld $5, 16(%1)\n\t" \ @@ -5880,24 +5883,24 @@ typedef : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ - lval = (__typeof__(lval)) _res; \ + lval = (__typeof__(lval)) (long)_res; \ } while (0) #define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8) \ do { \ volatile OrigFn _orig = (orig); \ - volatile unsigned long _argvec[9]; \ - volatile unsigned long _res; \ - _argvec[0] = (unsigned long)_orig.nraddr; \ - _argvec[1] = (unsigned long)(arg1); \ - _argvec[2] = (unsigned long)(arg2); \ - _argvec[3] = (unsigned long)(arg3); \ - _argvec[4] = (unsigned long)(arg4); \ - _argvec[5] = (unsigned long)(arg5); \ - _argvec[6] = (unsigned long)(arg6); \ - _argvec[7] = (unsigned long)(arg7); \ - _argvec[8] = (unsigned long)(arg8); \ + volatile unsigned long long _argvec[9]; \ + volatile unsigned long long _res; \ + _argvec[0] = MIPS64_LONG2REG_CAST(_orig.nraddr); \ + _argvec[1] = MIPS64_LONG2REG_CAST(arg1); \ + _argvec[2] = MIPS64_LONG2REG_CAST(arg2); \ + _argvec[3] = MIPS64_LONG2REG_CAST(arg3); \ + _argvec[4] = MIPS64_LONG2REG_CAST(arg4); \ + _argvec[5] = MIPS64_LONG2REG_CAST(arg5); \ + _argvec[6] = MIPS64_LONG2REG_CAST(arg6); \ + _argvec[7] = MIPS64_LONG2REG_CAST(arg7); \ + _argvec[8] = MIPS64_LONG2REG_CAST(arg8); \ __asm__ volatile( \ "ld $4, 8(%1)\n\t" \ "ld $5, 16(%1)\n\t" \ @@ -5914,25 +5917,25 @@ typedef : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ - lval = (__typeof__(lval)) _res; \ + lval = (__typeof__(lval)) (long)_res; \ } while (0) #define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9) \ do { \ volatile OrigFn _orig = (orig); \ - volatile unsigned long _argvec[10]; \ - volatile unsigned long _res; \ - _argvec[0] = (unsigned long)_orig.nraddr; \ - _argvec[1] = (unsigned long)(arg1); \ - _argvec[2] = (unsigned long)(arg2); \ - _argvec[3] = (unsigned long)(arg3); \ - _argvec[4] = (unsigned long)(arg4); \ - _argvec[5] = (unsigned long)(arg5); \ - _argvec[6] = (unsigned long)(arg6); \ - _argvec[7] = (unsigned long)(arg7); \ - _argvec[8] = (unsigned long)(arg8); \ - _argvec[9] = (unsigned long)(arg9); \ + volatile unsigned long long _argvec[10]; \ + volatile unsigned long long _res; \ + _argvec[0] = MIPS64_LONG2REG_CAST(_orig.nraddr); \ + _argvec[1] = MIPS64_LONG2REG_CAST(arg1); \ + _argvec[2] = MIPS64_LONG2REG_CAST(arg2); \ + _argvec[3] = MIPS64_LONG2REG_CAST(arg3); \ + _argvec[4] = MIPS64_LONG2REG_CAST(arg4); \ + _argvec[5] = MIPS64_LONG2REG_CAST(arg5); \ + _argvec[6] = MIPS64_LONG2REG_CAST(arg6); \ + _argvec[7] = MIPS64_LONG2REG_CAST(arg7); \ + _argvec[8] = MIPS64_LONG2REG_CAST(arg8); \ + _argvec[9] = MIPS64_LONG2REG_CAST(arg9); \ __asm__ volatile( \ "dsubu $29, $29, 8\n\t" \ "ld $4, 72(%1)\n\t" \ @@ -5953,26 +5956,26 @@ typedef : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ - lval = (__typeof__(lval)) _res; \ + lval = (__typeof__(lval)) (long)_res; \ } while (0) #define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9,arg10) \ do { \ volatile OrigFn _orig = (orig); \ - volatile unsigned long _argvec[11]; \ - volatile unsigned long _res; \ - _argvec[0] = (unsigned long)_orig.nraddr; \ - _argvec[1] = (unsigned long)(arg1); \ - _argvec[2] = (unsigned long)(arg2); \ - _argvec[3] = (unsigned long)(arg3); \ - _argvec[4] = (unsigned long)(arg4); \ - _argvec[5] = (unsigned long)(arg5); \ - _argvec[6] = (unsigned long)(arg6); \ - _argvec[7] = (unsigned long)(arg7); \ - _argvec[8] = (unsigned long)(arg8); \ - _argvec[9] = (unsigned long)(arg9); \ - _argvec[10] = (unsigned long)(arg10); \ + volatile unsigned long long _argvec[11]; \ + volatile unsigned long long _res; \ + _argvec[0] = MIPS64_LONG2REG_CAST(_orig.nraddr); \ + _argvec[1] = MIPS64_LONG2REG_CAST(arg1); \ + _argvec[2] = MIPS64_LONG2REG_CAST(arg2); \ + _argvec[3] = MIPS64_LONG2REG_CAST(arg3); \ + _argvec[4] = MIPS64_LONG2REG_CAST(arg4); \ + _argvec[5] = MIPS64_LONG2REG_CAST(arg5); \ + _argvec[6] = MIPS64_LONG2REG_CAST(arg6); \ + _argvec[7] = MIPS64_LONG2REG_CAST(arg7); \ + _argvec[8] = MIPS64_LONG2REG_CAST(arg8); \ + _argvec[9] = MIPS64_LONG2REG_CAST(arg9); \ + _argvec[10] = MIPS64_LONG2REG_CAST(arg10); \ __asm__ volatile( \ "dsubu $29, $29, 16\n\t" \ "ld $4, 72(%1)\n\t" \ @@ -5995,7 +5998,7 @@ typedef : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ - lval = (__typeof__(lval)) _res; \ + lval = (__typeof__(lval)) (long)_res; \ } while (0) #define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ @@ -6003,20 +6006,20 @@ typedef arg11) \ do { \ volatile OrigFn _orig = (orig); \ - volatile unsigned long _argvec[12]; \ - volatile unsigned long _res; \ - _argvec[0] = (unsigned long)_orig.nraddr; \ - _argvec[1] = (unsigned long)(arg1); \ - _argvec[2] = (unsigned long)(arg2); \ - _argvec[3] = (unsigned long)(arg3); \ - _argvec[4] = (unsigned long)(arg4); \ - _argvec[5] = (unsigned long)(arg5); \ - _argvec[6] = (unsigned long)(arg6); \ - _argvec[7] = (unsigned long)(arg7); \ - _argvec[8] = (unsigned long)(arg8); \ - _argvec[9] = (unsigned long)(arg9); \ - _argvec[10] = (unsigned long)(arg10); \ - _argvec[11] = (unsigned long)(arg11); \ + volatile unsigned long long _argvec[12]; \ + volatile unsigned long long _res; \ + _argvec[0] = MIPS64_LONG2REG_CAST(_orig.nraddr); \ + _argvec[1] = MIPS64_LONG2REG_CAST(arg1); \ + _argvec[2] = MIPS64_LONG2REG_CAST(arg2); \ + _argvec[3] = MIPS64_LONG2REG_CAST(arg3); \ + _argvec[4] = MIPS64_LONG2REG_CAST(arg4); \ + _argvec[5] = MIPS64_LONG2REG_CAST(arg5); \ + _argvec[6] = MIPS64_LONG2REG_CAST(arg6); \ + _argvec[7] = MIPS64_LONG2REG_CAST(arg7); \ + _argvec[8] = MIPS64_LONG2REG_CAST(arg8); \ + _argvec[9] = MIPS64_LONG2REG_CAST(arg9); \ + _argvec[10] = MIPS64_LONG2REG_CAST(arg10); \ + _argvec[11] = MIPS64_LONG2REG_CAST(arg11); \ __asm__ volatile( \ "dsubu $29, $29, 24\n\t" \ "ld $4, 72(%1)\n\t" \ @@ -6041,7 +6044,7 @@ typedef : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ - lval = (__typeof__(lval)) _res; \ + lval = (__typeof__(lval)) (long)_res; \ } while (0) #define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ @@ -6049,21 +6052,21 @@ typedef arg11,arg12) \ do { \ volatile OrigFn _orig = (orig); \ - volatile unsigned long _argvec[13]; \ - volatile unsigned long _res; \ - _argvec[0] = (unsigned long)_orig.nraddr; \ - _argvec[1] = (unsigned long)(arg1); \ - _argvec[2] = (unsigned long)(arg2); \ - _argvec[3] = (unsigned long)(arg3); \ - _argvec[4] = (unsigned long)(arg4); \ - _argvec[5] = (unsigned long)(arg5); \ - _argvec[6] = (unsigned long)(arg6); \ - _argvec[7] = (unsigned long)(arg7); \ - _argvec[8] = (unsigned long)(arg8); \ - _argvec[9] = (unsigned long)(arg9); \ - _argvec[10] = (unsigned long)(arg10); \ - _argvec[11] = (unsigned long)(arg11); \ - _argvec[12] = (unsigned long)(arg12); \ + volatile unsigned long long _argvec[13]; \ + volatile unsigned long long _res; \ + _argvec[0] = MIPS64_LONG2REG_CAST(_orig.nraddr); \ + _argvec[1] = MIPS64_LONG2REG_CAST(arg1); \ + _argvec[2] = MIPS64_LONG2REG_CAST(arg2); \ + _argvec[3] = MIPS64_LONG2REG_CAST(arg3); \ + _argvec[4] = MIPS64_LONG2REG_CAST(arg4); \ + _argvec[5] = MIPS64_LONG2REG_CAST(arg5); \ + _argvec[6] = MIPS64_LONG2REG_CAST(arg6); \ + _argvec[7] = MIPS64_LONG2REG_CAST(arg7); \ + _argvec[8] = MIPS64_LONG2REG_CAST(arg8); \ + _argvec[9] = MIPS64_LONG2REG_CAST(arg9); \ + _argvec[10] = MIPS64_LONG2REG_CAST(arg10); \ + _argvec[11] = MIPS64_LONG2REG_CAST(arg11); \ + _argvec[12] = MIPS64_LONG2REG_CAST(arg12); \ __asm__ volatile( \ "dsubu $29, $29, 32\n\t" \ "ld $4, 72(%1)\n\t" \ @@ -6090,7 +6093,7 @@ typedef : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ - lval = (__typeof__(lval)) _res; \ + lval = (__typeof__(lval)) (long)_res; \ } while (0) #endif /* PLAT_mips64_linux */ -- 2.34.1