Imported Upstream version 0.7.5 upstream/0.7.5
authorDongHun Kwak <dh0128.kwak@samsung.com>
Fri, 14 Jan 2022 04:50:18 +0000 (13:50 +0900)
committerDongHun Kwak <dh0128.kwak@samsung.com>
Fri, 14 Jan 2022 04:50:18 +0000 (13:50 +0900)
84 files changed:
.gitignore
Makefile
Makefile.inc
README.alua [new file with mode: 0644]
kpartx/Makefile
kpartx/del-part-nodes.rules
kpartx/devmapper.c
kpartx/dm-parts.rules
kpartx/kpartx_id
kpartx/test-kpartx
libdmmp/Makefile
libdmmp/docs/kernel-doc
libdmmp/libdmmp.c
libdmmp/libdmmp/libdmmp.h
libdmmp/libdmmp_misc.c
libdmmp/test/Makefile
libdmmp/test/libdmmp_test.c
libmpathcmd/Makefile
libmpathcmd/mpath_cmd.c
libmpathpersist/Makefile
libmultipath/Makefile
libmultipath/checkers/Makefile
libmultipath/checkers/rbd.c
libmultipath/checkers/tur.c
libmultipath/config.c
libmultipath/config.h
libmultipath/configure.c
libmultipath/configure.h
libmultipath/debug.c
libmultipath/defaults.h
libmultipath/devmapper.c
libmultipath/devmapper.h
libmultipath/dict.c
libmultipath/discovery.c
libmultipath/discovery.h
libmultipath/dm-generic.c [new file with mode: 0644]
libmultipath/dm-generic.h [new file with mode: 0644]
libmultipath/dmparser.c
libmultipath/foreign.c [new file with mode: 0644]
libmultipath/foreign.h [new file with mode: 0644]
libmultipath/foreign/Makefile [new file with mode: 0644]
libmultipath/foreign/nvme.c [new file with mode: 0644]
libmultipath/generic.c [new file with mode: 0644]
libmultipath/generic.h [new file with mode: 0644]
libmultipath/hwtable.c
libmultipath/io_err_stat.c
libmultipath/list.h
libmultipath/memory.h
libmultipath/parser.c
libmultipath/parser.h
libmultipath/pgpolicies.c
libmultipath/print.c
libmultipath/print.h
libmultipath/prioritizers/Makefile
libmultipath/prioritizers/path_latency.c
libmultipath/propsel.c
libmultipath/propsel.h
libmultipath/structs.c
libmultipath/structs.h
libmultipath/structs_vec.c
libmultipath/structs_vec.h
libmultipath/uevent.c
libmultipath/uevent.h
libmultipath/util.c
libmultipath/vector.h
libmultipath/version.h
mpathpersist/Makefile
multipath/11-dm-mpath.rules
multipath/Makefile
multipath/main.c
multipath/multipath.conf.5
multipath/multipath.rules
multipathd/Makefile
multipathd/cli.c
multipathd/cli.h
multipathd/cli_handlers.c
multipathd/main.c
multipathd/main.h
multipathd/multipathd.service
multipathd/multipathd.socket
multipathd/uxlsnr.c
tests/Makefile [new file with mode: 0644]
tests/globals.c [new file with mode: 0644]
tests/uevent.c [new file with mode: 0644]

index 57cf7e6..35c59a7 100644 (file)
@@ -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
+
index bfb168f..4b145c5 100644 (file)
--- 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
index 29c290a..a5b9d4e 100644 (file)
@@ -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 (file)
index 0000000..e39debd
--- /dev/null
@@ -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)".
index bf7362d..7eb467e 100644 (file)
@@ -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)
index cee945d..17bc505 100644 (file)
@@ -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"
index 4469fa8..eb9dac6 100644 (file)
@@ -11,6 +11,7 @@
 #include <sys/sysmacros.h>
 #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;
 }
 
index 235642f..b48b67c 100644 (file)
@@ -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}"
 
