# 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 \
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
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"; \
- 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".
*/
unlink(pathname);
}
- if (!fd_dasd) {
+ if (fd_dasd < 0) {
/* Couldn't open the device */
return -1;
}
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
# 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"
#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)
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;
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;
{
int r;
char params[PARAMS_SIZE];
- const char *tmp;
+ char *tmp;
char *uuid;
int major, minor;
char dev_t[32];
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)
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;
}
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;
#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:
- */
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);
}
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)
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;
uint16_t signature; /* expected to be MAC_DRIVER_MAGIC */
uint16_t block_size;
uint32_t block_count;
- /* ... more stuff */
+ /* ... more stuff */
};
#endif
# 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
#!/usr/bin/env perl
+# SPDX-License-Identifier: GPL-2.0
use warnings;
use strict;
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 {
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*;';
}
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 {
}
}
-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";
}
}
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;
# 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
}
}
}
- $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;
$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',
# 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;
}
}
- $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
$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*\(\(
(?:
}
while (1) {
- if ( $x =~ /([^{};]*)([{};])(.*)/ ) {
+ if ( $x =~ /([^\{\};]*)([\{\};])(.*)/ ) {
if( length $prototype ) {
$prototype .= " "
}
}
}
-# 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;
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);
}
# 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";
$(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))
#ifndef LIB_MPATH_CMD_H
#define LIB_MPATH_CMD_H
-#ifdef __cpluscplus
+#ifdef __cplusplus
extern "C" {
#endif
#include <ctype.h>
#include "propsel.h"
#include "util.h"
+#include "unaligned.h"
#include "mpath_persist.h"
#include "mpathpr.h"
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);
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;
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;
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) {
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;
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);
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){
}
if (!rollback && (thread[i].param.status == MPATH_PR_RESERV_CONFLICT)){
rollback = 1;
- sa_key = 0;
- for (i = 0; i < 8; ++i){
- if (i > 0)
- sa_key <<= 8;
- sa_key |= paramp->sa_key[i];
- }
+ sa_key = get_unaligned_be64(¶mp->sa_key[0]);
status = MPATH_PR_RESERV_CONFLICT ;
}
if (!rollback && (status == MPATH_PR_SUCCESS)){
}
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);
} 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);
}
}
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){
}
}
- 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)
.SH NAME
.\" ----------------------------------------------------------------------------
.
-mpath_persistent_reserve_in
+mpath_persistent_reserve_in \- send PRIN command to DM device
.
.
.\" ----------------------------------------------------------------------------
.SH NAME
.\" ----------------------------------------------------------------------------
.
-mpath_persistent_reserve_out
+mpath_persistent_reserve_out \- send PROUT command to DM device
.
.
.\" ----------------------------------------------------------------------------
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);
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;
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;
}
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;
}
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);
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;
{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;
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
#include <stdio.h>
#include <unistd.h>
-#include <errno.h>
-
#include <stdlib.h>
#include <stdarg.h>
#include <fcntl.h>
#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"
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);
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);
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
#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)
{
}
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;
}
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;
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)
{
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;
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.
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;
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
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)
{
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--;
+ }
+ }
+}
#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;
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 */
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;
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;
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;
#define READSECTOR0 "readsector0"
#define CCISS_TUR "cciss_tur"
#define NONE "none"
-#define RBD "rbd"
#define ASYNC_TIMEOUT_SEC 30
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 */
};
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 *);
int libcheck_check(struct checker *);
int libcheck_init(struct checker *);
void libcheck_free(struct checker *);
-void libcheck_repair(struct checker *);
#endif /* _CHECKERS_H */
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
return;
}
-void libcheck_repair (struct checker * c)
-{
- return;
-}
-
int libcheck_check(struct checker * c)
{
int rc;
free(ct);
}
-void libcheck_repair (struct checker * c)
-{
- return;
-}
-
static int
check_state(int fd, struct directio_context *ct, int sync, int timeout_secs)
{
free(c->context);
}
-void libcheck_repair (struct checker * c)
-{
- return;
-}
-
int libcheck_check (struct checker * c)
{
unsigned char sense_buffer[128] = { 0, };
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)
+++ /dev/null
-/*
- * 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);
-}
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)
return;
}
-void libcheck_repair (struct checker * c)
-{
- return;
-}
-
int libcheck_check (struct checker * c)
{
unsigned char buf[4096];
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)
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;
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 };
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) {
* 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) {
* 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;
}
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)
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);
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)
#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 ||
}
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;
}
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;
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)
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)
}
static char *
-set_param_str(char * str)
+set_param_str(const char * str)
{
char * dst;
int len;
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,
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)
{
}
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
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);
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
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;
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 *
/*
* 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);
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;
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
*/
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;
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();
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;
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);
CMD_RESET_WWIDS,
CMD_ADD_WWID,
CMD_USABLE_PATHS,
+ CMD_DUMP_CONFIG,
};
enum force_reload_types {
int skip_kpartx;
int max_sectors_kb;
int ghost_delay;
+ int all_tg_pt;
char * bl_product;
};
char * prio_args;
int prkey_source;
struct be64 reservation_key;
+ uint8_t sa_flags;
int pgpolicy;
int pgfailback;
int rr_weight;
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;
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);
invalid = 1;
pthread_cleanup_pop(1);
if (invalid) {
- orphan_path(pp1, "wwid blacklisted");
+ orphan_path(pp1, "blacklisted");
continue;
}
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);
mpp->size);
mpp->action = ACT_REJECT;
}
- if (pp2->priority == PRIO_UNDEF)
- mpp->action = ACT_REJECT;
}
verify_paths(mpp, vecs);
#include "defaults.h"
#include "memory.h"
+const char * const default_partition_delim = DEFAULT_PARTITION_DELIM;
+
char *
set_default (char * str)
{
#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
#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)
#define MULTIPATH_SHM_BASE "/dev/shm/multipath/"
char * set_default (char * str);
+extern const char *const default_partition_delim;
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);
}
#include "util.h"
#include <errno.h>
#include <inttypes.h>
+#include <libudev.h>
+#include <mpath_persist.h>
#include "mpath_cmd.h"
#include "dict.h"
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;
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
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",
}
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)
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);
}
const void * data)
{
return print_reservation_key(buff, len, conf->reservation_key,
+ conf->sa_flags,
conf->prkey_source);
}
if (!mpe)
return 1;
return set_reservation_key(strvec, &mpe->reservation_key,
+ &mpe->sa_flags,
&mpe->prkey_source);
}
{
const struct mpentry * mpe = (const struct mpentry *)data;
return print_reservation_key(buff, len, mpe->reservation_key,
+ mpe->sa_flags,
mpe->prkey_source);
}
}
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)
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)
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)
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;
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;
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,
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);
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);
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);
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);
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);
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 */
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;
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
* 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
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;
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
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");
/*
* 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
pp->sg_id.lun);
}
- return 0;
+ return PATHINFO_OK;
}
static int
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
pp->sg_id.channel,
pp->sg_id.scsi_id,
pp->sg_id.lun);
- return 0;
+
+ return PATHINFO_OK;
}
static int
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));
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
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))
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
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)
{
} 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) {
* 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,
/*
* 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);
/*
* 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;
}
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>
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
#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);
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>
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
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>
#include <limits.h>
#include <dirent.h>
#include <errno.h>
+#include <ctype.h>
#include "vector.h"
#include "generic.h"
#include "foreign.h"
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,
};
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;
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",
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;
return FOREIGN_ERR;
}
vector_set_slot(ctx->mpvec, map);
- _find_slaves(ctx, map);
+ _find_controllers(ctx, map);
return FOREIGN_CLAIMED;
}
vector_foreach_slot(ctx->mpvec, gm, i) {
struct nvme_map *map = gen_mp_to_nvme(gm);
- _find_slaves(ctx, map);
+ _find_controllers(ctx, map);
}
}
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/>.
*/
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
*
* 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",
/*
* 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,
* HPE
*/
{
+ /* 3PAR */
.vendor = "3PARdata",
.product = "VV",
.pgpolicy = GROUP_BY_PRIO,
},
/* SGI */
{
+ /* Total Performance 9100 */
.vendor = "SGI",
.product = "TP9100",
.pgpolicy = MULTIBUS,
.no_path_retry = 30,
},
{
- /* InfiniteStorage family */
+ /* (RDAC) InfiniteStorage */
.vendor = "SGI",
.product = "IS",
.bl_product = "Universal Xport",
.no_path_retry = 30,
},
{
- /* (DDN) */
+ /* (DDN) InfiniteStorage */
.vendor = "SGI",
.product = "^DD[46]A-",
.pgpolicy = GROUP_BY_PRIO,
* 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,
* 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",
.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",
* 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,
.pgpolicy = MULTIBUS,
},
{
+ /* ETERNUS 2000, 3000 and 4000 */
.vendor = "FUJITSU",
.product = "E[234]000",
.pgpolicy = GROUP_BY_PRIO,
.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,
.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,
.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,
.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 */
/*
* 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,
.no_path_retry = (300 / DEFAULT_CHECKINT),
},
{
+ /* AIX NVDISK */
.vendor = "AIX",
.product = "NVDISK",
.hwhandler = "1 alua",
/*
* ONTAP family
*
- * Maintainer : Martin George
- * Mail : marting@netapp.com
+ * Maintainer: Martin George <marting@netapp.com>
*/
.vendor = "NETAPP",
.product = "LUN",
/*
* 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",
/*
* SolidFir family
*
- * Maintainer : PJ Waskiewicz
- * Mail : pj.waskiewicz@netapp.com
+ * Maintainer: PJ Waskiewicz <pj.waskiewicz@netapp.com>
*/
.vendor = "SolidFir",
.product = "SSD SAN",
*/
.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,
/*
* 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,
.prio_name = PRIO_ALUA,
},
{
+ /* FS */
.vendor = "^Oracle",
.product = "^Oracle FS",
.pgpolicy = GROUP_BY_PRIO,
.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",
.no_path_retry = 30,
},
{
+ /* 6180 */
.vendor = "SUN",
.product = "SUN_6180",
.bl_product = "Universal Xport",
.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",
/*
* 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,
* Intel
*/
{
+ /* Multi-Flex */
.vendor = "(Intel|INTEL)",
.product = "Multi-Flex",
.bl_product = "VTrak V-LUN",
* Linux-IO Target
*/
{
+ /* Linux-IO Target */
.vendor = "(LIO-ORG|SUSE)",
.product = "RBD",
.hwhandler = "1 alua",
* DataCore
*/
{
+ /* SANmelody */
.vendor = "DataCore",
.product = "SANmelody",
.pgpolicy = GROUP_BY_PRIO,
* Pure Storage
*/
{
+ /* FlashArray */
.vendor = "PURE",
.product = "FlashArray",
.pgpolicy = MULTIBUS,
.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,
/*
* Infinidat
*
- * Maintainer: Arnon Yaari
- * Mail: arnony@infinidat.com
+ * Maintainer: Arnon Yaari <arnony@infinidat.com>
*/
{
+ /* InfiniBox */
.vendor = "NFINIDAT",
.product = "InfiniBox",
.pgpolicy = GROUP_BY_PRIO,
* 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",
.no_path_retry = 30,
},
{
+ /* 3000 / 6000 Series - ALUA mode */
.vendor = "VIOLIN",
.product = "SAN ARRAY ALUA",
.hwhandler = "1 alua",
* Promise Technology
*/
{
+ /* VTrak family */
.vendor = "Promise",
.product = "VTrak",
.bl_product = "VTrak V-LUN",
.no_path_retry = 30,
},
{
+ /* Vess family */
.vendor = "Promise",
.product = "Vess",
.bl_product = "Vess V-LUN",
* EOL
*/
{
+ /* NULL */
.vendor = NULL,
.product = NULL,
},
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)
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",
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);
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);
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);
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},
{'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}
};
}
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;
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;
}
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)
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;
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;
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;
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"
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;
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;
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;
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;
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;
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);
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);
}
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;
}
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);
}
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]" : "");
#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.
#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;
(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;
}
/*
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) {
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);
}
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;
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;
}
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;
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;
if (rc == -RTPG_NO_TPG_IDENTIFIER) {
PRINT_DEBUG("get_target_port_group: "
- "no TPG identifier found!\n");
+ "no TPG identifier found!");
}
out:
free(buf);
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);
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);
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;
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;
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;
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) {
#include <string.h>
#include <inttypes.h>
#include <errno.h>
+#include <libudev.h>
+#include <mpath_persist.h>
#define PRKEY_READ 0
#define PRKEY_WRITE 1
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;
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);
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;
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;
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);
}
"# 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 */
} \
} 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)";
#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) \
#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) \
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; \
} \
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);
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);
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;
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:
} \
} 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;
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;
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:
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;
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;
+}
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);
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;
}
udev_device_unref(pp->udev);
pp->udev = NULL;
}
+ vector_free(pp->hwe);
FREE(pp);
}
if (!pgp->paths) {
FREE(pgp);
- pgp = NULL;
+ return NULL;
}
dm_pathgroup_to_gen(pgp)->ops = &dm_gen_pathgroup_ops;
}
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;
}
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;
}
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;
}
struct multipath *
-find_mp_by_str (vector mpvec, char * str)
+find_mp_by_str (const struct _vector *mpvec, const char * str)
{
int minor;
}
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;
}
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;
return NULL;
}
-int pathcountgr(struct pathgroup *pgp, int state)
+int pathcountgr(const struct pathgroup *pgp, int state)
{
struct path *pp;
int count = 0;
return count;
}
-int pathcount(struct multipath *mpp, int state)
+int pathcount(const struct multipath *mpp, int state)
{
struct pathgroup *pgp;
int count = 0;
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;
}
struct path *
-first_path (struct multipath * mpp)
+first_path (const struct multipath * mpp)
{
struct pathgroup * pgp;
if (!mpp->pg)
#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
enum sysfs_buses {
SYSFS_BUS_UNDEF,
SYSFS_BUS_SCSI,
- SYSFS_BUS_IDE,
SYSFS_BUS_CCW,
SYSFS_BUS_CCISS,
- SYSFS_BUS_RBD,
SYSFS_BUS_NVME,
};
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,
};
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;
int io_err_pathfail_starttime;
int find_multipaths_timeout;
/* configlet pointers */
- struct hwentry * hwe;
+ vector hwe;
struct gen_path generic_path;
};
char * features;
char * hwhandler;
struct mpentry * mpe;
- struct hwentry * hwe;
+ vector hwe;
/* threads */
pthread_t waiter;
/* 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;
};
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 *);
}
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;
+}
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 */
#include <dirent.h>
#include <libudev.h>
#include <fnmatch.h>
+#include <limits.h>
#include "checkers.h"
#include "vector.h"
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;
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)",
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;
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;
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_ */
#include <dirent.h>
#include <unistd.h>
#include <errno.h>
+#include <libudev.h>
+#include <mpath_persist.h>
#include "util.h"
#include "debug.h"
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;
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);
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) {
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))
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;
+}
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);
#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"
{
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;
prout_flag = 1;
break;
+ case 'Y':
+ param_alltgpt = 1;
+ ++num_prout_param;
+ break;
case 'Z':
param_aptpl = 1;
++num_prout_param;
}
/* 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);
}
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)
{
" --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 "
{"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'},
Request PR Out command.
.
.TP
+.B \--param-alltgpt|\-Y
+PR Out parameter 'ALL_TG_PT'.
+.
+.TP
.B \--param-aptpl|\-Z
PR Out parameter 'APTPL'.
.
/* 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) {}
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"
" -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"
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) {
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);
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;
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;
*
* 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';
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;
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;
}
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;
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);
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;
.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
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.
.
.\"
.\" ----------------------------------------------------------------------------
.
-.TH MULTIPATH.CONF 5 2017-08-18 Linux
+.TH MULTIPATH.CONF 5 2018-05-21 Linux
.
.
.\" ----------------------------------------------------------------------------
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
.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
.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.
.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
.
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
.
.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
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
.
.
.\" ----------------------------------------------------------------------------
-.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
.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
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
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) {
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);
__SETPRKEY,
__UNSETPRKEY,
__KEY,
+ __LOCAL,
};
#define LIST (1 << __LIST)
#define SETPRKEY (1ULL << __SETPRKEY)
#define UNSETPRKEY (1ULL << __UNSETPRKEY)
#define KEY (1ULL << __KEY)
+#define LOCAL (1ULL << __LOCAL)
#define INITIAL_REPLY_LEN 1200
#include "sysfs.h"
#include <errno.h>
#include <libudev.h>
+#include <mpath_persist.h>
#include "util.h"
#include "prkey.h"
#include "propsel.h"
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;
}
{
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
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))
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) {
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);
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;
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;
char *mapname = get_keyparam(v, MAP);
char *keyparam = get_keyparam(v, KEY);
uint64_t prkey;
+ uint8_t flags;
int ret;
struct config *conf;
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;
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);
};
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;
}
{
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;
continue;
if (pp->state != PATH_DOWN) {
- struct config *conf = get_multipath_config();
+ struct config *conf;
int oldstate = pp->state;
int checkint;
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);
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
(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;
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);
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);
/*
* 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;
*/
}
+ 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);
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);
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;
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();
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)
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);
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
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);
}
}
}
pp->state = newstate;
- repair_path(pp);
if (pp->mpp->wait_for_udev)
return 1;
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){
}
if (map_discovery(vecs)) {
condlog(0, "configure failed at map discovery");
- return 1;
+ goto fail;
}
/*
force_reload = FORCE_RELOAD_YES;
if (ret) {
condlog(0, "configure failed while coalescing paths");
- return 1;
+ goto fail;
}
/*
*/
if (coalesce_maps(vecs, mpvec)) {
condlog(0, "configure failed while coalescing maps");
- return 1;
+ goto fail;
}
dm_lib_release();
i--;
}
return 0;
+
+fail:
+ vector_free(mpvec);
+ return 1;
}
int
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;
pthread_attr_t attr;
struct multipath * mpp;
+ if (pp->bus != SYSFS_BUS_SCSI)
+ return 0;
+
mpp = pp->mpp;
if (!get_be64(mpp->reservation_key))
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);
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.
if (ux_sock == -1) {
condlog(1, "could not create uxsock: %d", errno);
- return NULL;
+ exit_daemon();
}
pthread_cleanup_push(uxsock_cleanup, (void *)ux_sock);
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);
/* something went badly wrong! */
condlog(0, "uxsock: poll failed with %d", errno);
+ exit_daemon();
break;
}
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))
--- /dev/null
+/*
+ * 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;
+}
* 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/>.
*
*/
--- /dev/null
+/* 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;
+}
* 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/>.
*
*/
--- /dev/null
+#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;
+}
--- /dev/null
+#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
* 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/>.
*
*/
--- /dev/null
+#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;
+}
* 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/>.
*
*/
|| (__VALGRIND_MAJOR__ == 3 && __VALGRIND_MINOR__ >= 6))
*/
#define __VALGRIND_MAJOR__ 3
-#define __VALGRIND_MINOR__ 13
+#define __VALGRIND_MINOR__ 14
#include <stdarg.h>
"$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 \
: /*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 */ \
: /*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" \
: /*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" \
: /*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" \
: /*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" \
: /*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" \
: /*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" \
: /*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" \
: /*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" \
: /*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" \
: /*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, \
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" \
: /*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, \
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" \
: /*in*/ "r" (&_argvec[0]) \
: /*trash*/ "memory", __CALLER_SAVED_REGS \
); \
- lval = (__typeof__(lval)) _res; \
+ lval = (__typeof__(lval)) (long)_res; \
} while (0)
#endif /* PLAT_mips64_linux */