Imported Upstream version 0.7.8 upstream/0.7.8
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)
104 files changed:
Makefile.inc
README.alua
kpartx/dasd.c
kpartx/del-part-nodes.rules
kpartx/devmapper.c
kpartx/gpt.c
kpartx/gpt.h
kpartx/kpartx.c
kpartx/lopart.c
kpartx/mac.h
kpartx/test-kpartx
libdmmp/docs/doc-preclean.pl [changed mode: 0644->0755]
libdmmp/docs/kernel-doc
libdmmp/docs/split-man.pl [changed mode: 0644->0755]
libmpathcmd/Makefile
libmpathcmd/mpath_cmd.h
libmpathpersist/mpath_persist.c
libmpathpersist/mpath_persistent_reserve_in.3
libmpathpersist/mpath_persistent_reserve_out.3
libmpathpersist/mpath_pr_ioctl.c
libmpathpersist/mpath_updatepr.c
libmpathpersist/mpathpr.h
libmultipath/Makefile
libmultipath/blacklist.c
libmultipath/blacklist.h
libmultipath/checkers.c
libmultipath/checkers.h
libmultipath/checkers/Makefile
libmultipath/checkers/cciss_tur.c
libmultipath/checkers/directio.c
libmultipath/checkers/emc_clariion.c
libmultipath/checkers/hp_sw.c
libmultipath/checkers/rbd.c [deleted file]
libmultipath/checkers/rdac.c
libmultipath/checkers/readsector0.c
libmultipath/checkers/tur.c
libmultipath/config.c
libmultipath/config.h
libmultipath/configure.c
libmultipath/defaults.c
libmultipath/defaults.h
libmultipath/devmapper.c
libmultipath/dict.c
libmultipath/dict.h
libmultipath/discovery.c
libmultipath/dm-generic.c
libmultipath/dm-generic.h
libmultipath/file.h
libmultipath/foreign.c
libmultipath/foreign.h
libmultipath/foreign/nvme.c
libmultipath/generic.c
libmultipath/generic.h
libmultipath/hwtable.c
libmultipath/parser.c
libmultipath/print.c
libmultipath/print.h
libmultipath/prio.c
libmultipath/prio.h
libmultipath/prioritizers/alua.c
libmultipath/prioritizers/alua_rtpg.c
libmultipath/prioritizers/path_latency.c
libmultipath/prkey.c
libmultipath/prkey.h
libmultipath/propsel.c
libmultipath/propsel.h
libmultipath/structs.c
libmultipath/structs.h
libmultipath/structs_vec.c
libmultipath/structs_vec.h
libmultipath/sysfs.c
libmultipath/uevent.c
libmultipath/unaligned.h
libmultipath/util.c
libmultipath/util.h
libmultipath/vector.c
libmultipath/vector.h
libmultipath/version.h
mpathpersist/main.c
mpathpersist/main.h
mpathpersist/mpathpersist.8
multipath/main.c
multipath/multipath.8
multipath/multipath.conf.5
multipathd/cli.c
multipathd/cli.h
multipathd/cli_handlers.c
multipathd/cli_handlers.h
multipathd/dmevents.c
multipathd/main.c
multipathd/main.h
multipathd/multipathd.8
multipathd/uxlsnr.c
tests/Makefile
tests/blacklist.c [new file with mode: 0644]
tests/dmevents.c
tests/hwtable.c [new file with mode: 0644]
tests/parser.c
tests/test-lib.c [new file with mode: 0644]
tests/test-lib.h [new file with mode: 0644]
tests/uevent.c
tests/unaligned.c [new file with mode: 0644]
tests/util.c
third-party/valgrind/valgrind.h

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