index b7f802d..c45db2f 100755 (executable)
@@ -42,6 +42,7 @@ fi
 dmuuid=${UUID#*-}
 dmtbl=${UUID%%-*}
 dmpart=${dmtbl#part}
+dmserial=
 # kpartx types are 'part<num>'
 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
index 60b3eb2..09d15a9 100755 (executable)
@@ -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 <<EOF
+0 $((SIZE/SECTSIZ-OFFS)) linear $DEV1 $OFFS
+EOF
+
 usleep $WAIT_US
 [[ -b /dev/mapper/$SPAN1 ]]
 [[ -b /dev/mapper/$SPAN2 ]]
+[[ -b /dev/mapper/$USER1 ]]
 
 step "create vg on $LO3"
 # On the 3rd loop device, we create a VG and an LV
@@ -290,6 +298,7 @@ pop_cleanup
 # spans should not have been removed
 [[ -b /dev/mapper/$SPAN1 ]]
 [[ -b /dev/mapper/$SPAN2 ]]
+[[ -b /dev/mapper/$USER1 ]]
 # LVs neither
 [[ -b /dev/mapper/$VG-$LV ]]
 
index 6645a1a..1dd3f34 100644 (file)
@@ -5,7 +5,7 @@
 #
 include ../Makefile.inc
 
-LIBDMMP_VERSION=0.1.0
+LIBDMMP_VERSION=0.2.0
 SONAME=$(LIBDMMP_VERSION)
 DEVLIB = libdmmp.so
 LIBS = $(DEVLIB).$(SONAME)
@@ -56,11 +56,13 @@ uninstall:
        $(RM) $(DESTDIR)$(man3dir)/libdmmp.h*
        $(RM) $(DESTDIR)$(pkgconfdir)/$(PKGFILE)
 
-clean:
+clean: dep_clean
        $(RM) core *.a *.o *.gz *.so *.so.*
        $(RM) -r docs/man
        $(MAKE) -C test clean
 
+include $(wildcard $(OBJS:.o=.d))
+
 check: all
        $(MAKE) -C test check
 
@@ -84,3 +86,6 @@ docs/man/$(EXTRA_MAN_FILES).gz: $(HEADERS)
                gzip -f $$file; \
        done
        find docs/man -type f -name \*[0-9].gz
+
+dep_clean:
+       $(RM) $(OBJS:.o=.d)
index 8f5b546..7bd52b8 100644 (file)
@@ -1,5 +1,6 @@
-#!/usr/bin/perl -w
+#!/usr/bin/env perl
 
+use warnings;
 use strict;
 
 ## Copyright (c) 1998 Michael Zucchi, All Rights Reserved        ##
@@ -199,29 +200,36 @@ EOF
 # 'funcname()' - function
 # '$ENVVAR' - environmental variable
 # '&struct_name' - name of a structure (up to two words including 'struct')
+# '&struct_name.member' - name of a structure member
 # '@parameter' - name of a parameter
 # '%CONST' - name of a constant.
+# '``LITERAL``' - literal string without any spaces on it.
 
 ## init lots of data
 
-
 my $errors = 0;
 my $warnings = 0;
 my $anon_struct_union = 0;
 
 # match expressions used to find embedded type information
-my $type_constant = '\%([-_\w]+)';
+my $type_constant = '\b``([^\`]+)``\b';
+my $type_constant2 = '\%([-_\w]+)';
 my $type_func = '(\w+)\(\)';
 my $type_param = '\@(\w+(\.\.\.)?)';
 my $type_fp_param = '\@(\w+)\(\)';  # Special RST handling for func ptr params
-my $type_struct = '\&((struct\s*)*[_\w]+)';
-my $type_struct_xml = '\\&amp;((struct\s*)*[_\w]+)';
 my $type_env = '(\$\w+)';
-my $type_enum_full = '\&(enum)\s*([_\w]+)';
-my $type_struct_full = '\&(struct)\s*([_\w]+)';
-my $type_typedef_full = '\&(typedef)\s*([_\w]+)';
-my $type_union_full = '\&(union)\s*([_\w]+)';
-my $type_member = '\&([_\w]+)((\.|->)[_\w]+)';
+my $type_enum = '\&(enum\s*([_\w]+))';
+my $type_struct = '\&(struct\s*([_\w]+))';
+my $type_typedef = '\&(typedef\s*([_\w]+))';
+my $type_union = '\&(union\s*([_\w]+))';
+my $type_member = '\&([_\w]+)(\.|->)([_\w]+)';
+my $type_fallback = '\&([_\w]+)';
+my $type_enum_xml = '\&amp;(enum\s*([_\w]+))';
+my $type_struct_xml = '\&amp;(struct\s*([_\w]+))';
+my $type_typedef_xml = '\&amp;(typedef\s*([_\w]+))';
+my $type_union_xml = '\&amp;(union\s*([_\w]+))';
+my $type_member_xml = '\&amp;([_\w]+)(\.|-\&gt;)([_\w]+)';
+my $type_fallback_xml = '\&amp([_\w]+)';
 my $type_member_func = $type_member . '\(\)';
 
 # Output conversion substitutions.
@@ -230,10 +238,16 @@ my $type_member_func = $type_member . '\(\)';
 # these work fairly well
 my @highlights_html = (
                        [$type_constant, "<i>\$1</i>"],
+                       [$type_constant2, "<i>\$1</i>"],
                        [$type_func, "<b>\$1</b>"],
+                       [$type_enum_xml, "<i>\$1</i>"],
                        [$type_struct_xml, "<i>\$1</i>"],
+                       [$type_typedef_xml, "<i>\$1</i>"],
+                       [$type_union_xml, "<i>\$1</i>"],
                        [$type_env, "<b><i>\$1</i></b>"],
-                       [$type_param, "<tt><b>\$1</b></tt>"]
+                       [$type_param, "<tt><b>\$1</b></tt>"],
+                       [$type_member_xml, "<tt><i>\$1</i>\$2\$3</tt>"],
+                       [$type_fallback_xml, "<i>\$1</i>"]
                       );
 my $local_lt = "\\\\\\\\lt:";
 my $local_gt = "\\\\\\\\gt:";
@@ -242,10 +256,16 @@ my $blankline_html = $local_lt . "p" . $local_gt; # was "<p>"
 # html version 5
 my @highlights_html5 = (
                         [$type_constant, "<span class=\"const\">\$1</span>"],
+                        [$type_constant2, "<span class=\"const\">\$1</span>"],
                         [$type_func, "<span class=\"func\">\$1</span>"],
+                        [$type_enum_xml, "<span class=\"enum\">\$1</span>"],
                         [$type_struct_xml, "<span class=\"struct\">\$1</span>"],
+                        [$type_typedef_xml, "<span class=\"typedef\">\$1</span>"],
+                        [$type_union_xml, "<span class=\"union\">\$1</span>"],
                         [$type_env, "<span class=\"env\">\$1</span>"],
-                        [$type_param, "<span class=\"param\">\$1</span>]"]
+                        [$type_param, "<span class=\"param\">\$1</span>]"],
+                        [$type_member_xml, "<span class=\"literal\"><span class=\"struct\">\$1</span>\$2<span class=\"member\">\$3</span></span>"],
+                        [$type_fallback_xml, "<span class=\"struct\">\$1</span>"]
                       );
 my $blankline_html5 = $local_lt . "br /" . $local_gt;
 
@@ -253,55 +273,80 @@ my $blankline_html5 = $local_lt . "br /" . $local_gt;
 my @highlights_xml = (
                       ["([^=])\\\"([^\\\"<]+)\\\"", "\$1<quote>\$2</quote>"],
                       [$type_constant, "<constant>\$1</constant>"],
+                      [$type_constant2, "<constant>\$1</constant>"],
+                      [$type_enum_xml, "<type>\$1</type>"],
                       [$type_struct_xml, "<structname>\$1</structname>"],
+                      [$type_typedef_xml, "<type>\$1</type>"],
+                      [$type_union_xml, "<structname>\$1</structname>"],
                       [$type_param, "<parameter>\$1</parameter>"],
                       [$type_func, "<function>\$1</function>"],
-                      [$type_env, "<envar>\$1</envar>"]
+                      [$type_env, "<envar>\$1</envar>"],
+                      [$type_member_xml, "<literal><structname>\$1</structname>\$2<structfield>\$3</structfield></literal>"],
+                      [$type_fallback_xml, "<structname>\$1</structname>"]
                     );
 my $blankline_xml = $local_lt . "/para" . $local_gt . $local_lt . "para" . $local_gt . "\n";
 
 # gnome, docbook format
 my @highlights_gnome = (
                         [$type_constant, "<replaceable class=\"option\">\$1</replaceable>"],
+                        [$type_constant2, "<replaceable class=\"option\">\$1</replaceable>"],
                         [$type_func, "<function>\$1</function>"],
+                        [$type_enum, "<type>\$1</type>"],
                         [$type_struct, "<structname>\$1</structname>"],
+                        [$type_typedef, "<type>\$1</type>"],
+                        [$type_union, "<structname>\$1</structname>"],
                         [$type_env, "<envar>\$1</envar>"],
-                        [$type_param, "<parameter>\$1</parameter>" ]
+                        [$type_param, "<parameter>\$1</parameter>" ],
+                        [$type_member, "<literal><structname>\$1</structname>\$2<structfield>\$3</structfield></literal>"],
+                        [$type_fallback, "<structname>\$1</structname>"]
                       );
 my $blankline_gnome = "</para><para>\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 "  <varlistentry>\n   <term><parameter>$parameter</parameter></term>\n";
+           print "  <varlistentry>\n   <term><parameter>$type $parameter</parameter></term>\n";
            print "   <listitem>\n    <para>\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 "    <varlistentry>";
-      print "      <term>$parameter</term>\n";
+      print "      <term><literal>$type $parameter</literal></term>\n";
       print "      <listitem><para>\n";
       output_highlight($args{'parameterdescs'}{$parameter_name});
       print "      </para></listitem>\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--;
index b4e7f08..944cecd 100644 (file)
@@ -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);
index 72b79b9..e157982 100644 (file)
@@ -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
index 435ddfa..69b5a20 100644 (file)
@@ -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);
index acfb3bf..20b3945 100644 (file)
@@ -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)
index 56bd03e..d944e1e 100644 (file)
@@ -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");
index 4f32101..53c0899 100644 (file)
@@ -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)
index af618cf..29d148c 100644 (file)
@@ -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;
        }
