From: DongHun Kwak Date: Fri, 14 Jan 2022 04:50:18 +0000 (+0900) Subject: Imported Upstream version 0.7.5 X-Git-Tag: upstream/0.7.5^0 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=6d184dbf1cc4ac1ec827700214286fffced5d05f;p=platform%2Fupstream%2Fmultipath-tools.git Imported Upstream version 0.7.5 --- diff --git a/.gitignore b/.gitignore index 57cf7e6..35c59a7 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ *.so.0 *.a *.gz +*.d kpartx/kpartx multipath/multipath multipathd/multipathd @@ -18,3 +19,6 @@ libdmmp/docs/man/*.3.gz libdmmp/*.so.* libdmmp/test/libdmmp_test libdmmp/test/libdmmp_speed_test +tests/*-test +tests/*.out + diff --git a/Makefile b/Makefile index bfb168f..4b145c5 100644 --- a/Makefile +++ b/Makefile @@ -7,6 +7,7 @@ BUILDDIRS = \ libmultipath \ libmultipath/prioritizers \ libmultipath/checkers \ + libmultipath/foreign \ libmpathpersist \ multipath \ multipathd \ @@ -27,6 +28,7 @@ recurse_clean: @for dir in $(BUILDDIRS); do \ $(MAKE) -C $$dir clean || exit $?; \ done + $(MAKE) -C tests clean recurse_install: @for dir in $(BUILDDIRS); do \ @@ -44,6 +46,9 @@ install: recurse_install uninstall: recurse_uninstall +test: all + $(MAKE) -C tests + .PHONY: TAGS TAGS: etags -a libmultipath/*.c diff --git a/Makefile.inc b/Makefile.inc index 29c290a..a5b9d4e 100644 --- a/Makefile.inc +++ b/Makefile.inc @@ -87,10 +87,12 @@ STACKPROT := $(call TEST_CC_OPTION,-fstack-protector-strong,-fstack-protector) 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 \ -Wp,-D_FORTIFY_SOURCE=2 $(STACKPROT) \ --param=ssp-buffer-size=4 -CFLAGS = $(OPTFLAGS) -DLIB_STRING=\"${LIB}\" -DRUN_DIR=\"${RUN}\" +CFLAGS := $(OPTFLAGS) -DBIN_DIR=\"$(bindir)\" -DLIB_STRING=\"${LIB}\" -DRUN_DIR=\"${RUN}\" \ + -MMD -MP $(CFLAGS) BIN_CFLAGS = -fPIE -DPIE LIB_CFLAGS = -fPIC SHARED_FLAGS = -shared diff --git a/README.alua b/README.alua new file mode 100644 index 0000000..e39debd --- /dev/null +++ b/README.alua @@ -0,0 +1,17 @@ +This is a rough guide, consult your storage device manufacturer documentation. + +ALUA is supported in some devices, but usually it's disabled by default. +To enable ALUA, the following options should be changed: + +- EMC CLARiiON/VNX: + "Failover Mode" should be changed to "4". + +- HPE 3PAR: + "Host:" should be changed to "Generic-ALUA Persona 2 (UARepLun, SESLun, ALUA)". + +- Promise VTrak/Vess: + "LUN Affinity" and "ALUA" should be changed to "Enable", "Redundancy Type" + must be "Active-Active". + +- 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)". diff --git a/kpartx/Makefile b/kpartx/Makefile index bf7362d..7eb467e 100644 --- a/kpartx/Makefile +++ b/kpartx/Makefile @@ -44,5 +44,10 @@ uninstall: $(RM) $(DESTDIR)$(libudevdir)/rules.d/67-kpartx-compat.rules $(RM) $(DESTDIR)$(libudevdir)/rules.d/68-del-part-nodes.rules -clean: +clean: dep_clean $(RM) core *.o $(EXEC) *.gz + +include $(wildcard $(OBJS:.o=.d)) + +dep_clean: + $(RM) $(OBJS:.o=.d) diff --git a/kpartx/del-part-nodes.rules b/kpartx/del-part-nodes.rules index cee945d..17bc505 100644 --- a/kpartx/del-part-nodes.rules +++ b/kpartx/del-part-nodes.rules @@ -12,6 +12,7 @@ SUBSYSTEM!="block", GOTO="end_del_part_nodes" KERNEL!="sd*|dasd*|rbd*", GOTO="end_del_part_nodes" ACTION!="add|change", GOTO="end_del_part_nodes" +ENV{DEVTYPE}=="partition", GOTO="end_del_part_nodes" IMPORT{cmdline}="dont_del_part_nodes" ENV{dont_del_part_nodes}=="1", GOTO="end_del_part_nodes" diff --git a/kpartx/devmapper.c b/kpartx/devmapper.c index 4469fa8..eb9dac6 100644 --- a/kpartx/devmapper.c +++ b/kpartx/devmapper.c @@ -11,6 +11,7 @@ #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) @@ -695,7 +696,7 @@ int dm_find_part(const char *parent, const char *delim, int part, } else *part_uuid = uuid; out: - free((void*)tmp); + FREE_CONST(tmp); return r; } diff --git a/kpartx/dm-parts.rules b/kpartx/dm-parts.rules index 235642f..b48b67c 100644 --- a/kpartx/dm-parts.rules +++ b/kpartx/dm-parts.rules @@ -31,8 +31,8 @@ ENV{DM_UDEV_LOW_PRIORITY_FLAG}!="1", OPTIONS+="link_priority=50" IMPORT{program}=="kpartx_id %M %m $env{DM_UUID}" # DM_TYPE only has a reasonable value for partitions on multipath. -ENV{DM_UUID}=="*-mpath-*", ENV{DM_TYPE}=="?*" \ - SYMLINK+="disk/by-id/$env{DM_TYPE}-$env{DM_NAME}" +ENV{DM_UUID}=="*-mpath-*", ENV{DM_TYPE}=="?*", ENV{DM_SERIAL}=="?*" \ + SYMLINK+="disk/by-id/$env{DM_TYPE}-$env{DM_SERIAL}-part$env{DM_PART}" ENV{DM_WWN}=="?*", ENV{DM_PART}=="?*", \ SYMLINK+="disk/by-id/wwn-$env{DM_WWN}-part$env{DM_PART}" diff --git a/kpartx/kpartx_id b/kpartx/kpartx_id index b7f802d..c45db2f 100755 --- a/kpartx/kpartx_id +++ b/kpartx/kpartx_id @@ -42,6 +42,7 @@ fi dmuuid=${UUID#*-} dmtbl=${UUID%%-*} dmpart=${dmtbl#part} +dmserial= # kpartx types are 'part' if [ "$dmpart" = "$dmtbl" ] ; then dmpart= @@ -59,10 +60,12 @@ if [ "$dmtbl" = "part" ] ; then case "$dmuuid" in mpath-*) dmdeps=$($DMSETUP deps -u $dmuuid) + dmserial=${dmuuid#mpath-} ;; esac elif [ "$dmtbl" = "mpath" ] ; then dmname="$dmuuid" + dmserial="$dmuuid" # We need the dependencies of the table to figure out the type dmdeps=$($DMSETUP deps -u $UUID) fi @@ -84,11 +87,14 @@ if [ -n "$dmdeps" ] ; then ;; *) echo "DM_TYPE=scsi" - echo "DM_WWN=0x${dmname#?}" + echo "DM_WWN=0x${dmserial#?}" ;; esac else echo "DM_TYPE=raid" fi +if [[ $dmserial ]]; then + echo "DM_SERIAL=$dmserial" +fi exit 0 diff --git a/kpartx/test-kpartx b/kpartx/test-kpartx index 60b3eb2..09d15a9 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 @@ -142,9 +142,17 @@ push_cleanup 'dmsetup remove -f $SPAN1' dmsetup create $SPAN2 <<<"$TABLE" push_cleanup 'dmsetup remove -f $SPAN2' +# This is a non-kpartx pseudo "partition" mapping +USER1=user1 +push_cleanup 'dmsetup remove -f $USER1' +dmsetup create $USER1 <\$1"], + [$type_constant2, "\$1"], [$type_func, "\$1"], + [$type_enum_xml, "\$1"], [$type_struct_xml, "\$1"], + [$type_typedef_xml, "\$1"], + [$type_union_xml, "\$1"], [$type_env, "\$1"], - [$type_param, "\$1"] + [$type_param, "\$1"], + [$type_member_xml, "\$1\$2\$3"], + [$type_fallback_xml, "\$1"] ); my $local_lt = "\\\\\\\\lt:"; my $local_gt = "\\\\\\\\gt:"; @@ -242,10 +256,16 @@ my $blankline_html = $local_lt . "p" . $local_gt; # was "

" # html version 5 my @highlights_html5 = ( [$type_constant, "\$1"], + [$type_constant2, "\$1"], [$type_func, "\$1"], + [$type_enum_xml, "\$1"], [$type_struct_xml, "\$1"], + [$type_typedef_xml, "\$1"], + [$type_union_xml, "\$1"], [$type_env, "\$1"], - [$type_param, "\$1]"] + [$type_param, "\$1]"], + [$type_member_xml, "\$1\$2\$3"], + [$type_fallback_xml, "\$1"] ); my $blankline_html5 = $local_lt . "br /" . $local_gt; @@ -253,55 +273,80 @@ my $blankline_html5 = $local_lt . "br /" . $local_gt; my @highlights_xml = ( ["([^=])\\\"([^\\\"<]+)\\\"", "\$1\$2"], [$type_constant, "\$1"], + [$type_constant2, "\$1"], + [$type_enum_xml, "\$1"], [$type_struct_xml, "\$1"], + [$type_typedef_xml, "\$1"], + [$type_union_xml, "\$1"], [$type_param, "\$1"], [$type_func, "\$1"], - [$type_env, "\$1"] + [$type_env, "\$1"], + [$type_member_xml, "\$1\$2\$3"], + [$type_fallback_xml, "\$1"] ); my $blankline_xml = $local_lt . "/para" . $local_gt . $local_lt . "para" . $local_gt . "\n"; # gnome, docbook format my @highlights_gnome = ( [$type_constant, "\$1"], + [$type_constant2, "\$1"], [$type_func, "\$1"], + [$type_enum, "\$1"], [$type_struct, "\$1"], + [$type_typedef, "\$1"], + [$type_union, "\$1"], [$type_env, "\$1"], - [$type_param, "\$1" ] + [$type_param, "\$1" ], + [$type_member, "\$1\$2\$3"], + [$type_fallback, "\$1"] ); my $blankline_gnome = "\n"; # these are pretty rough my @highlights_man = ( [$type_constant, "\$1"], + [$type_constant2, "\$1"], [$type_func, "\\\\fB\$1\\\\fP"], + [$type_enum, "\\\\fI\$1\\\\fP"], [$type_struct, "\\\\fI\$1\\\\fP"], - [$type_param, "\\\\fI\$1\\\\fP"] + [$type_typedef, "\\\\fI\$1\\\\fP"], + [$type_union, "\\\\fI\$1\\\\fP"], + [$type_param, "\\\\fI\$1\\\\fP"], + [$type_member, "\\\\fI\$1\$2\$3\\\\fP"], + [$type_fallback, "\\\\fI\$1\\\\fP"] ); my $blankline_man = ""; # text-mode my @highlights_text = ( [$type_constant, "\$1"], + [$type_constant2, "\$1"], [$type_func, "\$1"], + [$type_enum, "\$1"], [$type_struct, "\$1"], - [$type_param, "\$1"] + [$type_typedef, "\$1"], + [$type_union, "\$1"], + [$type_param, "\$1"], + [$type_member, "\$1\$2\$3"], + [$type_fallback, "\$1"] ); my $blankline_text = ""; # rst-mode my @highlights_rst = ( [$type_constant, "``\$1``"], + [$type_constant2, "``\$1``"], # Note: need to escape () to avoid func matching later - [$type_member_func, "\\:c\\:type\\:`\$1\$2\\\\(\\\\) <\$1>`"], - [$type_member, "\\:c\\:type\\:`\$1\$2 <\$1>`"], + [$type_member_func, "\\:c\\:type\\:`\$1\$2\$3\\\\(\\\\) <\$1>`"], + [$type_member, "\\:c\\:type\\:`\$1\$2\$3 <\$1>`"], [$type_fp_param, "**\$1\\\\(\\\\)**"], [$type_func, "\\:c\\:func\\:`\$1()`"], - [$type_struct_full, "\\:c\\:type\\:`\$1 \$2 <\$2>`"], - [$type_enum_full, "\\:c\\:type\\:`\$1 \$2 <\$2>`"], - [$type_typedef_full, "\\:c\\:type\\:`\$1 \$2 <\$2>`"], - [$type_union_full, "\\:c\\:type\\:`\$1 \$2 <\$2>`"], + [$type_enum, "\\:c\\:type\\:`\$1 <\$2>`"], + [$type_struct, "\\:c\\:type\\:`\$1 <\$2>`"], + [$type_typedef, "\\:c\\:type\\:`\$1 <\$2>`"], + [$type_union, "\\:c\\:type\\:`\$1 <\$2>`"], # in rst this can refer to any type - [$type_struct, "\\:c\\:type\\:`\$1`"], + [$type_fallback, "\\:c\\:type\\:`\$1`"], [$type_param, "**\$1**"] ); my $blankline_rst = "\n"; @@ -309,9 +354,15 @@ my $blankline_rst = "\n"; # list mode my @highlights_list = ( [$type_constant, "\$1"], + [$type_constant2, "\$1"], [$type_func, "\$1"], + [$type_enum, "\$1"], [$type_struct, "\$1"], - [$type_param, "\$1"] + [$type_typedef, "\$1"], + [$type_union, "\$1"], + [$type_param, "\$1"], + [$type_member, "\$1"], + [$type_fallback, "\$1"] ); my $blankline_list = ""; @@ -414,7 +465,7 @@ my $doc_com = '\s*\*\s*'; my $doc_com_body = '\s*\* ?'; my $doc_decl = $doc_com . '(\w+)'; # @params and a strictly limited set of supported section names -my $doc_sect = $doc_com . +my $doc_sect = $doc_com . '\s*(\@[.\w]+|\@\.\.\.|description|context|returns?|notes?|examples?)\s*:(.*)'; my $doc_content = $doc_com_body . '(.*)'; my $doc_block = $doc_com . 'DOC:\s*(.*)?'; @@ -1131,8 +1182,9 @@ sub output_function_xml(%) { foreach $parameter (@{$args{'parameterlist'}}) { my $parameter_name = $parameter; $parameter_name =~ s/\[.*//; + $type = $args{'parametertypes'}{$parameter}; - print " \n $parameter\n"; + print " \n $type $parameter\n"; print " \n \n"; $lineprefix=" "; output_highlight($args{'parameterdescs'}{$parameter_name}); @@ -1223,8 +1275,9 @@ sub output_struct_xml(%) { defined($args{'parameterdescs'}{$parameter_name}) || next; ($args{'parameterdescs'}{$parameter_name} ne $undescribed) || next; + $type = $args{'parametertypes'}{$parameter}; print " "; - print " $parameter\n"; + print " $type $parameter\n"; print " \n"; output_highlight($args{'parameterdescs'}{$parameter_name}); print " \n"; @@ -1883,7 +1936,7 @@ sub output_function_rst(%) { $lineprefix = " "; foreach $parameter (@{$args{'parameterlist'}}) { my $parameter_name = $parameter; - #$parameter_name =~ s/\[.*//; + $parameter_name =~ s/\[.*//; $type = $args{'parametertypes'}{$parameter}; if ($type ne "") { @@ -2115,7 +2168,7 @@ sub dump_struct($$) { my $nested; if ($x =~ /(struct|union)\s+(\w+)\s*{(.*)}/) { - #my $decl_type = $1; + my $decl_type = $1; $declaration_name = $2; my $members = $3; @@ -2129,17 +2182,17 @@ sub dump_struct($$) { # strip comments: $members =~ s/\/\*.*?\*\///gos; $nested =~ s/\/\*.*?\*\///gos; - # strip kmemcheck_bitfield_{begin,end}.*; - $members =~ s/kmemcheck_bitfield_.*?;//gos; # strip attributes $members =~ s/__attribute__\s*\(\([a-z,_\*\s\(\)]*\)\)//i; $members =~ s/__aligned\s*\([^;]*\)//gos; $members =~ s/\s*CRYPTO_MINALIGN_ATTR//gos; # replace DECLARE_BITMAP $members =~ s/DECLARE_BITMAP\s*\(([^,)]+), ([^,)]+)\)/unsigned long $1\[BITS_TO_LONGS($2)\]/gos; + # replace DECLARE_HASHTABLE + $members =~ s/DECLARE_HASHTABLE\s*\(([^,)]+), ([^,)]+)\)/unsigned long $1\[1 << (($2) - 1)\]/gos; create_parameterlist($members, ';', $file); - check_sections($file, $declaration_name, "struct", $sectcheck, $struct_actual, $nested); + check_sections($file, $declaration_name, $decl_type, $sectcheck, $struct_actual, $nested); output_declaration($declaration_name, 'struct', @@ -2171,6 +2224,9 @@ sub dump_enum($$) { if ($x =~ /enum\s+(\w+)\s*{(.*)}/) { $declaration_name = $1; my $members = $2; + my %_members; + + $members =~ s/\s+$//; foreach my $arg (split ',', $members) { $arg =~ s/^\s*(\w+).*/$1/; @@ -2180,9 +2236,16 @@ sub dump_enum($$) { print STDERR "${file}:$.: warning: Enum value '$arg' ". "not described in enum '$declaration_name'\n"; } - + $_members{$arg} = 1; } + while (my ($k, $v) = each %parameterdescs) { + if (!exists($_members{$k})) { + print STDERR "${file}:$.: warning: Excess enum value " . + "'$k' description in '$declaration_name'\n"; + } + } + output_declaration($declaration_name, 'enum', {'enum' => $declaration_name, @@ -2350,8 +2413,7 @@ sub push_parameter($$$) { } $anon_struct_union = 0; - my $param_name = $param; - $param_name =~ s/\[.*//; + $param =~ s/[\[\)].*//; if ($type eq "" && $param =~ /\.\.\.$/) { @@ -2382,9 +2444,9 @@ sub push_parameter($$$) { # but inline preprocessor statements); # also ignore unnamed structs/unions; if (!$anon_struct_union) { - if (!defined $parameterdescs{$param_name} && $param_name !~ /^#/) { + if (!defined $parameterdescs{$param} && $param !~ /^#/) { - $parameterdescs{$param_name} = $undescribed; + $parameterdescs{$param} = $undescribed; if (($type eq 'function') || ($type eq 'enum')) { print STDERR "${file}:$.: warning: Function parameter ". @@ -2409,6 +2471,7 @@ sub push_parameter($$$) { # "[blah" in a parameter string; ###$param =~ s/\s*//g; push @parameterlist, $param; + $type =~ s/\s\s+/ /g; $parametertypes{$param} = $type; } @@ -2450,7 +2513,7 @@ sub check_sections($$$$$$) { } else { if ($nested !~ m/\Q$sects[$sx]\E/) { print STDERR "${file}:$.: warning: " . - "Excess struct/union/enum/typedef member " . + "Excess $decl_type member " . "'$sects[$sx]' " . "description in '$decl_name'\n"; ++$warnings; @@ -2505,7 +2568,13 @@ sub dump_function($$) { $prototype =~ s/__must_check +//; $prototype =~ s/__weak +//; my $define = $prototype =~ s/^#\s*define\s+//; #ak added - $prototype =~ s/__attribute__\s*\(\([a-z,]*\)\)//; + $prototype =~ s/__attribute__\s*\(\( + (?: + [\w\s]++ # attribute name + (?:\([^)]*+\))? # attribute arguments + \s*+,? # optional comma at the end + )+ + \)\)\s+//x; # Yes, this truly is vile. We are looking for: # 1. Return type (may be nothing if we're looking at a macro) @@ -2533,21 +2602,21 @@ sub dump_function($$) { $noret = 1; } elsif ($prototype =~ m/^()([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ || $prototype =~ m/^(\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ || - $prototype =~ m/^(\w+\s*\*)\s*([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ || + $prototype =~ m/^(\w+\s*\*+)\s*([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ || $prototype =~ m/^(\w+\s+\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ || $prototype =~ m/^(\w+\s+\w+\s*\*+)\s*([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ || $prototype =~ m/^(\w+\s+\w+\s+\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ || - $prototype =~ m/^(\w+\s+\w+\s+\w+\s*\*)\s*([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ || + $prototype =~ m/^(\w+\s+\w+\s+\w+\s*\*+)\s*([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ || $prototype =~ m/^()([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ || $prototype =~ m/^(\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ || - $prototype =~ m/^(\w+\s*\*)\s*([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ || + $prototype =~ m/^(\w+\s*\*+)\s*([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ || $prototype =~ m/^(\w+\s+\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ || - $prototype =~ m/^(\w+\s+\w+\s*\*)\s*([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ || + $prototype =~ m/^(\w+\s+\w+\s*\*+)\s*([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ || $prototype =~ m/^(\w+\s+\w+\s+\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ || - $prototype =~ m/^(\w+\s+\w+\s+\w+\s*\*)\s*([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ || + $prototype =~ m/^(\w+\s+\w+\s+\w+\s*\*+)\s*([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ || $prototype =~ m/^(\w+\s+\w+\s+\w+\s+\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ || - $prototype =~ m/^(\w+\s+\w+\s+\w+\s+\w+\s*\*)\s*([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ || - $prototype =~ m/^(\w+\s+\w+\s*\*\s*\w+\s*\*\s*)\s*([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/) { + $prototype =~ m/^(\w+\s+\w+\s+\w+\s+\w+\s*\*+)\s*([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ || + $prototype =~ m/^(\w+\s+\w+\s*\*+\s*\w+\s*\*+\s*)\s*([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/) { $return_type = $1; $declaration_name = $2; my $args = $3; @@ -2705,6 +2774,9 @@ sub process_proto_type($$) { while (1) { if ( $x =~ /([^{};]*)([{};])(.*)/ ) { + if( length $prototype ) { + $prototype .= " " + } $prototype .= $1 . $2; ($2 eq '{') && $brcount++; ($2 eq '}') && $brcount--; diff --git a/libdmmp/libdmmp.c b/libdmmp/libdmmp.c index b4e7f08..944cecd 100644 --- a/libdmmp/libdmmp.c +++ b/libdmmp/libdmmp.c @@ -48,6 +48,7 @@ #define _ERRNO_STR_BUFF_SIZE 256 #define _IPC_MAX_CMD_LEN 512 /* ^ Was _MAX_CMD_LEN in ./libmultipath/uxsock.h */ +#define _LAST_ERR_MSG_BUFF_SIZE 1024 struct dmmp_context { void (*log_func)(struct dmmp_context *ctx, int priority, @@ -56,6 +57,7 @@ struct dmmp_context { int log_priority; void *userdata; unsigned int tmo; + char last_err_msg[_LAST_ERR_MSG_BUFF_SIZE]; }; /* @@ -81,6 +83,9 @@ _dmmp_getter_func_gen(dmmp_context_userdata_get, struct dmmp_context, ctx, _dmmp_getter_func_gen(dmmp_context_timeout_get, struct dmmp_context, ctx, tmo, unsigned int); +_dmmp_getter_func_gen(dmmp_last_error_msg, struct dmmp_context, ctx, + last_err_msg, const char *); + _dmmp_array_free_func_gen(dmmp_mpath_array_free, struct dmmp_mpath, _dmmp_mpath_free); @@ -89,8 +94,14 @@ void _dmmp_log(struct dmmp_context *ctx, int priority, const char *file, { va_list args; + if (ctx->log_func == NULL) + return; + va_start(args, format); ctx->log_func(ctx, priority, file, line, func_name, format, args); + if (priority == DMMP_LOG_PRIORITY_ERROR) + vsnprintf(ctx->last_err_msg, _LAST_ERR_MSG_BUFF_SIZE, + format, args); va_end(args); } @@ -107,6 +118,7 @@ struct dmmp_context *dmmp_context_new(void) ctx->log_priority = DMMP_LOG_PRIORITY_DEFAULT; ctx->userdata = NULL; ctx->tmo = _DEFAULT_UXSOCK_TIMEOUT; + memset(ctx->last_err_msg, 0, _LAST_ERR_MSG_BUFF_SIZE); return ctx; } @@ -352,6 +364,14 @@ invoke: } } + if ((*output != NULL) && + strncmp(*output, "permission deny", + strlen("permission deny")) == 0) { + _error(ctx, "Permission deny, need to be root"); + rc = DMMP_ERR_PERMISSION_DENY; + goto out; + } + out: if (rc != DMMP_OK) { free(*output); diff --git a/libdmmp/libdmmp/libdmmp.h b/libdmmp/libdmmp/libdmmp.h index 72b79b9..e157982 100644 --- a/libdmmp/libdmmp/libdmmp.h +++ b/libdmmp/libdmmp/libdmmp.h @@ -42,6 +42,7 @@ extern "C" { #define DMMP_ERR_MPATH_BUSY 7 #define DMMP_ERR_MPATH_NOT_FOUND 8 #define DMMP_ERR_INVALID_ARGUMENT 9 +#define DMMP_ERR_PERMISSION_DENY 10 /* * Use the syslog severity level as log priority @@ -258,7 +259,8 @@ DMMP_DLL_EXPORT int dmmp_context_log_priority_get(struct dmmp_context *ctx); * Pointer of 'struct dmmp_context'. * If this pointer is NULL, your program will be terminated by assert. * @log_func: - * Pointer of log handler function. + * Pointer of log handler function. If set to NULL, all log will be + * ignored. * * Return: * void @@ -678,6 +680,8 @@ DMMP_DLL_EXPORT const char *dmmp_path_status_str(uint32_t path_status); * * * DMMP_ERR_INVALID_ARGUMENT * + * * DMMP_ERR_PERMISSION_DENY + * * Error number could be converted to string by dmmp_strerror(). */ DMMP_DLL_EXPORT int dmmp_flush_mpath(struct dmmp_context *ctx, @@ -703,10 +707,27 @@ DMMP_DLL_EXPORT int dmmp_flush_mpath(struct dmmp_context *ctx, * * * DMMP_ERR_NO_DAEMON * + * * DMMP_ERR_PERMISSION_DENY + * * Error number could be converted to string by dmmp_strerror(). */ DMMP_DLL_EXPORT int dmmp_reconfig(struct dmmp_context *ctx); +/** + * dmmp_last_error_msg() - Retrieves the last error message. + * + * Retrieves the last error message. + * + * @ctx: + * Pointer of 'struct dmmp_context'. + * If this pointer is NULL, your program will be terminated by assert. + * + * Return: + * const char *. No need to free this memory, the resources will get + * freed when dmmp_context_free(). + */ +DMMP_DLL_EXPORT const char *dmmp_last_error_msg(struct dmmp_context *ctx); + #ifdef __cplusplus } /* End of extern "C" */ #endif diff --git a/libdmmp/libdmmp_misc.c b/libdmmp/libdmmp_misc.c index 435ddfa..69b5a20 100644 --- a/libdmmp/libdmmp_misc.c +++ b/libdmmp/libdmmp_misc.c @@ -49,6 +49,7 @@ static const struct _num_str_conv _DMMP_RC_MSG_CONV[] = { {DMMP_ERR_MPATH_BUSY, "Specified multipath device map is in use"}, {DMMP_ERR_MPATH_NOT_FOUND, "Specified multipath not found"}, {DMMP_ERR_INVALID_ARGUMENT, "Invalid argument"}, + {DMMP_ERR_PERMISSION_DENY, "Permission deny"}, }; _dmmp_str_func_gen(dmmp_strerror, int, rc, _DMMP_RC_MSG_CONV); diff --git a/libdmmp/test/Makefile b/libdmmp/test/Makefile index acfb3bf..20b3945 100644 --- a/libdmmp/test/Makefile +++ b/libdmmp/test/Makefile @@ -26,5 +26,12 @@ speed_test: $(SPD_TEST_EXEC) sudo env LD_LIBRARY_PATH=$(_libdmmpdir):$(_mpathcmddir) \ time -p ./$(SPD_TEST_EXEC) -clean: +clean: dep_clean rm -f $(TEST_EXEC) $(SPD_TEST_EXEC) + +OBJS = $(TEST_EXEC).o $(SPD_TEST_EXEC).o +include $(wildcard $(OBJS:.o=.d)) + + +dep_clean: + $(RM) $(OBJS:.o=.d) diff --git a/libdmmp/test/libdmmp_test.c b/libdmmp/test/libdmmp_test.c index 56bd03e..d944e1e 100644 --- a/libdmmp/test/libdmmp_test.c +++ b/libdmmp/test/libdmmp_test.c @@ -126,7 +126,8 @@ int main(int argc, char *argv[]) "timeout to %u\n", TMO); if (dmmp_mpath_array_get(ctx, &dmmp_mps, &dmmp_mp_count) != 0) - FAIL(rc, out, "dmmp_mpath_array_get(): rc != 0\n"); + FAIL(rc, out, "dmmp_mpath_array_get() failed: %s\n", + dmmp_last_error_msg(ctx)); if (dmmp_mp_count == 0) FAIL(rc, out, "dmmp_mpath_array_get(): " "Got no multipath devices\n"); @@ -154,17 +155,20 @@ int main(int argc, char *argv[]) dmmp_mpath_array_free(dmmp_mps, dmmp_mp_count); if (dmmp_flush_mpath(ctx, old_name) != DMMP_OK) - FAIL(rc, out, "dmmp_flush_mpath(): Failed\n"); + FAIL(rc, out, "dmmp_flush_mpath(): failed %s\n", + dmmp_last_error_msg(ctx)); PASS("dmmp_flush_mpath(): OK\n"); if (dmmp_reconfig(ctx) != DMMP_OK) - FAIL(rc, out, "dmmp_reconfig(): Failed\n"); + FAIL(rc, out, "dmmp_reconfig() failed: %s\n", + dmmp_last_error_msg(ctx)); PASS("dmmp_reconfig(): OK\n"); if (dmmp_mpath_array_get(ctx, &dmmp_mps, &dmmp_mp_count) != 0) - FAIL(rc, out, "dmmp_mpath_array_get(): rc != 0\n"); + FAIL(rc, out, "dmmp_mpath_array_get() failed: %s\n", + dmmp_last_error_msg(ctx)); if (dmmp_mp_count == 0) FAIL(rc, out, "dmmp_mpath_array_get(): " "Got no multipath devices\n"); diff --git a/libmpathcmd/Makefile b/libmpathcmd/Makefile index 4f32101..53c0899 100644 --- a/libmpathcmd/Makefile +++ b/libmpathcmd/Makefile @@ -26,5 +26,11 @@ uninstall: $(RM) $(DESTDIR)$(syslibdir)/$(DEVLIB) $(RM) $(DESTDIR)$(includedir)/mpath_cmd.h -clean: - $(RM) core *.a *.o *.so *.so.* *.gz +clean: dep_clean + $(RM) core *.a *.o *.so *.so.* *.gz + +include $(wildcard $(OBJS:.o=.d)) + + +dep_clean: + $(RM) $(OBJS:.o=.d) diff --git a/libmpathcmd/mpath_cmd.c b/libmpathcmd/mpath_cmd.c index af618cf..29d148c 100644 --- a/libmpathcmd/mpath_cmd.c +++ b/libmpathcmd/mpath_cmd.c @@ -64,7 +64,7 @@ static size_t write_all(int fd, const void *buf, size_t len) } if (!n) return total; - buf = n + (char *)buf; + buf = n + (const char *)buf; len -= n; total += n; } diff --git a/libmpathpersist/Makefile b/libmpathpersist/Makefile index 1b4ec16..21fdad8 100644 --- a/libmpathpersist/Makefile +++ b/libmpathpersist/Makefile @@ -37,5 +37,11 @@ uninstall: $(RM) $(DESTDIR)$(includedir)/mpath_persist.h $(RM) $(DESTDIR)$(syslibdir)/$(DEVLIB) -clean: +clean: dep_clean $(RM) core *.a *.o *.so *.so.* *.gz + +include $(wildcard $(OBJS:.o=.d)) + + +dep_clean: + $(RM) $(OBJS:.o=.d) diff --git a/libmultipath/Makefile b/libmultipath/Makefile index 6447d8d..806aaa2 100644 --- a/libmultipath/Makefile +++ b/libmultipath/Makefile @@ -43,7 +43,7 @@ OBJS = memory.o parser.o vector.o devmapper.o callout.o \ switchgroup.o uxsock.o print.o alias.o log_pthread.o \ log.o configure.o structs_vec.o sysfs.o prio.o checkers.o \ lock.o waiter.o file.o wwids.o prioritizers/alua_rtpg.o prkey.o \ - io_err_stat.o + io_err_stat.o dm-generic.o generic.o foreign.o all: $(LIBS) @@ -61,5 +61,10 @@ uninstall: $(RM) $(DESTDIR)$(syslibdir)/$(LIBS) $(RM) $(DESTDIR)$(syslibdir)/$(DEVLIB) -clean: +clean: dep_clean $(RM) core *.a *.o *.so *.so.* *.gz + +include $(wildcard $(OBJS:.o=.d)) + +dep_clean: + $(RM) $(OBJS:.o=.d) diff --git a/libmultipath/checkers/Makefile b/libmultipath/checkers/Makefile index bce6b8b..9559038 100644 --- a/libmultipath/checkers/Makefile +++ b/libmultipath/checkers/Makefile @@ -36,5 +36,11 @@ install: uninstall: for file in $(LIBS); do $(RM) $(DESTDIR)$(libdir)/$$file; done -clean: +clean: dep_clean $(RM) core *.a *.o *.gz *.so + +OBJS := $(LIBS:libcheck%.so=%.o) libsg.o directio.o +include $(wildcard $(OBJS:.o=.d)) + +dep_clean: + $(RM) $(OBJS:.o=.d) diff --git a/libmultipath/checkers/rbd.c b/libmultipath/checkers/rbd.c index 2c18009..b1d99b4 100644 --- a/libmultipath/checkers/rbd.c +++ b/libmultipath/checkers/rbd.c @@ -288,7 +288,7 @@ void libcheck_free(struct checker * c) static int rbd_is_blacklisted(struct rbd_checker_context *ct, char *msg) { char *addr_tok, *start, *save; - char *cmd[2]; + const char *cmd[2]; char *blklist, *stat; size_t blklist_len, stat_len; int ret; @@ -436,7 +436,7 @@ static int sysfs_write_rbd_remove(const char *buf, int buf_len) static int rbd_rm_blacklist(struct rbd_checker_context *ct) { - char *cmd[2]; + const char *cmd[2]; char *stat, *cmd_str; size_t stat_len; int ret; diff --git a/libmultipath/checkers/tur.c b/libmultipath/checkers/tur.c index b4a5cb2..9155960 100644 --- a/libmultipath/checkers/tur.c +++ b/libmultipath/checkers/tur.c @@ -15,6 +15,7 @@ #include #include #include +#include #include "checkers.h" @@ -44,7 +45,6 @@ struct tur_checker_context { pthread_t thread; pthread_mutex_t lock; pthread_cond_t active; - pthread_spinlock_t hldr_lock; int holders; char message[CHECKER_MSG_LEN]; }; @@ -74,13 +74,12 @@ int libcheck_init (struct checker * c) ct->state = PATH_UNCHECKED; ct->fd = -1; - ct->holders = 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_spin_init(&ct->hldr_lock, PTHREAD_PROCESS_PRIVATE); c->context = ct; return 0; @@ -90,7 +89,6 @@ static void cleanup_context(struct tur_checker_context *ct) { pthread_mutex_destroy(&ct->lock); pthread_cond_destroy(&ct->active); - pthread_spin_destroy(&ct->hldr_lock); free(ct); } @@ -99,16 +97,14 @@ void libcheck_free (struct checker * c) if (c->context) { struct tur_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 + int running; + + running = uatomic_xchg(&ct->running, 0); + if (running) + pthread_cancel(ct->thread); + ct->thread = 0; + holders = uatomic_sub_return(&ct->holders, 1); + if (!holders) cleanup_context(ct); c->context = NULL; } @@ -220,26 +216,12 @@ static void cleanup_func(void *data) { int holders; struct tur_checker_context *ct = data; - pthread_spin_lock(&ct->hldr_lock); - ct->holders--; - holders = ct->holders; - ct->thread = 0; - pthread_spin_unlock(&ct->hldr_lock); + + holders = uatomic_sub_return(&ct->holders, 1); if (!holders) cleanup_context(ct); } -static int tur_running(struct tur_checker_context *ct) -{ - pthread_t thread; - - pthread_spin_lock(&ct->hldr_lock); - thread = ct->thread; - pthread_spin_unlock(&ct->hldr_lock); - - return thread != 0; -} - static void copy_msg_to_tcc(void *ct_p, const char *msg) { struct tur_checker_context *ct = ct_p; @@ -252,7 +234,7 @@ static void copy_msg_to_tcc(void *ct_p, const char *msg) static void *tur_thread(void *ctx) { struct tur_checker_context *ct = ctx; - int state; + int state, running; char devt[32]; condlog(3, "%s: tur checker starting up", @@ -278,6 +260,11 @@ static void *tur_thread(void *ctx) condlog(3, "%s: tur checker finished, state %s", tur_devt(devt, sizeof(devt), ct), checker_state_name(state)); + + running = uatomic_xchg(&ct->running, 0); + if (!running) + pause(); + tur_thread_cleanup_pop(ct); return ((void *)0); @@ -325,7 +312,6 @@ int libcheck_check(struct checker * c) int tur_status, r; char devt[32]; - if (!ct) return PATH_UNCHECKED; @@ -349,38 +335,29 @@ int libcheck_check(struct checker * c) return PATH_WILD; } - if (ct->running) { - /* - * Check if TUR checker is still running. Hold hldr_lock - * around the pthread_cancel() call to avoid that - * pthread_cancel() gets called after the (detached) TUR - * thread has exited. - */ - pthread_spin_lock(&ct->hldr_lock); - if (ct->thread) { - if (tur_check_async_timeout(c)) { - condlog(3, "%s: tur checker timeout", - tur_devt(devt, sizeof(devt), ct)); + if (ct->thread) { + if (tur_check_async_timeout(c)) { + int running = uatomic_xchg(&ct->running, 0); + if (running) pthread_cancel(ct->thread); - ct->running = 0; - MSG(c, MSG_TUR_TIMEOUT); - tur_status = PATH_TIMEOUT; - } else { - condlog(3, "%s: tur checker not finished", + condlog(3, "%s: tur checker timeout", + tur_devt(devt, sizeof(devt), ct)); + 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)); - ct->running++; - tur_status = PATH_PENDING; - } + tur_status = PATH_PENDING; } else { /* TUR checker done */ - ct->running = 0; + ct->thread = 0; tur_status = ct->state; strlcpy(c->message, ct->message, sizeof(c->message)); } - pthread_spin_unlock(&ct->hldr_lock); pthread_mutex_unlock(&ct->lock); } else { - if (tur_running(ct)) { + 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", @@ -391,19 +368,17 @@ int libcheck_check(struct checker * c) ct->state = PATH_UNCHECKED; ct->fd = c->fd; ct->timeout = c->timeout; - pthread_spin_lock(&ct->hldr_lock); - ct->holders++; - pthread_spin_unlock(&ct->hldr_lock); + uatomic_add(&ct->holders, 1); + uatomic_set(&ct->running, 1); tur_set_async_timeout(c); setup_thread_attr(&attr, 32 * 1024, 1); r = pthread_create(&ct->thread, &attr, tur_thread, ct); pthread_attr_destroy(&attr); if (r) { - pthread_spin_lock(&ct->hldr_lock); - ct->holders--; - pthread_spin_unlock(&ct->hldr_lock); - pthread_mutex_unlock(&ct->lock); + 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, @@ -414,12 +389,16 @@ int libcheck_check(struct checker * c) tur_status = ct->state; strlcpy(c->message, ct->message, sizeof(c->message)); pthread_mutex_unlock(&ct->lock); - if (tur_running(ct) && + 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)); - ct->running = 1; tur_status = PATH_PENDING; + } else { + int running = uatomic_xchg(&ct->running, 0); + if (running) + pthread_cancel(ct->thread); + ct->thread = 0; } } diff --git a/libmultipath/config.c b/libmultipath/config.c index eb03f0a..085a3e1 100644 --- a/libmultipath/config.c +++ b/libmultipath/config.c @@ -319,7 +319,7 @@ set_param_str(char * str) static int merge_hwe (struct hwentry * dst, struct hwentry * src) { - char id[SCSI_VENDOR_SIZE+SCSI_PRODUCT_SIZE]; + char id[SCSI_VENDOR_SIZE+PATH_PRODUCT_SIZE]; merge_str(vendor); merge_str(product); merge_str(revision); @@ -351,6 +351,7 @@ merge_hwe (struct hwentry * dst, struct hwentry * src) merge_num(delay_wait_checks); merge_num(skip_kpartx); merge_num(max_sectors_kb); + merge_num(ghost_delay); snprintf(id, sizeof(id), "%s/%s", dst->vendor, dst->product); reconcile_features_with_options(id, &dst->features, @@ -419,6 +420,7 @@ store_hwe (vector hwtable, struct hwentry * dhwe) hwe->retain_hwhandler = dhwe->retain_hwhandler; hwe->detect_prio = dhwe->detect_prio; hwe->detect_checker = dhwe->detect_checker; + hwe->ghost_delay = dhwe->ghost_delay; if (dhwe->bl_product && !(hwe->bl_product = set_param_str(dhwe->bl_product))) goto out; @@ -619,6 +621,7 @@ load_config (char * file) conf->uev_wait_timeout = DEFAULT_UEV_WAIT_TIMEOUT; conf->disable_changed_wwids = DEFAULT_DISABLE_CHANGED_WWIDS; conf->remove_retries = 0; + conf->ghost_delay = DEFAULT_GHOST_DELAY; /* * preload default hwtable diff --git a/libmultipath/config.h b/libmultipath/config.h index 51fe27b..a20ac2a 100644 --- a/libmultipath/config.h +++ b/libmultipath/config.h @@ -81,6 +81,7 @@ struct hwentry { int marginal_path_double_failed_time; int skip_kpartx; int max_sectors_kb; + int ghost_delay; char * bl_product; }; @@ -114,6 +115,7 @@ struct mpentry { int marginal_path_double_failed_time; int skip_kpartx; int max_sectors_kb; + int ghost_delay; uid_t uid; gid_t gid; mode_t mode; @@ -173,6 +175,7 @@ struct config { int disable_changed_wwids; int remove_retries; int max_sectors_kb; + int ghost_delay; unsigned int version[3]; char * multipath_dir; diff --git a/libmultipath/configure.c b/libmultipath/configure.c index 09821e8..fa6e21c 100644 --- a/libmultipath/configure.c +++ b/libmultipath/configure.c @@ -30,6 +30,7 @@ #include "discovery.h" #include "debug.h" #include "switchgroup.h" +#include "dm-generic.h" #include "print.h" #include "configure.h" #include "pgpolicies.h" @@ -40,6 +41,7 @@ #include "uxsock.h" #include "wwids.h" #include "sysfs.h" +#include "io_err_stat.h" /* group paths in pg by host adapter */ @@ -254,7 +256,8 @@ int rr_optimize_path_order(struct pathgroup *pgp) return 0; } -int setup_map(struct multipath *mpp, char *params, int params_size) +int setup_map(struct multipath *mpp, char *params, int params_size, + struct vectors *vecs) { struct pathgroup * pgp; struct config *conf; @@ -272,18 +275,26 @@ int setup_map(struct multipath *mpp, char *params, int params_size) * free features, selector, and hwhandler properties if they are being reused */ free_multipath_attributes(mpp); + if (mpp->disable_queueing && VECTOR_SIZE(mpp->paths) != 0) + mpp->disable_queueing = 0; /* * properties selectors + * + * Ordering matters for some properties: + * - features after no_path_retry and retain_hwhandler + * - hwhandler after retain_hwhandler + * No guarantee that this list is complete, check code in + * propsel.c if in doubt. */ conf = get_multipath_config(); select_pgfailback(conf, mpp); select_pgpolicy(conf, mpp); select_selector(conf, mpp); - select_hwhandler(conf, mpp); select_no_path_retry(conf, mpp); select_retain_hwhandler(conf, mpp); select_features(conf, mpp); + select_hwhandler(conf, mpp); select_rr_weight(conf, mpp); select_minio(conf, mpp); select_mode(conf, mpp); @@ -301,9 +312,17 @@ int setup_map(struct multipath *mpp, char *params, int params_size) select_marginal_path_double_failed_time(conf, mpp); select_skip_kpartx(conf, mpp); select_max_sectors_kb(conf, mpp); + select_ghost_delay(conf, mpp); + select_flush_on_last_del(conf, mpp); sysfs_set_scsi_tmo(mpp, conf->checkint); put_multipath_config(conf); + + if (mpp->marginal_path_double_failed_time > 0 && + mpp->marginal_path_err_sample_time > 0 && + mpp->marginal_path_err_recheck_gap_time > 0 && + mpp->marginal_path_err_rate_threshold >= 0) + start_io_err_stat_thread(vecs); /* * assign paths to path groups -- start with no groups and all paths * in mpp->paths @@ -556,13 +575,15 @@ select_action (struct multipath * mpp, vector curmp, int force_reload) } if (mpp->no_path_retry != NO_PATH_RETRY_UNDEF && - mpp->no_path_retry != cmpp->no_path_retry) { + !!strstr(mpp->features, "queue_if_no_path") != + !!strstr(cmpp->features, "queue_if_no_path")) { mpp->action = ACT_RELOAD; condlog(3, "%s: set ACT_RELOAD (no_path_retry change)", mpp->alias); return; } - if (mpp->retain_hwhandler != RETAIN_HWHANDLER_ON && + if ((mpp->retain_hwhandler != RETAIN_HWHANDLER_ON || + strcmp(cmpp->hwhandler, "0") == 0) && (strlen(cmpp->hwhandler) != strlen(mpp->hwhandler) || strncmp(cmpp->hwhandler, mpp->hwhandler, strlen(mpp->hwhandler)))) { @@ -573,7 +594,8 @@ select_action (struct multipath * mpp, vector curmp, int force_reload) } if (mpp->retain_hwhandler != RETAIN_HWHANDLER_UNDEF && - mpp->retain_hwhandler != cmpp->retain_hwhandler && + !!strstr(mpp->features, "retain_attached_hw_handler") != + !!strstr(cmpp->features, "retain_attached_hw_handler") && get_linux_version_code() < KERNEL_VERSION(4, 3, 0)) { mpp->action = ACT_RELOAD; condlog(3, "%s: set ACT_RELOAD (retain_hwhandler change)", @@ -761,6 +783,9 @@ int domap(struct multipath *mpp, char *params, int is_daemon) } sysfs_set_max_sectors_kb(mpp, 0); + if (is_daemon && mpp->ghost_delay > 0 && mpp->nr_active && + pathcount(mpp, PATH_GHOST) == mpp->nr_active) + mpp->ghost_delay_tick = mpp->ghost_delay; r = dm_addmap_create(mpp, params); lock_multipath(mpp, 0); @@ -768,11 +793,15 @@ int domap(struct multipath *mpp, char *params, int is_daemon) case ACT_RELOAD: sysfs_set_max_sectors_kb(mpp, 1); + if (mpp->ghost_delay_tick > 0 && pathcount(mpp, PATH_UP)) + mpp->ghost_delay_tick = 0; r = dm_addmap_reload(mpp, params, 0); break; case ACT_RESIZE: sysfs_set_max_sectors_kb(mpp, 1); + if (mpp->ghost_delay_tick > 0 && pathcount(mpp, PATH_UP)) + mpp->ghost_delay_tick = 0; r = dm_addmap_reload(mpp, params, 1); break; @@ -790,6 +819,9 @@ int domap(struct multipath *mpp, char *params, int is_daemon) put_multipath_config(conf); if (r) { sysfs_set_max_sectors_kb(mpp, 1); + if (mpp->ghost_delay_tick > 0 && + pathcount(mpp, PATH_UP)) + mpp->ghost_delay_tick = 0; r = dm_addmap_reload(mpp, params, 0); } break; @@ -995,7 +1027,7 @@ int coalesce_paths (struct vectors * vecs, vector newmp, char * refwwid, verify_paths(mpp, vecs); params[0] = '\0'; - if (setup_map(mpp, params, PARAMS_SIZE)) { + if (setup_map(mpp, params, PARAMS_SIZE, vecs)) { remove_map(mpp, vecs, 0); continue; } @@ -1048,21 +1080,6 @@ int coalesce_paths (struct vectors * vecs, vector newmp, char * refwwid, remove_feature(&mpp->features, "queue_if_no_path"); } - else if (mpp->no_path_retry != NO_PATH_RETRY_UNDEF) { - if (mpp->no_path_retry == NO_PATH_RETRY_FAIL) { - condlog(3, "%s: unset queue_if_no_path feature", - mpp->alias); - if (!dm_queue_if_no_path(mpp->alias, 0)) - remove_feature(&mpp->features, - "queue_if_no_path"); - } else { - condlog(3, "%s: set queue_if_no_path feature", - mpp->alias); - if (!dm_queue_if_no_path(mpp->alias, 1)) - add_feature(&mpp->features, - "queue_if_no_path"); - } - } if (!is_daemon && mpp->action != ACT_NOTHING) { conf = get_multipath_config(); @@ -1339,7 +1356,7 @@ int reload_map(struct vectors *vecs, struct multipath *mpp, int refresh, } } } - if (setup_map(mpp, params, PARAMS_SIZE)) { + if (setup_map(mpp, params, PARAMS_SIZE, vecs)) { condlog(0, "%s: failed to setup map", mpp->alias); return 1; } @@ -1351,12 +1368,6 @@ int reload_map(struct vectors *vecs, struct multipath *mpp, int refresh, "for reload map", mpp->alias, r); return 1; } - if (mpp->no_path_retry != NO_PATH_RETRY_UNDEF) { - if (mpp->no_path_retry == NO_PATH_RETRY_FAIL) - dm_queue_if_no_path(mpp->alias, 0); - else - dm_queue_if_no_path(mpp->alias, 1); - } return 0; } diff --git a/libmultipath/configure.h b/libmultipath/configure.h index 0ffc28e..27a7e6f 100644 --- a/libmultipath/configure.h +++ b/libmultipath/configure.h @@ -28,12 +28,12 @@ enum actions { struct vectors; -int setup_map (struct multipath * mpp, char * params, int params_size ); +int setup_map (struct multipath * mpp, char * params, int params_size, + struct vectors *vecs ); int domap (struct multipath * mpp, char * params, int is_daemon); int reinstate_paths (struct multipath *mpp); int coalesce_paths (struct vectors *vecs, vector curmp, char * refwwid, int force_reload, enum mpath_cmds cmd); int get_refwwid (enum mpath_cmds cmd, char * dev, enum devtypes dev_type, vector pathvec, char **wwid); int reload_map(struct vectors *vecs, struct multipath *mpp, int refresh, int is_daemon); -int sysfs_get_host_adapter_name(struct path *pp, char *adapter_name); struct udev_device *get_udev_device(const char *dev, enum devtypes dev_type); diff --git a/libmultipath/debug.c b/libmultipath/debug.c index f89b264..f95a3e5 100644 --- a/libmultipath/debug.c +++ b/libmultipath/debug.c @@ -37,9 +37,9 @@ void dlog (int sink, int prio, const char * fmt, ...) "%b %d %H:%M:%S", tb); buff[sizeof(buff)-1] = '\0'; - fprintf(stdout, "%s | ", buff); + fprintf(stderr, "%s | ", buff); } - vfprintf(stdout, fmt, ap); + vfprintf(stderr, fmt, ap); } else log_safe(prio + 3, fmt, ap); diff --git a/libmultipath/defaults.h b/libmultipath/defaults.h index 740ccf4..c9e3411 100644 --- a/libmultipath/defaults.h +++ b/libmultipath/defaults.h @@ -40,6 +40,7 @@ #define DEFAULT_SKIP_KPARTX SKIP_KPARTX_OFF #define DEFAULT_DISABLE_CHANGED_WWIDS 0 #define DEFAULT_MAX_SECTORS_KB MAX_SECTORS_KB_UNDEF +#define DEFAULT_GHOST_DELAY GHOST_DELAY_OFF #define DEFAULT_CHECKINT 5 #define MAX_CHECKINT(a) (a << 2) diff --git a/libmultipath/devmapper.c b/libmultipath/devmapper.c index fcac6bc..607aea8 100644 --- a/libmultipath/devmapper.c +++ b/libmultipath/devmapper.c @@ -27,12 +27,10 @@ #include #include +#define FREE_CONST(p) do { free((void*)(unsigned long)p); p = NULL; } while(0) #define MAX_WAIT 5 #define LOOPS_PER_SEC 5 -#define UUID_PREFIX "mpath-" -#define UUID_PREFIX_LEN 6 - static int dm_conf_verbosity; #ifdef LIBDM_API_DEFERRED @@ -378,7 +376,7 @@ static uint16_t build_udev_flags(const struct multipath *mpp, int reload) /* DM_UDEV_DISABLE_LIBRARY_FALLBACK is added in dm_addmap */ return (mpp->skip_kpartx == SKIP_KPARTX_ON ? MPATH_UDEV_NO_KPARTX_FLAG : 0) | - (mpp->nr_active == 0 ? + ((mpp->nr_active == 0 || mpp->ghost_delay_tick > 0)? MPATH_UDEV_NO_PATHS_FLAG : 0) | (reload && !mpp->force_udev_reload ? MPATH_UDEV_RELOAD_FLAG : 0); @@ -586,7 +584,7 @@ is_mpath_part(const char *part_name, const char *map_name) return 0; } -int dm_get_status(char *name, char *outstatus) +int dm_get_status(const char *name, char *outstatus) { int r = 1; struct dm_task *dmt; @@ -810,7 +808,7 @@ int _dm_flush_map (const char * mapname, int need_sync, int deferred_remove, if (need_suspend && !dm_get_map(mapname, &mapsize, params) && strstr(params, "queue_if_no_path")) { - if (!dm_queue_if_no_path((char *)mapname, 0)) + if (!dm_queue_if_no_path(mapname, 0)) queue_if_no_path = 1; else /* Leave queue_if_no_path alone if unset failed */ @@ -853,7 +851,7 @@ int _dm_flush_map (const char * mapname, int need_sync, int deferred_remove, } while (retries-- > 0); if (queue_if_no_path == 1) - dm_queue_if_no_path((char *)mapname, 1); + dm_queue_if_no_path(mapname, 1); return 1; } @@ -941,7 +939,7 @@ out: } int -dm_fail_path(char * mapname, char * path) +dm_fail_path(const char * mapname, char * path) { char message[32]; @@ -952,7 +950,7 @@ dm_fail_path(char * mapname, char * path) } int -dm_reinstate_path(char * mapname, char * path) +dm_reinstate_path(const char * mapname, char * path) { char message[32]; @@ -963,7 +961,7 @@ dm_reinstate_path(char * mapname, char * path) } int -dm_queue_if_no_path(char *mapname, int enable) +dm_queue_if_no_path(const char *mapname, int enable) { char *message; @@ -976,7 +974,7 @@ dm_queue_if_no_path(char *mapname, int enable) } static int -dm_groupmsg (char * msg, char * mapname, int index) +dm_groupmsg (const char * msg, const char * mapname, int index) { char message[32]; @@ -987,19 +985,19 @@ dm_groupmsg (char * msg, char * mapname, int index) } int -dm_switchgroup(char * mapname, int index) +dm_switchgroup(const char * mapname, int index) { return dm_groupmsg("switch", mapname, index); } int -dm_enablegroup(char * mapname, int index) +dm_enablegroup(const char * mapname, int index) { return dm_groupmsg("enable", mapname, index); } int -dm_disablegroup(char * mapname, int index) +dm_disablegroup(const char * mapname, int index) { return dm_groupmsg("disable", mapname, index); } @@ -1083,7 +1081,7 @@ out: } int -dm_geteventnr (char *name) +dm_geteventnr (const char *name) { struct dm_info info; @@ -1142,7 +1140,7 @@ dm_mapname(int major, int minor) map = dm_task_get_name(dmt); if (map && strlen(map)) - response = STRDUP((char *)dm_task_get_name(dmt)); + response = STRDUP((const char *)dm_task_get_name(dmt)); dm_task_destroy(dmt); return response; @@ -1267,7 +1265,7 @@ cancel_remove_partmap (const char *name, void *unused) } static int -dm_get_deferred_remove (char * mapname) +dm_get_deferred_remove (const char * mapname) { struct dm_info info; @@ -1415,10 +1413,10 @@ out: return r; } -void dm_reassign_deps(char *table, char *dep, char *newdep) +void dm_reassign_deps(char *table, const char *dep, const char *newdep) { - char *p, *n; - char *newtable; + char *n; + const char *p, *newtable; newtable = strdup(table); if (!newtable) @@ -1429,7 +1427,7 @@ void dm_reassign_deps(char *table, char *dep, char *newdep) n += strlen(newdep); p += strlen(dep); strcat(n, p); - free(newtable); + FREE_CONST(newtable); } int dm_reassign_table(const char *name, char *old, char *new) diff --git a/libmultipath/devmapper.h b/libmultipath/devmapper.h index 62e14d1..8c8ea6c 100644 --- a/libmultipath/devmapper.h +++ b/libmultipath/devmapper.h @@ -24,6 +24,9 @@ #define MPATH_UDEV_NO_PATHS_FLAG 0 #endif +#define UUID_PREFIX "mpath-" +#define UUID_PREFIX_LEN (sizeof(UUID_PREFIX) - 1) + void dm_init(int verbosity); void libmp_dm_init(void); void libmp_udev_set_sync_support(int on); @@ -35,7 +38,7 @@ int dm_addmap_create (struct multipath *mpp, char *params); int dm_addmap_reload (struct multipath *mpp, char *params, int flush); int dm_map_present (const char *); int dm_get_map(const char *, unsigned long long *, char *); -int dm_get_status(char *, char *); +int dm_get_status(const char *, char *); int dm_type(const char *, char *); int dm_is_mpath(const char *); int _dm_flush_map (const char *, int, int, int, int); @@ -46,14 +49,14 @@ int dm_flush_map_nopaths(const char * mapname, int deferred_remove); _dm_flush_map(mapname, 1, 0, 1, retries) int dm_cancel_deferred_remove(struct multipath *mpp); int dm_flush_maps (int retries); -int dm_fail_path(char * mapname, char * path); -int dm_reinstate_path(char * mapname, char * path); -int dm_queue_if_no_path(char *mapname, int enable); -int dm_switchgroup(char * mapname, int index); -int dm_enablegroup(char * mapname, int index); -int dm_disablegroup(char * mapname, int index); +int dm_fail_path(const char * mapname, char * path); +int dm_reinstate_path(const char * mapname, char * path); +int dm_queue_if_no_path(const char *mapname, int enable); +int dm_switchgroup(const char * mapname, int index); +int dm_enablegroup(const char * mapname, int index); +int dm_disablegroup(const char * mapname, int index); int dm_get_maps (vector mp); -int dm_geteventnr (char *name); +int dm_geteventnr (const char *name); int dm_is_suspended(const char *name); int dm_get_major_minor (const char *name, int *major, int *minor); char * dm_mapname(int major, int minor); diff --git a/libmultipath/dict.c b/libmultipath/dict.c index 3b36e1d..47dc2a3 100644 --- a/libmultipath/dict.c +++ b/libmultipath/dict.c @@ -92,46 +92,35 @@ set_yes_no_undef(vector strvec, void *ptr) } static int -print_int (char *buff, int len, void *ptr) +print_int (char *buff, int len, long v) { - int *int_ptr = (int *)ptr; - return snprintf(buff, len, "%i", *int_ptr); + return snprintf(buff, len, "%li", v); } static int -print_nonzero (char *buff, int len, void *ptr) +print_nonzero (char *buff, int len, long v) { - int *int_ptr = (int *)ptr; - if (!*int_ptr) - return 0; - return snprintf(buff, len, "%i", *int_ptr); + return snprintf(buff, len, "%li", v); } static int -print_str (char *buff, int len, void *ptr) +print_str (char *buff, int len, const char *ptr) { - char **str_ptr = (char **)ptr; - if (!*str_ptr) - return 0; - return snprintf(buff, len, "\"%s\"", *str_ptr); + return snprintf(buff, len, "\"%s\"", ptr); } static int -print_yes_no (char *buff, int len, void *ptr) +print_yes_no (char *buff, int len, long v) { - int *int_ptr = (int *)ptr; return snprintf(buff, len, "\"%s\"", - (*int_ptr == YN_NO)? "no" : "yes"); + (v == YN_NO)? "no" : "yes"); } static int -print_yes_no_undef (char *buff, int len, void *ptr) +print_yes_no_undef (char *buff, int len, long v) { - int *int_ptr = (int *)ptr; - if (!*int_ptr) - return 0; return snprintf(buff, len, "\"%s\"", - (*int_ptr == YNU_NO)? "no" : "yes"); + (v == YNU_NO)? "no" : "yes"); } #define declare_def_handler(option, function) \ @@ -143,29 +132,32 @@ def_ ## option ## _handler (struct config *conf, vector strvec) \ #define declare_def_snprint(option, function) \ static int \ -snprint_def_ ## option (struct config *conf, char * buff, int len, void * data) \ +snprint_def_ ## option (struct config *conf, char * buff, int len, \ + const void * data) \ { \ - return function (buff, len, &conf->option); \ + return function (buff, len, conf->option); \ } #define declare_def_snprint_defint(option, function, value) \ static int \ -snprint_def_ ## option (struct config *conf, char * buff, int len, void * data) \ +snprint_def_ ## option (struct config *conf, char * buff, int len, \ + const void * data) \ { \ int i = value; \ if (!conf->option) \ - return function (buff, len, &i); \ - return function (buff, len, &conf->option); \ + return function (buff, len, i); \ + return function (buff, len, conf->option); \ } #define declare_def_snprint_defstr(option, function, value) \ static int \ -snprint_def_ ## option (struct config *conf, char * buff, int len, void * data) \ +snprint_def_ ## option (struct config *conf, char * buff, int len, \ + const void * data) \ { \ - char *s = value; \ + static const char *s = value; \ if (!conf->option) \ - return function (buff, len, &s); \ - return function (buff, len, &conf->option); \ + return function (buff, len, s); \ + return function (buff, len, conf->option); \ } #define declare_hw_handler(option, function) \ @@ -180,10 +172,11 @@ hw_ ## option ## _handler (struct config *conf, vector strvec) \ #define declare_hw_snprint(option, function) \ static int \ -snprint_hw_ ## option (struct config *conf, char * buff, int len, void * data) \ +snprint_hw_ ## option (struct config *conf, char * buff, int len, \ + const void * data) \ { \ - struct hwentry * hwe = (struct hwentry *)data; \ - return function (buff, len, &hwe->option); \ + const struct hwentry * hwe = (const struct hwentry *)data; \ + return function (buff, len, hwe->option); \ } #define declare_ovr_handler(option, function) \ @@ -197,9 +190,10 @@ ovr_ ## option ## _handler (struct config *conf, vector strvec) \ #define declare_ovr_snprint(option, function) \ static int \ -snprint_ovr_ ## option (struct config *conf, char * buff, int len, void * data) \ +snprint_ovr_ ## option (struct config *conf, char * buff, int len, \ + const void * data) \ { \ - return function (buff, len, &conf->overrides->option); \ + return function (buff, len, conf->overrides->option); \ } #define declare_mp_handler(option, function) \ @@ -214,10 +208,11 @@ mp_ ## option ## _handler (struct config *conf, vector strvec) \ #define declare_mp_snprint(option, function) \ static int \ -snprint_mp_ ## option (struct config *conf, char * buff, int len, void * data) \ +snprint_mp_ ## option (struct config *conf, char * buff, int len, \ + const void * data) \ { \ - struct mpentry * mpe = (struct mpentry *)data; \ - return function (buff, len, &mpe->option); \ + const struct mpentry * mpe = (const struct mpentry *)data; \ + return function (buff, len, mpe->option); \ } declare_def_handler(checkint, set_int) @@ -328,7 +323,7 @@ declare_mp_snprint(minio_rq, print_nonzero) declare_def_handler(queue_without_daemon, set_yes_no) static int snprint_def_queue_without_daemon (struct config *conf, - char * buff, int len, void * data) + char * buff, int len, const void * data) { switch (conf->queue_without_daemon) { case QUE_NO_DAEMON_OFF: @@ -459,10 +454,11 @@ def_ ## option ## _handler (struct config *conf, vector strvec) \ #define declare_def_attr_snprint(option, function) \ static int \ -snprint_def_ ## option (struct config *conf, char * buff, int len, void * data) \ +snprint_def_ ## option (struct config *conf, char * buff, int len, \ + const void * data) \ { \ - return function (buff, len, &conf->option, \ - &conf->attribute_flags); \ + return function (buff, len, conf->option, \ + conf->attribute_flags); \ } #define declare_mp_attr_handler(option, function) \ @@ -477,11 +473,12 @@ mp_ ## option ## _handler (struct config *conf, vector strvec) \ #define declare_mp_attr_snprint(option, function) \ static int \ -snprint_mp_ ## option (struct config *conf, char * buff, int len, void * data) \ +snprint_mp_ ## option (struct config *conf, char * buff, int len, \ + const void * data) \ { \ - struct mpentry * mpe = (struct mpentry *)data; \ - return function (buff, len, &mpe->option, \ - &mpe->attribute_flags); \ + const struct mpentry * mpe = (const struct mpentry *)data; \ + return function (buff, len, mpe->option, \ + mpe->attribute_flags); \ } static int @@ -556,30 +553,30 @@ set_gid(vector strvec, void *ptr, int *flags) } static int -print_mode(char * buff, int len, void *ptr, int *flags) +print_mode(char * buff, int len, long v, int flags) { - mode_t *mode_ptr = (mode_t *)ptr; - if ((*flags & (1 << ATTR_MODE)) == 0) + mode_t mode = (mode_t)v; + if ((flags & (1 << ATTR_MODE)) == 0) return 0; - return snprintf(buff, len, "0%o", *mode_ptr); + return snprintf(buff, len, "0%o", mode); } static int -print_uid(char * buff, int len, void *ptr, int *flags) +print_uid(char * buff, int len, long v, int flags) { - uid_t *uid_ptr = (uid_t *)ptr; - if ((*flags & (1 << ATTR_UID)) == 0) + uid_t uid = (uid_t)v; + if ((flags & (1 << ATTR_UID)) == 0) return 0; - return snprintf(buff, len, "0%o", *uid_ptr); + return snprintf(buff, len, "0%o", uid); } static int -print_gid(char * buff, int len, void *ptr, int *flags) +print_gid(char * buff, int len, long v, int flags) { - gid_t *gid_ptr = (gid_t *)ptr; - if ((*flags & (1 << ATTR_GID)) == 0) + gid_t gid = (gid_t)v; + if ((flags & (1 << ATTR_GID)) == 0) return 0; - return snprintf(buff, len, "0%o", *gid_ptr); + return snprintf(buff, len, "0%o", gid); } declare_def_attr_handler(mode, set_mode) @@ -620,17 +617,15 @@ set_fast_io_fail(vector strvec, void *ptr) } int -print_fast_io_fail(char * buff, int len, void *ptr) +print_fast_io_fail(char * buff, int len, long v) { - int *int_ptr = (int *)ptr; - - if (*int_ptr == MP_FAST_IO_FAIL_UNSET) + if (v == MP_FAST_IO_FAIL_UNSET) return 0; - if (*int_ptr == MP_FAST_IO_FAIL_OFF) + if (v == MP_FAST_IO_FAIL_OFF) return snprintf(buff, len, "\"off\""); - if (*int_ptr == MP_FAST_IO_FAIL_ZERO) + if (v == MP_FAST_IO_FAIL_ZERO) return snprintf(buff, len, "0"); - return snprintf(buff, len, "%d", *int_ptr); + return snprintf(buff, len, "%ld", v); } declare_def_handler(fast_io_fail, set_fast_io_fail) @@ -660,15 +655,11 @@ set_dev_loss(vector strvec, void *ptr) } int -print_dev_loss(char * buff, int len, void *ptr) +print_dev_loss(char * buff, int len, unsigned long v) { - unsigned int *uint_ptr = (unsigned int *)ptr; - - if (!*uint_ptr) - return 0; - if (*uint_ptr >= MAX_DEV_LOSS_TMO) + if (v >= MAX_DEV_LOSS_TMO) return snprintf(buff, len, "\"infinity\""); - return snprintf(buff, len, "%u", *uint_ptr); + return snprintf(buff, len, "%lu", v); } declare_def_handler(dev_loss, set_dev_loss) @@ -695,10 +686,9 @@ set_pgpolicy(vector strvec, void *ptr) } int -print_pgpolicy(char * buff, int len, void *ptr) +print_pgpolicy(char * buff, int len, long pgpolicy) { char str[POLICY_NAME_SIZE]; - int pgpolicy = *(int *)ptr; if (!pgpolicy) return 0; @@ -776,7 +766,7 @@ max_fds_handler(struct config *conf, vector strvec) } static int -snprint_max_fds (struct config *conf, char * buff, int len, void * data) +snprint_max_fds (struct config *conf, char * buff, int len, const void * data) { int r = 0, max_fds; @@ -813,15 +803,13 @@ set_rr_weight(vector strvec, void *ptr) } int -print_rr_weight (char * buff, int len, void *ptr) +print_rr_weight (char * buff, int len, long v) { - int *int_ptr = (int *)ptr; - - if (!*int_ptr) + if (!v) return 0; - if (*int_ptr == RR_WEIGHT_PRIO) + if (v == RR_WEIGHT_PRIO) return snprintf(buff, len, "\"priorities\""); - if (*int_ptr == RR_WEIGHT_NONE) + if (v == RR_WEIGHT_NONE) return snprintf(buff, len, "\"uniform\""); return 0; @@ -861,11 +849,9 @@ set_pgfailback(vector strvec, void *ptr) } int -print_pgfailback (char * buff, int len, void *ptr) +print_pgfailback (char * buff, int len, long v) { - int *int_ptr = (int *)ptr; - - switch(*int_ptr) { + switch(v) { case FAILBACK_UNDEF: return 0; case -FAILBACK_MANUAL: @@ -875,7 +861,7 @@ print_pgfailback (char * buff, int len, void *ptr) case -FAILBACK_FOLLOWOVER: return snprintf(buff, len, "\"followover\""); default: - return snprintf(buff, len, "%i", *int_ptr); + return snprintf(buff, len, "%li", v); } } @@ -910,11 +896,9 @@ set_no_path_retry(vector strvec, void *ptr) } int -print_no_path_retry(char * buff, int len, void *ptr) +print_no_path_retry(char * buff, int len, long v) { - int *int_ptr = (int *)ptr; - - switch(*int_ptr) { + switch(v) { case NO_PATH_RETRY_UNDEF: return 0; case NO_PATH_RETRY_FAIL: @@ -922,7 +906,7 @@ print_no_path_retry(char * buff, int len, void *ptr) case NO_PATH_RETRY_QUEUE: return snprintf(buff, len, "\"queue\""); default: - return snprintf(buff, len, "%i", *int_ptr); + return snprintf(buff, len, "%li", v); } } @@ -955,7 +939,8 @@ def_log_checker_err_handler(struct config *conf, vector strvec) } static int -snprint_def_log_checker_err (struct config *conf, char * buff, int len, void * data) +snprint_def_log_checker_err (struct config *conf, char * buff, int len, + const void * data) { if (conf->log_checker_err == LOG_CHKR_ERR_ONCE) return snprintf(buff, len, "once"); @@ -1008,7 +993,7 @@ def_reservation_key_handler(struct config *conf, vector strvec) static int snprint_def_reservation_key (struct config *conf, char * buff, int len, - void * data) + const void * data) { return print_reservation_key(buff, len, conf->reservation_key, conf->prkey_source); @@ -1026,9 +1011,9 @@ mp_reservation_key_handler(struct config *conf, vector strvec) static int snprint_mp_reservation_key (struct config *conf, char * buff, int len, - void * data) + const void * data) { - struct mpentry * mpe = (struct mpentry *)data; + const struct mpentry * mpe = (const struct mpentry *)data; return print_reservation_key(buff, len, mpe->reservation_key, mpe->prkey_source); } @@ -1053,17 +1038,15 @@ set_off_int_undef(vector strvec, void *ptr) } int -print_off_int_undef(char * buff, int len, void *ptr) +print_off_int_undef(char * buff, int len, long v) { - int *int_ptr = (int *)ptr; - - switch(*int_ptr) { + switch(v) { case NU_UNDEF: return 0; case NU_NO: return snprintf(buff, len, "\"no\""); default: - return snprintf(buff, len, "%i", *int_ptr); + return snprintf(buff, len, "%li", v); } } @@ -1120,6 +1103,14 @@ declare_hw_snprint(marginal_path_double_failed_time, print_off_int_undef) declare_mp_handler(marginal_path_double_failed_time, set_off_int_undef) declare_mp_snprint(marginal_path_double_failed_time, print_off_int_undef) +declare_def_handler(ghost_delay, set_off_int_undef) +declare_def_snprint(ghost_delay, print_off_int_undef) +declare_ovr_handler(ghost_delay, set_off_int_undef) +declare_ovr_snprint(ghost_delay, print_off_int_undef) +declare_hw_handler(ghost_delay, set_off_int_undef) +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) static int @@ -1223,15 +1214,17 @@ declare_ble_handler(blist_property) declare_ble_handler(elist_property) static int -snprint_def_uxsock_timeout(struct config *conf, char * buff, int len, void * data) +snprint_def_uxsock_timeout(struct config *conf, char * buff, int len, + const void * data) { return snprintf(buff, len, "%u", conf->uxsock_timeout); } static int -snprint_ble_simple (struct config *conf, char * buff, int len, void * data) +snprint_ble_simple (struct config *conf, char * buff, int len, + const void * data) { - struct blentry * ble = (struct blentry *)data; + const struct blentry * ble = (const struct blentry *)data; return snprintf(buff, len, "\"%s\"", ble->str); } @@ -1254,17 +1247,21 @@ declare_ble_device_handler(product, blist_device, NULL, buff) declare_ble_device_handler(product, elist_device, NULL, buff) static int -snprint_bled_vendor (struct config *conf, char * buff, int len, void * data) +snprint_bled_vendor (struct config *conf, char * buff, int len, + const void * data) { - struct blentry_device * bled = (struct blentry_device *)data; + const struct blentry_device * bled = + (const struct blentry_device *)data; return snprintf(buff, len, "\"%s\"", bled->vendor); } static int -snprint_bled_product (struct config *conf, char * buff, int len, void * data) +snprint_bled_product (struct config *conf, char * buff, int len, + const void * data) { - struct blentry_device * bled = (struct blentry_device *)data; + const struct blentry_device * bled = + (const struct blentry_device *)data; return snprintf(buff, len, "\"%s\"", bled->product); } @@ -1394,7 +1391,8 @@ deprecated_handler(struct config *conf, vector strvec) } static int -snprint_deprecated (struct config *conf, char * buff, int len, void * data) +snprint_deprecated (struct config *conf, char * buff, int len, + const void * data) { return 0; } @@ -1469,6 +1467,7 @@ init_keywords(vector keywords) install_keyword("disable_changed_wwids", &def_disable_changed_wwids_handler, &snprint_def_disable_changed_wwids); install_keyword("remove_retries", &def_remove_retries_handler, &snprint_def_remove_retries); install_keyword("max_sectors_kb", &def_max_sectors_kb_handler, &snprint_def_max_sectors_kb); + install_keyword("ghost_delay", &def_ghost_delay_handler, &snprint_def_ghost_delay); __deprecated install_keyword("default_selector", &def_selector_handler, NULL); __deprecated install_keyword("default_path_grouping_policy", &def_pgpolicy_handler, NULL); __deprecated install_keyword("default_uid_attribute", &def_uid_attribute_handler, NULL); @@ -1549,6 +1548,7 @@ init_keywords(vector keywords) install_keyword("marginal_path_double_failed_time", &hw_marginal_path_double_failed_time_handler, &snprint_hw_marginal_path_double_failed_time); 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_sublevel_end(); install_keyword_root("overrides", &overrides_handler); @@ -1584,6 +1584,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_root("multipaths", &multipaths_handler); install_keyword_multi("multipath", &multipath_handler, NULL); @@ -1616,5 +1617,6 @@ init_keywords(vector keywords) install_keyword("marginal_path_double_failed_time", &mp_marginal_path_double_failed_time_handler, &snprint_mp_marginal_path_double_failed_time); install_keyword("skip_kpartx", &mp_skip_kpartx_handler, &snprint_mp_skip_kpartx); install_keyword("max_sectors_kb", &mp_max_sectors_kb_handler, &snprint_mp_max_sectors_kb); + install_keyword("ghost_delay", &mp_ghost_delay_handler, &snprint_mp_ghost_delay); install_sublevel_end(); } diff --git a/libmultipath/discovery.c b/libmultipath/discovery.c index cadf461..9efcaac 100644 --- a/libmultipath/discovery.c +++ b/libmultipath/discovery.c @@ -31,10 +31,11 @@ #include "prio.h" #include "defaults.h" #include "prioritizers/alua_rtpg.h" +#include "foreign.h" int alloc_path_with_pathinfo (struct config *conf, struct udev_device *udevice, - char *wwid, int flag, struct path **pp_ptr) + const char *wwid, int flag, struct path **pp_ptr) { int err = PATHINFO_FAILED; struct path * pp; @@ -121,7 +122,7 @@ path_discover (vector pathvec, struct config * conf, if (!devname) return PATHINFO_FAILED; - pp = find_path_by_dev(pathvec, (char *)devname); + pp = find_path_by_dev(pathvec, devname); if (!pp) { char devt[BLK_DEV_SIZE]; dev_t devnum = udev_device_get_devnum(udevice); @@ -276,7 +277,7 @@ sysfs_get_timeout(struct path *pp, unsigned int *timeout) } *timeout = t; - return 0; + return 1; } int @@ -401,7 +402,7 @@ sysfs_get_tgt_nodename (struct path *pp, char * node) return 0; } -int sysfs_get_host_adapter_name(struct path *pp, char *adapter_name) +int sysfs_get_host_adapter_name(const struct path *pp, char *adapter_name) { int proto_id; @@ -427,7 +428,7 @@ int sysfs_get_host_adapter_name(struct path *pp, char *adapter_name) return sysfs_get_host_pci_name(pp, adapter_name); } -int sysfs_get_host_pci_name(struct path *pp, char *pci_name) +int sysfs_get_host_pci_name(const struct path *pp, char *pci_name) { struct udev_device *hostdev, *parent; char host_name[HOST_NAME_LEN]; @@ -466,7 +467,7 @@ int sysfs_get_host_pci_name(struct path *pp, char *pci_name) return 1; } -int sysfs_get_iscsi_ip_address(struct path *pp, char *ip_address) +int sysfs_get_iscsi_ip_address(const struct path *pp, char *ip_address) { struct udev_device *hostdev; char host_name[HOST_NAME_LEN]; @@ -766,7 +767,7 @@ do_inq(int sg_fd, int cmddt, int evpd, unsigned int pg_op, io_hdr.dxferp = resp; io_hdr.cmdp = inqCmdBlk; io_hdr.sbp = sense_b; - io_hdr.timeout = DEF_TIMEOUT; + io_hdr.timeout = DEF_TIMEOUT * 1000; if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) return -1; @@ -837,7 +838,7 @@ detect_alua(struct path * pp, struct config *conf) #define DEFAULT_SGIO_LEN 254 static int -sgio_get_vpd (unsigned char * buff, int maxlen, int fd) +sgio_get_vpd (unsigned char * buff, int maxlen, int fd, int pg) { int len = DEFAULT_SGIO_LEN; @@ -846,13 +847,13 @@ sgio_get_vpd (unsigned char * buff, int maxlen, int fd) return -1; } retry: - if (0 == do_inq(fd, 0, 1, 0x83, buff, len)) { + if (0 == do_inq(fd, 0, 1, pg, buff, len)) { len = buff[3] + (buff[2] << 8); if (len >= maxlen) return len; if (len > DEFAULT_SGIO_LEN) goto retry; - return 0; + return len; } return -1; } @@ -905,12 +906,12 @@ static int parse_vpd_pg83(const unsigned char *in, size_t in_len, char *out, size_t out_len) { - unsigned char *d; - unsigned char *vpd = NULL; + const unsigned char *d; + const unsigned char *vpd = NULL; int len = -ENODATA, vpd_type, vpd_len, prio = -1, i, naa_prio; - d = (unsigned char *)in + 4; - while (d < (unsigned char *)in + in_len) { + d = in + 4; + while (d < in + in_len) { /* Select 'association: LUN' */ if ((d[1] & 0x30) != 0) { d += d[3] + 4; @@ -1027,7 +1028,7 @@ parse_vpd_pg83(const unsigned char *in, size_t in_len, out[len] = '\0'; } } else if (vpd_type == 0x1) { - unsigned char *p; + const unsigned char *p; int p_len; out[0] = '1'; @@ -1099,7 +1100,7 @@ get_vpd_sgio (int fd, int pg, char * str, int maxlen) unsigned char buff[4096]; memset(buff, 0x0, 4096); - if (sgio_get_vpd(buff, 4096, fd) <= 0) { + if (sgio_get_vpd(buff, 4096, fd, pg) <= 0) { condlog(3, "failed to issue vpd inquiry for pg%02x", pg); return -errno; @@ -1118,7 +1119,11 @@ get_vpd_sgio (int fd, int pg, char * str, int maxlen) len = parse_vpd_pg80(buff, str, maxlen); else if (pg == 0x83) len = parse_vpd_pg83(buff, buff_len, str, maxlen); - else + else if (pg == 0xc9 && maxlen >= 8) { + len = buff_len < 8 ? -ENODATA : + (buff_len <= maxlen ? buff_len : maxlen); + memcpy (str, buff, len); + } else len = -ENOSYS; return len; @@ -1154,12 +1159,12 @@ scsi_sysfs_pathinfo (struct path * pp, vector hwtable) condlog(3, "%s: vendor = %s", pp->dev, pp->vendor_id); - if (sysfs_get_model(parent, pp->product_id, SCSI_PRODUCT_SIZE) <= 0) + if (sysfs_get_model(parent, pp->product_id, PATH_PRODUCT_SIZE) <= 0) return 1; condlog(3, "%s: product = %s", pp->dev, pp->product_id); - if (sysfs_get_rev(parent, pp->rev, SCSI_REV_SIZE) < 0) + if (sysfs_get_rev(parent, pp->rev, PATH_REV_SIZE) < 0) return 1; condlog(3, "%s: rev = %s", pp->dev, pp->rev); @@ -1196,6 +1201,7 @@ nvme_sysfs_pathinfo (struct path * pp, vector hwtable) { struct udev_device *parent; const char *attr_path = NULL; + const char *attr; attr_path = udev_device_get_sysname(pp->udev); if (!attr_path) @@ -1205,19 +1211,24 @@ nvme_sysfs_pathinfo (struct path * pp, vector hwtable) &pp->sg_id.host_no, &pp->sg_id.scsi_id) != 2) return 1; - pp->sg_id.channel = 0; - pp->sg_id.lun = 0; - parent = udev_device_get_parent(pp->udev); + parent = udev_device_get_parent_with_subsystem_devtype(pp->udev, + "nvme", NULL); if (!parent) return 1; + attr = udev_device_get_sysattr_value(pp->udev, "nsid"); + pp->sg_id.lun = attr ? atoi(attr) : 0; + + attr = udev_device_get_sysattr_value(parent, "cntlid"); + pp->sg_id.channel = attr ? atoi(attr) : 0; + snprintf(pp->vendor_id, SCSI_VENDOR_SIZE, "NVME"); - snprintf(pp->product_id, SCSI_PRODUCT_SIZE, "%s", + snprintf(pp->product_id, PATH_PRODUCT_SIZE, "%s", udev_device_get_sysattr_value(parent, "model")); snprintf(pp->serial, SERIAL_SIZE, "%s", udev_device_get_sysattr_value(parent, "serial")); - snprintf(pp->rev, SCSI_REV_SIZE, "%s", + snprintf(pp->rev, PATH_REV_SIZE, "%s", udev_device_get_sysattr_value(parent, "firmware_rev")); condlog(3, "%s: vendor = %s", pp->dev, pp->vendor_id); @@ -1332,12 +1343,12 @@ cciss_sysfs_pathinfo (struct path * pp, vector hwtable) condlog(3, "%s: vendor = %s", pp->dev, pp->vendor_id); - if (sysfs_get_model(parent, pp->product_id, SCSI_PRODUCT_SIZE) <= 0) + if (sysfs_get_model(parent, pp->product_id, PATH_PRODUCT_SIZE) <= 0) return 1; condlog(3, "%s: product = %s", pp->dev, pp->product_id); - if (sysfs_get_rev(parent, pp->rev, SCSI_REV_SIZE) <= 0) + if (sysfs_get_rev(parent, pp->rev, PATH_REV_SIZE) <= 0) return 1; condlog(3, "%s: rev = %s", pp->dev, pp->rev); @@ -1899,7 +1910,8 @@ 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 && filter_property(conf, pp->udev) > 0) + if (pp->udev && (is_claimed_by_foreign(pp->udev) || + filter_property(conf, pp->udev) > 0)) return PATHINFO_SKIPPED; if (filter_devnode(conf->blist_devnode, diff --git a/libmultipath/discovery.h b/libmultipath/discovery.h index b151cc7..9aacf75 100644 --- a/libmultipath/discovery.h +++ b/libmultipath/discovery.h @@ -38,14 +38,16 @@ int get_state (struct path * pp, struct config * conf, int daemon, int state); int get_vpd_sgio (int fd, int pg, char * str, int maxlen); int pathinfo (struct path * pp, struct config * conf, int mask); int alloc_path_with_pathinfo (struct config *conf, struct udev_device *udevice, - char *wwid, int flag, struct path **pp_ptr); + const char *wwid, int flag, struct path **pp_ptr); int store_pathinfo (vector pathvec, struct config *conf, struct udev_device *udevice, int flag, struct path **pp_ptr); int sysfs_set_scsi_tmo (struct multipath *mpp, int checkint); int sysfs_get_timeout(struct path *pp, unsigned int *timeout); -int sysfs_get_host_pci_name(struct path *pp, char *pci_name); -int sysfs_get_iscsi_ip_address(struct path *pp, char *ip_address); +int sysfs_get_host_pci_name(const struct path *pp, char *pci_name); +int sysfs_get_iscsi_ip_address(const struct path *pp, char *ip_address); +int sysfs_get_host_adapter_name(const struct path *pp, + char *adapter_name); ssize_t sysfs_get_vpd (struct udev_device * udev, int pg, unsigned char * buff, size_t len); int sysfs_get_asymmetric_access_state(struct path *pp, diff --git a/libmultipath/dm-generic.c b/libmultipath/dm-generic.c new file mode 100644 index 0000000..bdc9ca0 --- /dev/null +++ b/libmultipath/dm-generic.c @@ -0,0 +1,70 @@ +/* + Copyright (c) 2018 Martin Wilck, SUSE Linux GmbH + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. + */ + +#include +#include +#include "generic.h" +#include "dm-generic.h" +#include "structs.h" +#include "structs_vec.h" +#include "config.h" +#include "print.h" + +static const struct _vector* +dm_mp_get_pgs(const struct gen_multipath *gmp) +{ + return vector_convert(NULL, gen_multipath_to_dm(gmp)->pg, + struct pathgroup, dm_pathgroup_to_gen); +} + +static void dm_mp_rel_pgs(const struct gen_multipath *gmp, + const struct _vector* v) +{ + vector_free_const(v); +} + +static const struct _vector* +dm_pg_get_paths(const struct gen_pathgroup *gpg) +{ + return vector_convert(NULL, gen_pathgroup_to_dm(gpg)->paths, + struct path, dm_path_to_gen); +} + +static void dm_mp_rel_paths(const struct gen_pathgroup *gpg, + const struct _vector* v) +{ + vector_free_const(v); +} + +const struct gen_multipath_ops dm_gen_multipath_ops = { + .get_pathgroups = dm_mp_get_pgs, + .rel_pathgroups = dm_mp_rel_pgs, + .snprint = snprint_multipath_attr, + .style = snprint_multipath_style, +}; + +const struct gen_pathgroup_ops dm_gen_pathgroup_ops = { + .get_paths = dm_pg_get_paths, + .rel_paths = dm_mp_rel_paths, + .snprint = snprint_pathgroup_attr, +}; + +const struct gen_path_ops dm_gen_path_ops = { + .snprint = snprint_path_attr, +}; diff --git a/libmultipath/dm-generic.h b/libmultipath/dm-generic.h new file mode 100644 index 0000000..5d59724 --- /dev/null +++ b/libmultipath/dm-generic.h @@ -0,0 +1,41 @@ +/* + Copyright (c) 2018 Martin Wilck, SUSE Linux GmbH + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. + */ +#ifndef _DM_GENERIC_H +#define _DM_GENERIC_H +#include "generic.h" +#include "list.h" /* for container_of */ +#include "structs.h" + +#define dm_multipath_to_gen(mpp) (&((mpp)->generic_mp)) +#define gen_multipath_to_dm(gm) \ + container_of_const((gm), struct multipath, generic_mp) + +#define dm_pathgroup_to_gen(pg) (&(pg->generic_pg)) +#define gen_pathgroup_to_dm(gpg) \ + container_of_const((gpg), struct pathgroup, generic_pg) + +#define dm_path_to_gen(pp) (&((pp)->generic_path)) +#define gen_path_to_dm(gp) \ + container_of_const((gp), struct path, generic_path) + +extern const struct gen_multipath_ops dm_gen_multipath_ops; +extern const struct gen_pathgroup_ops dm_gen_pathgroup_ops; +extern const struct gen_path_ops dm_gen_path_ops; + +#endif /* _DM_GENERIC_H */ diff --git a/libmultipath/dmparser.c b/libmultipath/dmparser.c index b647c25..783c934 100644 --- a/libmultipath/dmparser.c +++ b/libmultipath/dmparser.c @@ -83,23 +83,15 @@ assemble_map (struct multipath * mp, char * params, int len) nr_priority_groups = VECTOR_SIZE(mp->pg); initial_pg_nr = (nr_priority_groups ? mp->bestpg : 0); - f = STRDUP(mp->features); - - /* - * We have to set 'queue_if_no_path' here even - * to avoid path failures during map reload. - */ - if (mp->no_path_retry == NO_PATH_RETRY_FAIL) { - /* remove queue_if_no_path settings */ - condlog(3, "%s: remove queue_if_no_path from '%s'", - mp->alias, mp->features); - remove_feature(&f, no_path_retry); - } else if (mp->no_path_retry != NO_PATH_RETRY_UNDEF) { - add_feature(&f, no_path_retry); + if (mp->no_path_retry != NO_PATH_RETRY_UNDEF && + mp->no_path_retry != NO_PATH_RETRY_FAIL) { + add_feature(&mp->features, no_path_retry); } if (mp->retain_hwhandler == RETAIN_HWHANDLER_ON && get_linux_version_code() < KERNEL_VERSION(4, 3, 0)) - add_feature(&f, retain_hwhandler); + add_feature(&mp->features, retain_hwhandler); + + f = STRDUP(mp->features); APPEND(p, end, "%s %s %i %i", f, mp->hwhandler, nr_priority_groups, initial_pg_nr); @@ -122,7 +114,6 @@ assemble_map (struct multipath * mp, char * params, int len) APPEND(p, end, " %s %d", pp->dev_t, tmp_minio); } } - APPEND(p, end, "\n"); FREE(f); condlog(3, "%s: assembled map [%s]", mp->alias, params); @@ -148,7 +139,6 @@ int disassemble_map(vector pathvec, char *params, struct multipath *mpp, int num_paths = 0; int num_paths_args = 0; int def_minio = 0; - int no_path_retry = NO_PATH_RETRY_UNDEF; struct path * pp; struct pathgroup * pgp; @@ -165,8 +155,6 @@ int disassemble_map(vector pathvec, char *params, struct multipath *mpp, return 1; num_features = atoi(mpp->features); - no_path_retry = mpp->no_path_retry; - mpp->no_path_retry = NO_PATH_RETRY_UNDEF; for (i = 0; i < num_features; i++) { p += get_word(p, &word); @@ -178,22 +166,10 @@ int disassemble_map(vector pathvec, char *params, struct multipath *mpp, FREE(word); return 1; } - setup_feature(mpp, word); FREE(word); } - /* - * Reset no_path_retry. - * - if not set from features - * - if queue_if_no_path is set from features but - * no_path_retry > 0 is selected. - */ - if ((mpp->no_path_retry == NO_PATH_RETRY_UNDEF || - mpp->no_path_retry == NO_PATH_RETRY_QUEUE) && - mpp->no_path_retry != no_path_retry) - mpp->no_path_retry = no_path_retry; - /* * hwhandler */ @@ -291,7 +267,7 @@ int disassemble_map(vector pathvec, char *params, struct multipath *mpp, if (!pgp) goto out; - if (store_pathgroup(mpp->pg, pgp)) + if (add_pathgroup(mpp, pgp)) goto out; p += get_word(p, &word); diff --git a/libmultipath/foreign.c b/libmultipath/foreign.c new file mode 100644 index 0000000..7217184 --- /dev/null +++ b/libmultipath/foreign.c @@ -0,0 +1,602 @@ +/* + Copyright (c) 2018 Martin Wilck, SUSE Linux GmbH + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "vector.h" +#include "debug.h" +#include "util.h" +#include "foreign.h" +#include "structs.h" +#include "structs_vec.h" +#include "print.h" + +static vector foreigns; + +/* This protects vector foreigns */ +static pthread_rwlock_t foreign_lock = PTHREAD_RWLOCK_INITIALIZER; + +static void rdlock_foreigns(void) +{ + pthread_rwlock_rdlock(&foreign_lock); +} + +static void wrlock_foreigns(void) +{ + pthread_rwlock_wrlock(&foreign_lock); +} + +static void unlock_foreigns(void *unused) +{ + pthread_rwlock_unlock(&foreign_lock); +} + +#define get_dlsym(foreign, sym, lbl) \ + do { \ + foreign->sym = dlsym(foreign->handle, #sym); \ + if (foreign->sym == NULL) { \ + condlog(0, "%s: symbol \"%s\" not found in \"%s\"", \ + __func__, #sym, foreign->name); \ + goto lbl; \ + } \ + } while(0) + +static void free_foreign(struct foreign *fgn) +{ + struct context *ctx; + + if (fgn == NULL) + return; + + ctx = fgn->context; + fgn->context = NULL; + if (ctx != NULL) + fgn->cleanup(ctx); + + if (fgn->handle != NULL) + dlclose(fgn->handle); + free(fgn); +} + +void _cleanup_foreign(void) +{ + struct foreign *fgn; + int i; + + if (foreigns == NULL) + return; + + vector_foreach_slot_backwards(foreigns, fgn, i) { + vector_del_slot(foreigns, i); + free_foreign(fgn); + } + vector_free(foreigns); + foreigns = NULL; +} + +void cleanup_foreign(void) +{ + wrlock_foreigns(); + _cleanup_foreign(); + unlock_foreigns(NULL); +} + +static const char foreign_pattern[] = "libforeign-*.so"; + +static int select_foreign_libs(const struct dirent *di) +{ + + return fnmatch(foreign_pattern, di->d_name, FNM_FILE_NAME) == 0; +} + +static int _init_foreign(const char *multipath_dir) +{ + char pathbuf[PATH_MAX]; + struct dirent **di; + int r, i; + + foreigns = vector_alloc(); + if (foreigns == NULL) + return -ENOMEM; + + r = scandir(multipath_dir, &di, select_foreign_libs, alphasort); + + if (r == 0) { + condlog(3, "%s: no foreign multipath libraries found", + __func__); + return 0; + } else if (r < 0) { + r = errno; + condlog(1, "%s: error %d scanning foreign multipath libraries", + __func__, r); + _cleanup_foreign(); + return -r; + } + + pthread_cleanup_push(free, di); + for (i = 0; i < r; i++) { + const char *msg, *fn, *c; + struct foreign *fgn; + int len, namesz; + + fn = di[i]->d_name; + + len = strlen(fn); + c = strchr(fn, '-'); + if (len < sizeof(foreign_pattern) - 1 || c == NULL) { + condlog(0, "%s: bad file name %s, fnmatch error?", + __func__, fn); + continue; + } + c++; + condlog(4, "%s: found %s", __func__, fn); + + namesz = len - sizeof(foreign_pattern) + 3; + fgn = malloc(sizeof(*fgn) + namesz); + if (fgn == NULL) + continue; + memset(fgn, 0, sizeof(*fgn)); + strlcpy((char*)fgn + offsetof(struct foreign, name), c, namesz); + + snprintf(pathbuf, sizeof(pathbuf), "%s/%s", multipath_dir, fn); + fgn->handle = dlopen(pathbuf, RTLD_NOW|RTLD_LOCAL); + msg = dlerror(); + if (fgn->handle == NULL) { + condlog(1, "%s: failed to dlopen %s: %s", __func__, + pathbuf, msg); + goto dl_err; + } + + get_dlsym(fgn, init, dl_err); + get_dlsym(fgn, cleanup, dl_err); + get_dlsym(fgn, add, dl_err); + get_dlsym(fgn, change, dl_err); + get_dlsym(fgn, delete, dl_err); + get_dlsym(fgn, delete_all, dl_err); + get_dlsym(fgn, check, dl_err); + get_dlsym(fgn, lock, dl_err); + get_dlsym(fgn, unlock, dl_err); + get_dlsym(fgn, get_multipaths, dl_err); + get_dlsym(fgn, release_multipaths, dl_err); + get_dlsym(fgn, get_paths, dl_err); + get_dlsym(fgn, release_paths, dl_err); + + fgn->context = fgn->init(LIBMP_FOREIGN_API, fgn->name); + if (fgn->context == NULL) { + condlog(0, "%s: init() failed for %s", __func__, fn); + goto dl_err; + } + + if (vector_alloc_slot(foreigns) == NULL) { + goto dl_err; + } + + vector_set_slot(foreigns, fgn); + condlog(3, "foreign library \"%s\" loaded successfully", + fgn->name); + + continue; + + dl_err: + free_foreign(fgn); + } + pthread_cleanup_pop(1); + return 0; +} + +int init_foreign(const char *multipath_dir) +{ + int ret; + + wrlock_foreigns(); + + if (foreigns != NULL) { + unlock_foreigns(NULL); + condlog(0, "%s: already initialized", __func__); + return -EEXIST; + } + + pthread_cleanup_push(unlock_foreigns, NULL); + ret = _init_foreign(multipath_dir); + pthread_cleanup_pop(1); + + return ret; +} + +int add_foreign(struct udev_device *udev) +{ + struct foreign *fgn; + dev_t dt; + int j; + int r = FOREIGN_IGNORED; + + if (udev == NULL) { + condlog(1, "%s called with NULL udev", __func__); + return FOREIGN_ERR; + } + + rdlock_foreigns(); + if (foreigns == NULL) { + unlock_foreigns(NULL); + return FOREIGN_ERR; + } + pthread_cleanup_push(unlock_foreigns, NULL); + + dt = udev_device_get_devnum(udev); + vector_foreach_slot(foreigns, fgn, j) { + r = fgn->add(fgn->context, udev); + + if (r == FOREIGN_CLAIMED) { + condlog(3, "%s: foreign \"%s\" claims device %d:%d", + __func__, fgn->name, major(dt), minor(dt)); + break; + } else if (r == FOREIGN_OK) { + condlog(4, "%s: foreign \"%s\" owns device %d:%d", + __func__, fgn->name, major(dt), minor(dt)); + break; + } else if (r != FOREIGN_IGNORED) { + condlog(1, "%s: unexpected return value %d from \"%s\"", + __func__, r, fgn->name); + } + } + + pthread_cleanup_pop(1); + return r; +} + +int change_foreign(struct udev_device *udev) +{ + struct foreign *fgn; + int j; + dev_t dt; + int r = FOREIGN_IGNORED; + + if (udev == NULL) { + condlog(1, "%s called with NULL udev", __func__); + return FOREIGN_ERR; + } + + rdlock_foreigns(); + if (foreigns == NULL) { + unlock_foreigns(NULL); + return FOREIGN_ERR; + } + pthread_cleanup_push(unlock_foreigns, NULL); + + dt = udev_device_get_devnum(udev); + vector_foreach_slot(foreigns, fgn, j) { + r = fgn->change(fgn->context, udev); + + if (r == FOREIGN_OK) { + condlog(4, "%s: foreign \"%s\" completed %d:%d", + __func__, fgn->name, major(dt), minor(dt)); + break; + } else if (r != FOREIGN_IGNORED) { + condlog(1, "%s: unexpected return value %d from \"%s\"", + __func__, r, fgn->name); + } + } + + pthread_cleanup_pop(1); + return r; +} + +int delete_foreign(struct udev_device *udev) +{ + struct foreign *fgn; + int j; + dev_t dt; + int r = FOREIGN_IGNORED; + + if (udev == NULL) { + condlog(1, "%s called with NULL udev", __func__); + return FOREIGN_ERR; + } + + rdlock_foreigns(); + if (foreigns == NULL) { + unlock_foreigns(NULL); + return FOREIGN_ERR; + } + pthread_cleanup_push(unlock_foreigns, NULL); + + dt = udev_device_get_devnum(udev); + vector_foreach_slot(foreigns, fgn, j) { + r = fgn->delete(fgn->context, udev); + + if (r == FOREIGN_OK) { + condlog(3, "%s: foreign \"%s\" deleted device %d:%d", + __func__, fgn->name, major(dt), minor(dt)); + break; + } else if (r != FOREIGN_IGNORED) { + condlog(1, "%s: unexpected return value %d from \"%s\"", + __func__, r, fgn->name); + } + } + + pthread_cleanup_pop(1); + return r; +} + +int delete_all_foreign(void) +{ + struct foreign *fgn; + int j; + + rdlock_foreigns(); + if (foreigns == NULL) { + unlock_foreigns(NULL); + return FOREIGN_ERR; + } + pthread_cleanup_push(unlock_foreigns, NULL); + + vector_foreach_slot(foreigns, fgn, j) { + int r; + + r = fgn->delete_all(fgn->context); + if (r != FOREIGN_IGNORED && r != FOREIGN_OK) { + condlog(1, "%s: unexpected return value %d from \"%s\"", + __func__, r, fgn->name); + } + } + + pthread_cleanup_pop(1); + return FOREIGN_OK; +} + +void check_foreign(void) +{ + struct foreign *fgn; + int j; + + rdlock_foreigns(); + if (foreigns == NULL) { + unlock_foreigns(NULL); + return; + } + pthread_cleanup_push(unlock_foreigns, NULL); + + vector_foreach_slot(foreigns, fgn, j) { + fgn->check(fgn->context); + } + + pthread_cleanup_pop(1); +} + +/* Call this after get_path_layout */ +void foreign_path_layout(void) +{ + struct foreign *fgn; + int i; + + rdlock_foreigns(); + if (foreigns == NULL) { + unlock_foreigns(NULL); + return; + } + pthread_cleanup_push(unlock_foreigns, NULL); + + vector_foreach_slot(foreigns, fgn, i) { + const struct _vector *vec; + + fgn->lock(fgn->context); + pthread_cleanup_push(fgn->unlock, fgn->context); + + vec = fgn->get_paths(fgn->context); + if (vec != NULL) { + _get_path_layout(vec, LAYOUT_RESET_NOT); + } + fgn->release_paths(fgn->context, vec); + + pthread_cleanup_pop(1); + } + + pthread_cleanup_pop(1); +} + +/* Call this after get_multipath_layout */ +void foreign_multipath_layout(void) +{ + struct foreign *fgn; + int i; + + rdlock_foreigns(); + if (foreigns == NULL) { + unlock_foreigns(NULL); + return; + } + pthread_cleanup_push(unlock_foreigns, NULL); + + vector_foreach_slot(foreigns, fgn, i) { + const struct _vector *vec; + + fgn->lock(fgn->context); + pthread_cleanup_push(fgn->unlock, fgn->context); + + vec = fgn->get_multipaths(fgn->context); + if (vec != NULL) { + _get_multipath_layout(vec, LAYOUT_RESET_NOT); + } + fgn->release_multipaths(fgn->context, vec); + + pthread_cleanup_pop(1); + } + + pthread_cleanup_pop(1); +} + +int snprint_foreign_topology(char *buf, int len, int verbosity) +{ + struct foreign *fgn; + int i; + char *c = buf; + + rdlock_foreigns(); + if (foreigns == NULL) { + unlock_foreigns(NULL); + return 0; + } + pthread_cleanup_push(unlock_foreigns, NULL); + + vector_foreach_slot(foreigns, fgn, i) { + const struct _vector *vec; + const struct gen_multipath *gm; + int j; + + fgn->lock(fgn->context); + pthread_cleanup_push(fgn->unlock, fgn->context); + + vec = fgn->get_multipaths(fgn->context); + if (vec != NULL) { + vector_foreach_slot(vec, gm, j) { + + c += _snprint_multipath_topology(gm, c, + buf + len - c, + verbosity); + if (c >= buf + len - 1) + break; + } + if (c >= buf + len - 1) + break; + } + fgn->release_multipaths(fgn->context, vec); + pthread_cleanup_pop(1); + } + + pthread_cleanup_pop(1); + return c - buf; +} + +void print_foreign_topology(int verbosity) +{ + int buflen = MAX_LINE_LEN * MAX_LINES; + char *buf = NULL, *tmp = NULL; + + buf = malloc(buflen); + buf[0] = '\0'; + while (buf != NULL) { + char *c = buf; + + c += snprint_foreign_topology(buf, buflen, + verbosity); + if (c < buf + buflen - 1) + break; + + buflen *= 2; + tmp = buf; + buf = realloc(buf, buflen); + } + + if (buf == NULL && tmp != NULL) + buf = tmp; + + if (buf != NULL) { + printf("%s", buf); + free(buf); + } +} + +int snprint_foreign_paths(char *buf, int len, const char *style, int pretty) +{ + struct foreign *fgn; + int i; + char *c = buf; + + rdlock_foreigns(); + if (foreigns == NULL) { + unlock_foreigns(NULL); + return 0; + } + pthread_cleanup_push(unlock_foreigns, NULL); + + vector_foreach_slot(foreigns, fgn, i) { + const struct _vector *vec; + const struct gen_path *gp; + int j; + + fgn->lock(fgn->context); + pthread_cleanup_push(fgn->unlock, fgn->context); + + vec = fgn->get_paths(fgn->context); + if (vec != NULL) { + vector_foreach_slot(vec, gp, j) { + c += _snprint_path(gp, c, buf + len - c, + style, pretty); + if (c >= buf + len - 1) + break; + } + if (c >= buf + len - 1) + break; + } + fgn->release_paths(fgn->context, vec); + pthread_cleanup_pop(1); + } + + pthread_cleanup_pop(1); + return c - buf; +} + +int snprint_foreign_multipaths(char *buf, int len, + const char *style, int pretty) +{ + struct foreign *fgn; + int i; + char *c = buf; + + rdlock_foreigns(); + if (foreigns == NULL) { + unlock_foreigns(NULL); + return 0; + } + pthread_cleanup_push(unlock_foreigns, NULL); + + vector_foreach_slot(foreigns, fgn, i) { + const struct _vector *vec; + const struct gen_multipath *gm; + int j; + + fgn->lock(fgn->context); + pthread_cleanup_push(fgn->unlock, fgn->context); + + vec = fgn->get_multipaths(fgn->context); + if (vec != NULL) { + vector_foreach_slot(vec, gm, j) { + c += _snprint_multipath(gm, c, buf + len - c, + style, pretty); + if (c >= buf + len - 1) + break; + } + if (c >= buf + len - 1) + break; + } + fgn->release_multipaths(fgn->context, vec); + pthread_cleanup_pop(1); + } + + pthread_cleanup_pop(1); + return c - buf; +} diff --git a/libmultipath/foreign.h b/libmultipath/foreign.h new file mode 100644 index 0000000..0ade2d7 --- /dev/null +++ b/libmultipath/foreign.h @@ -0,0 +1,322 @@ +/* + Copyright (c) 2018 Martin Wilck, SUSE Linux GmbH + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ +#ifndef _FOREIGN_H +#define _FOREIGN_H +#include +#include + +#define LIBMP_FOREIGN_API ((1 << 8) | 0) + +struct context; + +/* return codes of functions below returning "int" */ +enum foreign_retcode { + FOREIGN_OK, + FOREIGN_CLAIMED, + FOREIGN_IGNORED, + FOREIGN_UNCLAIMED, + FOREIGN_NODEV, + FOREIGN_ERR, + __LAST_FOREIGN_RETCODE, +}; + +/** + * Foreign multipath library API + * Foreign libraries must implement the following methods. + */ +struct foreign { + /** + * method: init(api, name) + * Initialize foreign library, and check API compatibility + * return pointer to opaque internal data strucure if successful, + * NULL otherwise. + * + * @param[in] api: API version + * @param[in] name: name to use for references to self in log messages, + * doesn't need to be strdup'd + * @returns context pointer to use in future method calls. + */ + struct context* (*init)(unsigned int api, const char *name); + + /** + * method: cleanup(context) + * Free data structures used by foreign library, including + * context itself. + * + * @param[in] context foreign library context. This shouldn't be + * referenced any more after calling cleanup(). + */ + void (*cleanup)(struct context *); + + /** + * method: add(context, udev) + * This is called during path detection, and for udev ADD events. + * + * @param[in] context foreign library context + * @param[in] udev udev device to add + * @returns status code + * @retval FOREIGN_CLAIMED: device newly claimed + * @retval FOREIGN_OK: device already registered, no action taken + * @retval FOREIGN_IGNORED: device is ignored, no action taken + * @retval FOREIGN_ERR: an error occured (e.g. out-of-memory) + */ + int (*add)(struct context *, struct udev_device *); + + /** + * method: change + * This is called on udev CHANGE events. + * + * @param[in] context foreign library context + * @param[in] udev udev device that has generated the event + * @returns status code + * @retval FOREIGN_OK: event processed + * @retval FOREIGN_IGNORED: the device is ignored + * @retval FOREIGN_ERR: an error occured (e.g. out-of-memory) + * + * Note: theoretically it can happen that the status of a foreign device + * (claimed vs. not claimed) changes in a change event. + * Supporting this correctly would require big efforts. For now, we + * don't support it. "multipathd reconfigure" starts foreign device + * detection from scratch and should be able to handle this situation. + */ + int (*change)(struct context *, struct udev_device *); + + /** + * method: delete + * This is called on udev DELETE events. + * + * @param[in] context foreign library context + * @param[in] udev udev device that has generated the event and + * should be deleted + * @returns status code + * @retval FOREIGN_OK: processed correctly (device deleted) + * @retval FOREIGN_IGNORED: device wasn't registered internally + * @retval FOREIGN_ERR: error occured. + */ + int (*delete)(struct context *, struct udev_device *); + + /** + * method: delete_all + * This is called if multipathd reconfigures itself. + * Deletes all registered devices (maps and paths) + * + * @param[in] context foreign library context + * @returns status code + * @retval FOREIGN_OK: processed correctly + * @retval FOREIGN_IGNORED: nothing to delete + * @retval FOREIGN_ERR: error occured + */ + int (*delete_all)(struct context*); + + /** + * method: check + * This is called from multipathd's checker loop. + * + * Check status of managed devices, update internal status, and print + * log messages if appropriate. + * @param[in] context foreign library context + */ + void (*check)(struct context *); + + /** + * lock internal data stuctures. + * @param[in] ctx: foreign context + */ + void (*lock)(struct context *ctx); + + /** + * unlock internal data stuctures. + * @param[in] ctx: foreign context (void* in order to use the function + * as argument to pthread_cleanup_push()) + */ + void (*unlock)(void *ctx); + + /** + * method: get_multipaths(context) + * Returned vector must be freed by calling release_multipaths(). + * Lock must be held until release_multipaths() is called. + * + * @param[in] context foreign library context + * @returns a vector of "struct gen_multipath*" with the map devices + * belonging to this library (see generic.h). + */ + const struct _vector* (*get_multipaths)(const struct context *); + + /** + * method: release_multipaths(context, mpvec) + * release data structures obtained with get_multipaths (if any) + * + * @param[in] ctx the foreign context + * @param[in] mpvec the vector allocated with get_multipaths() + */ + void (*release_multipaths)(const struct context *ctx, + const struct _vector* mpvec); + + /** + * method: get_paths + * Returned vector must be freed by calling release_paths(). + * Lock must be held until release_paths() is called. + * + * @param[in] context foreign library context + * @returns a vector of "struct gen_path*" with the path devices + * belonging to this library (see generic.h) + */ + const struct _vector* (*get_paths)(const struct context *); + + /** + * release data structures obtained with get_multipaths (if any) + * + * @param[in] ctx the foreign context + * @param[in] ppvec the vector allocated with get_paths() + */ + void (*release_paths)(const struct context *ctx, + const struct _vector* ppvec); + + void *handle; + struct context *context; + const char name[0]; +}; + +/** + * init_foreign(dir) + * load and initialize foreign multipath libraries in dir (libforeign-*.so). + * @param dir: directory to search + * @returns: 0 on success, negative value on failure. + */ +int init_foreign(const char *multipath_dir); + +/** + * cleanup_foreign(dir) + * cleanup and free all data structures owned by foreign libraries + */ +void cleanup_foreign(void); + +/** + * add_foreign(udev) + * check if a device belongs to any foreign library. + * calls add() for all known foreign libs, in the order registered, + * until the first one returns FOREIGN_CLAIMED or FOREIGN_OK. + * @param udev: udev device to check + * @returns: status code + * @retval FOREIGN_CLAIMED: newly claimed by a foreign lib + * @retval FOREIGN_OK: already claimed by a foreign lib + * @retval FOREIGN_IGNORED: ignored by all foreign libs + * @retval FOREIGN_ERR: an error occured + */ +int add_foreign(struct udev_device *); + +/** + * change_foreign(udev) + * Notify foreign libraries of an udev CHANGE event + * @param udev: udev device to check + * @returns: status code (see change() method above). + */ +int change_foreign(struct udev_device *); + +/** + * delete_foreign(udev) + * @param udev: udev device being removed + * @returns: status code (see remove() above) + */ +int delete_foreign(struct udev_device *); + +/** + * delete_all_foreign() + * call delete_all() for all foreign libraries + * @returns: status code (see delete_all() above) + */ +int delete_all_foreign(void); + +/** + * check_foreign() + * call check() (see above) for all foreign libraries + */ +void check_foreign(void); + +/** + * foreign_path_layout() + * call this before printing paths, after get_path_layout(), to determine + * output field width. + */ +void foreign_path_layout(void); + +/** + * foreign_multipath_layout() + * call this before printing maps, after get_multipath_layout(), to determine + * output field width. + */ +void foreign_multipath_layout(void); + +/** + * snprint_foreign_topology(buf, len, verbosity); + * prints topology information from foreign libraries into buffer, + * '\0' - terminated. + * @param buf: output buffer + * @param len: size of output buffer + * @param verbosity: verbosity level + * @returns: number of printed characters excluding trailing '\0'. + */ +int snprint_foreign_topology(char *buf, int len, int verbosity); + +/** + * snprint_foreign_paths(buf, len, style, pad); + * prints formatted path information from foreign libraries into buffer, + * '\0' - terminated. + * @param buf: output buffer + * @param len: size of output buffer + * @param style: format string + * @param pad: whether to pad field width + * @returns: number of printed characters excluding trailing '\0'. + */ +int snprint_foreign_paths(char *buf, int len, const char *style, int pad); + +/** + * snprint_foreign_multipaths(buf, len, style, pad); + * prints formatted map information from foreign libraries into buffer, + * '\0' - terminated. + * @param buf: output buffer + * @param len: size of output buffer + * @param style: format string + * @param pad: whether to pad field width + * @returns: number of printed characters excluding trailing '\0'. + */ +int snprint_foreign_multipaths(char *buf, int len, + const char *style, int pretty); + +/** + * print_foreign_topology(v) + * print foreign topology to stdout + * @param verbosity: verbosity level + */ +void print_foreign_topology(int verbosity); + +/** + * is_claimed_by_foreign(ud) + * @param udev: udev device + * @returns: true iff device is (newly or already) claimed by a foreign lib + */ +static inline bool +is_claimed_by_foreign(struct udev_device *ud) +{ + int rc = add_foreign(ud); + + return (rc == FOREIGN_CLAIMED || rc == FOREIGN_OK); +} + +#endif /* _FOREIGN_H */ diff --git a/libmultipath/foreign/Makefile b/libmultipath/foreign/Makefile new file mode 100644 index 0000000..dfba11e --- /dev/null +++ b/libmultipath/foreign/Makefile @@ -0,0 +1,30 @@ +# +# Copyright (C) 2003 Christophe Varoqui, +# +include ../../Makefile.inc + +CFLAGS += $(LIB_CFLAGS) -I.. + +# If you add or remove a checker also update multipath/multipath.conf.5 +LIBS= \ + libforeign-nvme.so + +all: $(LIBS) + +libforeign-%.so: %.o + $(CC) $(LDFLAGS) $(SHARED_FLAGS) -o $@ $^ + +install: + $(INSTALL_PROGRAM) -m 755 $(LIBS) $(DESTDIR)$(libdir) + +uninstall: + for file in $(LIBS); do $(RM) $(DESTDIR)$(libdir)/$$file; done + +clean: dep_clean + $(RM) core *.a *.o *.gz *.so + +OBJS := $(LIBS:libforeign-%.so=%.o) +include $(wildcard $(OBJS:.o=.d)) + +dep_clean: + $(RM) $(OBJS:.o=.d) diff --git a/libmultipath/foreign/nvme.c b/libmultipath/foreign/nvme.c new file mode 100644 index 0000000..235f75d --- /dev/null +++ b/libmultipath/foreign/nvme.c @@ -0,0 +1,761 @@ +/* + Copyright (c) 2018 Martin Wilck, SUSE Linux GmbH + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "vector.h" +#include "generic.h" +#include "foreign.h" +#include "debug.h" +#include "structs.h" +#include "sysfs.h" + +static const char nvme_vendor[] = "NVMe"; +static const char N_A[] = "n/a"; +const char *THIS; + +struct nvme_map; +struct nvme_path { + struct gen_path gen; + struct udev_device *udev; + struct udev_device *ctl; + struct nvme_map *map; + bool seen; +}; + +struct nvme_pathgroup { + struct gen_pathgroup gen; + vector pathvec; +}; + +struct nvme_map { + struct gen_multipath gen; + struct udev_device *udev; + struct udev_device *subsys; + dev_t devt; + /* Just one static pathgroup for NVMe for now */ + struct nvme_pathgroup pg; + struct gen_pathgroup *gpg; + struct _vector pgvec; + vector pathvec; + int nr_live; +}; + +#define NAME_LEN 64 /* buffer length for temp attributes */ +#define const_gen_mp_to_nvme(g) ((const struct nvme_map*)(g)) +#define gen_mp_to_nvme(g) ((struct nvme_map*)(g)) +#define nvme_mp_to_gen(n) &((n)->gen) +#define const_gen_pg_to_nvme(g) ((const struct nvme_pathgroup*)(g)) +#define gen_pg_to_nvme(g) ((struct nvme_pathgroup*)(g)) +#define nvme_pg_to_gen(n) &((n)->gen) +#define const_gen_path_to_nvme(g) ((const struct nvme_path*)(g)) +#define gen_path_to_nvme(g) ((struct nvme_path*)(g)) +#define nvme_path_to_gen(n) &((n)->gen) + +static void cleanup_nvme_path(struct nvme_path *path) +{ + condlog(5, "%s: %p %p", __func__, path, path->udev); + if (path->udev) + udev_device_unref(path->udev); + /* ctl is implicitly referenced by udev, no need to unref */ + free(path); +} + +static void cleanup_nvme_map(struct nvme_map *map) +{ + if (map->pathvec) { + struct nvme_path *path; + int i; + + vector_foreach_slot_backwards(map->pathvec, path, i) { + condlog(5, "%s: %d %p", __func__, i, path); + cleanup_nvme_path(path); + vector_del_slot(map->pathvec, i); + } + } + vector_free(map->pathvec); + if (map->udev) + udev_device_unref(map->udev); + /* subsys is implicitly referenced by udev, no need to unref */ + free(map); +} + +static const struct _vector* +nvme_mp_get_pgs(const struct gen_multipath *gmp) { + const struct nvme_map *nvme = const_gen_mp_to_nvme(gmp); + + /* This is all used under the lock, no need to copy */ + return &nvme->pgvec; +} + +static void +nvme_mp_rel_pgs(const struct gen_multipath *gmp, const struct _vector *v) +{ + /* empty */ +} + +static void rstrip(char *str) +{ + int n; + + for (n = strlen(str) - 1; n >= 0 && str[n] == ' '; n--); + str[n+1] = '\0'; +} + +static int snprint_nvme_map(const struct gen_multipath *gmp, + char *buff, int len, char wildcard) +{ + const struct nvme_map *nvm = const_gen_mp_to_nvme(gmp); + char fld[NAME_LEN]; + const char *val; + + switch (wildcard) { + case 'd': + return snprintf(buff, len, "%s", + udev_device_get_sysname(nvm->udev)); + case 'n': + return snprintf(buff, len, "%s:NQN:%s", + udev_device_get_sysname(nvm->subsys), + udev_device_get_sysattr_value(nvm->subsys, + "subsysnqn")); + case 'w': + return snprintf(buff, len, "%s", + udev_device_get_sysattr_value(nvm->udev, + "wwid")); + case 'N': + return snprintf(buff, len, "%u", nvm->nr_live); + case 'S': + return snprintf(buff, len, "%s", + udev_device_get_sysattr_value(nvm->udev, + "size")); + case 'v': + return snprintf(buff, len, "%s", nvme_vendor); + case 's': + case 'p': + snprintf(fld, sizeof(fld), "%s", + udev_device_get_sysattr_value(nvm->subsys, + "model")); + rstrip(fld); + if (wildcard == 'p') + return snprintf(buff, len, "%s", fld); + return snprintf(buff, len, "%s,%s,%s", nvme_vendor, fld, + udev_device_get_sysattr_value(nvm->subsys, + "firmware_rev")); + case 'e': + return snprintf(buff, len, "%s", + udev_device_get_sysattr_value(nvm->subsys, + "firmware_rev")); + case 'r': + val = udev_device_get_sysattr_value(nvm->udev, "ro"); + if (val[0] == 1) + return snprintf(buff, len, "%s", "ro"); + else + return snprintf(buff, len, "%s", "rw"); + case 'G': + return snprintf(buff, len, "%s", THIS); + default: + return snprintf(buff, len, N_A); + break; + } + return 0; +} + +static const struct _vector* +nvme_pg_get_paths(const struct gen_pathgroup *gpg) { + const struct nvme_pathgroup *gp = const_gen_pg_to_nvme(gpg); + + /* This is all used under the lock, no need to copy */ + return gp->pathvec; +} + +static void +nvme_pg_rel_paths(const struct gen_pathgroup *gpg, const struct _vector *v) +{ + /* empty */ +} + +static int snprint_nvme_pg(const struct gen_pathgroup *gmp, + char *buff, int len, char wildcard) +{ + return snprintf(buff, len, N_A); +} + +static int snprint_hcil(const struct nvme_path *np, char *buf, int len) +{ + unsigned int nvmeid, ctlid, nsid; + int rc; + const char *sysname = udev_device_get_sysname(np->udev); + + rc = sscanf(sysname, "nvme%uc%un%u", &nvmeid, &ctlid, &nsid); + if (rc != 3) { + condlog(1, "%s: failed to scan %s", __func__, sysname); + rc = snprintf(buf, len, "(ERR:%s)", sysname); + } else + rc = snprintf(buf, len, "%u:%u:%u", nvmeid, ctlid, nsid); + return (rc < len ? rc : len); +} + +static int snprint_nvme_path(const struct gen_path *gp, + char *buff, int len, char wildcard) +{ + const struct nvme_path *np = const_gen_path_to_nvme(gp); + dev_t devt; + char fld[NAME_LEN]; + struct udev_device *pci; + + switch (wildcard) { + case 'w': + return snprintf(buff, len, "%s", + udev_device_get_sysattr_value(np->udev, + "wwid")); + case 'd': + return snprintf(buff, len, "%s", + udev_device_get_sysname(np->udev)); + case 'i': + return snprint_hcil(np, buff, len); + case 'D': + devt = udev_device_get_devnum(np->udev); + return snprintf(buff, len, "%u:%u", major(devt), minor(devt)); + case 'o': + sysfs_attr_get_value(np->ctl, "state", fld, sizeof(fld)); + return snprintf(buff, len, "%s", fld); + case 's': + snprintf(fld, sizeof(fld), "%s", + udev_device_get_sysattr_value(np->ctl, + "model")); + rstrip(fld); + return snprintf(buff, len, "%s,%s,%s", nvme_vendor, fld, + udev_device_get_sysattr_value(np->ctl, + "firmware_rev")); + case 'S': + return snprintf(buff, len, "%s", + udev_device_get_sysattr_value(np->udev, + "size")); + case 'z': + return snprintf(buff, len, "%s", + udev_device_get_sysattr_value(np->ctl, + "serial")); + case 'm': + return snprintf(buff, len, "%s", + udev_device_get_sysname(np->map->udev)); + case 'N': + case 'R': + return snprintf(buff, len, "%s:%s", + udev_device_get_sysattr_value(np->ctl, + "transport"), + udev_device_get_sysattr_value(np->ctl, + "address")); + case 'G': + return snprintf(buff, len, "[%s]", THIS); + case 'a': + pci = udev_device_get_parent_with_subsystem_devtype(np->ctl, + "pci", + NULL); + if (pci != NULL) + return snprintf(buff, len, "PCI:%s", + udev_device_get_sysname(pci)); + /* fall through */ + default: + return snprintf(buff, len, "%s", N_A); + break; + } + return 0; +} + +static const struct gen_multipath_ops nvme_map_ops = { + .get_pathgroups = nvme_mp_get_pgs, + .rel_pathgroups = nvme_mp_rel_pgs, + .style = generic_style, + .snprint = snprint_nvme_map, +}; + +static const struct gen_pathgroup_ops nvme_pg_ops __attribute__((unused)) = { + .get_paths = nvme_pg_get_paths, + .rel_paths = nvme_pg_rel_paths, + .snprint = snprint_nvme_pg, +}; + +static const struct gen_path_ops nvme_path_ops __attribute__((unused)) = { + .snprint = snprint_nvme_path, +}; + +struct context { + pthread_mutex_t mutex; + vector mpvec; + struct udev *udev; +}; + +void lock(struct context *ctx) +{ + pthread_mutex_lock(&ctx->mutex); +} + +void unlock(void *arg) +{ + struct context *ctx = arg; + + pthread_mutex_unlock(&ctx->mutex); +} + +static int _delete_all(struct context *ctx) +{ + struct nvme_map *nm; + int n = VECTOR_SIZE(ctx->mpvec), i; + + if (n == 0) + return FOREIGN_IGNORED; + + vector_foreach_slot_backwards(ctx->mpvec, nm, i) { + vector_del_slot(ctx->mpvec, i); + cleanup_nvme_map(nm); + } + return FOREIGN_OK; +} + +int delete_all(struct context *ctx) +{ + int rc; + + condlog(5, "%s called for \"%s\"", __func__, THIS); + + lock(ctx); + pthread_cleanup_push(unlock, ctx); + rc = _delete_all(ctx); + pthread_cleanup_pop(1); + + return rc; +} + +void cleanup(struct context *ctx) +{ + (void)delete_all(ctx); + + lock(ctx); + /* + * Locking is not strictly necessary here, locking in foreign.c + * makes sure that no other code is called with this ctx any more. + * But this should make static checkers feel better. + */ + pthread_cleanup_push(unlock, ctx); + if (ctx->udev) + udev_unref(ctx->udev); + if (ctx->mpvec) + vector_free(ctx->mpvec); + ctx->mpvec = NULL; + ctx->udev = NULL; + pthread_cleanup_pop(1); + pthread_mutex_destroy(&ctx->mutex); + + free(ctx); +} + +struct context *init(unsigned int api, const char *name) +{ + struct context *ctx; + + if (api > LIBMP_FOREIGN_API) { + condlog(0, "%s: api version mismatch: %08x > %08x\n", + __func__, api, LIBMP_FOREIGN_API); + return NULL; + } + + if ((ctx = calloc(1, sizeof(*ctx)))== NULL) + return NULL; + + pthread_mutex_init(&ctx->mutex, NULL); + + ctx->udev = udev_new(); + if (ctx->udev == NULL) + goto err; + + ctx->mpvec = vector_alloc(); + if (ctx->mpvec == NULL) + goto err; + + THIS = name; + return ctx; +err: + cleanup(ctx); + return NULL; +} + +static struct nvme_map *_find_nvme_map_by_devt(const struct context *ctx, + dev_t devt) +{ + struct nvme_map *nm; + int i; + + if (ctx->mpvec == NULL) + return NULL; + + vector_foreach_slot(ctx->mpvec, nm, i) { + if (nm->devt == devt) + return nm; + } + + return NULL; +} + +static struct nvme_path * +_find_path_by_syspath(struct nvme_map *map, const char *syspath) +{ + struct nvme_path *path; + char real[PATH_MAX]; + const char *ppath; + int i; + + ppath = realpath(syspath, real); + if (ppath == NULL) { + condlog(1, "%s: %s: error in realpath", __func__, THIS); + ppath = syspath; + } + + vector_foreach_slot(map->pathvec, path, i) { + if (!strcmp(ppath, + udev_device_get_syspath(path->udev))) + return path; + } + condlog(4, "%s: %s: %s not found", __func__, THIS, ppath); + return NULL; +} + +static int no_dotfiles(const struct dirent *di) +{ + return di->d_name[0] != '.'; +} + +static void _find_slaves(struct context *ctx, struct nvme_map *map) +{ + char pathbuf[PATH_MAX]; + struct dirent **di = NULL; + struct nvme_path *path; + int r, i; + + if (map == NULL || map->udev == NULL) + return; + + vector_foreach_slot(map->pathvec, path, i) + path->seen = false; + + snprintf(pathbuf, sizeof(pathbuf), + "%s/slaves", + udev_device_get_syspath(map->udev)); + + r = scandir(pathbuf, &di, no_dotfiles, alphasort); + + if (r == 0) { + condlog(3, "%s: %s: no paths 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)); + return; + } + + pthread_cleanup_push(free, di); + for (i = 0; i < r; i++) { + char *fn = di[i]->d_name; + struct udev_device *udev; + + if (snprintf(pathbuf, sizeof(pathbuf), "%s/slaves/%s", + udev_device_get_syspath(map->udev), fn) + >= sizeof(pathbuf)) + continue; + + path = _find_path_by_syspath(map, pathbuf); + if (path != NULL) { + path->seen = true; + condlog(4, "%s: %s already known", + __func__, fn); + 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; + + path->gen.ops = &nvme_path_ops; + path->udev = udev; + path->seen = true; + path->map = map; + path->ctl = udev_device_get_parent_with_subsystem_devtype + (udev, "nvme", NULL); + if (path->ctl == NULL) { + condlog(1, "%s: %s: failed to get controller for %s", + __func__, THIS, fn); + cleanup_nvme_path(path); + continue; + } + + if (vector_alloc_slot(map->pathvec) == NULL) { + cleanup_nvme_path(path); + continue; + } + condlog(3, "%s: %s: new path %s added to %s", + __func__, THIS, udev_device_get_sysname(udev), + udev_device_get_sysname(map->udev)); + vector_set_slot(map->pathvec, path); + } + pthread_cleanup_pop(1); + + map->nr_live = 0; + vector_foreach_slot_backwards(map->pathvec, path, i) { + if (!path->seen) { + condlog(1, "path %d not found in %s any more", + i, udev_device_get_sysname(map->udev)); + vector_del_slot(map->pathvec, i); + cleanup_nvme_path(path); + } else { + static const char live_state[] = "live"; + char state[16]; + + if ((sysfs_attr_get_value(path->ctl, "state", state, + sizeof(state)) > 0) && + !strncmp(state, live_state, sizeof(live_state) - 1)) + map->nr_live++; + } + } + condlog(3, "%s: %s: map %s has %d/%d live paths", __func__, THIS, + udev_device_get_sysname(map->udev), map->nr_live, + VECTOR_SIZE(map->pathvec)); +} + +static int _add_map(struct context *ctx, struct udev_device *ud, + struct udev_device *subsys) +{ + dev_t devt = udev_device_get_devnum(ud); + struct nvme_map *map; + + if (_find_nvme_map_by_devt(ctx, devt) != NULL) + return FOREIGN_OK; + + map = calloc(1, sizeof(*map)); + if (map == NULL) + return FOREIGN_ERR; + + map->devt = devt; + map->udev = udev_device_ref(ud); + /* + * subsys is implicitly referenced by map->udev, + * no need to take a reference here. + */ + map->subsys = subsys; + map->gen.ops = &nvme_map_ops; + + map->pathvec = vector_alloc(); + if (map->pathvec == NULL) { + cleanup_nvme_map(map); + return FOREIGN_ERR; + } + + map->pg.gen.ops = &nvme_pg_ops; + map->pg.pathvec = map->pathvec; + map->gpg = nvme_pg_to_gen(&map->pg); + + map->pgvec.allocated = 1; + map->pgvec.slot = (void**)&map->gpg; + + if (vector_alloc_slot(ctx->mpvec) == NULL) { + cleanup_nvme_map(map); + return FOREIGN_ERR; + } + vector_set_slot(ctx->mpvec, map); + _find_slaves(ctx, map); + + return FOREIGN_CLAIMED; +} + +int add(struct context *ctx, struct udev_device *ud) +{ + struct udev_device *subsys; + int rc; + + condlog(5, "%s called for \"%s\"", __func__, THIS); + + if (ud == NULL) + return FOREIGN_ERR; + if (strcmp("disk", udev_device_get_devtype(ud))) + return FOREIGN_IGNORED; + + subsys = udev_device_get_parent_with_subsystem_devtype(ud, + "nvme-subsystem", + NULL); + if (subsys == NULL) + return FOREIGN_IGNORED; + + lock(ctx); + pthread_cleanup_push(unlock, ctx); + rc = _add_map(ctx, ud, subsys); + pthread_cleanup_pop(1); + + if (rc == FOREIGN_CLAIMED) + condlog(3, "%s: %s: added map %s", __func__, THIS, + udev_device_get_sysname(ud)); + else if (rc != FOREIGN_OK) + condlog(1, "%s: %s: retcode %d adding %s", + __func__, THIS, rc, udev_device_get_sysname(ud)); + + return rc; +} + +int change(struct context *ctx, struct udev_device *ud) +{ + condlog(5, "%s called for \"%s\"", __func__, THIS); + return FOREIGN_IGNORED; +} + +static int _delete_map(struct context *ctx, struct udev_device *ud) +{ + int k; + struct nvme_map *map; + dev_t devt = udev_device_get_devnum(ud); + + map = _find_nvme_map_by_devt(ctx, devt); + if (map ==NULL) + return FOREIGN_IGNORED; + + k = find_slot(ctx->mpvec, map); + if (k == -1) + return FOREIGN_ERR; + else + vector_del_slot(ctx->mpvec, k); + + cleanup_nvme_map(map); + + return FOREIGN_OK; +} + +int delete(struct context *ctx, struct udev_device *ud) +{ + int rc; + + condlog(5, "%s called for \"%s\"", __func__, THIS); + + if (ud == NULL) + return FOREIGN_ERR; + + lock(ctx); + pthread_cleanup_push(unlock, ctx); + rc = _delete_map(ctx, ud); + pthread_cleanup_pop(1); + + if (rc == FOREIGN_OK) + condlog(3, "%s: %s: map %s deleted", __func__, THIS, + udev_device_get_sysname(ud)); + else if (rc != FOREIGN_IGNORED) + condlog(1, "%s: %s: retcode %d deleting map %s", __func__, + THIS, rc, udev_device_get_sysname(ud)); + + return rc; +} + +void _check(struct context *ctx) +{ + struct gen_multipath *gm; + int i; + + vector_foreach_slot(ctx->mpvec, gm, i) { + struct nvme_map *map = gen_mp_to_nvme(gm); + + _find_slaves(ctx, map); + } +} + +void check(struct context *ctx) +{ + condlog(4, "%s called for \"%s\"", __func__, THIS); + lock(ctx); + pthread_cleanup_push(unlock, ctx); + _check(ctx); + pthread_cleanup_pop(1); + return; +} + +/* + * It's safe to pass our internal pointer, this is only used under the lock. + */ +const struct _vector *get_multipaths(const struct context *ctx) +{ + condlog(5, "%s called for \"%s\"", __func__, THIS); + return ctx->mpvec; +} + +void release_multipaths(const struct context *ctx, const struct _vector *mpvec) +{ + condlog(5, "%s called for \"%s\"", __func__, THIS); + /* NOP */ +} + +/* + * It's safe to pass our internal pointer, this is only used under the lock. + */ +const struct _vector * get_paths(const struct context *ctx) +{ + vector paths = NULL; + const struct gen_multipath *gm; + int i; + + condlog(5, "%s called for \"%s\"", __func__, THIS); + vector_foreach_slot(ctx->mpvec, gm, i) { + const struct nvme_map *nm = const_gen_mp_to_nvme(gm); + paths = vector_convert(paths, nm->pathvec, + struct gen_path, identity); + } + return paths; +} + +void release_paths(const struct context *ctx, const struct _vector *mpvec) +{ + condlog(5, "%s called for \"%s\"", __func__, THIS); + vector_free_const(mpvec); +} + +/* compile-time check whether all methods are present and correctly typed */ +#define _METHOD_INIT(x) .x = x +static struct foreign __methods __attribute__((unused)) = { + _METHOD_INIT(init), + _METHOD_INIT(cleanup), + _METHOD_INIT(change), + _METHOD_INIT(delete), + _METHOD_INIT(delete_all), + _METHOD_INIT(check), + _METHOD_INIT(lock), + _METHOD_INIT(unlock), + _METHOD_INIT(get_multipaths), + _METHOD_INIT(release_multipaths), + _METHOD_INIT(get_paths), + _METHOD_INIT(release_paths), +}; diff --git a/libmultipath/generic.c b/libmultipath/generic.c new file mode 100644 index 0000000..6f7a2cd --- /dev/null +++ b/libmultipath/generic.c @@ -0,0 +1,39 @@ +/* + Copyright (c) 2018 Martin Wilck, SUSE Linux GmbH + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. + */ + + +#include +#include "generic.h" +#include "structs.h" + +int generic_style(const struct gen_multipath* gm, + char *buf, int len, int verbosity) +{ + char alias_buf[WWID_SIZE]; + char wwid_buf[WWID_SIZE]; + int n = 0; + + gm->ops->snprint(gm, alias_buf, sizeof(alias_buf), 'n'); + gm->ops->snprint(gm, wwid_buf, sizeof(wwid_buf), 'w'); + + n += snprintf(buf, len, "%%n %s[%%G]:%%d %%s", + strcmp(alias_buf, wwid_buf) ? "(%w) " : ""); + + return (n < len ? n : len - 1); +} diff --git a/libmultipath/generic.h b/libmultipath/generic.h new file mode 100644 index 0000000..7f7fe66 --- /dev/null +++ b/libmultipath/generic.h @@ -0,0 +1,136 @@ +/* + Copyright (c) 2018 Martin Wilck, SUSE Linux GmbH + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. + */ +#ifndef _GENERIC_H +#define _GENERIC_H +#include "vector.h" + +struct gen_multipath; +struct gen_pathgroup; +struct gen_path; + +/** + * Methods implemented for gen_multipath "objects" + */ +struct gen_multipath_ops { + /** + * method: get_pathgroups(gmp) + * caller is responsible to returned data using rel_pathgroups() + * caller is also responsible to lock the gmp (directly or indirectly) + * while working with the return value. + * @param gmp: generic multipath object to act on + * @returns a vector of const struct gen_pathgroup* + */ + const struct _vector* (*get_pathgroups)(const struct gen_multipath*); + /** + * method: rel_pathgroups(gmp, v) + * free data allocated by get_pathgroups(), if any + * @param gmp: generic multipath object to act on + * @param v the value returned by get_pathgroups() + */ + void (*rel_pathgroups)(const struct gen_multipath*, + const struct _vector*); + /** + * method: snprint(gmp, buf, len, wildcard) + * prints the property of the multipath map matching + * the passed-in wildcard character into "buf", + * 0-terminated, no more than "len" characters including trailing '\0'. + * + * @param gmp: generic multipath object to act on + * @param buf: output buffer + * @param buflen: buffer size + * @param wildcard: the multipath wildcard (see print.c) + * @returns the number of characters printed (without trailing '\0'). + */ + int (*snprint)(const struct gen_multipath*, + char *buf, int len, char wildcard); + /** + * method: style(gmp, buf, len, verbosity) + * returns the format string to be used for the multipath object, + * defined with the wildcards as defined in print.c + * generic_style() should work well in most cases. + * @param gmp: generic multipath object to act on + * @param buf: output buffer + * @param buflen: buffer size + * @param verbosity: verbosity level + * @returns number of format chars printed + */ + int (*style)(const struct gen_multipath*, + char *buf, int len, int verbosity); +}; + +/** + * Methods implemented for gen_pathgroup "objects" + */ +struct gen_pathgroup_ops { + /** + * method: get_paths(gpg) + * caller is responsible to returned data using rel_paths() + * @param gpg: generic pathgroup object to act on + * @returns a vector of const struct gen_path* + */ + const struct _vector* (*get_paths)(const struct gen_pathgroup*); + /** + * method: rel_paths(gpg, v) + * free data allocated by get_paths(), if any + * @param gmp: generic pathgroup object to act on + * @param v the value returned by get_paths() + */ + void (*rel_paths)(const struct gen_pathgroup*, const struct _vector*); + /** + * Method snprint() + * see gen_multipath_ops->snprint() above + */ + int (*snprint)(const struct gen_pathgroup*, + char *buf, int len, char wildcard); +}; + +struct gen_path_ops { + /** + * Method snprint() + * see gen_multipath_ops->snprint() above + */ + int (*snprint)(const struct gen_path*, + char *buf, int len, char wildcard); +}; + +struct gen_multipath { + const struct gen_multipath_ops *ops; +}; + +struct gen_pathgroup { + const struct gen_pathgroup_ops *ops; +}; + +struct gen_path { + const struct gen_path_ops *ops; +}; + +/** + * Helper functions for setting up the various generic_X_ops + */ + +/** + * generic_style() + * A simple style() method (see above) that should fit most + * foreign libraries. + */ +int generic_style(const struct gen_multipath*, + char *buf, int len, int verbosity); + +#endif /* _GENERIC_H */ diff --git a/libmultipath/hwtable.c b/libmultipath/hwtable.c index 17e8ac6..6ba70c8 100644 --- a/libmultipath/hwtable.c +++ b/libmultipath/hwtable.c @@ -72,10 +72,25 @@ .delay_wait_checks = DELAY_CHECKS_OFF, .skip_kpartx = SKIP_KPARTX_OFF, .max_sectors_kb = MAX_SECTORS_KB_UNDEF, + .ghost_delay = GHOST_DELAY_OFF }, #endif static struct hwentry default_hw[] = { + /* + * 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. + */ + { + .vendor = "NVME", + .product = ".*", + .uid_attribute = "ID_WWN", + .checker_name = NONE, + .retain_hwhandler = RETAIN_HWHANDLER_OFF, + }, /* * Apple * @@ -639,8 +654,8 @@ static struct hwentry default_hw[] = { /* * SANtricity(RDAC) family * - * Maintainer : Sean Stewart - * Mail : sean.stewart@netapp.com + * Maintainer : NetApp RDAC team + * Mail : ng-eseries-upstream-maintainers@netapp.com */ .vendor = "(NETAPP|LSI|ENGENIO)", .product = "INF-01-00", @@ -665,6 +680,20 @@ static struct hwentry default_hw[] = { .pgpolicy = MULTIBUS, .no_path_retry = 24, }, + /* + * NetApp NVMe-FC namespace devices: MULTIBUS, queueing preferred + * + * The table is searched backwards, so place this after generic NVMe + */ + { + .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 * @@ -1132,16 +1161,6 @@ static struct hwentry default_hw[] = { .prio_name = PRIO_ALUA, .no_path_retry = 30, }, - /* - * Generic NVMe devices - */ - { - .vendor = "NVME", - .product = ".*", - .uid_attribute = "ID_WWN", - .checker_name = NONE, - .retain_hwhandler = RETAIN_HWHANDLER_OFF, - }, /* * Dot Hill Systems - Seagate Technology */ @@ -1180,6 +1199,17 @@ static struct hwentry default_hw[] = { .pgpolicy = MULTIBUS, .no_path_retry = 30, }, + /* + * INSPUR + */ + { + /* AS5300/AS5500 G2 */ + .vendor = "INSPUR", + .product = "MCS", + .pgpolicy = GROUP_BY_PRIO, + .pgfailback = -FAILBACK_IMMEDIATE, + .prio_name = PRIO_ALUA, + }, /* * EOL */ diff --git a/libmultipath/io_err_stat.c b/libmultipath/io_err_stat.c index 75a6df6..ac81b4b 100644 --- a/libmultipath/io_err_stat.c +++ b/libmultipath/io_err_stat.c @@ -21,6 +21,7 @@ #include #include #include +#include #include "vector.h" #include "memory.h" @@ -73,6 +74,10 @@ struct io_err_stat_path { pthread_t io_err_stat_thr; pthread_attr_t io_err_stat_attr; +static pthread_mutex_t io_err_thread_lock = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t io_err_thread_cond = PTHREAD_COND_INITIALIZER; +static int io_err_thread_running = 0; + static struct io_err_stat_pathvec *paths; struct vectors *vecs; io_context_t ioctx; @@ -315,6 +320,9 @@ int io_err_stat_handle_pathfail(struct path *path) struct timespec curr_time; int res; + if (uatomic_read(&io_err_thread_running) == 0) + return 1; + if (path->io_err_disable_reinstate) { io_err_stat_log(3, "%s: reinstate is already disabled", path->dev); @@ -379,17 +387,16 @@ int hit_io_err_recheck_time(struct path *pp) struct timespec curr_time; int r; - if (pp->io_err_disable_reinstate == 0) - return 1; - if (clock_gettime(CLOCK_MONOTONIC, &curr_time) != 0) - return 1; - if (pp->io_err_pathfail_cnt != PATH_IO_ERR_IN_POLLING_RECHECK) - return 1; + if (uatomic_read(&io_err_thread_running) == 0) + return 0; if (pp->mpp->nr_active <= 0) { io_err_stat_log(2, "%s: recover path early", pp->dev); goto recover; } - if ((curr_time.tv_sec - pp->io_err_dis_reinstate_time) > + if (pp->io_err_pathfail_cnt != PATH_IO_ERR_IN_POLLING_RECHECK) + return 1; + if (clock_gettime(CLOCK_MONOTONIC, &curr_time) != 0 || + (curr_time.tv_sec - pp->io_err_dis_reinstate_time) > pp->mpp->marginal_path_err_recheck_gap_time) { io_err_stat_log(4, "%s: reschedule checking after %d seconds", pp->dev, @@ -692,24 +699,62 @@ static void service_paths(void) pthread_mutex_unlock(&paths->mutex); } +static void cleanup_unlock(void *arg) +{ + pthread_mutex_unlock((pthread_mutex_t*) arg); +} + +static void cleanup_exited(void *arg) +{ + uatomic_set(&io_err_thread_running, 0); +} + static void *io_err_stat_loop(void *data) { + sigset_t set; + vecs = (struct vectors *)data; pthread_cleanup_push(rcu_unregister, NULL); rcu_register_thread(); + pthread_cleanup_push(cleanup_exited, NULL); + + sigfillset(&set); + sigdelset(&set, SIGUSR2); + mlockall(MCL_CURRENT | MCL_FUTURE); + + pthread_mutex_lock(&io_err_thread_lock); + uatomic_set(&io_err_thread_running, 1); + pthread_cond_broadcast(&io_err_thread_cond); + pthread_mutex_unlock(&io_err_thread_lock); + while (1) { + struct timespec ts; + service_paths(); - usleep(100000); + + ts.tv_sec = 0; + ts.tv_nsec = 100 * 1000 * 1000; + /* + * pselect() with no fds, a timeout, and a sigmask: + * sleep for 100ms and react on SIGUSR2. + */ + pselect(1, NULL, NULL, NULL, &ts, &set); } + pthread_cleanup_pop(1); pthread_cleanup_pop(1); return NULL; } int start_io_err_stat_thread(void *data) { + int ret; + + if (uatomic_read(&io_err_thread_running) == 1) + return 0; + if (io_setup(CONCUR_NR_EVENT, &ioctx) != 0) { io_err_stat_log(4, "io_setup failed"); return 1; @@ -718,12 +763,24 @@ int start_io_err_stat_thread(void *data) if (!paths) goto destroy_ctx; - if (pthread_create(&io_err_stat_thr, &io_err_stat_attr, - io_err_stat_loop, data)) { + pthread_mutex_lock(&io_err_thread_lock); + pthread_cleanup_push(cleanup_unlock, &io_err_thread_lock); + + ret = pthread_create(&io_err_stat_thr, &io_err_stat_attr, + io_err_stat_loop, data); + + while (!ret && !uatomic_read(&io_err_thread_running) && + pthread_cond_wait(&io_err_thread_cond, + &io_err_thread_lock) == 0); + + pthread_cleanup_pop(1); + + if (ret) { io_err_stat_log(0, "cannot create io_error statistic thread"); goto out_free; } - io_err_stat_log(3, "thread started"); + + io_err_stat_log(2, "io_error statistic thread started"); return 0; out_free: @@ -736,8 +793,10 @@ destroy_ctx: void stop_io_err_stat_thread(void) { - pthread_cancel(io_err_stat_thr); - pthread_kill(io_err_stat_thr, SIGUSR2); + if (uatomic_read(&io_err_thread_running) == 1) + pthread_cancel(io_err_stat_thr); + + pthread_join(io_err_stat_thr, NULL); free_io_err_pathvec(paths); io_destroy(ioctx); } diff --git a/libmultipath/list.h b/libmultipath/list.h index 2b1dcf3..ced021f 100644 --- a/libmultipath/list.h +++ b/libmultipath/list.h @@ -18,8 +18,12 @@ * @member: the name of the member within the struct. * */ -#define container_of(ptr, type, member) ({ \ +#define container_of_const(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (const type *)( (const char *)__mptr - offsetof(type,member) );}) + +#define container_of(ptr, type, member) ({ \ + typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );}) /* diff --git a/libmultipath/memory.h b/libmultipath/memory.h index a3c478e..63f59d8 100644 --- a/libmultipath/memory.h +++ b/libmultipath/memory.h @@ -43,6 +43,7 @@ int debug; (__FILE__), (char *)(__FUNCTION__), (__LINE__)) ) #define STRDUP(n) ( dbg_strdup((n), \ (__FILE__), (char *)(__FUNCTION__), (__LINE__)) ) +#define FREE_CONST(p) do { FREE((void*)(unsigned long)p); } while(0) /* Memory debug prototypes defs */ extern void *dbg_malloc(unsigned long, char *, char *, int); @@ -55,6 +56,11 @@ extern void dbg_free_final(char *); #define MALLOC(n) (calloc(1,(n))) #define FREE(p) do { free(p); p = NULL; } while(0) +/* + * Double cast to avoid warnings with -Wcast-qual + * use this for valid free() operations on const pointers + */ +#define FREE_CONST(p) do { free((void*)(unsigned long)p); p = NULL; } while(0) #define REALLOC(p,n) (realloc((p),(n))) #define STRDUP(n) (strdup(n)) diff --git a/libmultipath/parser.c b/libmultipath/parser.c index c47d891..5caa201 100644 --- a/libmultipath/parser.c +++ b/libmultipath/parser.c @@ -33,7 +33,8 @@ static int line_nr; int keyword_alloc(vector keywords, char *string, int (*handler) (struct config *, vector), - int (*print) (struct config *, char *, int, void *), int unique) + int (*print) (struct config *, char *, int, const void*), + int unique) { struct keyword *keyword; @@ -71,7 +72,8 @@ install_sublevel_end(void) int _install_keyword(vector keywords, char *string, int (*handler) (struct config *, vector), - int (*print) (struct config *, char *, int, void *), int unique) + int (*print) (struct config *, char *, int, const void*), + int unique) { int i = 0; struct keyword *keyword; @@ -143,7 +145,8 @@ find_keyword(vector keywords, vector v, char * name) } int -snprint_keyword(char *buff, int len, char *fmt, struct keyword *kw, void *data) +snprint_keyword(char *buff, int len, char *fmt, struct keyword *kw, + const void *data) { int r; int fwd = 0; diff --git a/libmultipath/parser.h b/libmultipath/parser.h index 519b805..0a74750 100644 --- a/libmultipath/parser.h +++ b/libmultipath/parser.h @@ -43,7 +43,7 @@ struct keyword { char *string; int (*handler) (struct config *, vector); - int (*print) (struct config *, char *, int, void *); + int (*print) (struct config *, char *, int, const void *); vector sub; int unique; }; @@ -60,13 +60,17 @@ struct keyword { /* Prototypes */ extern int keyword_alloc(vector keywords, char *string, int (*handler) (struct config *, vector), - int (*print) (struct config *, char *, int, void *), int unique); + int (*print) (struct config *, char *, int, + const void *), + int unique); #define install_keyword_root(str, h) keyword_alloc(keywords, str, h, NULL, 1) extern void install_sublevel(void); extern void install_sublevel_end(void); extern int _install_keyword(vector keywords, char *string, int (*handler) (struct config *, vector), - int (*print) (struct config *, char *, int, void *), int unique); + int (*print) (struct config *, char *, int, + const void *), + int unique); #define install_keyword(str, vec, pri) _install_keyword(keywords, str, vec, pri, 1) #define install_keyword_multi(str, vec, pri) _install_keyword(keywords, str, vec, pri, 0) extern void dump_keywords(vector keydump, int level); @@ -76,6 +80,6 @@ extern void *set_value(vector strvec); extern int process_file(struct config *conf, char *conf_file); extern struct keyword * find_keyword(vector keywords, vector v, char * name); int snprint_keyword(char *buff, int len, char *fmt, struct keyword *kw, - void *data); + const void *data); #endif diff --git a/libmultipath/pgpolicies.c b/libmultipath/pgpolicies.c index 4ae4afb..ac2596a 100644 --- a/libmultipath/pgpolicies.c +++ b/libmultipath/pgpolicies.c @@ -120,7 +120,7 @@ int group_by_node_name(struct multipath * mp) if (!pgp) goto out1; - if (store_pathgroup(mp->pg, pgp)) + if (add_pathgroup(mp, pgp)) goto out2; /* feed the first path */ @@ -196,7 +196,7 @@ int group_by_serial(struct multipath * mp) if (!pgp) goto out1; - if (store_pathgroup(mp->pg, pgp)) + if (add_pathgroup(mp, pgp)) goto out2; /* feed the first path */ @@ -254,7 +254,7 @@ int one_path_per_group(struct multipath *mp) if (!pgp) goto out; - if (store_pathgroup(mp->pg, pgp)) + if (add_pathgroup(mp, pgp)) goto out1; if (store_path(pgp->paths, pp)) @@ -293,7 +293,7 @@ int one_group(struct multipath *mp) /* aka multibus */ vector_free(pgp->paths); - if (store_pathgroup(mp->pg, pgp)) + if (add_pathgroup(mp, pgp)) goto out1; pgp->paths = mp->paths; @@ -367,8 +367,9 @@ int group_by_prio(struct multipath *mp) if (i < VECTOR_SIZE(mp->pg)) { if (!vector_insert_slot(mp->pg, i, pgp)) goto out2; + pgp->mpp = mp; } else { - if (store_pathgroup(mp->pg, pgp)) + if (add_pathgroup(mp, pgp)) goto out2; } diff --git a/libmultipath/print.c b/libmultipath/print.c index 65a9824..d532994 100644 --- a/libmultipath/print.c +++ b/libmultipath/print.c @@ -28,8 +28,10 @@ #include "devmapper.h" #include "uevent.h" #include "debug.h" +#include "discovery.h" -#define MAX(x,y) (x > y) ? x : y +#define MAX(x,y) (((x) > (y)) ? (x) : (y)) +#define MIN(x,y) (((x) > (y)) ? (y) : (x)) #define TAIL (line + len - 1 - c) #define NOPAD s = c #define PAD(x) \ @@ -97,7 +99,7 @@ snprint_size (char * buff, size_t len, unsigned long long size) * multipath info printing functions */ static int -snprint_name (char * buff, size_t len, struct multipath * mpp) +snprint_name (char * buff, size_t len, const struct multipath * mpp) { if (mpp->alias) return snprintf(buff, len, "%s", mpp->alias); @@ -106,7 +108,7 @@ snprint_name (char * buff, size_t len, struct multipath * mpp) } static int -snprint_sysfs (char * buff, size_t len, struct multipath * mpp) +snprint_sysfs (char * buff, size_t len, const struct multipath * mpp) { if (mpp->dmi) return snprintf(buff, len, "dm-%i", mpp->dmi->minor); @@ -115,7 +117,7 @@ snprint_sysfs (char * buff, size_t len, struct multipath * mpp) } static int -snprint_ro (char * buff, size_t len, struct multipath * mpp) +snprint_ro (char * buff, size_t len, const struct multipath * mpp) { if (!mpp->dmi) return snprintf(buff, len, "undef"); @@ -154,7 +156,7 @@ out: } static int -snprint_failback (char * buff, size_t len, struct multipath * mpp) +snprint_failback (char * buff, size_t len, const struct multipath * mpp) { if (mpp->pgfailback == -FAILBACK_IMMEDIATE) return snprintf(buff, len, "immediate"); @@ -169,7 +171,7 @@ snprint_failback (char * buff, size_t len, struct multipath * mpp) } static int -snprint_queueing (char * buff, size_t len, struct multipath * mpp) +snprint_queueing (char * buff, size_t len, const struct multipath * mpp) { if (mpp->no_path_retry == NO_PATH_RETRY_FAIL) return snprintf(buff, len, "off"); @@ -191,13 +193,13 @@ snprint_queueing (char * buff, size_t len, struct multipath * mpp) } static int -snprint_nb_paths (char * buff, size_t len, struct multipath * mpp) +snprint_nb_paths (char * buff, size_t len, const struct multipath * mpp) { return snprint_int(buff, len, mpp->nr_active); } static int -snprint_dm_map_state (char * buff, size_t len, struct multipath * mpp) +snprint_dm_map_state (char * buff, size_t len, const struct multipath * mpp) { if (mpp->dmi && mpp->dmi->suspended) return snprintf(buff, len, "suspend"); @@ -206,67 +208,67 @@ snprint_dm_map_state (char * buff, size_t len, struct multipath * mpp) } static int -snprint_multipath_size (char * buff, size_t len, struct multipath * mpp) +snprint_multipath_size (char * buff, size_t len, const struct multipath * mpp) { return snprint_size(buff, len, mpp->size); } static int -snprint_features (char * buff, size_t len, struct multipath * mpp) +snprint_features (char * buff, size_t len, const struct multipath * mpp) { return snprint_str(buff, len, mpp->features); } static int -snprint_hwhandler (char * buff, size_t len, struct multipath * mpp) +snprint_hwhandler (char * buff, size_t len, const struct multipath * mpp) { return snprint_str(buff, len, mpp->hwhandler); } static int -snprint_path_faults (char * buff, size_t len, struct multipath * mpp) +snprint_path_faults (char * buff, size_t len, const struct multipath * mpp) { return snprint_uint(buff, len, mpp->stat_path_failures); } static int -snprint_switch_grp (char * buff, size_t len, struct multipath * mpp) +snprint_switch_grp (char * buff, size_t len, const struct multipath * mpp) { return snprint_uint(buff, len, mpp->stat_switchgroup); } static int -snprint_map_loads (char * buff, size_t len, struct multipath * mpp) +snprint_map_loads (char * buff, size_t len, const struct multipath * mpp) { return snprint_uint(buff, len, mpp->stat_map_loads); } static int -snprint_total_q_time (char * buff, size_t len, struct multipath * mpp) +snprint_total_q_time (char * buff, size_t len, const struct multipath * mpp) { return snprint_uint(buff, len, mpp->stat_total_queueing_time); } static int -snprint_q_timeouts (char * buff, size_t len, struct multipath * mpp) +snprint_q_timeouts (char * buff, size_t len, const struct multipath * mpp) { return snprint_uint(buff, len, mpp->stat_queueing_timeouts); } static int -snprint_map_failures (char * buff, size_t len, struct multipath * mpp) +snprint_map_failures (char * buff, size_t len, const struct multipath * mpp) { return snprint_uint(buff, len, mpp->stat_map_failures); } static int -snprint_multipath_uuid (char * buff, size_t len, struct multipath * mpp) +snprint_multipath_uuid (char * buff, size_t len, const struct multipath * mpp) { return snprint_str(buff, len, mpp->wwid); } static int -snprint_multipath_vpr (char * buff, size_t len, struct multipath * mpp) +snprint_multipath_vpr (char * buff, size_t len, const struct multipath * mpp) { struct pathgroup * pgp; struct path * pp; @@ -286,7 +288,7 @@ snprint_multipath_vpr (char * buff, size_t len, struct multipath * mpp) static int -snprint_multipath_vend (char * buff, size_t len, struct multipath * mpp) +snprint_multipath_vend (char * buff, size_t len, const struct multipath * mpp) { struct pathgroup * pgp; struct path * pp; @@ -304,7 +306,7 @@ snprint_multipath_vend (char * buff, size_t len, struct multipath * mpp) } static int -snprint_multipath_prod (char * buff, size_t len, struct multipath * mpp) +snprint_multipath_prod (char * buff, size_t len, const struct multipath * mpp) { struct pathgroup * pgp; struct path * pp; @@ -322,7 +324,7 @@ snprint_multipath_prod (char * buff, size_t len, struct multipath * mpp) } static int -snprint_multipath_rev (char * buff, size_t len, struct multipath * mpp) +snprint_multipath_rev (char * buff, size_t len, const struct multipath * mpp) { struct pathgroup * pgp; struct path * pp; @@ -340,7 +342,13 @@ snprint_multipath_rev (char * buff, size_t len, struct multipath * mpp) } static int -snprint_action (char * buff, size_t len, struct multipath * mpp) +snprint_multipath_foreign (char * buff, size_t len, const struct multipath * pp) +{ + return snprintf(buff, len, "%s", "--"); +} + +static int +snprint_action (char * buff, size_t len, const struct multipath * mpp) { switch (mpp->action) { case ACT_REJECT: @@ -362,13 +370,13 @@ snprint_action (char * buff, size_t len, struct multipath * mpp) * path info printing functions */ static int -snprint_path_uuid (char * buff, size_t len, struct path * pp) +snprint_path_uuid (char * buff, size_t len, const struct path * pp) { return snprint_str(buff, len, pp->wwid); } static int -snprint_hcil (char * buff, size_t len, struct path * pp) +snprint_hcil (char * buff, size_t len, const struct path * pp) { if (!pp || pp->sg_id.host_no < 0) return snprintf(buff, len, "#:#:#:#"); @@ -381,7 +389,7 @@ snprint_hcil (char * buff, size_t len, struct path * pp) } static int -snprint_dev (char * buff, size_t len, struct path * pp) +snprint_dev (char * buff, size_t len, const struct path * pp) { if (!pp || !strlen(pp->dev)) return snprintf(buff, len, "-"); @@ -390,7 +398,7 @@ snprint_dev (char * buff, size_t len, struct path * pp) } static int -snprint_dev_t (char * buff, size_t len, struct path * pp) +snprint_dev_t (char * buff, size_t len, const struct path * pp) { if (!pp || !strlen(pp->dev)) return snprintf(buff, len, "#:#"); @@ -399,7 +407,7 @@ snprint_dev_t (char * buff, size_t len, struct path * pp) } static int -snprint_offline (char * buff, size_t len, struct path * pp) +snprint_offline (char * buff, size_t len, const struct path * pp) { if (!pp || !pp->mpp) return snprintf(buff, len, "unknown"); @@ -410,7 +418,7 @@ snprint_offline (char * buff, size_t len, struct path * pp) } static int -snprint_chk_state (char * buff, size_t len, struct path * pp) +snprint_chk_state (char * buff, size_t len, const struct path * pp) { if (!pp || !pp->mpp) return snprintf(buff, len, "undef"); @@ -436,7 +444,7 @@ snprint_chk_state (char * buff, size_t len, struct path * pp) } static int -snprint_dm_path_state (char * buff, size_t len, struct path * pp) +snprint_dm_path_state (char * buff, size_t len, const struct path * pp) { if (!pp) return snprintf(buff, len, "undef"); @@ -452,14 +460,14 @@ snprint_dm_path_state (char * buff, size_t len, struct path * pp) } static int -snprint_vpr (char * buff, size_t len, struct path * pp) +snprint_vpr (char * buff, size_t len, const struct path * pp) { return snprintf(buff, len, "%s,%s", pp->vendor_id, pp->product_id); } static int -snprint_next_check (char * buff, size_t len, struct path * pp) +snprint_next_check (char * buff, size_t len, const struct path * pp) { if (!pp || !pp->mpp) return snprintf(buff, len, "orphan"); @@ -468,32 +476,27 @@ snprint_next_check (char * buff, size_t len, struct path * pp) } static int -snprint_pri (char * buff, size_t len, struct path * pp) +snprint_pri (char * buff, size_t len, const struct path * pp) { return snprint_int(buff, len, pp ? pp->priority : -1); } static int -snprint_pg_selector (char * buff, size_t len, struct pathgroup * pgp) +snprint_pg_selector (char * buff, size_t len, const struct pathgroup * pgp) { - return snprint_str(buff, len, pgp->selector); + const char *s = pgp->mpp->selector; + + return snprint_str(buff, len, s ? s : ""); } static int -snprint_pg_pri (char * buff, size_t len, struct pathgroup * pgp) -{ - /* - * path group priority is not updated for every path prio change, - * but only on switch group code path. - * - * Printing is another reason to update. - */ - path_group_prio_update(pgp); +snprint_pg_pri (char * buff, size_t len, const struct pathgroup * pgp) +{ return snprint_int(buff, len, pgp->priority); } static int -snprint_pg_state (char * buff, size_t len, struct pathgroup * pgp) +snprint_pg_state (char * buff, size_t len, const struct pathgroup * pgp) { switch (pgp->status) { case PGSTATE_ENABLED: @@ -508,19 +511,19 @@ snprint_pg_state (char * buff, size_t len, struct pathgroup * pgp) } static int -snprint_path_size (char * buff, size_t len, struct path * pp) +snprint_path_size (char * buff, size_t len, const struct path * pp) { return snprint_size(buff, len, pp->size); } int -snprint_path_serial (char * buff, size_t len, struct path * pp) +snprint_path_serial (char * buff, size_t len, const struct path * pp) { return snprint_str(buff, len, pp->serial); } static int -snprint_path_mpp (char * buff, size_t len, struct path * pp) +snprint_path_mpp (char * buff, size_t len, const struct path * pp) { if (!pp->mpp) return snprintf(buff, len, "[orphan]"); @@ -530,7 +533,7 @@ snprint_path_mpp (char * buff, size_t len, struct path * pp) } static int -snprint_host_attr (char * buff, size_t len, struct path * pp, char *attr) +snprint_host_attr (char * buff, size_t len, const struct path * pp, char *attr) { struct udev_device *host_dev = NULL; char host_id[32]; @@ -557,19 +560,19 @@ out: } int -snprint_host_wwnn (char * buff, size_t len, struct path * pp) +snprint_host_wwnn (char * buff, size_t len, const struct path * pp) { return snprint_host_attr(buff, len, pp, "node_name"); } int -snprint_host_wwpn (char * buff, size_t len, struct path * pp) +snprint_host_wwpn (char * buff, size_t len, const struct path * pp) { return snprint_host_attr(buff, len, pp, "port_name"); } int -snprint_tgt_wwpn (char * buff, size_t len, struct path * pp) +snprint_tgt_wwpn (char * buff, size_t len, const struct path * pp) { struct udev_device *rport_dev = NULL; char rport_id[32]; @@ -599,7 +602,7 @@ out: int -snprint_tgt_wwnn (char * buff, size_t len, struct path * pp) +snprint_tgt_wwnn (char * buff, size_t len, const struct path * pp) { if (pp->tgt_node_name[0] == '\0') return snprintf(buff, len, "[undef]"); @@ -607,7 +610,7 @@ snprint_tgt_wwnn (char * buff, size_t len, struct path * pp) } static int -snprint_host_adapter (char * buff, size_t len, struct path * pp) +snprint_host_adapter (char * buff, size_t len, const struct path * pp) { char adapter[SLOT_NAME_SIZE]; @@ -617,12 +620,18 @@ snprint_host_adapter (char * buff, size_t len, struct path * pp) } static int -snprint_path_checker (char * buff, size_t len, struct path * pp) +snprint_path_checker (char * buff, size_t len, const struct path * pp) { - struct checker * c = &pp->checker; + const struct checker * c = &pp->checker; return snprint_str(buff, len, c->name); } +static int +snprint_path_foreign (char * buff, size_t len, const struct path * pp) +{ + return snprintf(buff, len, "%s", "--"); +} + struct multipath_data mpd[] = { {'n', "name", 0, snprint_name}, {'w', "uuid", 0, snprint_multipath_uuid}, @@ -646,6 +655,7 @@ struct multipath_data mpd[] = { {'v', "vend", 0, snprint_multipath_vend}, {'p', "prod", 0, snprint_multipath_prod}, {'e', "rev", 0, snprint_multipath_rev}, + {'G', "foreign", 0, snprint_multipath_foreign}, {0, NULL, 0 , NULL} }; @@ -669,6 +679,7 @@ struct path_data pd[] = { {'R', "host WWPN", 0, snprint_host_wwpn}, {'r', "target WWPN", 0, snprint_tgt_wwpn}, {'a', "host adapter", 0, snprint_host_adapter}, + {'G', "foreign", 0, snprint_path_foreign}, {0, NULL, 0 , NULL} }; @@ -700,20 +711,48 @@ snprint_wildcards (char * buff, int len) } void -get_path_layout (vector pathvec, int header) +get_path_layout(vector pathvec, int header) +{ + vector gpvec = vector_convert(NULL, pathvec, struct path, + dm_path_to_gen); + _get_path_layout(gpvec, + header ? LAYOUT_RESET_HEADER : LAYOUT_RESET_ZERO); + vector_free(gpvec); +} + +static void +reset_width(int *width, enum layout_reset reset, const char *header) +{ + switch (reset) { + case LAYOUT_RESET_HEADER: + *width = strlen(header); + break; + case LAYOUT_RESET_ZERO: + *width = 0; + break; + default: + /* don't reset */ + break; + } +} + +void +_get_path_layout (const struct _vector *gpvec, enum layout_reset reset) { int i, j; char buff[MAX_FIELD_LEN]; - struct path * pp; + const struct gen_path *gp; for (j = 0; pd[j].header; j++) { - if (header) - pd[j].width = strlen(pd[j].header); - else - pd[j].width = 0; - vector_foreach_slot (pathvec, pp, i) { - pd[j].snprint(buff, MAX_FIELD_LEN, pp); + reset_width(&pd[j].width, reset, pd[j].header); + + if (gpvec == NULL) + continue; + + vector_foreach_slot (gpvec, gp, i) { + gp->ops->snprint(gp, buff, MAX_FIELD_LEN, + pd[j].wildcard); pd[j].width = MAX(pd[j].width, strlen(buff)); } } @@ -729,22 +768,35 @@ reset_multipath_layout (void) } void -get_multipath_layout (vector mpvec, int header) +get_multipath_layout (vector mpvec, int header) { + vector gmvec = vector_convert(NULL, mpvec, struct multipath, + dm_multipath_to_gen); + _get_multipath_layout(gmvec, + header ? LAYOUT_RESET_HEADER : LAYOUT_RESET_ZERO); + vector_free(gmvec); +} + +void +_get_multipath_layout (const struct _vector *gmvec, + enum layout_reset reset) { int i, j; char buff[MAX_FIELD_LEN]; - struct multipath * mpp; + const struct gen_multipath * gm; for (j = 0; mpd[j].header; j++) { - if (header) - mpd[j].width = strlen(mpd[j].header); - else - mpd[j].width = 0; - vector_foreach_slot (mpvec, mpp, i) { - mpd[j].snprint(buff, MAX_FIELD_LEN, mpp); + reset_width(&mpd[j].width, reset, mpd[j].header); + + if (gmvec == NULL) + continue; + + vector_foreach_slot (gmvec, gm, i) { + gm->ops->snprint(gm, buff, MAX_FIELD_LEN, + mpd[j].wildcard); mpd[j].width = MAX(mpd[j].width, strlen(buff)); } + condlog(4, "%s: width %d", mpd[j].header, mpd[j].width); } } @@ -760,6 +812,17 @@ mpd_lookup(char wildcard) return NULL; } +int snprint_multipath_attr(const struct gen_multipath* gm, + char *buf, int len, char wildcard) +{ + const struct multipath *mpp = gen_multipath_to_dm(gm); + struct multipath_data *mpd = mpd_lookup(wildcard); + + if (mpd == NULL) + return 0; + return mpd->snprint(buf, len, mpp); +} + static struct path_data * pd_lookup(char wildcard) { @@ -772,6 +835,17 @@ pd_lookup(char wildcard) return NULL; } +int snprint_path_attr(const struct gen_path* gp, + char *buf, int len, char wildcard) +{ + const struct path *pp = gen_path_to_dm(gp); + struct path_data *pd = pd_lookup(wildcard); + + if (pd == NULL) + return 0; + return pd->snprint(buf, len, pp); +} + static struct pathgroup_data * pgd_lookup(char wildcard) { @@ -784,12 +858,23 @@ pgd_lookup(char wildcard) return NULL; } +int snprint_pathgroup_attr(const struct gen_pathgroup* gpg, + char *buf, int len, char wildcard) +{ + const struct pathgroup *pg = gen_pathgroup_to_dm(gpg); + struct pathgroup_data *pdg = pgd_lookup(wildcard); + + if (pdg == NULL) + return 0; + return pdg->snprint(buf, len, pg); +} + int -snprint_multipath_header (char * line, int len, char * format) +snprint_multipath_header (char * line, int len, const char * format) { char * c = line; /* line cursor */ char * s = line; /* for padding */ - char * f = format; /* format string cursor */ + const char * f = format; /* format string cursor */ int fwd; struct multipath_data * data; @@ -816,12 +901,12 @@ snprint_multipath_header (char * line, int len, char * format) } int -snprint_multipath (char * line, int len, char * format, - struct multipath * mpp, int pad) +_snprint_multipath (const struct gen_multipath * gmp, + char * line, int len, const char * format, int pad) { char * c = line; /* line cursor */ char * s = line; /* for padding */ - char * f = format; /* format string cursor */ + const char * f = format; /* format string cursor */ int fwd; struct multipath_data * data; char buff[MAX_FIELD_LEN] = {}; @@ -840,7 +925,7 @@ snprint_multipath (char * line, int len, char * format, if (!(data = mpd_lookup(*f))) continue; - data->snprint(buff, MAX_FIELD_LEN, mpp); + gmp->ops->snprint(gmp, buff, MAX_FIELD_LEN, *f); PRINT(c, TAIL, "%s", buff); if (pad) PAD(data->width); @@ -852,11 +937,11 @@ snprint_multipath (char * line, int len, char * format, } int -snprint_path_header (char * line, int len, char * format) +snprint_path_header (char * line, int len, const char * format) { char * c = line; /* line cursor */ char * s = line; /* for padding */ - char * f = format; /* format string cursor */ + const char * f = format; /* format string cursor */ int fwd; struct path_data * data; @@ -883,12 +968,12 @@ snprint_path_header (char * line, int len, char * format) } int -snprint_path (char * line, int len, char * format, - struct path * pp, int pad) +_snprint_path (const struct gen_path * gp, char * line, int len, + const char * format, int pad) { char * c = line; /* line cursor */ char * s = line; /* for padding */ - char * f = format; /* format string cursor */ + const char * f = format; /* format string cursor */ int fwd; struct path_data * data; char buff[MAX_FIELD_LEN]; @@ -907,7 +992,7 @@ snprint_path (char * line, int len, char * format, if (!(data = pd_lookup(*f))) continue; - data->snprint(buff, MAX_FIELD_LEN, pp); + gp->ops->snprint(gp, buff, MAX_FIELD_LEN, *f); PRINT(c, TAIL, "%s", buff); if (pad) PAD(data->width); @@ -918,8 +1003,8 @@ snprint_path (char * line, int len, char * format, } int -snprint_pathgroup (char * line, int len, char * format, - struct pathgroup * pgp) +_snprint_pathgroup (const struct gen_pathgroup * ggp, char * line, int len, + char * format) { char * c = line; /* line cursor */ char * s = line; /* for padding */ @@ -942,7 +1027,7 @@ snprint_pathgroup (char * line, int len, char * format, if (!(data = pgd_lookup(*f))) continue; - data->snprint(buff, MAX_FIELD_LEN, pgp); + ggp->ops->snprint(ggp, buff, MAX_FIELD_LEN, *f); PRINT(c, TAIL, "%s", buff); PAD(data->width); } while (*f++); @@ -950,8 +1035,10 @@ snprint_pathgroup (char * line, int len, char * format, __endline(line, len, c); return (c - line); } +#define snprint_pathgroup(line, len, fmt, pgp) \ + _snprint_pathgroup(dm_pathgroup_to_gen(pgp), line, len, fmt) -void print_multipath_topology(struct multipath *mpp, int verbosity) +void _print_multipath_topology(const struct gen_multipath *gmp, int verbosity) { int resize; char *buff = NULL; @@ -968,7 +1055,7 @@ void print_multipath_topology(struct multipath *mpp, int verbosity) return; } - len = snprint_multipath_topology(buff, maxlen, mpp, verbosity); + len = _snprint_multipath_topology(gmp, buff, maxlen, verbosity); resize = (len == maxlen - 1); if (resize) { @@ -981,12 +1068,30 @@ void print_multipath_topology(struct multipath *mpp, int verbosity) FREE(buff); } -int snprint_multipath_topology(char *buff, int len, struct multipath *mpp, - int verbosity) +int +snprint_multipath_style(const struct gen_multipath *gmp, char *style, int len, + int verbosity) +{ + int n; + const struct multipath *mpp = gen_multipath_to_dm(gmp); + bool need_action = (verbosity > 1 && + mpp->action != ACT_NOTHING && + mpp->action != ACT_UNDEF && + mpp->action != ACT_IMPOSSIBLE); + bool need_wwid = (strncmp(mpp->alias, mpp->wwid, WWID_SIZE)); + + n = snprintf(style, len, "%s%s%s%s", + need_action ? "%A: " : "", "%n", + need_wwid ? " (%w)" : "", " %d %s"); + return MIN(n, len - 1); +} + +int _snprint_multipath_topology(const struct gen_multipath *gmp, + char *buff, int len, int verbosity) { int j, i, fwd = 0; - struct path * pp = NULL; - struct pathgroup * pgp = NULL; + const struct _vector *pgvec; + const struct gen_pathgroup *gpg; char style[64]; char * c = style; char fmt[64]; @@ -998,61 +1103,70 @@ int snprint_multipath_topology(char *buff, int len, struct multipath *mpp, reset_multipath_layout(); if (verbosity == 1) - return snprint_multipath(buff, len, "%n", mpp, 1); + return _snprint_multipath(gmp, buff, len, "%n", 1); if(isatty(1)) c += sprintf(c, "%c[%dm", 0x1B, 1); /* bold on */ - if (verbosity > 1 && - mpp->action != ACT_NOTHING && - mpp->action != ACT_UNDEF && mpp->action != ACT_IMPOSSIBLE) - c += sprintf(c, "%%A: "); - - c += sprintf(c, "%%n"); - - if (strncmp(mpp->alias, mpp->wwid, WWID_SIZE)) - c += sprintf(c, " (%%w)"); - - c += sprintf(c, " %%d %%s"); + c += gmp->ops->style(gmp, c, sizeof(style) - (c - style), + verbosity); if(isatty(1)) c += sprintf(c, "%c[%dm", 0x1B, 0); /* bold off */ - fwd += snprint_multipath(buff + fwd, len - fwd, style, mpp, 1); + fwd += _snprint_multipath(gmp, buff + fwd, len - fwd, style, 1); if (fwd >= len) return len; - fwd += snprint_multipath(buff + fwd, len - fwd, PRINT_MAP_PROPS, mpp, - 1); + fwd += _snprint_multipath(gmp, buff + fwd, len - fwd, + PRINT_MAP_PROPS, 1); if (fwd >= len) return len; - if (!mpp->pg) + pgvec = gmp->ops->get_pathgroups(gmp); + if (pgvec == NULL) return fwd; - vector_foreach_slot (mpp->pg, pgp, j) { + vector_foreach_slot (pgvec, gpg, j) { + const struct _vector *pathvec; + struct gen_path *gp; + f=fmt; - pgp->selector = mpp->selector; /* hack */ - if (j + 1 < VECTOR_SIZE(mpp->pg)) { + + if (j + 1 < VECTOR_SIZE(pgvec)) { strcpy(f, "|-+- " PRINT_PG_INDENT); } else strcpy(f, "`-+- " PRINT_PG_INDENT); - fwd += snprint_pathgroup(buff + fwd, len - fwd, fmt, pgp); - if (fwd >= len) - return len; + fwd += _snprint_pathgroup(gpg, buff + fwd, len - fwd, fmt); + + if (fwd >= len) { + fwd = len; + break; + } - vector_foreach_slot (pgp->paths, pp, i) { + pathvec = gpg->ops->get_paths(gpg); + if (pathvec == NULL) + continue; + + vector_foreach_slot (pathvec, gp, i) { f=fmt; if (*f != '|') *f=' '; f++; - if (i + 1 < VECTOR_SIZE(pgp->paths)) + if (i + 1 < VECTOR_SIZE(pathvec)) strcpy(f, " |- " PRINT_PATH_INDENT); else strcpy(f, " `- " PRINT_PATH_INDENT); - fwd += snprint_path(buff + fwd, len - fwd, fmt, pp, 1); - if (fwd >= len) - return len; + fwd += _snprint_path(gp, buff + fwd, len - fwd, fmt, 1); + if (fwd >= len) { + fwd = len; + break; + } } + gpg->ops->rel_paths(gpg, pathvec); + + if (fwd == len) + break; } + gmp->ops->rel_pathgroups(gmp, pgvec); return fwd; } @@ -1106,7 +1220,7 @@ snprint_json_elem_footer (char * buff, int len, int indent, int last) static int snprint_multipath_fields_json (char * buff, int len, - struct multipath * mpp, int last) + const struct multipath * mpp, int last) { int i, j, fwd = 0; struct path *pp; @@ -1122,7 +1236,6 @@ snprint_multipath_fields_json (char * buff, int len, vector_foreach_slot (mpp->pg, pgp, i) { - pgp->selector = mpp->selector; fwd += snprint_pathgroup(buff + fwd, len - fwd, PRINT_JSON_GROUP, pgp); if (fwd >= len) return fwd; @@ -1165,7 +1278,7 @@ snprint_multipath_fields_json (char * buff, int len, int snprint_multipath_map_json (char * buff, int len, - struct multipath * mpp, int last){ + const struct multipath * mpp, int last){ int fwd = 0; fwd += snprint_json_header(buff, len); @@ -1191,7 +1304,7 @@ snprint_multipath_map_json (char * buff, int len, } int -snprint_multipath_topology_json (char * buff, int len, struct vectors * vecs) +snprint_multipath_topology_json (char * buff, int len, const struct vectors * vecs) { int i, fwd = 0; struct multipath * mpp; @@ -1222,7 +1335,7 @@ snprint_multipath_topology_json (char * buff, int len, struct vectors * vecs) } static int -snprint_hwentry (struct config *conf, char * buff, int len, struct hwentry * hwe) +snprint_hwentry (struct config *conf, char * buff, int len, const struct hwentry * hwe) { int i; int fwd = 0; @@ -1280,7 +1393,7 @@ int snprint_hwtable(struct config *conf, char *buff, int len, vector hwtable) } static int -snprint_mpentry (struct config *conf, char * buff, int len, struct mpentry * mpe) +snprint_mpentry (struct config *conf, char * buff, int len, const struct mpentry * mpe) { int i; int fwd = 0; @@ -1332,7 +1445,7 @@ int snprint_mptable(struct config *conf, char *buff, int len, vector mptable) } int snprint_overrides(struct config *conf, char * buff, int len, - struct hwentry *overrides) + const struct hwentry *overrides) { int fwd = 0; int i; @@ -1656,7 +1769,7 @@ int snprint_blacklist_except(struct config *conf, char *buff, int len) return fwd; } -int snprint_status(char *buff, int len, struct vectors *vecs) +int snprint_status(char *buff, int len, const struct vectors *vecs) { int fwd = 0; int i; @@ -1688,7 +1801,7 @@ int snprint_status(char *buff, int len, struct vectors *vecs) } int snprint_devices(struct config *conf, char * buff, int len, - struct vectors *vecs) + const struct vectors *vecs) { DIR *blkdir; struct dirent *blkdev; @@ -1765,32 +1878,6 @@ void print_path(struct path *pp, char *style) printf("%s", line); } -void print_multipath(struct multipath *mpp, char *style) -{ - char line[MAX_LINE_LEN]; - - memset(&line[0], 0, MAX_LINE_LEN); - snprint_multipath(&line[0], MAX_LINE_LEN, style, mpp, 1); - printf("%s", line); -} - -void print_pathgroup(struct pathgroup *pgp, char *style) -{ - char line[MAX_LINE_LEN]; - - memset(&line[0], 0, MAX_LINE_LEN); - snprint_pathgroup(&line[0], MAX_LINE_LEN, style, pgp); - printf("%s", line); -} - -void print_map(struct multipath *mpp, char *params) -{ - if (mpp->size && params) - printf("0 %llu %s %s\n", - mpp->size, TGT_MPATH, params); - return; -} - void print_all_paths(vector pathvec, int banner) { print_all_paths_custo(pathvec, banner, PRINT_PATH_LONG); diff --git a/libmultipath/print.h b/libmultipath/print.h index b8c3436..7ba6438 100644 --- a/libmultipath/print.h +++ b/libmultipath/print.h @@ -1,3 +1,7 @@ +#ifndef _PRINT_H +#define _PRINT_H +#include "dm-generic.h" + #define PRINT_PATH_LONG "%w %i %d %D %p %t %T %s %o" #define PRINT_PATH_INDENT "%i %d %D %t %T %o" #define PRINT_PATH_CHECKER "%i %d %D %p %t %T %o %C" @@ -73,56 +77,80 @@ struct path_data { char wildcard; char * header; int width; - int (*snprint)(char * buff, size_t len, struct path * pp); + int (*snprint)(char * buff, size_t len, const struct path * pp); }; struct multipath_data { char wildcard; char * header; int width; - int (*snprint)(char * buff, size_t len, struct multipath * mpp); + int (*snprint)(char * buff, size_t len, const struct multipath * mpp); }; struct pathgroup_data { char wildcard; char * header; int width; - int (*snprint)(char * buff, size_t len, struct pathgroup * pgp); + int (*snprint)(char * buff, size_t len, const struct pathgroup * pgp); +}; + +enum layout_reset { + LAYOUT_RESET_NOT, + LAYOUT_RESET_ZERO, + LAYOUT_RESET_HEADER, }; +void _get_path_layout (const struct _vector *gpvec, enum layout_reset); void get_path_layout (vector pathvec, int header); +void _get_multipath_layout (const struct _vector *gmvec, enum layout_reset); void get_multipath_layout (vector mpvec, int header); -int snprint_path_header (char *, int, char *); -int snprint_multipath_header (char *, int, char *); -int snprint_path (char *, int, char *, struct path *, int); -int snprint_multipath (char *, int, char *, struct multipath *, int); -int snprint_multipath_topology (char *, int, struct multipath * mpp, - int verbosity); +int snprint_path_header (char *, int, const char *); +int snprint_multipath_header (char *, int, const char *); +int _snprint_path (const struct gen_path *, char *, int, const char *, int); +#define snprint_path(buf, len, fmt, pp, v) \ + _snprint_path(dm_path_to_gen(pp), buf, len, fmt, v) +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 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, - struct vectors * vecs); + const struct vectors * vecs); int snprint_multipath_map_json (char * buff, int len, - struct multipath * mpp, int last); + 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, struct vectors *); -int snprint_devices (struct config *, char *, int, struct vectors *); -int snprint_hwtable (struct config *, char *, int, vector); -int snprint_mptable (struct config *, char *, int, vector); -int snprint_overrides (struct config *, char *, int, struct hwentry *); -int snprint_path_serial (char *, size_t, struct path *); -int snprint_host_wwnn (char *, size_t, struct path *); -int snprint_host_wwpn (char *, size_t, struct path *); -int snprint_tgt_wwnn (char *, size_t, struct path *); -int snprint_tgt_wwpn (char *, size_t, struct path *); +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 *); + +void _print_multipath_topology (const struct gen_multipath * gmp, + int verbosity); +#define print_multipath_topology(mpp, v) \ + _print_multipath_topology(dm_multipath_to_gen(mpp), v) -void print_multipath_topology (struct multipath * mpp, int verbosity); -void print_path (struct path * pp, char * style); -void print_multipath (struct multipath * mpp, char * style); -void print_pathgroup (struct pathgroup * pgp, char * style); -void print_map (struct multipath * mpp, char * params); void print_all_paths (vector pathvec, int banner); void print_all_paths_custo (vector pathvec, int banner, char *fmt); -void print_hwtable (vector hwtable); + +int snprint_path_attr(const struct gen_path* gp, + char *buf, int len, char wildcard); +int snprint_pathgroup_attr(const struct gen_pathgroup* gpg, + char *buf, int len, char wildcard); +int snprint_multipath_attr(const struct gen_multipath* gm, + char *buf, int len, char wildcard); +int snprint_multipath_style(const struct gen_multipath *gmp, + char *style, int len, int verbosity); +#endif /* _PRINT_H */ diff --git a/libmultipath/prioritizers/Makefile b/libmultipath/prioritizers/Makefile index 0c71e63..b3cc944 100644 --- a/libmultipath/prioritizers/Makefile +++ b/libmultipath/prioritizers/Makefile @@ -38,5 +38,11 @@ install: $(LIBS) uninstall: for file in $(LIBS); do $(RM) $(DESTDIR)$(libdir)/$$file; done -clean: +clean: dep_clean $(RM) core *.a *.o *.gz *.so + +OBJS = $(LIBS:libprio%.so=%.o) alua_rtpg.o +include $(wildcard $(OBJS:.o=.d)) + +dep_clean: + $(RM) $(OBJS:.o=.d) diff --git a/libmultipath/prioritizers/path_latency.c b/libmultipath/prioritizers/path_latency.c index 9d5397e..765265c 100644 --- a/libmultipath/prioritizers/path_latency.c +++ b/libmultipath/prioritizers/path_latency.c @@ -34,6 +34,7 @@ #include "prio.h" #include "structs.h" #include "util.h" +#include "time-util.h" #define pp_pl_log(prio, fmt, args...) condlog(prio, "path_latency prio: " fmt, ##args) @@ -42,8 +43,9 @@ #define DEF_IO_NUM 100 #define MAX_BASE_NUM 10 -#define MIN_BASE_NUM 1.01 -#define DEF_BASE_NUM 1.5 +#define MIN_BASE_NUM 1.1 +// This is 10**(1/4). 4 prio steps correspond to a factor of 10. +#define DEF_BASE_NUM 1.77827941004 #define MAX_AVG_LATENCY 100000000. /* Unit: us */ #define MIN_AVG_LATENCY 1. /* Unit: us */ @@ -55,14 +57,6 @@ #define DEF_BLK_SIZE 4096 -static double lg_path_latency[MAX_IO_NUM]; - -static inline long long timeval_to_us(const struct timespec *tv) -{ - return ((long long)tv->tv_sec * USEC_PER_SEC) + - (tv->tv_nsec / NSEC_PER_USEC); -} - static int prepare_directio_read(int fd, int *blksz, char **pbuf, int *restore_flags) { @@ -198,22 +192,6 @@ out: return 0; } -double calc_standard_deviation(double *lg_path_latency, int size, - double lg_avglatency) -{ - int index; - double sum = 0; - - for (index = 0; index < size; index++) { - sum += (lg_path_latency[index] - lg_avglatency) * - (lg_path_latency[index] - lg_avglatency); - } - - sum /= (size - 1); - - return sqrt(sum); -} - /* * Do not scale the prioriy in a certain range such as [0, 1024] * because scaling will eliminate the effect of base_num. @@ -233,20 +211,16 @@ int calcPrio(double lg_avglatency, double lg_maxavglatency, int getprio(struct path *pp, char *args, unsigned int timeout) { int rc, temp; - int index = 0; int io_num = 0; double base_num = 0; double lg_avglatency, lg_maxavglatency, lg_minavglatency; double standard_deviation; double lg_toldelay = 0; - long long before, after; - struct timespec tv; int blksize; char *buf; int restore_flags = 0; double lg_base; - long long sum_latency = 0; - long long arith_mean_lat; + double sum_squares = 0; if (pp->fd < 0) return -1; @@ -259,7 +233,6 @@ int getprio(struct path *pp, char *args, unsigned int timeout) pp->dev, io_num, base_num); } - memset(lg_path_latency, 0, sizeof(lg_path_latency)); lg_base = log(base_num); lg_maxavglatency = log(MAX_AVG_LATENCY) / lg_base; lg_minavglatency = log(MIN_AVG_LATENCY) / lg_base; @@ -268,8 +241,10 @@ int getprio(struct path *pp, char *args, unsigned int timeout) temp = io_num; while (temp-- > 0) { - (void)clock_gettime(CLOCK_MONOTONIC, &tv); - before = timeval_to_us(&tv); + struct timespec tv_before, tv_after, tv_diff; + double diff, reldiff; + + (void)clock_gettime(CLOCK_MONOTONIC, &tv_before); if (do_directio_read(pp->fd, timeout, buf, blksize)) { pp_pl_log(0, "%s: path down", pp->dev); @@ -277,70 +252,51 @@ int getprio(struct path *pp, char *args, unsigned int timeout) return -1; } - (void)clock_gettime(CLOCK_MONOTONIC, &tv); - after = timeval_to_us(&tv); + (void)clock_gettime(CLOCK_MONOTONIC, &tv_after); + + timespecsub(&tv_after, &tv_before, &tv_diff); + diff = tv_diff.tv_sec * 1000 * 1000 + tv_diff.tv_nsec / 1000; + + if (diff == 0) + /* + * Avoid taking log(0). + * This unlikely case is treated as minimum - + * the sums don't increase + */ + continue; + + /* we scale by lg_base here */ + reldiff = log(diff) / lg_base; + /* * We assume that the latency complies with Log-normal * distribution. The logarithm of latency is in normal * distribution. */ - lg_path_latency[index] = log(after - before) / lg_base; - lg_toldelay += lg_path_latency[index++]; - sum_latency += after - before; + lg_toldelay += reldiff; + sum_squares += reldiff * reldiff; } cleanup_directio_read(pp->fd, buf, restore_flags); lg_avglatency = lg_toldelay / (long long)io_num; - arith_mean_lat = sum_latency / (long long)io_num; - pp_pl_log(4, "%s: arithmetic mean latency is (%lld us), geometric mean latency is (%lld us)", - pp->dev, arith_mean_lat, - (long long)pow(base_num, lg_avglatency)); if (lg_avglatency > lg_maxavglatency) { - pp_pl_log(0, + pp_pl_log(2, "%s: average latency (%lld us) is outside the thresold (%lld us)", pp->dev, (long long)pow(base_num, lg_avglatency), (long long)MAX_AVG_LATENCY); return DEFAULT_PRIORITY; } - standard_deviation = calc_standard_deviation(lg_path_latency, - index, lg_avglatency); - /* - * In calPrio(), we let prio y = f(x) = log(max, base) - log (x, base); - * So if we want to let the priority of the latency outside 2 standard - * deviations can be distinguished from the latency inside 2 standard - * deviation, in others words at most 95% are the same and at least 5% - * are different according interval estimation of normal distribution, - * we should warn the user to set the base_num to be smaller if the - * log(x_threshold, base) is small than 2 standard deviation. - * x_threshold is derived from: - * y + 1 = f(x) + 1 = f(x) + log(base, base), so x_threadshold = - * base_num; Note that we only can compare the logarithm of x_threshold - * with the standard deviation because the standard deviation is derived - * from logarithm of latency. - * - * therefore , we recommend the base_num to meet the condition : - * 1 <= 2 * standard_deviation - */ - pp_pl_log(5, "%s: standard deviation for logarithm of latency = %.6f", - pp->dev, standard_deviation); - if (standard_deviation <= 0.5) - pp_pl_log(3, "%s: the base_num(%.3lf) is too big to distinguish different priority " - "of two far-away latency. It is recommend to be set smaller", - pp->dev, base_num); - /* - * If the standard deviation is too large , we should also warn the user - */ - - if (standard_deviation > 4) - pp_pl_log(3, "%s: the base_num(%.3lf) is too small to avoid noise disturbance " - ".It is recommend to be set larger", - pp->dev, base_num); - + standard_deviation = sqrt((sum_squares - lg_toldelay * lg_avglatency) + / (io_num - 1)); rc = calcPrio(lg_avglatency, lg_maxavglatency, lg_minavglatency); + pp_pl_log(3, "%s: latency avg=%.2e uncertainty=%.1f prio=%d\n", + pp->dev, exp(lg_avglatency * lg_base), + exp(standard_deviation * lg_base), rc); + return rc; } diff --git a/libmultipath/propsel.c b/libmultipath/propsel.c index 0d29ed2..58a6a42 100644 --- a/libmultipath/propsel.c +++ b/libmultipath/propsel.c @@ -19,9 +19,11 @@ #include "discovery.h" #include "dict.h" #include "util.h" +#include "sysfs.h" #include "prioritizers/alua_rtpg.h" #include "prkey.h" #include +#include pgpolicyfn *pgpolicies[] = { NULL, @@ -353,9 +355,42 @@ out: return 0; } +static int get_dh_state(struct path *pp, char *value, size_t value_len) +{ + struct udev_device *ud; + + if (pp->udev == NULL) + return -1; + + ud = udev_device_get_parent_with_subsystem_devtype( + pp->udev, "scsi", "scsi_device"); + if (ud == NULL) + return -1; + + return sysfs_attr_get_value(ud, "dh_state", value, value_len); +} + int select_hwhandler(struct config *conf, struct multipath *mp) { char *origin; + struct path *pp; + /* dh_state is no longer than "detached" */ + char handler[12]; + char *dh_state; + int i; + + dh_state = &handler[2]; + if (mp->retain_hwhandler != RETAIN_HWHANDLER_OFF) { + vector_foreach_slot(mp->paths, pp, i) { + if (get_dh_state(pp, dh_state, sizeof(handler) - 2) > 0 + && strcmp(dh_state, "detached")) { + memcpy(handler, "1 ", 2); + mp->hwhandler = handler; + origin = "(setting: retained by kernel driver)"; + goto out; + } + } + } mp_set_hwe(hwhandler); mp_set_conf(hwhandler); @@ -462,7 +497,7 @@ detect_prio(struct config *conf, struct path * pp) if (pp->tpgs <= 0) return; - if (pp->tpgs == 2 && !check_rdac(pp)) { + if (pp->tpgs == 2 || !check_rdac(pp)) { if (sysfs_get_asymmetric_access_state(pp, buff, 512) >= 0) default_prio = PRIO_SYSFS; } @@ -520,8 +555,8 @@ int select_no_path_retry(struct config *conf, struct multipath *mp) char *origin = NULL; char buff[12]; - if (mp->flush_on_last_del == FLUSH_IN_PROGRESS) { - condlog(0, "%s: flush_on_last_del in progress", mp->alias); + if (mp->disable_queueing) { + condlog(0, "%s: queueing disabled", mp->alias); mp->no_path_retry = NO_PATH_RETRY_FAIL; return 0; } @@ -613,8 +648,6 @@ int select_flush_on_last_del(struct config *conf, struct multipath *mp) { char *origin; - if (mp->flush_on_last_del == FLUSH_IN_PROGRESS) - return 0; mp_set_mpe(flush_on_last_del); mp_set_ovr(flush_on_last_del); mp_set_hwe(flush_on_last_del); @@ -863,3 +896,18 @@ out: origin); return 0; } + +int select_ghost_delay (struct config *conf, struct multipath * mp) +{ + char *origin, buff[12]; + + mp_set_mpe(ghost_delay); + mp_set_ovr(ghost_delay); + mp_set_hwe(ghost_delay); + mp_set_conf(ghost_delay); + mp_set_default(ghost_delay, DEFAULT_GHOST_DELAY); +out: + print_off_int_undef(buff, 12, &mp->ghost_delay); + condlog(3, "%s: ghost_delay = %s %s", mp->alias, buff, origin); + return 0; +} diff --git a/libmultipath/propsel.h b/libmultipath/propsel.h index 347cb32..136f906 100644 --- a/libmultipath/propsel.h +++ b/libmultipath/propsel.h @@ -29,6 +29,7 @@ int select_marginal_path_err_sample_time(struct config *conf, struct multipath * int select_marginal_path_err_rate_threshold(struct config *conf, struct multipath *mp); int select_marginal_path_err_recheck_gap_time(struct config *conf, struct multipath *mp); int select_marginal_path_double_failed_time(struct config *conf, struct multipath *mp); +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); diff --git a/libmultipath/structs.c b/libmultipath/structs.c index 3e057f5..991095c 100644 --- a/libmultipath/structs.c +++ b/libmultipath/structs.c @@ -18,6 +18,7 @@ #include "blacklist.h" #include "prio.h" #include "prioritizers/alua_spc3.h" +#include "dm-generic.h" struct adapter_group * alloc_adaptergroup(void) @@ -100,6 +101,7 @@ alloc_path (void) pp->tpgs = TPGS_UNDEF; pp->priority = PRIO_UNDEF; checker_clear(&pp->checker); + dm_path_to_gen(pp)->ops = &dm_gen_path_ops; } return pp; } @@ -160,6 +162,7 @@ alloc_pathgroup (void) pgp = NULL; } + dm_pathgroup_to_gen(pgp)->ops = &dm_gen_pathgroup_ops; return pgp; } @@ -200,6 +203,7 @@ alloc_multipath (void) mpp->mpcontext = NULL; mpp->no_path_retry = NO_PATH_RETRY_UNDEF; mpp->fast_io_fail = MP_FAST_IO_FAIL_UNSET; + dm_multipath_to_gen(mpp)->ops = &dm_gen_multipath_ops; } return mpp; } @@ -318,6 +322,16 @@ store_pathgroup (vector pgvec, struct pathgroup * pgp) return 0; } +int add_pathgroup(struct multipath *mpp, struct pathgroup *pgp) +{ + int ret = store_pathgroup(mpp->pg, pgp); + + if (ret) + return ret; + pgp->mpp = mpp; + return 0; +} + int store_hostgroup(vector hostgroupvec, struct host_group * hgp) { @@ -374,7 +388,7 @@ find_mp_by_wwid (vector mpvec, char * wwid) } struct multipath * -find_mp_by_alias (vector mpvec, char * alias) +find_mp_by_alias (vector mpvec, const char * alias) { int i; int len; @@ -408,7 +422,7 @@ find_mp_by_str (vector mpvec, char * str) } struct path * -find_path_by_dev (vector pathvec, char * dev) +find_path_by_dev (vector pathvec, const char * dev) { int i; struct path * pp; @@ -425,7 +439,7 @@ find_path_by_dev (vector pathvec, char * dev) } struct path * -find_path_by_devt (vector pathvec, char * dev_t) +find_path_by_devt (vector pathvec, const char * dev_t) { int i; struct path * pp; @@ -497,23 +511,6 @@ first_path (struct multipath * mpp) return pgp?VECTOR_SLOT(pgp->paths, 0):NULL; } -void setup_feature(struct multipath *mpp, char *feature) -{ - if (!strncmp(feature, "queue_if_no_path", 16)) { - if (mpp->no_path_retry <= NO_PATH_RETRY_UNDEF) - mpp->no_path_retry = NO_PATH_RETRY_QUEUE; - else - condlog(1, "%s: ignoring feature queue_if_no_path because no_path_retry = %d", - mpp->alias, mpp->no_path_retry); - } else if (!strcmp(feature, "retain_attached_hw_handler")) { - if (mpp->retain_hwhandler != RETAIN_HWHANDLER_OFF) - mpp->retain_hwhandler = RETAIN_HWHANDLER_ON; - else - condlog(1, "%s: ignoring feature 'retain_attached_hw_handler'", - mpp->alias); - } -} - int add_feature(char **f, const char *n) { int c = 0, d, l; diff --git a/libmultipath/structs.h b/libmultipath/structs.h index c2cf3fb..88a4b78 100644 --- a/libmultipath/structs.h +++ b/libmultipath/structs.h @@ -6,6 +6,7 @@ #include "prio.h" #include "byteorder.h" +#include "generic.h" #define WWID_SIZE 128 #define SERIAL_SIZE 65 @@ -25,6 +26,12 @@ #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 + +/* This must be the maximum of SCSI and NVME sizes */ +#define PATH_PRODUCT_SIZE NVME_MODEL_SIZE +#define PATH_REV_SIZE NVME_REV_SIZE #define NO_PATH_RETRY_UNDEF 0 #define NO_PATH_RETRY_FAIL -1 @@ -99,7 +106,6 @@ enum flush_states { FLUSH_UNDEF = YNU_UNDEF, FLUSH_DISABLED = YNU_NO, FLUSH_ENABLED = YNU_YES, - FLUSH_IN_PROGRESS, }; enum log_checker_err_states { @@ -167,6 +173,11 @@ enum no_undef_states { NU_UNDEF = 0, }; +enum ghost_delay_states { + GHOST_DELAY_OFF = NU_NO, + GHOST_DELAY_UNDEF = NU_UNDEF, +}; + enum initialized_states { INIT_FAILED, INIT_MISSING_UDEV, @@ -210,8 +221,8 @@ struct path { struct hd_geometry geom; char wwid[WWID_SIZE]; char vendor_id[SCSI_VENDOR_SIZE]; - char product_id[SCSI_PRODUCT_SIZE]; - char rev[SCSI_REV_SIZE]; + char product_id[PATH_PRODUCT_SIZE]; + char rev[PATH_REV_SIZE]; char serial[SERIAL_SIZE]; char tgt_node_name[NODE_NAME_SIZE]; unsigned long long size; @@ -246,6 +257,7 @@ struct path { int io_err_pathfail_starttime; /* configlet pointers */ struct hwentry * hwe; + struct gen_path generic_path; }; typedef int (pgpolicyfn) (struct multipath *); @@ -267,6 +279,7 @@ struct multipath { int nr_active; /* current available(= not known as failed) paths */ int no_path_retry; /* number of retries after all paths are down */ int retry_tick; /* remaining times for retries */ + int disable_queueing; int minio; int flush_on_last_del; int attribute_flags; @@ -283,6 +296,8 @@ struct multipath { int max_sectors_kb; int force_readonly; int force_udev_reload; + int ghost_delay; + int ghost_delay_tick; unsigned int dev_loss; uid_t uid; gid_t gid; @@ -319,6 +334,7 @@ struct multipath { int prkey_source; struct be64 reservation_key; unsigned char prflag; + struct gen_multipath generic_mp; }; struct pathgroup { @@ -327,7 +343,8 @@ struct pathgroup { int priority; int enabled_paths; vector paths; - char * selector; + struct multipath *mpp; + struct gen_pathgroup generic_pg; }; struct adapter_group { @@ -366,20 +383,20 @@ int store_hostgroup(vector hostgroupvec, struct host_group *hgp); 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, char * alias); +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 path * find_path_by_devt (vector pathvec, char * devt); -struct path * find_path_by_dev (vector pathvec, char * dev); +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); int pathcountgr (struct pathgroup *, int); int pathcount (struct multipath *, int); int pathcmp (struct pathgroup *, struct pathgroup *); -void setup_feature(struct multipath *, char *); 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 22be8e0..f9dc8a8 100644 --- a/libmultipath/structs_vec.c +++ b/libmultipath/structs_vec.c @@ -16,6 +16,9 @@ #include "propsel.h" #include "discovery.h" #include "prio.h" +#include "configure.h" +#include "libdevmapper.h" +#include "io_err_stat.h" /* * creates or updates mpp->paths reading mpp->pg @@ -186,66 +189,36 @@ void remove_maps_and_stop_waiters(struct vectors *vecs) _remove_maps(vecs, STOP_WAITER); } -static struct hwentry * +void extract_hwe_from_path(struct multipath * mpp) { struct path * pp = NULL; - int pg_num = -1, p_num = -1, i; - struct pathgroup * pgp = NULL; - - condlog(3, "%s: searching paths for valid hwe", mpp->alias); + int i; - if (mpp && mpp->pg) { - vector_foreach_slot(mpp->pg, pgp, i) { - if (pgp->status == PGSTATE_ACTIVE || - pgp->status == PGSTATE_ENABLED) { - pg_num = i; - break; - } - } - if (pg_num >= 0) - pgp = VECTOR_SLOT(mpp->pg, pg_num); - } + if (mpp->hwe || !mpp->paths) + return; - if (pgp && pgp->paths) { - vector_foreach_slot(pgp->paths, pp, i) { - if (pp->dmstate == PSTATE_FAILED) - continue; - if (strlen(pp->vendor_id) > 0 && - strlen(pp->product_id) > 0 && - strlen(pp->rev) > 0) { - p_num = i; - break; - } + condlog(3, "%s: searching paths for valid hwe", mpp->alias); + /* doing this in two passes seems like paranoia to me */ + vector_foreach_slot(mpp->paths, pp, i) { + if (pp->state != PATH_UP) + continue; + if (pp->hwe) { + mpp->hwe = pp->hwe; + return; } - if (p_num >= 0) - pp = VECTOR_SLOT(pgp->paths, i); } - - if (pp) { - if (!strlen(pp->vendor_id) || - !strlen(pp->product_id) || - !strlen(pp->rev)) { - condlog(3, "%s: no device details available", pp->dev); - return NULL; - } - condlog(3, "%s: vendor = %s", pp->dev, pp->vendor_id); - condlog(3, "%s: product = %s", pp->dev, pp->product_id); - condlog(3, "%s: rev = %s", pp->dev, pp->rev); - if (!pp->hwe) { - struct config *conf = get_multipath_config(); - - condlog(3, "searching hwtable"); - pp->hwe = find_hwe(conf->hwtable, pp->vendor_id, - pp->product_id, pp->rev); - put_multipath_config(conf); + vector_foreach_slot(mpp->paths, pp, i) { + if (pp->state == PATH_UP) + continue; + if (pp->hwe) { + mpp->hwe = pp->hwe; + return; } } - - return pp?pp->hwe:NULL; } -static int +int update_multipath_table (struct multipath *mpp, vector pathvec, int is_daemon) { char params[PARAMS_SIZE] = {0}; @@ -266,7 +239,7 @@ update_multipath_table (struct multipath *mpp, vector pathvec, int is_daemon) return 0; } -static int +int update_multipath_status (struct multipath *mpp) { char status[PARAMS_SIZE] = {0}; @@ -335,76 +308,67 @@ update_multipath_strings(struct multipath *mpp, vector pathvec, int is_daemon) return 0; } -void set_no_path_retry(struct config *conf, struct multipath *mpp) +void enter_recovery_mode(struct multipath *mpp) +{ + struct config *conf = get_multipath_config(); + + /* + * Enter retry mode. + * meaning of +1: retry_tick may be decremented in checkerloop before + * starting retry. + */ + mpp->stat_queueing_timeouts++; + mpp->retry_tick = mpp->no_path_retry * conf->checkint + 1; + condlog(1, "%s: Entering recovery mode: max_retries=%d", + mpp->alias, mpp->no_path_retry); + put_multipath_config(conf); +} + +static void set_no_path_retry(struct multipath *mpp) { - mpp->retry_tick = 0; + char is_queueing = 0; + mpp->nr_active = pathcount(mpp, PATH_UP) + pathcount(mpp, PATH_GHOST); - select_no_path_retry(conf, mpp); + if (mpp->features && strstr(mpp->features, "queue_if_no_path")) + is_queueing = 1; switch (mpp->no_path_retry) { case NO_PATH_RETRY_UNDEF: break; case NO_PATH_RETRY_FAIL: - dm_queue_if_no_path(mpp->alias, 0); + if (is_queueing) + dm_queue_if_no_path(mpp->alias, 0); break; case NO_PATH_RETRY_QUEUE: - dm_queue_if_no_path(mpp->alias, 1); + if (!is_queueing) + dm_queue_if_no_path(mpp->alias, 1); break; default: - dm_queue_if_no_path(mpp->alias, 1); - if (mpp->nr_active == 0) { - struct config *conf = get_multipath_config(); - /* Enter retry mode */ - mpp->retry_tick = mpp->no_path_retry * conf->checkint; - condlog(1, "%s: Entering recovery mode: max_retries=%d", - mpp->alias, mpp->no_path_retry); - put_multipath_config(conf); - } + if (mpp->nr_active > 0) { + mpp->retry_tick = 0; + dm_queue_if_no_path(mpp->alias, 1); + } else if (is_queueing && mpp->retry_tick == 0) + enter_recovery_mode(mpp); break; } } int __setup_multipath(struct vectors *vecs, struct multipath *mpp, - int reset, int is_daemon) + int reset) { - struct config *conf; - if (dm_get_info(mpp->alias, &mpp->dmi)) { /* Error accessing table */ condlog(3, "%s: cannot access table", mpp->alias); goto out; } - if (!dm_map_present(mpp->alias)) { - /* Table has been removed */ - condlog(3, "%s: table does not exist", mpp->alias); - goto out; - } - - if (update_multipath_strings(mpp, vecs->pathvec, is_daemon)) { + if (update_multipath_strings(mpp, vecs->pathvec, 1)) { condlog(0, "%s: failed to setup multipath", mpp->alias); goto out; } - set_multipath_wwid(mpp); - conf = get_multipath_config(); - mpp->mpe = find_mpe(conf->mptable, mpp->wwid); - put_multipath_config(conf); - condlog(3, "%s: discover", mpp->alias); - - if (!mpp->hwe) - mpp->hwe = extract_hwe_from_path(mpp); - if (!mpp->hwe) { - condlog(3, "%s: no hardware entry found, using defaults", - mpp->alias); - } if (reset) { - conf = get_multipath_config(); - select_rr_weight(conf, mpp); - select_pgfailback(conf, mpp); - set_no_path_retry(conf, mpp); - select_flush_on_last_del(conf, mpp); - put_multipath_config(conf); + set_no_path_retry(mpp); if (VECTOR_SIZE(mpp->paths) != 0) dm_cancel_deferred_remove(mpp); } @@ -415,9 +379,82 @@ out: return 1; } -struct multipath *add_map_without_path (struct vectors *vecs, char *alias) +void +sync_map_state(struct multipath *mpp) +{ + struct pathgroup *pgp; + struct path *pp; + unsigned int i, j; + + if (!mpp->pg) + return; + + vector_foreach_slot (mpp->pg, pgp, i){ + vector_foreach_slot (pgp->paths, pp, j){ + if (pp->state == PATH_UNCHECKED || + pp->state == PATH_WILD || + pp->state == PATH_DELAYED) + continue; + if (mpp->ghost_delay_tick > 0) + continue; + if ((pp->dmstate == PSTATE_FAILED || + pp->dmstate == PSTATE_UNDEF) && + (pp->state == PATH_UP || pp->state == PATH_GHOST)) + dm_reinstate_path(mpp->alias, pp->dev_t); + else if ((pp->dmstate == PSTATE_ACTIVE || + pp->dmstate == PSTATE_UNDEF) && + (pp->state == PATH_DOWN || + pp->state == PATH_SHAKY)) + dm_fail_path(mpp->alias, pp->dev_t); + } + } +} + +int +update_map (struct multipath *mpp, struct vectors *vecs) +{ + int retries = 3; + char params[PARAMS_SIZE] = {0}; + +retry: + condlog(4, "%s: updating new map", mpp->alias); + if (adopt_paths(vecs->pathvec, mpp)) { + condlog(0, "%s: failed to adopt paths for new map update", + mpp->alias); + retries = -1; + goto fail; + } + verify_paths(mpp, vecs); + mpp->action = ACT_RELOAD; + + extract_hwe_from_path(mpp); + if (setup_map(mpp, params, PARAMS_SIZE, vecs)) { + condlog(0, "%s: failed to setup new map in update", mpp->alias); + retries = -1; + goto fail; + } + if (domap(mpp, params, 1) <= 0 && retries-- > 0) { + condlog(0, "%s: map_udate sleep", mpp->alias); + sleep(1); + goto retry; + } + dm_lib_release(); + +fail: + if (setup_multipath(vecs, mpp)) + return 1; + + sync_map_state(mpp); + + if (retries < 0) + condlog(0, "%s: failed reload in new map update", mpp->alias); + return 0; +} + +struct multipath *add_map_without_path (struct vectors *vecs, const char *alias) { struct multipath * mpp = alloc_multipath(); + struct config *conf; if (!mpp) return NULL; @@ -428,10 +465,18 @@ struct multipath *add_map_without_path (struct vectors *vecs, char *alias) mpp->alias = STRDUP(alias); - if (setup_multipath(vecs, mpp)) - return NULL; /* mpp freed in setup_multipath */ + if (dm_get_info(mpp->alias, &mpp->dmi)) { + condlog(3, "%s: cannot access table", mpp->alias); + goto out; + } + set_multipath_wwid(mpp); + conf = get_multipath_config(); + mpp->mpe = find_mpe(conf->mptable, mpp->wwid); + put_multipath_config(conf); - if (adopt_paths(vecs->pathvec, mpp)) + if (update_multipath_table(mpp, vecs->pathvec, 1)) + goto out; + if (update_multipath_status(mpp)) goto out; if (!vector_alloc_slot(vecs->mpvec)) @@ -439,6 +484,9 @@ struct multipath *add_map_without_path (struct vectors *vecs, char *alias) vector_set_slot(vecs->mpvec, mpp); + if (update_map(mpp, vecs) != 0) /* map removed */ + return NULL; + if (start_waiter_thread(mpp, vecs)) goto out; @@ -554,7 +602,7 @@ int update_multipath (struct vectors *vecs, char *mapname, int reset) return 2; } - if (__setup_multipath(vecs, mpp, reset, 1)) + if (__setup_multipath(vecs, mpp, reset)) return 1; /* mpp freed in setup_multipath */ /* @@ -598,21 +646,9 @@ int update_multipath (struct vectors *vecs, char *mapname, int reset) void update_queue_mode_del_path(struct multipath *mpp) { if (--mpp->nr_active == 0) { - if (mpp->no_path_retry > 0) { - struct config *conf = get_multipath_config(); - - /* - * Enter retry mode. - * meaning of +1: retry_tick may be decremented in - * checkerloop before starting retry. - */ - mpp->stat_queueing_timeouts++; - mpp->retry_tick = mpp->no_path_retry * - conf->checkint + 1; - condlog(1, "%s: Entering recovery mode: max_retries=%d", - mpp->alias, mpp->no_path_retry); - put_multipath_config(conf); - } else if (mpp->no_path_retry != NO_PATH_RETRY_QUEUE) + if (mpp->no_path_retry > 0) + enter_recovery_mode(mpp); + else if (mpp->no_path_retry != NO_PATH_RETRY_QUEUE) mpp->stat_map_failures++; } condlog(2, "%s: remaining active paths: %d", mpp->alias, mpp->nr_active); diff --git a/libmultipath/structs_vec.h b/libmultipath/structs_vec.h index 46f30af..3749eb6 100644 --- a/libmultipath/structs_vec.h +++ b/libmultipath/structs_vec.h @@ -11,7 +11,7 @@ struct vectors { vector mpvec; }; -void set_no_path_retry(struct config *conf, struct multipath *mpp); +void enter_recovery_mode(struct multipath *mpp); int adopt_paths (vector pathvec, struct multipath * mpp); void orphan_paths (vector pathvec, struct multipath * mpp); @@ -20,21 +20,27 @@ void orphan_path (struct path * pp, const char *reason); int verify_paths(struct multipath * mpp, struct vectors * vecs); int update_mpp_paths(struct multipath * mpp, vector pathvec); int __setup_multipath (struct vectors * vecs, struct multipath * mpp, - int reset, int is_daemon); -#define setup_multipath(vecs, mpp) __setup_multipath(vecs, mpp, 1, 1) + int reset); +#define setup_multipath(vecs, mpp) __setup_multipath(vecs, mpp, 1) int update_multipath_strings (struct multipath *mpp, vector pathvec, int is_daemon); +void extract_hwe_from_path(struct multipath * mpp); void remove_map (struct multipath * mpp, struct vectors * vecs, int purge_vec); void remove_map_and_stop_waiter (struct multipath * mpp, struct vectors * vecs, int purge_vec); void remove_maps (struct vectors * vecs); void remove_maps_and_stop_waiters (struct vectors * vecs); -struct multipath * add_map_without_path (struct vectors * vecs, char * alias); +void sync_map_state (struct multipath *); +int update_map (struct multipath *mpp, struct vectors *vecs); +struct multipath * add_map_without_path (struct vectors * vecs, const char * alias); struct multipath * add_map_with_path (struct vectors * vecs, struct path * pp, int add_vec); int update_multipath (struct vectors *vecs, char *mapname, int reset); void update_queue_mode_del_path(struct multipath *mpp); 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); #endif /* _STRUCTS_VEC_H */ diff --git a/libmultipath/uevent.c b/libmultipath/uevent.c index 80bf1dd..685ef33 100644 --- a/libmultipath/uevent.c +++ b/libmultipath/uevent.c @@ -52,6 +52,7 @@ #include "util.h" #include "config.h" #include "blacklist.h" +#include "devmapper.h" #define MAX_ACCUMULATION_COUNT 2048 #define MAX_ACCUMULATION_TIME 30*1000 @@ -104,29 +105,70 @@ uevq_cleanup(struct list_head *tmpq) } } +static const char* uevent_get_env_var(const struct uevent *uev, + const char *attr) +{ + int i, len; + const char *p = NULL; + + if (attr == NULL) + goto invalid; + + len = strlen(attr); + if (len == 0) + goto invalid; + + for (i = 0; uev->envp[i] != NULL; i++) { + const char *var = uev->envp[i]; + + if (strlen(var) > len && + !memcmp(var, attr, len) && var[len] == '=') { + p = var + len + 1; + break; + } + } + + condlog(4, "%s: %s -> '%s'", __func__, attr, p); + return p; + +invalid: + condlog(2, "%s: empty variable name", __func__); + return NULL; +} + +static int uevent_get_env_positive_int(const struct uevent *uev, + const char *attr) +{ + const char *p = uevent_get_env_var(uev, attr); + char *q; + int ret; + + if (p == NULL || *p == '\0') + return -1; + + ret = strtoul(p, &q, 10); + if (*q != '\0' || ret < 0) { + condlog(2, "%s: invalid %s: '%s'", __func__, attr, p); + return -1; + } + return ret; +} + void uevent_get_wwid(struct uevent *uev) { - int i; - char *uid_attribute; + const char *uid_attribute; + const char *val; struct config * conf; conf = get_multipath_config(); uid_attribute = parse_uid_attribute_by_attrs(conf->uid_attrs, uev->kernel); put_multipath_config(conf); - if (!uid_attribute) - return; - - for (i = 0; uev->envp[i] != NULL; i++) { - if (!strncmp(uev->envp[i], uid_attribute, strlen(uid_attribute)) && - strlen(uev->envp[i]) > strlen(uid_attribute) && - uev->envp[i][strlen(uid_attribute)] == '=') { - uev->wwid = uev->envp[i] + strlen(uid_attribute) + 1; - break; - } - } - free(uid_attribute); + val = uevent_get_env_var(uev, uid_attribute); + if (val) + uev->wwid = val; + FREE_CONST(uid_attribute); } bool @@ -850,107 +892,52 @@ out: return err; } -int uevent_get_major(struct uevent *uev) +int uevent_get_major(const struct uevent *uev) { - char *p, *q; - int i, major = -1; - - for (i = 0; uev->envp[i] != NULL; i++) { - if (!strncmp(uev->envp[i], "MAJOR", 5) && strlen(uev->envp[i]) > 6) { - p = uev->envp[i] + 6; - major = strtoul(p, &q, 10); - if (p == q) { - condlog(2, "invalid major '%s'", p); - major = -1; - } - break; - } - } - return major; + return uevent_get_env_positive_int(uev, "MAJOR"); } -int uevent_get_minor(struct uevent *uev) +int uevent_get_minor(const struct uevent *uev) { - char *p, *q; - int i, minor = -1; + return uevent_get_env_positive_int(uev, "MINOR"); +} - for (i = 0; uev->envp[i] != NULL; i++) { - if (!strncmp(uev->envp[i], "MINOR", 5) && strlen(uev->envp[i]) > 6) { - p = uev->envp[i] + 6; - minor = strtoul(p, &q, 10); - if (p == q) { - condlog(2, "invalid minor '%s'", p); - minor = -1; - } - break; - } - } - return minor; +int uevent_get_disk_ro(const struct uevent *uev) +{ + return uevent_get_env_positive_int(uev, "DISK_RO"); } -int uevent_get_disk_ro(struct uevent *uev) +static const char *uevent_get_dm_str(const struct uevent *uev, char *attr) { - char *p, *q; - int i, ro = -1; + const char *tmp = uevent_get_env_var(uev, attr); - for (i = 0; uev->envp[i] != NULL; i++) { - if (!strncmp(uev->envp[i], "DISK_RO", 7) && strlen(uev->envp[i]) > 8) { - p = uev->envp[i] + 8; - ro = strtoul(p, &q, 10); - if (p == q) { - condlog(2, "invalid read_only setting '%s'", p); - ro = -1; - } - break; - } - } - return ro; + if (tmp == NULL) + return NULL; + return strdup(tmp); } -char *uevent_get_dm_name(struct uevent *uev) +const char *uevent_get_dm_name(const struct uevent *uev) { - char *p = NULL; - int i; - - for (i = 0; uev->envp[i] != NULL; i++) { - if (!strncmp(uev->envp[i], "DM_NAME", 7) && - strlen(uev->envp[i]) > 8) { - p = MALLOC(strlen(uev->envp[i] + 8) + 1); - strcpy(p, uev->envp[i] + 8); - break; - } - } - return p; + return uevent_get_dm_str(uev, "DM_NAME"); } -char *uevent_get_dm_path(struct uevent *uev) +const char *uevent_get_dm_path(const struct uevent *uev) { - char *p = NULL; - int i; + return uevent_get_dm_str(uev, "DM_PATH"); +} - for (i = 0; uev->envp[i] != NULL; i++) { - if (!strncmp(uev->envp[i], "DM_PATH", 7) && - strlen(uev->envp[i]) > 8) { - p = MALLOC(strlen(uev->envp[i] + 8) + 1); - strcpy(p, uev->envp[i] + 8); - break; - } - } - return p; +const char *uevent_get_dm_action(const struct uevent *uev) +{ + return uevent_get_dm_str(uev, "DM_ACTION"); } -char *uevent_get_dm_action(struct uevent *uev) +bool uevent_is_mpath(const struct uevent *uev) { - char *p = NULL; - int i; + const char *uuid = uevent_get_env_var(uev, "DM_UUID"); - for (i = 0; uev->envp[i] != NULL; i++) { - if (!strncmp(uev->envp[i], "DM_ACTION", 9) && - strlen(uev->envp[i]) > 10) { - p = MALLOC(strlen(uev->envp[i] + 10) + 1); - strcpy(p, uev->envp[i] + 10); - break; - } - } - return p; + if (uuid == NULL) + return false; + if (strncmp(uuid, UUID_PREFIX, UUID_PREFIX_LEN)) + return false; + return uuid[UUID_PREFIX_LEN] != '\0'; } diff --git a/libmultipath/uevent.h b/libmultipath/uevent.h index 6f5af0a..cb5347e 100644 --- a/libmultipath/uevent.h +++ b/libmultipath/uevent.h @@ -23,7 +23,7 @@ struct uevent { char *devpath; char *action; char *kernel; - char *wwid; + const char *wwid; unsigned long seqnum; char *envp[HOTPLUG_NUM_ENVP]; }; @@ -33,11 +33,12 @@ int is_uevent_busy(void); int uevent_listen(struct udev *udev); int uevent_dispatch(int (*store_uev)(struct uevent *, void * trigger_data), void * trigger_data); -int uevent_get_major(struct uevent *uev); -int uevent_get_minor(struct uevent *uev); -int uevent_get_disk_ro(struct uevent *uev); -char *uevent_get_dm_name(struct uevent *uev); -char *uevent_get_dm_path(struct uevent *uev); -char *uevent_get_dm_action(struct uevent *uev); +int uevent_get_major(const struct uevent *uev); +int uevent_get_minor(const struct uevent *uev); +int uevent_get_disk_ro(const struct uevent *uev); +const char *uevent_get_dm_name(const struct uevent *uev); +const char *uevent_get_dm_path(const struct uevent *uev); +const char *uevent_get_dm_action(const struct uevent *uev); +bool uevent_is_mpath(const struct uevent *uev); #endif /* _UEVENT_H */ diff --git a/libmultipath/util.c b/libmultipath/util.c index 0b43d29..d3dd3eb 100644 --- a/libmultipath/util.c +++ b/libmultipath/util.c @@ -32,7 +32,7 @@ strchop(char *str) int basenamecpy (const char * str1, char * str2, int str2len) { - char *p; + const char *p; if (!str1 || !strlen(str1)) return 0; @@ -43,7 +43,7 @@ basenamecpy (const char * str1, char * str2, int str2len) if (!str2) return 0; - p = (char *)str1 + (strlen(str1) - 1); + p = str1 + (strlen(str1) - 1); while (*--p != '/' && p != str1) continue; @@ -454,7 +454,7 @@ int safe_write(int fd, const void *buf, size_t count) return -errno; } count -= r; - buf = (char *)buf + r; + buf = (const char *)buf + r; } return 0; } diff --git a/libmultipath/vector.h b/libmultipath/vector.h index 5cfd4d0..6018b57 100644 --- a/libmultipath/vector.h +++ b/libmultipath/vector.h @@ -42,10 +42,40 @@ typedef struct _vector *vector; #define vector_foreach_slot_backwards(v,p,i) \ for (i = VECTOR_SIZE(v); i > 0 && ((p) = (v)->slot[i-1]); i--) +#define identity(x) (x) +/* + * Given a vector vec with elements of given type, + * return a newly allocated vector with elements conv(e) for each element + * e in vec. "conv" may be a macro or a function. + * Use "identity" for a simple copy. + */ +#define vector_convert(new, vec, type, conv) \ + ({ \ + const struct _vector *__v = (vec); \ + vector __t = (new); \ + type *__j; \ + int __i; \ + \ + if (__t == NULL) \ + __t = vector_alloc(); \ + if (__t != NULL) { \ + vector_foreach_slot(__v, __j, __i) { \ + if (vector_alloc_slot(__t) == NULL) { \ + vector_free(__t); \ + __t = NULL; \ + break; \ + } \ + vector_set_slot(__t, conv(__j)); \ + } \ + } \ + __t; \ + }) + /* Prototypes */ extern vector vector_alloc(void); extern void *vector_alloc_slot(vector v); extern void vector_free(vector v); +#define vector_free_const(x) vector_free((vector)(long)(x)) extern void free_strvec(vector strvec); extern void vector_set_slot(vector v, void *value); extern void vector_del_slot(vector v, int slot); diff --git a/libmultipath/version.h b/libmultipath/version.h index ca628b2..6b028ee 100644 --- a/libmultipath/version.h +++ b/libmultipath/version.h @@ -20,8 +20,8 @@ #ifndef _VERSION_H #define _VERSION_H -#define VERSION_CODE 0x000704 -#define DATE_CODE 0x0b0f11 +#define VERSION_CODE 0x000705 +#define DATE_CODE 0x030712 #define PROG "multipath-tools" diff --git a/mpathpersist/Makefile b/mpathpersist/Makefile index 6e5acd3..5126801 100644 --- a/mpathpersist/Makefile +++ b/mpathpersist/Makefile @@ -22,9 +22,14 @@ install: $(INSTALL_PROGRAM) -d $(DESTDIR)$(man8dir) $(INSTALL_PROGRAM) -m 644 $(EXEC).8.gz $(DESTDIR)$(man8dir) -clean: +clean: dep_clean $(RM) core *.o $(EXEC) *.gz +include $(wildcard $(OBJS:.o=.d)) + uninstall: $(RM) $(DESTDIR)$(bindir)/$(EXEC) $(RM) $(DESTDIR)$(man8dir)/$(EXEC).8.gz + +dep_clean: + $(RM) $(OBJS:.o=.d) diff --git a/multipath/11-dm-mpath.rules b/multipath/11-dm-mpath.rules index 91d8058..03ac5da 100644 --- a/multipath/11-dm-mpath.rules +++ b/multipath/11-dm-mpath.rules @@ -100,7 +100,8 @@ ENV{DM_UDEV_LOW_PRIORITY_FLAG}!="1", OPTIONS+="link_priority=50" TEST=="/usr/lib/udev/kpartx_id", \ IMPORT{program}=="kpartx_id %M %m $env{DM_UUID}" -ENV{DM_TYPE}=="?*", SYMLINK+="disk/by-id/$env{DM_TYPE}-$env{DM_NAME}" +ENV{DM_TYPE}=="?*", ENV{DM_SERIAL}=="?*", \ + SYMLINK+="disk/by-id/$env{DM_TYPE}-$env{DM_SERIAL}" ENV{DM_WWN}=="?*", SYMLINK+="disk/by-id/wwn-$env{DM_WWN}" LABEL="mpath_end" diff --git a/multipath/Makefile b/multipath/Makefile index 468c056..0828a8f 100644 --- a/multipath/Makefile +++ b/multipath/Makefile @@ -14,7 +14,7 @@ OBJS = main.o all: $(EXEC) -$(EXEC): $(OBJS) +$(EXEC): $(OBJS) $(multipathdir)/libmultipath.so $(mpathcmddir)/libmpathcmd.so $(CC) $(CFLAGS) $(OBJS) -o $(EXEC) $(LDFLAGS) $(LIBDEPS) $(GZIP) $(EXEC).8 > $(EXEC).8.gz $(GZIP) $(EXEC).conf.5 > $(EXEC).conf.5.gz @@ -37,5 +37,10 @@ uninstall: $(RM) $(DESTDIR)$(man8dir)/$(EXEC).8.gz $(RM) $(DESTDIR)$(man5dir)/$(EXEC).conf.5.gz -clean: +clean: dep_clean $(RM) core *.o $(EXEC) *.gz + +include $(wildcard $(OBJS:.o=.d)) + +dep_clean: + $(RM) $(OBJS:.o=.d) diff --git a/multipath/main.c b/multipath/main.c index bffe065..8732cf8 100644 --- a/multipath/main.c +++ b/multipath/main.c @@ -47,6 +47,7 @@ #include "discovery.h" #include "debug.h" #include "switchgroup.h" +#include "dm-generic.h" #include "print.h" #include "alias.h" #include "configure.h" @@ -58,6 +59,7 @@ #include "wwids.h" #include "uxsock.h" #include "mpath_cmd.h" +#include "foreign.h" int logsink; struct udev *udev; @@ -149,7 +151,7 @@ usage (char * progname) } static int -update_paths (struct multipath * mpp) +update_paths (struct multipath * mpp, int quick) { int i, j; struct pathgroup * pgp; @@ -171,9 +173,12 @@ update_paths (struct multipath * mpp) * path is not in sysfs anymore */ pp->chkrstate = pp->state = PATH_DOWN; + pp->offline = 1; continue; } pp->mpp = mpp; + if (quick) + continue; conf = get_multipath_config(); if (pathinfo(pp, conf, DI_ALL)) pp->state = PATH_UNCHECKED; @@ -181,6 +186,8 @@ update_paths (struct multipath * mpp) continue; } pp->mpp = mpp; + if (quick) + continue; if (pp->state == PATH_UNCHECKED || pp->state == PATH_WILD) { conf = get_multipath_config(); @@ -238,8 +245,7 @@ get_dm_mpvec (enum mpath_cmds cmd, vector curmp, vector pathvec, char * refwwid) * If not in "fast list mode", we need to fetch information * about them */ - if (cmd != CMD_LIST_SHORT) - update_paths(mpp); + update_paths(mpp, (cmd == CMD_LIST_SHORT)); if (cmd == CMD_LIST_LONG) mpp->bestpg = select_path_group(mpp); @@ -256,6 +262,14 @@ get_dm_mpvec (enum mpath_cmds cmd, vector curmp, vector pathvec, char * refwwid) if (cmd == CMD_CREATE) reinstate_paths(mpp); } + + if (cmd == CMD_LIST_SHORT || cmd == CMD_LIST_LONG) { + struct config *conf = get_multipath_config(); + + print_foreign_topology(conf->verbosity); + put_multipath_config(conf); + } + return 0; } @@ -325,7 +339,8 @@ static int check_usable_paths(struct config *conf, } } found: - condlog(2, "%s:%s usable paths found", devpath, r == 0 ? "" : " no"); + condlog(r == 0 ? 3 : 2, "%s:%s usable paths found", + devpath, r == 0 ? "" : " no"); free: FREE(mapname); free_multipath(mpp, FREE_PATHS); @@ -458,6 +473,7 @@ configure (struct config *conf, enum mpath_cmds cmd, print_all_paths(pathvec, 1); get_path_layout(pathvec, 0); + foreign_path_layout(); if (get_dm_mpvec(cmd, curmp, pathvec, refwwid)) goto out; @@ -578,6 +594,64 @@ get_dev_type(char *dev) { return DEV_NONE; } +/* + * Some multipath commands are dangerous to run while multipathd is running. + * For example, "multipath -r" may apply a modified configuration to the kernel, + * while multipathd is still using the old configuration, leading to + * inconsistent state. + * + * It is safer to use equivalent multipathd client commands instead. + */ +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; + int n, r = 0; + + fd = mpath_connect(); + if (fd == -1) + return 0; + + p = command; + *p = '\0'; + n = sizeof(command); + + if (cmd == CMD_CREATE && conf->force_reload == FORCE_RELOAD_YES) { + p += snprintf(p, n, "reconfigure"); + } + /* Add other translations here */ + + if (strlen(command) == 0) + /* No command found, no need to delegate */ + return 0; + else 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) { + condlog(1, "error in multipath command %s: %s", + command, strerror(errno)); + goto out; + } + + if (reply != NULL && *reply != '\0' && strcmp(reply, "ok\n")) + printf("%s", reply); + r = 1; + +out: + FREE(reply); + close(fd); + if (r < 0) + exit(1); + return r; +} + int main (int argc, char *argv[]) { @@ -757,6 +831,8 @@ main (int argc, char *argv[]) condlog(0, "failed to initialize prioritizers"); goto out; } + /* Failing here is non-fatal */ + init_foreign(conf->multipath_dir); if (cmd == CMD_USABLE_PATHS) { r = check_usable_paths(conf, dev, dev_type); goto out; @@ -781,10 +857,15 @@ main (int argc, char *argv[]) } else mpath_disconnect(fd); } + if (cmd == CMD_REMOVE_WWID && !dev) { condlog(0, "the -w option requires a device"); goto out; } + + if (delegate_to_multipathd(cmd, dev, dev_type, conf)) + exit(0); + if (cmd == CMD_RESET_WWIDS) { struct multipath * mpp; int i; @@ -827,6 +908,7 @@ out: dm_lib_release(); dm_lib_exit(); + cleanup_foreign(); cleanup_prio(); cleanup_checkers(); diff --git a/multipath/multipath.conf.5 b/multipath/multipath.conf.5 index 36551b4..ab151e7 100644 --- a/multipath/multipath.conf.5 +++ b/multipath/multipath.conf.5 @@ -867,7 +867,7 @@ accounting process for the path will last for \fImarginal_path_err_sample_time\fR. If the rate of IO error on a particular path is greater than the \fImarginal_path_err_rate_threshold\fR, then the path will not reinstate for -\fImarginal_path_err_rate_threshold\fR seconds unless there is only one +\fImarginal_path_err_recheck_gap_time\fR seconds unless there is only one active path. After \fImarginal_path_err_recheck_gap_time\fR expires, the path will be requeueed for rechecking. If checking result is good enough, the path will be reinstated. @@ -1046,6 +1046,19 @@ The default is: \fB\fR .RE . . +.TP +.B ghost_delay +Sets the number of seconds that multipath will wait after creating a device +with only ghost paths before marking it ready for use in systemd. This gives +the active paths time to appear before the multipath runs the hardware handler +to switch the ghost paths to active ones. Setting this to \fI0\fR or \fIon\fR +makes multipath immediately mark a device with only ghost paths as ready. +.RS +.TP +The default is \fBno\fR +.RE +. +. .\" ---------------------------------------------------------------------------- .SH "blacklist section" .\" ---------------------------------------------------------------------------- @@ -1188,6 +1201,8 @@ are taken from the \fIdefaults\fR or \fIdevices\fR section: .B skip_kpartx .TP .B max_sectors_kb +.TP +.B ghost_delay .RE .PD .LP @@ -1317,6 +1332,8 @@ section: .B skip_kpartx .TP .B max_sectors_kb +.TP +.B ghost_delay .RE .PD .LP @@ -1389,6 +1406,8 @@ the values are taken from the \fIdevices\fR or \fIdefaults\fR sections: .B delay_wait_checks .TP .B skip_kpartx +.TP +.B ghost_delay .RE .PD .LP diff --git a/multipath/multipath.rules b/multipath/multipath.rules index bc1a852..6f8ee2b 100644 --- a/multipath/multipath.rules +++ b/multipath/multipath.rules @@ -1,7 +1,7 @@ # Set DM_MULTIPATH_DEVICE_PATH if the device should be handled by multipath SUBSYSTEM!="block", GOTO="end_mpath" ACTION!="add|change", GOTO="end_mpath" -KERNEL!="sd*|dasd*", GOTO="end_mpath" +KERNEL!="sd*|dasd*|nvme*", GOTO="end_mpath" IMPORT{cmdline}="nompath" ENV{nompath}=="?*", GOTO="end_mpath" diff --git a/multipathd/Makefile b/multipathd/Makefile index e6f140b..4c9d296 100644 --- a/multipathd/Makefile +++ b/multipathd/Makefile @@ -28,7 +28,7 @@ EXEC = multipathd all : $(EXEC) -$(EXEC): $(OBJS) +$(EXEC): $(OBJS) $(multipathdir)/libmultipath.so $(mpathcmddir)/libmpathcmd.so $(CC) $(CFLAGS) $(OBJS) $(LDFLAGS) -o $(EXEC) $(LIBDEPS) $(GZIP) $(EXEC).8 > $(EXEC).8.gz @@ -49,5 +49,10 @@ uninstall: $(RM) $(DESTDIR)$(unitdir)/$(EXEC).service $(RM) $(DESTDIR)$(unitdir)/$(EXEC).socket -clean: +clean: dep_clean $(RM) core *.o $(EXEC) *.gz + +include $(wildcard $(OBJS:.o=.d)) + +dep_clean: + $(RM) $(OBJS:.o=.d) diff --git a/multipathd/cli.c b/multipathd/cli.c index deb72cb..f10f862 100644 --- a/multipathd/cli.c +++ b/multipathd/cli.c @@ -480,7 +480,7 @@ parse_cmd (char * cmd, char ** reply, int * len, void * data, int timeout ) /* * execute handler */ - if (clock_gettime(CLOCK_MONOTONIC, &tmo) == 0) { + if (clock_gettime(CLOCK_REALTIME, &tmo) == 0) { tmo.tv_sec += timeout; } else { tmo.tv_sec = 0; diff --git a/multipathd/cli.h b/multipathd/cli.h index d289167..021f25b 100644 --- a/multipathd/cli.h +++ b/multipathd/cli.h @@ -1,3 +1,6 @@ +#ifndef _CLI_H_ +#define _CLI_H_ + #include enum { @@ -126,3 +129,5 @@ void free_handlers (void); int cli_init (void); void cli_exit(void); char * key_generator (const char * str, int state); + +#endif /* _CLI_H_ */ diff --git a/multipathd/cli_handlers.c b/multipathd/cli_handlers.c index e232fb2..60ec48b 100644 --- a/multipathd/cli_handlers.c +++ b/multipathd/cli_handlers.c @@ -16,16 +16,18 @@ #include "configure.h" #include "blacklist.h" #include "debug.h" +#include "dm-generic.h" #include "print.h" #include "sysfs.h" #include #include #include "util.h" #include "prkey.h" - +#include "propsel.h" #include "main.h" #include "cli.h" #include "uevent.h" +#include "foreign.h" int show_paths (char ** r, int * len, struct vectors * vecs, char * style, @@ -34,11 +36,13 @@ show_paths (char ** r, int * len, struct vectors * vecs, char * style, int i; struct path * pp; char * c; - char * reply; + char * reply, * header; unsigned int maxlen = INITIAL_REPLY_LEN; int again = 1; get_path_layout(vecs->pathvec, 1); + foreign_path_layout(); + reply = MALLOC(maxlen); while (again) { @@ -47,18 +51,29 @@ show_paths (char ** r, int * len, struct vectors * vecs, char * style, c = reply; - if (pretty && VECTOR_SIZE(vecs->pathvec) > 0) + if (pretty) c += snprint_path_header(c, reply + maxlen - c, style); + header = c; vector_foreach_slot(vecs->pathvec, pp, i) c += snprint_path(c, reply + maxlen - c, style, pp, pretty); + c += snprint_foreign_paths(c, reply + maxlen - c, + style, pretty); + again = ((c - reply) == (maxlen - 1)); REALLOC_REPLY(reply, again, maxlen); } + + if (pretty && c == header) { + /* No output - clear header */ + *reply = '\0'; + c = reply; + } + *r = reply; *len = (int)(c - reply + 1); return 0; @@ -133,6 +148,8 @@ show_maps_topology (char ** r, int * len, struct vectors * vecs) int again = 1; get_path_layout(vecs->pathvec, 0); + foreign_path_layout(); + reply = MALLOC(maxlen); while (again) { @@ -149,11 +166,13 @@ show_maps_topology (char ** r, int * len, struct vectors * vecs) c += snprint_multipath_topology(c, reply + maxlen - c, mpp, 2); } + c += snprint_foreign_topology(c, reply + maxlen - c, 2); again = ((c - reply) == (maxlen - 1)); REALLOC_REPLY(reply, again, maxlen); } + *r = reply; *len = (int)(c - reply + 1); return 0; @@ -498,12 +517,14 @@ show_maps (char ** r, int *len, struct vectors * vecs, char * style, { int i; struct multipath * mpp; - char * c; + char * c, *header; char * reply; unsigned int maxlen = INITIAL_REPLY_LEN; int again = 1; get_multipath_layout(vecs->mpvec, 1); + foreign_multipath_layout(); + reply = MALLOC(maxlen); while (again) { @@ -511,9 +532,10 @@ show_maps (char ** r, int *len, struct vectors * vecs, char * style, return 1; c = reply; - if (pretty && VECTOR_SIZE(vecs->mpvec) > 0) + if (pretty) c += snprint_multipath_header(c, reply + maxlen - c, style); + header = c; vector_foreach_slot(vecs->mpvec, mpp, i) { if (update_multipath(vecs, mpp->alias, 0)) { @@ -522,12 +544,20 @@ show_maps (char ** r, int *len, struct vectors * vecs, char * style, } c += snprint_multipath(c, reply + maxlen - c, style, mpp, pretty); - } + } + c += snprint_foreign_multipaths(c, reply + maxlen - c, + style, pretty); again = ((c - reply) == (maxlen - 1)); REALLOC_REPLY(reply, again, maxlen); } + + if (pretty && c == header) { + /* No output - clear header */ + *reply = '\0'; + c = reply; + } *r = reply; *len = (int)(c - reply + 1); return 0; @@ -864,7 +894,7 @@ 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); + setup_map(mpp, params, PARAMS_SIZE, vecs); mpp->action = ACT_RESIZE; mpp->force_udev_reload = 1; if (domap(mpp, params, 1) <= 0) { @@ -973,6 +1003,7 @@ cli_restore_queueing(void *v, char **reply, int *len, void *data) char * mapname = get_keyparam(v, MAP); struct multipath *mpp; int minor; + struct config *conf; mapname = convert_dev(mapname, 0); condlog(2, "%s: restore map queueing (operator)", mapname); @@ -986,15 +1017,19 @@ cli_restore_queueing(void *v, char **reply, int *len, void *data) return 1; } + conf = get_multipath_config(); + mpp->disable_queueing = 0; + select_no_path_retry(conf, mpp); + put_multipath_config(conf); + if (mpp->no_path_retry != NO_PATH_RETRY_UNDEF && mpp->no_path_retry != NO_PATH_RETRY_FAIL) { dm_queue_if_no_path(mpp->alias, 1); - if (mpp->nr_active > 0) - mpp->retry_tick = 0; - else { - struct config *conf = get_multipath_config(); - mpp->retry_tick = mpp->no_path_retry * conf->checkint; - put_multipath_config(conf); + if (mpp->no_path_retry > 0) { + if (mpp->nr_active > 0) + mpp->retry_tick = 0; + else + enter_recovery_mode(mpp); } } return 0; @@ -1009,15 +1044,18 @@ cli_restore_all_queueing(void *v, char **reply, int *len, void *data) condlog(2, "restore queueing (operator)"); vector_foreach_slot(vecs->mpvec, mpp, i) { + struct config *conf = get_multipath_config(); + mpp->disable_queueing = 0; + select_no_path_retry(conf, mpp); + put_multipath_config(conf); if (mpp->no_path_retry != NO_PATH_RETRY_UNDEF && mpp->no_path_retry != NO_PATH_RETRY_FAIL) { dm_queue_if_no_path(mpp->alias, 1); - if (mpp->nr_active > 0) - mpp->retry_tick = 0; - else { - struct config *conf = get_multipath_config(); - mpp->retry_tick = mpp->no_path_retry * conf->checkint; - put_multipath_config(conf); + if (mpp->no_path_retry > 0) { + if (mpp->nr_active > 0) + mpp->retry_tick = 0; + else + enter_recovery_mode(mpp); } } } @@ -1046,7 +1084,9 @@ cli_disable_queueing(void *v, char **reply, int *len, void *data) if (mpp->nr_active == 0) mpp->stat_map_failures++; - mpp->retry_tick = -1; + mpp->retry_tick = 0; + mpp->no_path_retry = NO_PATH_RETRY_FAIL; + mpp->disable_queueing = 1; dm_queue_if_no_path(mpp->alias, 0); return 0; } @@ -1062,7 +1102,9 @@ cli_disable_all_queueing(void *v, char **reply, int *len, void *data) vector_foreach_slot(vecs->mpvec, mpp, i) { if (mpp->nr_active == 0) mpp->stat_map_failures++; - mpp->retry_tick = -1; + mpp->retry_tick = 0; + mpp->no_path_retry = NO_PATH_RETRY_FAIL; + mpp->disable_queueing = 1; dm_queue_if_no_path(mpp->alias, 0); } return 0; diff --git a/multipathd/main.c b/multipathd/main.c index 31ce923..327cc19 100644 --- a/multipathd/main.c +++ b/multipathd/main.c @@ -39,8 +39,6 @@ static int use_watchdog; #endif -int uxsock_timeout; - /* * libmultipath */ @@ -86,6 +84,7 @@ int uxsock_timeout; #include "waiter.h" #include "io_err_stat.h" #include "wwids.h" +#include "foreign.h" #include "../third-party/valgrind/drd.h" #define FILE_NAME_SIZE 256 @@ -106,6 +105,7 @@ struct mpath_event_param }; int logsink; +int uxsock_timeout; int verbosity; int bindings_read_only; int ignore_new_devs; @@ -252,8 +252,9 @@ need_switch_pathgroup (struct multipath * mpp, int refresh) struct path * pp; unsigned int i, j; struct config *conf; + int bestpg; - if (!mpp || mpp->pgfailback == -FAILBACK_MANUAL) + if (!mpp) return 0; /* @@ -272,8 +273,11 @@ need_switch_pathgroup (struct multipath * mpp, int refresh) if (!mpp->pg || VECTOR_SIZE(mpp->paths) == 0) return 0; - mpp->bestpg = select_path_group(mpp); + bestpg = select_path_group(mpp); + if (mpp->pgfailback == -FAILBACK_MANUAL) + return 0; + mpp->bestpg = bestpg; if (mpp->bestpg != mpp->nextpg) return 1; @@ -338,35 +342,6 @@ coalesce_maps(struct vectors *vecs, vector nmpv) return 0; } -void -sync_map_state(struct multipath *mpp) -{ - struct pathgroup *pgp; - struct path *pp; - unsigned int i, j; - - if (!mpp->pg) - return; - - vector_foreach_slot (mpp->pg, pgp, i){ - vector_foreach_slot (pgp->paths, pp, j){ - if (pp->state == PATH_UNCHECKED || - pp->state == PATH_WILD || - pp->state == PATH_DELAYED) - continue; - if ((pp->dmstate == PSTATE_FAILED || - pp->dmstate == PSTATE_UNDEF) && - (pp->state == PATH_UP || pp->state == PATH_GHOST)) - dm_reinstate_path(mpp->alias, pp->dev_t); - else if ((pp->dmstate == PSTATE_ACTIVE || - pp->dmstate == PSTATE_UNDEF) && - (pp->state == PATH_DOWN || - pp->state == PATH_SHAKY)) - dm_fail_path(mpp->alias, pp->dev_t); - } - } -} - static void sync_maps_state(vector mpvec) { @@ -414,51 +389,10 @@ flush_map(struct multipath * mpp, struct vectors * vecs, int nopaths) return 0; } -int -update_map (struct multipath *mpp, struct vectors *vecs) -{ - int retries = 3; - char params[PARAMS_SIZE] = {0}; - -retry: - condlog(4, "%s: updating new map", mpp->alias); - if (adopt_paths(vecs->pathvec, mpp)) { - condlog(0, "%s: failed to adopt paths for new map update", - mpp->alias); - retries = -1; - goto fail; - } - verify_paths(mpp, vecs); - mpp->flush_on_last_del = FLUSH_UNDEF; - mpp->action = ACT_RELOAD; - - if (setup_map(mpp, params, PARAMS_SIZE)) { - condlog(0, "%s: failed to setup new map in update", mpp->alias); - retries = -1; - goto fail; - } - if (domap(mpp, params, 1) <= 0 && retries-- > 0) { - condlog(0, "%s: map_udate sleep", mpp->alias); - sleep(1); - goto retry; - } - dm_lib_release(); - -fail: - if (setup_multipath(vecs, mpp)) - return 1; - - sync_map_state(mpp); - - if (retries < 0) - condlog(0, "%s: failed reload in new map update", mpp->alias); - return 0; -} - static int uev_add_map (struct uevent * uev, struct vectors * vecs) { - char *alias; + const char *alias; int major = -1, minor = -1, rc; condlog(3, "%s: add map (uevent)", uev->kernel); @@ -479,22 +413,23 @@ uev_add_map (struct uevent * uev, struct vectors * vecs) pthread_testcancel(); rc = ev_add_map(uev->kernel, alias, vecs); lock_cleanup_pop(vecs->lock); - FREE(alias); + FREE_CONST(alias); return rc; } +/* + * ev_add_map expects that the multipath device already exists in kernel + * before it is called. It just adds a device to multipathd or updates an + * existing device. + */ int -ev_add_map (char * dev, char * alias, struct vectors * vecs) +ev_add_map (char * dev, const char * alias, struct vectors * vecs) { - char * refwwid; struct multipath * mpp; - int map_present; - int r = 1, delayed_reconfig, reassign_maps; + int delayed_reconfig, reassign_maps; struct config *conf; - map_present = dm_map_present(alias); - - if (map_present && !dm_is_mpath(alias)) { + if (!dm_is_mpath(alias)) { condlog(4, "%s: not a multipath map", alias); return 0; } @@ -539,39 +474,20 @@ ev_add_map (char * dev, char * alias, struct vectors * vecs) /* * now we can register the map */ - if (map_present) { - if ((mpp = add_map_without_path(vecs, alias))) { - sync_map_state(mpp); - condlog(2, "%s: devmap %s registered", alias, dev); - return 0; - } else { - condlog(2, "%s: uev_add_map failed", dev); - return 1; - } - } - r = get_refwwid(CMD_NONE, dev, DEV_DEVMAP, vecs->pathvec, &refwwid); - - if (refwwid) { - r = coalesce_paths(vecs, NULL, refwwid, FORCE_RELOAD_NONE, - CMD_NONE); - dm_lib_release(); + if ((mpp = add_map_without_path(vecs, alias))) { + sync_map_state(mpp); + condlog(2, "%s: devmap %s registered", alias, dev); + return 0; + } else { + condlog(2, "%s: ev_add_map failed", dev); + return 1; } - - if (!r) - condlog(2, "%s: devmap %s added", alias, dev); - else if (r == 2) - condlog(2, "%s: uev_add_map %s blacklisted", alias, dev); - else - condlog(0, "%s: uev_add_map %s failed", alias, dev); - - FREE(refwwid); - return r; } static int uev_remove_map (struct uevent * uev, struct vectors * vecs) { - char *alias; + const char *alias; int minor; struct multipath *mpp; @@ -603,7 +519,7 @@ uev_remove_map (struct uevent * uev, struct vectors * vecs) remove_map_and_stop_waiter(mpp, vecs, 1); out: lock_cleanup_pop(vecs->lock); - FREE(alias); + FREE_CONST(alias); return 0; } @@ -651,7 +567,7 @@ uev_add_path (struct uevent *uev, struct vectors * vecs, int need_do_map) if (pp) { int r; - condlog(0, "%s: spurious uevent, path already in pathvec", + condlog(2, "%s: spurious uevent, path already in pathvec", uev->kernel); if (!pp->mpp && !strlen(pp->wwid)) { condlog(3, "%s: reinitialize path", uev->kernel); @@ -738,7 +654,8 @@ ev_add_path (struct path * pp, struct vectors * vecs, int need_do_map) mpp = find_mp_by_wwid(vecs->mpvec, pp->wwid); if (mpp && mpp->wait_for_udev && (pathcount(mpp, PATH_UP) > 0 || - (pathcount(mpp, PATH_GHOST) > 0 && pp->tpgs != TPGS_IMPLICIT))) { + (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); mpp->wait_for_udev = 2; @@ -766,8 +683,8 @@ rescan: goto fail; /* leave path added to pathvec */ verify_paths(mpp, vecs); - mpp->flush_on_last_del = FLUSH_UNDEF; mpp->action = ACT_RELOAD; + extract_hwe_from_path(mpp); } else { if (!should_multipath(pp, vecs->pathvec)) { orphan_path(pp, "only one path"); @@ -799,7 +716,7 @@ rescan: /* * push the map to the device-mapper */ - if (setup_map(mpp, params, PARAMS_SIZE)) { + if (setup_map(mpp, params, PARAMS_SIZE, vecs)) { condlog(0, "%s: failed to setup map for addition of new " "path %s", mpp->alias, pp->dev); goto fail_map; @@ -868,6 +785,8 @@ uev_remove_path (struct uevent *uev, struct vectors * vecs, int need_do_map) int ret; condlog(2, "%s: remove path (uevent)", uev->kernel); + delete_foreign(uev->udev); + pthread_cleanup_push(cleanup_lock, &vecs->lock); lock(&vecs->lock); pthread_testcancel(); @@ -920,7 +839,7 @@ ev_remove_path (struct path *pp, struct vectors * vecs, int need_do_map) condlog(2, "%s Last path deleted, disabling queueing", mpp->alias); mpp->retry_tick = 0; mpp->no_path_retry = NO_PATH_RETRY_FAIL; - mpp->flush_on_last_del = FLUSH_IN_PROGRESS; + mpp->disable_queueing = 1; mpp->stat_map_failures++; dm_queue_if_no_path(mpp->alias, 0); } @@ -936,7 +855,7 @@ ev_remove_path (struct path *pp, struct vectors * vecs, int need_do_map) */ } - if (setup_map(mpp, params, PARAMS_SIZE)) { + if (setup_map(mpp, params, PARAMS_SIZE, vecs)) { condlog(0, "%s: failed to setup map for" " removal of path %s", mpp->alias, pp->dev); goto fail; @@ -987,12 +906,27 @@ fail: static int uev_update_path (struct uevent *uev, struct vectors * vecs) { - int ro, retval = 0; + int ro, retval = 0, rc; struct path * pp; struct config *conf; int disable_changed_wwids; int needs_reinit = 0; + switch ((rc = change_foreign(uev->udev))) { + case FOREIGN_OK: + /* known foreign path, ignore event */ + return 0; + case FOREIGN_IGNORED: + break; + case FOREIGN_ERR: + condlog(3, "%s: error in change_foreign", __func__); + break; + default: + condlog(1, "%s: return code %d of change_forein is unsupported", + __func__, rc); + break; + } + conf = get_multipath_config(); disable_changed_wwids = conf->disable_changed_wwids; put_multipath_config(conf); @@ -1071,9 +1005,9 @@ out: static int uev_pathfail_check(struct uevent *uev, struct vectors *vecs) { - char *action = NULL, *devt = NULL; + const char *action = NULL, *devt = NULL; struct path *pp; - int r; + int r = 1; action = uevent_get_dm_action(uev); if (!action) @@ -1090,17 +1024,19 @@ uev_pathfail_check(struct uevent *uev, struct vectors *vecs) lock(&vecs->lock); pthread_testcancel(); pp = find_path_by_devt(vecs->pathvec, devt); + if (!pp) + goto out_lock; r = io_err_stat_handle_pathfail(pp); - lock_cleanup_pop(vecs->lock); - if (r) condlog(3, "io_err_stat: %s: cannot handle pathfail uevent", pp->dev); - FREE(devt); - FREE(action); - return 0; +out_lock: + lock_cleanup_pop(vecs->lock); + FREE_CONST(devt); + FREE_CONST(action); + return r; out: - FREE(action); + FREE_CONST(action); return 1; } @@ -1114,8 +1050,11 @@ map_discovery (struct vectors * vecs) return 1; vector_foreach_slot (vecs->mpvec, mpp, i) - if (setup_multipath(vecs, mpp)) + if (update_multipath_table(mpp, vecs->pathvec, 1) || + update_multipath_status(mpp)) { + remove_map(mpp, vecs, 1); i--; + } return 0; } @@ -1187,6 +1126,13 @@ uev_trigger (struct uevent * uev, void * trigger_data) * are not fully initialised then. */ if (!strncmp(uev->kernel, "dm-", 3)) { + if (!uevent_is_mpath(uev)) { + if (!strncmp(uev->action, "change", 6)) + (void)add_foreign(uev->udev); + else if (!strncmp(uev->action, "remove", 6)) + (void)delete_foreign(uev->udev); + goto out; + } if (!strncmp(uev->action, "change", 6)) { r = uev_add_map(uev, vecs); @@ -1197,11 +1143,8 @@ uev_trigger (struct uevent * uev, void * trigger_data) * cess. */ uev_pathfail_check(uev, vecs); - goto out; - } - if (!strncmp(uev->action, "remove", 6)) { + } else if (!strncmp(uev->action, "remove", 6)) { r = uev_remove_map(uev, vecs); - goto out; } goto out; } @@ -1462,6 +1405,28 @@ missing_uev_wait_tick(struct vectors *vecs) } } +static void +ghost_delay_tick(struct vectors *vecs) +{ + struct multipath * mpp; + unsigned int i; + + vector_foreach_slot (vecs->mpvec, mpp, i) { + if (mpp->ghost_delay_tick <= 0) + continue; + if (--mpp->ghost_delay_tick <= 0) { + condlog(0, "%s: timed out waiting for active path", + mpp->alias); + mpp->force_udev_reload = 1; + if (update_map(mpp, vecs) != 0) { + /* update_map removed map */ + i--; + continue; + } + } + } +} + static void defered_failback_tick (vector mpvec) { @@ -1814,16 +1779,21 @@ check_path (struct vectors * vecs, struct path * pp, int ticks) pp->tick = pp->checkint; } } - else if (newstate == PATH_DOWN) { - int log_checker_err; + else if (newstate != PATH_UP && newstate != PATH_GHOST) { + if (pp->dmstate == PSTATE_ACTIVE || + pp->dmstate == PSTATE_UNDEF) + fail_path(pp, 0); + if (newstate == PATH_DOWN) { + int log_checker_err; - conf = get_multipath_config(); - log_checker_err = conf->log_checker_err; - put_multipath_config(conf); - if (log_checker_err == LOG_CHKR_ERR_ONCE) - LOG_MSG(3, checker_message(&pp->checker)); - else - LOG_MSG(2, checker_message(&pp->checker)); + conf = get_multipath_config(); + log_checker_err = conf->log_checker_err; + put_multipath_config(conf); + if (log_checker_err == LOG_CHKR_ERR_ONCE) + LOG_MSG(3, checker_message(&pp->checker)); + else + LOG_MSG(2, checker_message(&pp->checker)); + } } pp->state = newstate; @@ -1935,6 +1905,7 @@ checkerloop (void *ap) defered_failback_tick(vecs->mpvec); retry_count_tick(vecs->mpvec); missing_uev_wait_tick(vecs); + ghost_delay_tick(vecs); lock_cleanup_pop(vecs->lock); if (count) @@ -1970,7 +1941,7 @@ checkerloop (void *ap) diff_time.tv_sec); } } - + check_foreign(); post_config_state(DAEMON_IDLE); conf = get_multipath_config(); strict_timing = conf->strict_timing; @@ -2010,7 +1981,7 @@ checkerloop (void *ap) } int -configure (struct vectors * vecs, int start_waiters) +configure (struct vectors * vecs) { struct multipath * mpp; struct path * pp; @@ -2109,11 +2080,9 @@ configure (struct vectors * vecs, int start_waiters) i--; continue; } - if (start_waiters) { - if (start_waiter_thread(mpp, vecs)) { - remove_map(mpp, vecs, 1); - i--; - } + if (start_waiter_thread(mpp, vecs)) { + remove_map(mpp, vecs, 1); + i--; } } return 0; @@ -2159,6 +2128,7 @@ reconfigure (struct vectors * vecs) free_pathvec(vecs->pathvec, FREE_PATHS); vecs->pathvec = NULL; + delete_all_foreign(); /* Re-read any timezone changes */ tzset(); @@ -2180,7 +2150,7 @@ reconfigure (struct vectors * vecs) rcu_assign_pointer(multipath_conf, conf); call_rcu(&old->rcu, rcu_free_config); - configure(vecs, 1); + configure(vecs); return 0; @@ -2221,12 +2191,15 @@ signal_set(int signo, void (*func) (int)) } void -handle_signals(void) +handle_signals(bool nonfatal) { if (exit_sig) { condlog(2, "exit (signal)"); + exit_sig = 0; exit_daemon(); } + if (!nonfatal) + return; if (reconfig_sig) { condlog(2, "reconfigure (signal)"); set_config_state(DAEMON_CONFIGURE); @@ -2237,7 +2210,6 @@ handle_signals(void) log_reset("multipathd"); pthread_mutex_unlock(&logq_lock); } - exit_sig = 0; reconfig_sig = 0; log_reset_sig = 0; } @@ -2271,10 +2243,13 @@ signal_init(void) { sigset_t set; - sigemptyset(&set); - sigaddset(&set, SIGUSR2); + /* block all signals */ + sigfillset(&set); + /* SIGPIPE occurs if logging fails */ + sigdelset(&set, SIGPIPE); pthread_sigmask(SIG_SETMASK, &set, NULL); + /* Other signals will be unblocked in the uxlsnr thread */ signal_set(SIGHUP, sighup); signal_set(SIGUSR1, sigusr1); signal_set(SIGUSR2, sigusr2); @@ -2370,7 +2345,7 @@ child (void * param) setup_thread_attr(&misc_attr, 64 * 1024, 0); setup_thread_attr(&uevent_attr, DEFAULT_UEVENT_STACKSIZE * 1024, 0); setup_thread_attr(&waiter_attr, 32 * 1024, 1); - setup_thread_attr(&io_err_stat_attr, 32 * 1024, 1); + setup_thread_attr(&io_err_stat_attr, 32 * 1024, 0); if (logsink == 1) { setup_thread_attr(&log_attr, 64 * 1024, 0); @@ -2410,6 +2385,9 @@ child (void * param) condlog(0, "failed to initialize prioritizers"); goto failed; } + /* Failing this is non-fatal */ + + init_foreign(conf->multipath_dir); setlogmask(LOG_UPTO(conf->verbosity + 3)); @@ -2493,10 +2471,6 @@ child (void * param) /* * start threads */ - rc = start_io_err_stat_thread(vecs); - if (rc) - goto failed; - if ((rc = pthread_create(&check_thr, &misc_attr, checkerloop, vecs))) { condlog(0,"failed to create checker loop thread: %d", rc); goto failed; @@ -2546,8 +2520,6 @@ child (void * param) remove_maps_and_stop_waiters(vecs); unlock(&vecs->lock); - stop_io_err_stat_thread(); - pthread_cancel(check_thr); pthread_cancel(uevent_thr); pthread_cancel(uxlsnr_thr); @@ -2558,6 +2530,8 @@ child (void * param) pthread_join(uxlsnr_thr, NULL); pthread_join(uevq_thr, NULL); + stop_io_err_stat_thread(); + lock(&vecs->lock); free_pathvec(vecs->pathvec, FREE_PATHS); vecs->pathvec = NULL; @@ -2567,6 +2541,7 @@ child (void * param) FREE(vecs); vecs = NULL; + cleanup_foreign(); cleanup_checkers(); cleanup_prio(); diff --git a/multipathd/main.h b/multipathd/main.h index 094b04f..0e9c5e3 100644 --- a/multipathd/main.h +++ b/multipathd/main.h @@ -24,9 +24,8 @@ int need_to_delay_reconfig (struct vectors *); int reconfigure (struct vectors *); int ev_add_path (struct path *, struct vectors *, int); int ev_remove_path (struct path *, struct vectors *, int); -int ev_add_map (char *, char *, struct vectors *); +int ev_add_map (char *, const char *, struct vectors *); int ev_remove_map (char *, char *, int, struct vectors *); -void sync_map_state (struct multipath *); 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, @@ -39,6 +38,6 @@ int mpath_pr_event_handle(struct path *pp); void * mpath_pr_event_handler_fn (void * ); int update_map_pr(struct multipath *mpp); void * mpath_pr_event_handler_fn (void * pathp ); -void handle_signals(void); +void handle_signals(bool); #endif /* MAIN_H */ diff --git a/multipathd/multipathd.service b/multipathd/multipathd.service index fd66cf6..ba24983 100644 --- a/multipathd/multipathd.service +++ b/multipathd/multipathd.service @@ -1,7 +1,7 @@ [Unit] Description=Device-Mapper Multipath Device Controller Wants=systemd-udev-trigger.service systemd-udev-settle.service -Before=iscsi.service iscsid.service lvm2-lvmetad.service lvm2-activation-early.service +Before=iscsi.service iscsid.service lvm2-activation-early.service Before=local-fs-pre.target blk-availability.service After=multipathd.socket systemd-udev-trigger.service systemd-udev-settle.service DefaultDependencies=no @@ -16,6 +16,7 @@ LimitCORE=infinity ExecStartPre=-/sbin/modprobe -a scsi_dh_alua scsi_dh_emc scsi_dh_rdac dm-multipath ExecStart=/sbin/multipathd -d -s ExecReload=/sbin/multipathd reconfigure +TasksMax=infinity [Install] WantedBy=sysinit.target diff --git a/multipathd/multipathd.socket b/multipathd/multipathd.socket index 921706d..0ed4a1f 100644 --- a/multipathd/multipathd.socket +++ b/multipathd/multipathd.socket @@ -5,3 +5,6 @@ Before=sockets.target [Socket] ListenStream=@/org/kernel/linux/storage/multipathd + +[Install] +WantedBy=sockets.target diff --git a/multipathd/uxlsnr.c b/multipathd/uxlsnr.c index 98ac25a..0531061 100644 --- a/multipathd/uxlsnr.c +++ b/multipathd/uxlsnr.c @@ -102,14 +102,21 @@ static void new_client(int ux_sock) /* * kill off a dead client */ -static void dead_client(struct client *c) +static void _dead_client(struct client *c) { - pthread_mutex_lock(&client_lock); + int fd = c->fd; list_del_init(&c->node); - pthread_mutex_unlock(&client_lock); - close(c->fd); c->fd = -1; FREE(c); + close(fd); +} + +static void dead_client(struct client *c) +{ + pthread_cleanup_push(cleanup_lock, &client_lock); + pthread_mutex_lock(&client_lock); + _dead_client(c); + pthread_cleanup_pop(1); } void free_polls (void) @@ -139,6 +146,18 @@ void check_timeout(struct timespec start_time, char *inbuf, void uxsock_cleanup(void *arg) { + struct client *client_loop; + struct client *client_tmp; + int ux_sock = (int)arg; + + close(ux_sock); + + pthread_mutex_lock(&client_lock); + list_for_each_entry_safe(client_loop, client_tmp, &clients, node) { + _dead_client(client_loop); + } + pthread_mutex_unlock(&client_lock); + cli_exit(); free_polls(); } @@ -162,7 +181,7 @@ void * uxsock_listen(uxsock_trigger_fn uxsock_trigger, void * trigger_data) return NULL; } - pthread_cleanup_push(uxsock_cleanup, NULL); + pthread_cleanup_push(uxsock_cleanup, (void *)ux_sock); condlog(3, "uxsock: startup listener"); polls = (struct pollfd *)MALLOC((MIN_POLLS + 1) * sizeof(struct pollfd)); @@ -170,11 +189,11 @@ void * uxsock_listen(uxsock_trigger_fn uxsock_trigger, void * trigger_data) condlog(0, "uxsock: failed to allocate poll fds"); return NULL; } - sigemptyset(&mask); - sigaddset(&mask, SIGINT); - sigaddset(&mask, SIGTERM); - sigaddset(&mask, SIGHUP); - sigaddset(&mask, SIGUSR1); + sigfillset(&mask); + sigdelset(&mask, SIGINT); + sigdelset(&mask, SIGTERM); + sigdelset(&mask, SIGHUP); + sigdelset(&mask, SIGUSR1); while (1) { struct client *c, *tmp; int i, poll_count, num_clients; @@ -221,9 +240,10 @@ void * uxsock_listen(uxsock_trigger_fn uxsock_trigger, void * trigger_data) /* most of our life is spent in this call */ poll_count = ppoll(polls, i, &sleep_time, &mask); + handle_signals(false); if (poll_count == -1) { if (errno == EINTR) { - handle_signals(); + handle_signals(true); continue; } @@ -233,7 +253,7 @@ void * uxsock_listen(uxsock_trigger_fn uxsock_trigger, void * trigger_data) } if (poll_count == 0) { - handle_signals(); + handle_signals(true); continue; } @@ -292,6 +312,8 @@ void * uxsock_listen(uxsock_trigger_fn uxsock_trigger, void * trigger_data) FREE(inbuf); } } + /* see if we got a non-fatal signal */ + handle_signals(true); /* see if we got a new client */ if (polls[0].revents & POLLIN) { @@ -300,6 +322,5 @@ void * uxsock_listen(uxsock_trigger_fn uxsock_trigger, void * trigger_data) } pthread_cleanup_pop(1); - close(ux_sock); return NULL; } diff --git a/tests/Makefile b/tests/Makefile new file mode 100644 index 0000000..f6b5583 --- /dev/null +++ b/tests/Makefile @@ -0,0 +1,30 @@ +include ../Makefile.inc + +CFLAGS += $(BIN_CFLAGS) -I$(multipathdir) -I$(mpathcmddir) +LIBDEPS += -L$(multipathdir) -lmultipath -lcmocka + +TESTS := uevent + +.SILENT: $(TESTS:%=%.o) +.PRECIOUS: $(TESTS:%=%-test) + +%-test: %.o globals.c $(multipathdir)/libmultipath.so + @$(CC) -o $@ $< $(LDFLAGS) $(LIBDEPS) + +%.out: %-test + @echo == running $< == + @LD_LIBRARY_PATH=$(multipathdir):$(mpathcmddir) ./$< >$@ + +all: $(TESTS:%=%.out) + +clean: dep_clean + rm -f $(TESTS:%=%-test) $(TESTS:%=%.out) $(TESTS:%=%.o) + +OBJS = $(TESTS:%=%.o) +include $(wildcard $(OBJS:.o=.d)) + + + + +dep_clean: + $(RM) $(OBJS:.o=.d) diff --git a/tests/globals.c b/tests/globals.c new file mode 100644 index 0000000..96a5651 --- /dev/null +++ b/tests/globals.c @@ -0,0 +1,17 @@ +#include "structs.h" +#include "config.h" + +/* Required globals */ +struct udev *udev; +int logsink = 0; +struct config conf = { + .uid_attrs = "sd:ID_BOGUS", +}; + +struct config *get_multipath_config(void) +{ + return &conf; +} + +void put_multipath_config(struct config* c) +{} diff --git a/tests/uevent.c b/tests/uevent.c new file mode 100644 index 0000000..b7d6458 --- /dev/null +++ b/tests/uevent.c @@ -0,0 +1,304 @@ +/* + * Copyright (c) 2018 SUSE Linux GmbH + * + * 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, write to the Free Software + * + */ + +#include +#include +#include +#include +#include +#include +#include "list.h" +#include "uevent.h" + +#include "globals.c" + +/* Private prototypes missing in uevent.h */ +struct uevent * alloc_uevent(void); +void uevent_get_wwid(struct uevent *uev); + +/* Stringify helpers */ +#define _str_(x) #x +#define str(x) _str_(x) + +#define MAJOR 17 +#define MINOR 217 +#define DISK_RO 0 +#define DM_NAME "spam" +#define WWID "foo" + +static int setup_uev(void **state) +{ + struct uevent *uev = alloc_uevent(); + + if (uev == NULL) + return -1; + + *state = uev; + uev->kernel = "sdo"; + uev->envp[0] = "MAJOR=" str(MAJOR); + uev->envp[1] = "ID_BOGUS=" WWID; + uev->envp[2] = "MINOR=" str(MINOR); + uev->envp[3] = "DM_NAME=" DM_NAME; + uev->envp[4] = "DISK_RO=" str(DISK_RO); + uev->envp[5] = NULL; + return 0; +} + +static int teardown(void **state) +{ + free(*state); + return 0; +} + +static void test_major_good(void **state) +{ + struct uevent *uev = *state; + + assert_int_equal(uevent_get_major(uev), MAJOR); +} + +static void test_minor_good(void **state) +{ + struct uevent *uev = *state; + + assert_int_equal(uevent_get_minor(uev), MINOR); +} + +static void test_ro_good(void **state) +{ + struct uevent *uev = *state; + + assert_int_equal(uevent_get_disk_ro(uev), DISK_RO); +} + +static void test_wwid(void **state) +{ + struct uevent *uev = *state; + uevent_get_wwid(uev); + + assert_string_equal(uev->wwid, WWID); +} + +static void test_major_bad_0(void **state) +{ + struct uevent *uev = *state; + + uev->envp[0] = "MAJOR" str(MAJOR); + assert_int_equal(uevent_get_major(uev), -1); +} + +static void test_major_bad_1(void **state) +{ + struct uevent *uev = *state; + + uev->envp[0] = "MAJOr=" str(MAJOR); + assert_int_equal(uevent_get_major(uev), -1); +} + +static void test_major_bad_2(void **state) +{ + struct uevent *uev = *state; + + uev->envp[0] = "MAJORIE=" str(MAJOR); + assert_int_equal(uevent_get_major(uev), -1); +} + +static void test_major_bad_3(void **state) +{ + struct uevent *uev = *state; + + uev->envp[0] = "MAJOR=max"; + assert_int_equal(uevent_get_major(uev), -1); +} + +static void test_major_bad_4(void **state) +{ + struct uevent *uev = *state; + + uev->envp[0] = "MAJOR=0x10"; + assert_int_equal(uevent_get_major(uev), -1); +} + +static void test_major_bad_5(void **state) +{ + struct uevent *uev = *state; + + uev->envp[0] = "MAJO=" str(MAJOR); + assert_int_equal(uevent_get_major(uev), -1); +} + +static void test_major_bad_6(void **state) +{ + struct uevent *uev = *state; + + uev->envp[0] = "MAJOR=" str(-MAJOR); + assert_int_equal(uevent_get_major(uev), -1); +} + +static void test_major_bad_7(void **state) +{ + struct uevent *uev = *state; + + uev->envp[0] = "MAJOR="; + assert_int_equal(uevent_get_major(uev), -1); +} + +static void test_major_bad_8(void **state) +{ + struct uevent *uev = *state; + + uev->envp[0] = "MAJOR"; + assert_int_equal(uevent_get_major(uev), -1); +} + +static void test_dm_name_good(void **state) +{ + struct uevent *uev = *state; + const char *name = uevent_get_dm_name(uev); + + assert_string_equal(name, DM_NAME); + free((void*)name); +} + +static void test_dm_name_bad_0(void **state) +{ + struct uevent *uev = *state; + const char *name; + + uev->envp[3] = "DM_NAME" DM_NAME; + name = uevent_get_dm_name(uev); + assert_ptr_equal(name, NULL); + free((void*)name); +} + +static void test_dm_name_bad_1(void **state) +{ + struct uevent *uev = *state; + const char *name; + + uev->envp[3] = "DM_NAMES=" DM_NAME; + name = uevent_get_dm_name(uev); + assert_ptr_equal(name, NULL); + free((void*)name); +} + +static void test_dm_name_good_1(void **state) +{ + struct uevent *uev = *state; + const char *name; + + /* Note we change index 2 here */ + uev->envp[2] = "DM_NAME=" DM_NAME; + name = uevent_get_dm_name(uev); + assert_string_equal(name, DM_NAME); + free((void*)name); +} + +static void test_dm_uuid_false_0(void **state) +{ + struct uevent *uev = *state; + + assert_false(uevent_is_mpath(uev)); +} + +static void test_dm_uuid_true_0(void **state) +{ + struct uevent *uev = *state; + + uev->envp[3] = "DM_UUID=mpath-foo"; + assert_true(uevent_is_mpath(uev)); +} + +static void test_dm_uuid_false_1(void **state) +{ + struct uevent *uev = *state; + + uev->envp[3] = "DM_UUID.mpath-foo"; + assert_false(uevent_is_mpath(uev)); +} + +static void test_dm_uuid_false_2(void **state) +{ + struct uevent *uev = *state; + + uev->envp[3] = "DM_UUID=mpath-"; + assert_false(uevent_is_mpath(uev)); +} + +static void test_dm_uuid_false_3(void **state) +{ + struct uevent *uev = *state; + + uev->envp[3] = "DM_UU=mpath-foo"; + assert_false(uevent_is_mpath(uev)); +} + +static void test_dm_uuid_false_4(void **state) +{ + struct uevent *uev = *state; + + uev->envp[3] = "DM_UUID=mpathfoo"; + assert_false(uevent_is_mpath(uev)); +} + +static void test_dm_uuid_false_5(void **state) +{ + struct uevent *uev = *state; + + uev->envp[3] = "DM_UUID="; + assert_false(uevent_is_mpath(uev)); +} + +int test_uevent_get_XXX(void) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_major_good), + cmocka_unit_test(test_minor_good), + cmocka_unit_test(test_ro_good), + cmocka_unit_test(test_dm_name_good), + cmocka_unit_test(test_wwid), + cmocka_unit_test(test_major_bad_0), + cmocka_unit_test(test_major_bad_1), + cmocka_unit_test(test_major_bad_2), + cmocka_unit_test(test_major_bad_3), + cmocka_unit_test(test_major_bad_4), + cmocka_unit_test(test_major_bad_5), + cmocka_unit_test(test_major_bad_6), + cmocka_unit_test(test_major_bad_7), + cmocka_unit_test(test_major_bad_8), + cmocka_unit_test(test_dm_name_bad_0), + cmocka_unit_test(test_dm_name_bad_1), + cmocka_unit_test(test_dm_name_good_1), + cmocka_unit_test(test_dm_uuid_false_0), + cmocka_unit_test(test_dm_uuid_true_0), + cmocka_unit_test(test_dm_uuid_false_1), + cmocka_unit_test(test_dm_uuid_false_2), + cmocka_unit_test(test_dm_uuid_false_3), + cmocka_unit_test(test_dm_uuid_false_4), + cmocka_unit_test(test_dm_uuid_false_5), + }; + return cmocka_run_group_tests(tests, setup_uev, teardown); +} + +int main(void) +{ + int ret = 0; + + ret += test_uevent_get_XXX(); + return ret; +}