index 1b4ec16..21fdad8 100644 (file)
@@ -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)
index 6447d8d..806aaa2 100644 (file)
@@ -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)
index bce6b8b..9559038 100644 (file)
@@ -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)
index 2c18009..b1d99b4 100644 (file)
@@ -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;
index b4a5cb2..9155960 100644 (file)
@@ -15,6 +15,7 @@
 #include <errno.h>
 #include <sys/time.h>
 #include <pthread.h>
+#include <urcu/uatomic.h>
 
 #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;
                }
        }
 
index eb03f0a..085a3e1 100644 (file)
@@ -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
index 51fe27b..a20ac2a 100644 (file)
@@ -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;
index 09821e8..fa6e21c 100644 (file)
@@ -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;
 }
index 0ffc28e..27a7e6f 100644 (file)
@@ -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);
index f89b264..f95a3e5 100644 (file)
@@ -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);
index 740ccf4..c9e3411 100644 (file)
@@ -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)
index fcac6bc..607aea8 100644 (file)
 #include <sys/types.h>
 #include <time.h>
 
+#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)
index 62e14d1..8c8ea6c 100644 (file)
@@ -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);
index 3b36e1d..47dc2a3 100644 (file)
@@ -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();
 }
index cadf461..9efcaac 100644 (file)
 #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,
index b151cc7..9aacf75 100644 (file)
@@ -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 (file)
index 0000000..bdc9ca0
--- /dev/null
@@ -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 <stdint.h>
+#include <sys/types.h>
+#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 (file)
index 0000000..5d59724
--- /dev/null
@@ -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 */
index b647c25..783c934 100644 (file)
@@ -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,23 +166,11 @@ 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
         */
        p += get_word(p, &mpp->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 (file)
index 0000000..7217184
--- /dev/null
@@ -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 <sys/sysmacros.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <limits.h>
+#include <dirent.h>
+#include <fnmatch.h>
+#include <dlfcn.h>
+#include <libudev.h>
+#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 (file)
index 0000000..0ade2d7
--- /dev/null
@@ -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 <stdbool.h>
+#include <libudev.h>
+
+#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 (file)
index 0000000..dfba11e
--- /dev/null
@@ -0,0 +1,30 @@
+#
+# Copyright (C) 2003 Christophe Varoqui, <christophe.varoqui@opensvc.com>
+#
+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 (file)
index 0000000..235f75d
--- /dev/null
@@ -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 <sys/sysmacros.h>
+#include <libudev.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+#include <libudev.h>
+#include <pthread.h>
+#include <limits.h>
+#include <dirent.h>
+#include <errno.h>
+#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 (file)
index 0000000..6f7a2cd
--- /dev/null
@@ -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 <string.h>
+#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 (file)
index 0000000..7f7fe66
--- /dev/null
@@ -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 */
index 17e8ac6..6ba70c8 100644 (file)
                .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",
@@ -666,6 +681,20 @@ static struct hwentry default_hw[] = {
                .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
         *
         * Maintainer : Yacine Kheddache
@@ -1133,16 +1162,6 @@ static struct hwentry default_hw[] = {
                .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
         */
        {
@@ -1181,6 +1200,17 @@ static struct hwentry default_hw[] = {
                .no_path_retry = 30,
        },
        /*
+        * INSPUR
+        */
+       {
+               /* AS5300/AS5500 G2 */
+               .vendor        = "INSPUR",
+               .product       = "MCS",
+               .pgpolicy      = GROUP_BY_PRIO,
+               .pgfailback    = -FAILBACK_IMMEDIATE,
+               .prio_name     = PRIO_ALUA,
+       },
+       /*
         * EOL
         */
        {
index 75a6df6..ac81b4b 100644 (file)
@@ -21,6 +21,7 @@
 #include <libaio.h>
 #include <errno.h>
 #include <sys/mman.h>
+#include <sys/select.h>
 
 #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);
 }
index 2b1dcf3..ced021f 100644 (file)
  * @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) );})
 
 /*
index a3c478e..63f59d8 100644 (file)
@@ -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))
 
index c47d891..5caa201 100644 (file)
@@ -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;
index 519b805..0a74750 100644 (file)
@@ -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
index 4ae4afb..ac2596a 100644 (file)
@@ -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;
                }
 
index 65a9824..d532994 100644 (file)
 #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);
index b8c3436..7ba6438 100644 (file)
@@ -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 */
index 0c71e63..b3cc944 100644 (file)
@@ -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)
index 9d5397e..765265c 100644 (file)
@@ -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 */
 
 #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;
 }
index 0d29ed2..58a6a42 100644 (file)
 #include "discovery.h"
 #include "dict.h"
 #include "util.h"
+#include "sysfs.h"
 #include "prioritizers/alua_rtpg.h"
 #include "prkey.h"
 #include <inttypes.h>
+#include <libudev.h>
 
 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;
+}
index 347cb32..136f906 100644 (file)
@@ -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);
index 3e057f5..991095c 100644 (file)
@@ -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;
index c2cf3fb..88a4b78 100644 (file)
@@ -6,6 +6,7 @@
 
 #include "prio.h"
 #include "byteorder.h"
+#include "generic.h"
 
 #define WWID_SIZE              128
 #define SERIAL_SIZE            65
 #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 *);
 
index 22be8e0..f9dc8a8 100644 (file)
@@ -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);
index 46f30af..3749eb6 100644 (file)
@@ -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 */
index 80bf1dd..685ef33 100644 (file)
@@ -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';
 }
index 6f5af0a..cb5347e 100644 (file)
@@ -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 */
index 0b43d29..d3dd3eb 100644 (file)
@@ -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;
 }
index 5cfd4d0..6018b57 100644 (file)
@@ -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);
index ca628b2..6b028ee 100644 (file)
@@ -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"
 
index 6e5acd3..5126801 100644 (file)
@@ -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)
index 91d8058..03ac5da 100644 (file)
@@ -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"
index 468c056..0828a8f 100644 (file)
@@ -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)
index bffe065..8732cf8 100644 (file)
@@ -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();
 
index 36551b4..ab151e7 100644 (file)
@@ -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<device dependent>\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
index bc1a852..6f8ee2b 100644 (file)
@@ -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"
index e6f140b..4c9d296 100644 (file)
@@ -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)
index deb72cb..f10f862 100644 (file)
@@ -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;
index d289167..021f25b 100644 (file)
@@ -1,3 +1,6 @@
+#ifndef _CLI_H_
+#define _CLI_H_
+
 #include <stdint.h>
 
 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_ */
index e232fb2..60ec48b 100644 (file)
 #include "configure.h"
 #include "blacklist.h"
 #include "debug.h"
+#include "dm-generic.h"
 #include "print.h"
 #include "sysfs.h"
 #include <errno.h>
 #include <libudev.h>
 #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;
index 31ce923..327cc19 100644 (file)
@@ -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;
        }
@@ -1463,6 +1406,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)
 {
        struct multipath * mpp;
@@ -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();
 
index 094b04f..0e9c5e3 100644 (file)
@@ -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 */
index fd66cf6..ba24983 100644 (file)
@@ -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
index 921706d..0ed4a1f 100644 (file)
@@ -5,3 +5,6 @@ Before=sockets.target
 
 [Socket]
 ListenStream=@/org/kernel/linux/storage/multipathd
+
+[Install]
+WantedBy=sockets.target
index 98ac25a..0531061 100644 (file)
@@ -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 (file)
index 0000000..f6b5583
--- /dev/null
@@ -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 (file)
index 0000000..96a5651
--- /dev/null
@@ -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 (file)
index 0000000..b7d6458
--- /dev/null
@@ -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 <stdbool.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <stdlib.h>
+#include <cmocka.h>
+#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;
+}