From e7e18dce4a598c6400c6d5bc90ed05e7a28b867008cf239d10d819a0de54392d Mon Sep 17 00:00:00 2001 From: OBS User unknown Date: Thu, 17 May 2007 09:47:22 +0000 Subject: [PATCH] OBS-URL: https://build.opensuse.org/package/show/openSUSE:Factory/multipath-tools?expand=0&rev=2 --- 71-multipath.rules | 25 - 72-multipath-compat.rules | 10 - boot.multipath | 104 - kpartx_id | 110 - mpath_id | 19 - multipath-tools-git-update.patch | 3519 +++++++++++++++++++++++++-- multipath-tools-local-fixes | 2036 ++++++++++++++++ multipath-tools-no-gz-for-manpage | 140 -- multipath-tools-online-device.patch | 107 - multipath-tools-strip.patch | 121 - multipath-tools-suse-update | 3096 +++++++++++++++++++++++ multipath-tools.changes | 13 + multipath-tools.spec | 42 +- multipathd | 155 -- 14 files changed, 8414 insertions(+), 1083 deletions(-) delete mode 100644 71-multipath.rules delete mode 100644 72-multipath-compat.rules delete mode 100644 boot.multipath delete mode 100644 kpartx_id delete mode 100644 mpath_id create mode 100644 multipath-tools-local-fixes delete mode 100644 multipath-tools-no-gz-for-manpage delete mode 100644 multipath-tools-online-device.patch delete mode 100644 multipath-tools-strip.patch create mode 100644 multipath-tools-suse-update delete mode 100644 multipathd diff --git a/71-multipath.rules b/71-multipath.rules deleted file mode 100644 index 1d1dc86..0000000 --- a/71-multipath.rules +++ /dev/null @@ -1,25 +0,0 @@ -# -# persistent links for device-mapper devices -# only hardware-backed device-mapper devices (ie multipath, dmraid, -# and kpartx) have meaningful persistent device names -# - -KERNEL!="dm-*", GOTO="multipath_end" -ACTION=="add|remove", GOTO="multipath_end" - -ACTION=="change", IMPORT{program}=="/sbin/kpartx_id %M %m" - -# Create persistent links for tables -ACTION=="change", ENV{ID_DM_TABLE}=="mpath|dmraid", ENV{ID_DM_TYPE}=="?*", \ - SYMLINK+="disk/by-id/$env{ID_DM_TYPE}-$env{ID_DM_NAME}" - -# Create dm tables for partitions -ACTION=="change", ENV{ID_DM_TABLE}=="mpath|dmraid", \ - RUN+="/sbin/kpartx -a -p _part /dev/mapper/$env{ID_DM_NAME}" - -# Create persistent links for partitions -ACTION=="change", ENV{ID_DM_TABLE}=="part", ENV{ID_DM_TYPE}=="?*", \ - SYMLINK+="disk/by-id/$env{ID_DM_TYPE}-$env{ID_DM_NAME}-part$env{ID_DM_PART}" - -LABEL="multipath_end" - diff --git a/72-multipath-compat.rules b/72-multipath-compat.rules deleted file mode 100644 index bc1fcdd..0000000 --- a/72-multipath-compat.rules +++ /dev/null @@ -1,10 +0,0 @@ -# -# SLES9 compability symlinks for multipathed devices -# - -# Devices -ENV{ID_DMTYPE}=="multipath", ENV{ID_MPATH}=="?*", \ - SYMLINK+="disk/by-name/$env{ID_MPATH}" -# Partitions -ENV{ID_DMTYPE}=="linear", ENV{ID_MPATH}=="?*", \ - SYMLINK+="disk/by-name/$env{ID_MPATH}" diff --git a/boot.multipath b/boot.multipath deleted file mode 100644 index 3cf67f6..0000000 --- a/boot.multipath +++ /dev/null @@ -1,104 +0,0 @@ -#! /bin/sh -# Copyright (c) 2005 SuSE GmbH Nuernberg, Germany. -# -# Author: Hannes Reinecke -# -# init.d/boot.multipath -# -### BEGIN INIT INFO -# Provides: boot.multipath -# Required-Start: boot.device-mapper boot.udev -# Required-Stop: -# Default-Start: B -# Default-Stop: -# Description: Create multipath device targets -### END INIT INFO - -PATH=/bin:/usr/bin:/sbin:/usr/sbin -PROGRAM=/sbin/multipath - -# Set the maximum number of open files -MAX_OPEN_FDS=4096 - -test -x $PROGRAM || exit 5 - -# Shell functions sourced from /etc/rc.status: -# rc_check check and set local and overall rc status -# rc_status check and set local and overall rc status -# rc_status -v ditto but be verbose in local rc status -# rc_status -v -r ditto and clear the local rc status -# rc_failed set local and overall rc status to failed -# rc_reset clear local rc status (overall remains) -# rc_exit exit appropriate to overall rc status -. /etc/rc.status - -# First reset status of this service -rc_reset - -# Return values acc. to LSB for all commands but status: -# 0 - success -# 1 - misc error -# 2 - invalid or excess args -# 3 - unimplemented feature (e.g. reload) -# 4 - insufficient privilege -# 5 - program not installed -# 6 - program not configured -# 7 - program is not running -# -# Note that starting an already running service, stopping -# or restarting a not-running service as well as the restart -# with force-reload (in case signalling is not supported) are -# considered a success. - -case "$1" in - start) - echo -n "Creating multipath targets" - # Check whether multipath daemon is already running - if /sbin/multipathd -k"list paths" > /dev/null 2>&1 ; then - echo -n " (multipathd running)" - rc_status -v - rc_exit - fi - - # Load prerequisite module - modprobe dm-multipath - - # Be a chicken and flush all existing maps - $PROGRAM -F - - # Clear /dev/disk/by-name/ prior to start-up; multipath will - # recreate them. - rm -f /dev/disk/by-name/* 2>&1 >/dev/null - - # Set the maximum number of open files - if [ -n "$MAX_OPEN_FDS" ] ; then - ulimit -n $MAX_OPEN_FDS - fi - - # Start the program directly as checkproc doesn't work here - $PROGRAM -v 0 - - # Create all partitions which might have been missing - /sbin/dmsetup ls --target multipath --exec "/sbin/kpartx -a -p -part" - - # Remember status and be verbose - rc_status -v - sleep 1 - ;; - stop) - # Remove all partition mappings - if dmsetup ls | grep -q -- -part; then - /sbin/dmsetup ls --target multipath --exec "/sbin/kpartx -d -p -part" - fi - - # And remove the multipath mappings themselves - for map in $(/sbin/dmsetup ls --target multipath | cut -f 1); do - /sbin/dmsetup remove $map - done - ;; - *) - echo "Usage: $0 {start|stop}" - exit 1 - ;; -esac -rc_exit diff --git a/kpartx_id b/kpartx_id deleted file mode 100644 index b02de49..0000000 --- a/kpartx_id +++ /dev/null @@ -1,110 +0,0 @@ -#!/bin/bash -# -# kpartx_id -# -# Generates ID information for device-mapper tables. -# -# Copyright (C) 2006 SUSE Linux Products GmbH -# Author: -# Hannes Reinecke -# -# -# 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 version 2 of the License. -# -# This script generates ID information used to generate persistent symlinks. -# It relies on the UUID strings generated by the various programs; the name -# of the tables are of no consequence. -# -# Please note that dmraid does not provide the UUIDs (yet); a patch has been -# sent upstream but has not been accepted yet. -# - -DMSETUP=/sbin/dmsetup - -MAJOR=$1 -MINOR=$2 - -if [ -z "$MAJOR" -o -z "$MINOR" ]; then - echo "usage: $0 major minor" - exit 1; -fi - -# Device-mapper not installed; not an error -if [ ! -x $DMSETUP ] ; then - exit 0 -fi - -# Get the table info -tblinfo=$($DMSETUP info -c --noheadings -o name,uuid -j $MAJOR -m $MINOR) -if [ $? -ne 0 ] || [ -z "$tblinfo" ]; then - exit $? -fi - -set -- $(IFS=":"; echo $tblinfo) -tblname=$1 -tbluuid=$2 - -if [ -z "$tbluuid" ] ; then - exit 0 -fi - -# Table UUIDs are always '-'. -dmuuid=${tbluuid#*-} -dmtbl=${tbluuid%%-*} -dmpart=${dmtbl#part} -# kpartx types are 'part' -if [ "$dmpart" == "$dmtbl" ] ; then - dmpart= -else - dmtbl=part -fi - -# Set the name of the table. We're only interested in dmraid, -# multipath, and kparts tables; everything else is ignored. -if [ "$dmtbl" == "part" ] ; then - # The name of the kpartx table is the name of the parent table - dmname=$($DMSETUP info -c --noheadings -o name -u $dmuuid) - # We need the dependencies of the parent table to figure out - # the type if the parent is a multipath table - case "$dmparent" in - mpath-*) - dmdeps=$($DMSETUP deps -u $dmuuid) - ;; - esac -elif [ "$dmtbl" == "mpath" ] ; then - dmname=$tblname - # We need the dependencies of the table to figure out the type - dmdeps=$($DMSETUP deps -u $tbluuid) -elif [ "$dmtbl" == "dmraid" ] ; then - dmname=$tblname -fi - -if [ -z "$dmname" ] ; then - exit 0 -fi - -echo "ID_DM_TABLE=$dmtbl" -echo "ID_DM_NAME=$dmname" -[ -n "$dmpart" ] && echo "ID_DM_PART=$dmpart" - -# Figure out the type of the map. For non-multipath maps it's -# always 'raid'. -if [ -n "$dmdeps" ] ; then - case "$dmdeps" in - *\(94,*) - echo "ID_DM_TYPE=dasd" - ;; - *\(9*) - echo "ID_DM_TYPE=raid" - ;; - *) - echo "ID_DM_TYPE=scsi" - ;; - esac -else - echo "ID_DM_TYPE=raid" -fi - -exit 0 diff --git a/mpath_id b/mpath_id deleted file mode 100644 index 0ff4949..0000000 --- a/mpath_id +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/sh - -major=$1 -minor=$2 - -mpstatus=$(/sbin/dmsetup status -j $major -m $minor --target multipath) -if [ -z "$mpstatus" ]; then - exit 1 -fi - -mpid=$(/sbin/dmsetup info -c --noopencount --noheadings -o name -j $major -m $minor) - -if [ -z "$mpid" ]; then - exit 1 -fi -echo ID_MPATH=\"$mpid\" - -exit 0 - diff --git a/multipath-tools-git-update.patch b/multipath-tools-git-update.patch index 7cec1df..27b3392 100644 --- a/multipath-tools-git-update.patch +++ b/multipath-tools-git-update.patch @@ -1,61 +1,80 @@ - Makefile | 4 - devmap_name/devmap_name.8 | 2 - kpartx/devmapper.c | 46 ++- - kpartx/devmapper.h | 4 - kpartx/kpartx.8 | 2 - kpartx/kpartx.c | 15 - - libcheckers/checkers.h | 54 +++ - libcheckers/emc_clariion.c | 13 - - libcheckers/hp_sw.c | 3 - libcheckers/readsector0.c | 9 - libcheckers/tur.c | 2 - libmultipath/Makefile | 3 - libmultipath/callout.c | 2 - libmultipath/config.c | 75 +++- - libmultipath/config.h | 6 - libmultipath/configure.c | 60 ++- - libmultipath/configure.h | 2 - libmultipath/debug.c | 5 - libmultipath/debug.h | 4 - libmultipath/devmapper.c | 38 ++ - libmultipath/discovery.c | 41 +- - libmultipath/discovery.h | 6 - libmultipath/dmparser.c | 19 + - libmultipath/hwtable.c | 202 +++++++++-- - libmultipath/lock.c | 8 + Makefile | 4 + + devmap_name/devmap_name.8 | 2 +- + kpartx/dasd.c | 28 +- + kpartx/devmapper.c | 46 +++- + kpartx/devmapper.h | 4 +- + kpartx/kpartx.8 | 2 +- + kpartx/kpartx.c | 15 +- + libcheckers/Makefile | 2 +- + libcheckers/checkers.c | 13 +- + libcheckers/checkers.h | 61 +++- + libcheckers/emc_clariion.c | 157 ++++++-- + libcheckers/hp_sw.c | 3 +- + libcheckers/libsg.c | 79 ++++ + libcheckers/libsg.h | 8 + + libcheckers/rdac.c | 109 +++++ + libcheckers/rdac.h | 8 + + libcheckers/readsector0.c | 76 +---- + libcheckers/tur.c | 2 +- + libmultipath/Makefile | 9 +- + libmultipath/alias.c | 97 +++-- + libmultipath/blacklist.c | 179 ++++++++-- + libmultipath/blacklist.h | 21 +- + libmultipath/config.c | 100 ++++- + libmultipath/config.h | 15 +- + libmultipath/configure.c | 74 ++-- + libmultipath/configure.h | 2 +- + libmultipath/debug.c | 5 +- + libmultipath/debug.h | 4 +- + libmultipath/defaults.h | 1 + + libmultipath/devmapper.c | 111 +++++- + libmultipath/devmapper.h | 3 +- + libmultipath/dict.c | 229 +++++++++++- + libmultipath/discovery.c | 51 ++- + libmultipath/discovery.h | 6 +- + libmultipath/dmparser.c | 19 +- + libmultipath/hwtable.c | 198 ++++++++-- + libmultipath/lock.c | 8 + libmultipath/lock.h | 22 + - libmultipath/parser.c | 15 + - libmultipath/parser.h | 1 - libmultipath/pgpolicies.h | 2 - libmultipath/print.c | 43 ++ - libmultipath/propsel.c | 33 +- - libmultipath/structs.c | 9 - libmultipath/structs.h | 12 - - libmultipath/structs_vec.c | 94 +++++ - libmultipath/structs_vec.h | 14 - - libmultipath/util.c | 9 - libmultipath/util.h | 1 + libmultipath/parser.c | 15 +- + libmultipath/parser.h | 1 + + libmultipath/pgpolicies.h | 2 +- + libmultipath/print.c | 266 ++++++++++++- + libmultipath/print.h | 3 + + libmultipath/propsel.c | 71 +++- + libmultipath/propsel.h | 1 + + libmultipath/structs.c | 15 +- + libmultipath/structs.h | 21 +- + libmultipath/structs_vec.c | 95 +++++- + libmultipath/structs_vec.h | 14 +- + libmultipath/switchgroup.c | 2 +- + libmultipath/util.c | 9 + + libmultipath/util.h | 1 + libmultipath/version.h | 37 ++ - libmultipath/waiter.c | 234 +++++++++++++ - libmultipath/waiter.h | 23 + - multipath-tools.spec.in | 1 - multipath.conf.annotated | 6 - multipath/main.c | 18 + + libmultipath/waiter.c | 234 +++++++++++ + libmultipath/waiter.h | 23 ++ + multipath-tools.spec.in | 1 + + multipath.conf.annotated | 22 +- + multipath.conf.synthetic | 6 +- + multipath/main.c | 36 ++- multipath/main.h | 39 -- - multipath/multipath.8 | 15 - - multipathd/cli.c | 3 - multipathd/cli_handlers.c | 6 - multipathd/main.c | 385 +-------------------- - multipathd/multipathd.8 | 6 - path_priority/pp_alua/main.c | 4 - path_priority/pp_alua/mpath_prio_alua.8 | 2 - path_priority/pp_alua/rtpg.c | 36 ++ - path_priority/pp_alua/spc3.h | 4 + multipath/multipath.8 | 15 +- + multipathd/cli.c | 232 ++++++++++-- + multipathd/cli.h | 7 + + multipathd/cli_handlers.c | 94 +++++- + multipathd/cli_handlers.h | 2 + + multipathd/main.c | 439 +++------------------ + multipathd/multipathd.8 | 96 ++++- + multipathd/uxclnt.c | 6 + + path_priority/pp_alua/main.c | 4 +- + path_priority/pp_alua/mpath_prio_alua.8 | 2 +- + path_priority/pp_alua/rtpg.c | 36 ++- + path_priority/pp_alua/spc3.h | 4 +- path_priority/pp_balance_units/pp_balance_units.c | 74 ++-- path_priority/pp_hds_modular/Makefile | 22 + - path_priority/pp_hds_modular/pp_hds_modular.c | 252 ++++++++++++++ - path_priority/pp_tpc/pp_tpc.c | 12 - - 57 files changed, 1375 insertions(+), 684 deletions(-) + path_priority/pp_hds_modular/pp_hds_modular.c | 252 ++++++++++++ + path_priority/pp_tpc/pp_tpc.c | 12 +- + 76 files changed, 3026 insertions(+), 958 deletions(-) diff --git a/Makefile b/Makefile index 83ae2fe..aacede3 100644 @@ -83,11 +102,80 @@ index f4f03c3..86d0931 100644 .SH NAME devmap_name \- Query device-mapper name .SH SYNOPSIS +diff --git a/kpartx/dasd.c b/kpartx/dasd.c +index 69b9807..f31111f 100644 +--- a/kpartx/dasd.c ++++ b/kpartx/dasd.c +@@ -40,14 +40,20 @@ + #include "byteorder.h" + #include "dasd.h" + ++unsigned long sectors512(unsigned long sectors, int blocksize) ++{ ++ return sectors * (blocksize >> 9); ++} ++ + /* + */ + int + read_dasd_pt(int fd, struct slice all, struct slice *sp, int ns) + { + int retval = -1; +- int blocksize, offset, size; ++ int blocksize; + long disksize; ++ unsigned long offset, size; + dasd_information_t info; + struct hd_geometry geo; + char type[5] = {0,}; +@@ -172,13 +178,13 @@ read_dasd_pt(int fd, struct slice all, s + /* disk is reserved minidisk */ + blocksize = label[3]; + offset = label[13]; +- size = (label[7] - 1)*(blocksize >> 9); ++ size = sectors512(label[7] - 1, blocksize); + } else { +- offset = (info.label_block + 1) * (blocksize >> 9); +- size = disksize - offset; ++ offset = info.label_block + 1; ++ size = disksize; + } +- sp[0].start = offset * (blocksize >> 9); +- sp[0].size = size - offset * (blocksize >> 9); ++ sp[0].start = sectors512(offset, blocksize); ++ sp[0].size = size - sp[0].start; + retval = 1; + } else if ((strncmp(type, "VOL1", 4) == 0) && + (!info.FBA_layout) && (!strcmp(info.type, "ECKD"))) { +@@ -214,8 +220,8 @@ read_dasd_pt(int fd, struct slice all, s + offset = cchh2blk(&f1.DS1EXT1.llimit, &geo); + size = cchh2blk(&f1.DS1EXT1.ulimit, &geo) - + offset + geo.sectors; +- sp[counter].start = offset * (blocksize >> 9); +- sp[counter].size = size * (blocksize >> 9); ++ sp[counter].start = sectors512(offset, blocksize); ++ sp[counter].size = sectors512(size, blocksize); + counter++; + blk++; + } +@@ -224,10 +230,8 @@ read_dasd_pt(int fd, struct slice all, s + /* + * Old style LNX1 or unlabeled disk + */ +- offset = (info.label_block + 1) * (blocksize >> 9); +- size = disksize - offset; +- sp[0].start = offset * (blocksize >> 9); +- sp[0].size = size - offset * (blocksize >> 9); ++ sp[0].start = sectors512(info.label_block + 1, blocksize); ++ sp[0].size = disksize - sp[0].start; + retval = 1; + } + diff --git a/kpartx/devmapper.c b/kpartx/devmapper.c index 1253941..5b27487 100644 --- a/kpartx/devmapper.c +++ b/kpartx/devmapper.c -@@ -7,6 +7,10 @@ #include +@@ -7,6 +7,10 @@ #include #include #include @@ -226,7 +314,7 @@ index 2198302..0ee0cb3 100644 fd = open(device, O_RDONLY); if (fd == -1) { -@@ -420,7 +423,7 @@ #endif +@@ -420,7 +423,7 @@ main(int argc, char **argv){ DM_DEVICE_RELOAD : DM_DEVICE_CREATE); dm_addmap(op, partname, DM_TARGET, params, @@ -235,11 +323,63 @@ index 2198302..0ee0cb3 100644 if (op == DM_DEVICE_RELOAD) dm_simplecmd(DM_DEVICE_RESUME, +diff --git a/libcheckers/Makefile b/libcheckers/Makefile +index ec8c10d..6340a68 100644 +--- a/libcheckers/Makefile ++++ b/libcheckers/Makefile +@@ -6,7 +6,7 @@ BUILD = glibc + + include ../Makefile.inc + +-OBJS = checkers.o readsector0.o tur.o directio.o emc_clariion.o hp_sw.o ++OBJS = libsg.o checkers.o readsector0.o tur.o directio.o emc_clariion.o hp_sw.o rdac.o + + all: $(BUILD) + +diff --git a/libcheckers/checkers.c b/libcheckers/checkers.c +index 53770a6..a49ad59 100644 +--- a/libcheckers/checkers.c ++++ b/libcheckers/checkers.c +@@ -7,6 +7,7 @@ + #include "tur.h" + #include "hp_sw.h" + #include "emc_clariion.h" ++#include "rdac.h" + #include "readsector0.h" + + static struct checker checkers[] = { +@@ -48,6 +49,15 @@ static struct checker checkers[] = { + }, + { + .fd = 0, ++ .name = RDAC, ++ .message = "", ++ .context = NULL, ++ .check = rdac, ++ .init = rdac_init, ++ .free = rdac_free ++ }, ++ { ++ .fd = 0, + .name = READSECTOR0, + .message = "", + .context = NULL, +@@ -75,8 +85,9 @@ struct checker * checker_lookup (char * + return NULL; + } + +-int checker_init (struct checker * c) ++int checker_init (struct checker * c, void ** mpctxt_addr) + { ++ c->mpcontext = mpctxt_addr; + return c->init(c); + } + diff --git a/libcheckers/checkers.h b/libcheckers/checkers.h -index ac795ed..219dfaf 100644 +index ac795ed..d4dad9c 100644 --- a/libcheckers/checkers.h +++ b/libcheckers/checkers.h -@@ -2,7 +2,44 @@ #ifndef _CHECKERS_H +@@ -2,7 +2,44 @@ #define _CHECKERS_H /* @@ -285,7 +425,14 @@ index ac795ed..219dfaf 100644 */ #define PATH_WILD -1 #define PATH_UNCHECKED 0 -@@ -20,6 +57,21 @@ #define READSECTOR0 "readsector0" +@@ -14,12 +51,28 @@ + #define DIRECTIO "directio" + #define TUR "tur" + #define HP_SW "hp_sw" ++#define RDAC "rdac" + #define EMC_CLARIION "emc_clariion" + #define READSECTOR0 "readsector0" + #define DEFAULT_CHECKER READSECTOR0 /* @@ -307,11 +454,133 @@ index ac795ed..219dfaf 100644 * strings lengths */ #define CHECKER_NAME_LEN 16 +@@ -31,14 +84,16 @@ struct checker { + char name[CHECKER_NAME_LEN]; + char message[CHECKER_MSG_LEN]; /* comm with callers */ + void * context; /* store for persistent data */ ++ void ** mpcontext; /* store for persistent data ++ shared multipath-wide */ + int (*check)(struct checker *); + int (*init)(struct checker *); /* to allocate the context */ + void (*free)(struct checker *); /* to free the context */ + }; + +-#define MSG(c, a) snprintf((c)->message, CHECKER_MSG_LEN, a); ++#define MSG(c, fmt, args...) snprintf((c)->message, CHECKER_MSG_LEN, fmt, ##args); + +-int checker_init (struct checker *); ++int checker_init (struct checker *, void **); + void checker_put (struct checker *); + void checker_reset (struct checker * c); + void checker_set_fd (struct checker *, int); diff --git a/libcheckers/emc_clariion.c b/libcheckers/emc_clariion.c -index 1d7b684..a883e3d 100644 +index 1d7b684..384a943 100644 --- a/libcheckers/emc_clariion.c +++ b/libcheckers/emc_clariion.c -@@ -57,7 +57,7 @@ int emc_clariion(struct checker * c) +@@ -11,25 +11,76 @@ + #include + #include + +-#include "checkers.h" +- + #include "../libmultipath/sg_include.h" ++#include "libsg.h" ++#include "checkers.h" + + #define INQUIRY_CMD 0x12 + #define INQUIRY_CMDLEN 6 + #define HEAVY_CHECK_COUNT 10 + +-struct emc_clariion_checker_context { ++/* ++ * Mechanism to track CLARiiON inactive snapshot LUs. ++ * This is done so that we can fail passive paths ++ * to an inactive snapshot LU even though since a ++ * simple read test would return 02/04/03 instead ++ * of 05/25/01 sensekey/ASC/ASCQ data. ++ */ ++#define IS_INACTIVE_SNAP(c) (c->mpcontext ? \ ++ ((struct emc_clariion_checker_LU_context *) \ ++ (*c->mpcontext))->inactive_snap \ ++ : 0) ++ ++#define SET_INACTIVE_SNAP(c) if (c->mpcontext) \ ++ ((struct emc_clariion_checker_LU_context *)\ ++ (*c->mpcontext))->inactive_snap = 1 ++ ++#define CLR_INACTIVE_SNAP(c) if (c->mpcontext) \ ++ ((struct emc_clariion_checker_LU_context *)\ ++ (*c->mpcontext))->inactive_snap = 0 ++ ++struct emc_clariion_checker_path_context { + char wwn[16]; + unsigned wwn_set; + }; + ++struct emc_clariion_checker_LU_context { ++ int inactive_snap; ++}; ++ ++extern void ++hexadecimal_to_ascii(char * wwn, char *wwnstr) ++{ ++ int i,j, nbl; ++ ++ for (i=0,j=0;i<16;i++) { ++ wwnstr[j++] = ((nbl = ((wwn[i]&0xf0) >> 4)) <= 9) ? ++ '0' + nbl : 'a' + (nbl - 10); ++ wwnstr[j++] = ((nbl = (wwn[i]&0x0f)) <= 9) ? ++ '0' + nbl : 'a' + (nbl - 10); ++ } ++ wwnstr[32]=0; ++} ++ + int emc_clariion_init (struct checker * c) + { +- c->context = malloc(sizeof(struct emc_clariion_checker_context)); ++ /* ++ * Allocate and initialize the path specific context. ++ */ ++ c->context = malloc(sizeof(struct emc_clariion_checker_path_context)); + if (!c->context) + return 1; +- ((struct emc_clariion_checker_context *)c->context)->wwn_set = 0; ++ ((struct emc_clariion_checker_path_context *)c->context)->wwn_set = 0; ++ ++ /* ++ * Allocate and initialize the multi-path global context. ++ */ ++ if (c->mpcontext) { ++ void * mpctxt = malloc(sizeof(int)); ++ *c->mpcontext = mpctxt; ++ CLR_INACTIVE_SNAP(c); ++ } ++ + return 0; + } + +@@ -40,13 +91,15 @@ void emc_clariion_free (struct checker * + + int emc_clariion(struct checker * c) + { +- unsigned char sense_buffer[256] = { 0, }; +- unsigned char sb[128] = { 0, }; ++ unsigned char sense_buffer[128] = { 0, }; ++ unsigned char sb[SENSE_BUFF_LEN] = { 0, }, *sbb; + unsigned char inqCmdBlk[INQUIRY_CMDLEN] = {INQUIRY_CMD, 1, 0xC0, 0, +- sizeof(sb), 0}; ++ sizeof(sense_buffer), 0}; + struct sg_io_hdr io_hdr; +- struct emc_clariion_checker_context * ct = +- (struct emc_clariion_checker_context *)c->context; ++ struct emc_clariion_checker_path_context * ct = ++ (struct emc_clariion_checker_path_context *)c->context; ++ char wwnstr[33]; ++ int ret; + + memset(&io_hdr, 0, sizeof (struct sg_io_hdr)); + io_hdr.interface_id = 'S'; +@@ -57,7 +110,7 @@ int emc_clariion(struct checker * c) io_hdr.dxferp = sense_buffer; io_hdr.cmdp = inqCmdBlk; io_hdr.sbp = sb; @@ -320,7 +589,31 @@ index 1d7b684..a883e3d 100644 io_hdr.pack_id = 0; if (ioctl(c->fd, SG_IO, &io_hdr) < 0) { MSG(c, "emc_clariion_checker: sending query command failed"); -@@ -89,14 +89,11 @@ int emc_clariion(struct checker * c) +@@ -69,7 +122,8 @@ int emc_clariion(struct checker * c) + } + if (/* Verify the code page - right page & revision */ + sense_buffer[1] != 0xc0 || sense_buffer[9] != 0x00) { +- MSG(c, "emc_clariion_checker: Path unit report page in unknown format"); ++ MSG(c, "emc_clariion_checker: Path unit report page in " ++ "unknown format"); + return PATH_DOWN; + } + +@@ -79,24 +133,24 @@ int emc_clariion(struct checker * c) + || (sense_buffer[28] & 0x07) != 0x04 + /* Arraycommpath should be set to 1 */ + || (sense_buffer[30] & 0x04) != 0x04) { +- MSG(c, "emc_clariion_checker: Path not correctly configured for failover"); ++ MSG(c, "emc_clariion_checker: Path not correctly configured " ++ "for failover"); + return PATH_DOWN; + } + + if ( /* LUN operations should indicate normal operations */ + sense_buffer[48] != 0x00) { +- MSG(c, "emc_clariion_checker: Path not available for normal operations"); ++ MSG(c, "emc_clariion_checker: Path not available for normal " ++ "operations"); return PATH_SHAKY; } @@ -332,18 +625,92 @@ index 1d7b684..a883e3d 100644 - return PATH_UP; + if ( /* LUN should at least be bound somewhere and not be LUNZ */ + sense_buffer[4] == 0x00) { -+ MSG(c, "emc_clariion_checker: Logical Unit is unbound or LUNZ"); ++ MSG(c, "emc_clariion_checker: Logical Unit is unbound " ++ "or LUNZ"); + return PATH_DOWN; } -#endif /* * store the LUN WWN there and compare that it indeed did not +@@ -105,7 +159,8 @@ int emc_clariion(struct checker * c) + */ + if (ct->wwn_set) { + if (memcmp(ct->wwn, &sense_buffer[10], 16) != 0) { +- MSG(c, "emc_clariion_checker: Logical Unit WWN has changed!"); ++ MSG(c, "emc_clariion_checker: Logical Unit WWN " ++ "has changed!"); + return PATH_DOWN; + } + } else { +@@ -113,7 +168,59 @@ int emc_clariion(struct checker * c) + ct->wwn_set = 1; + } + +- +- MSG(c, "emc_clariion_checker: Path healthy"); +- return PATH_UP; ++ /* ++ * Issue read on active path to determine if inactive snapshot. ++ */ ++ if (sense_buffer[4] == 2) {/* if active path */ ++ unsigned char buf[512]; ++ ++ ret = sg_read(c->fd, &buf[0], sbb = &sb[0]); ++ if (ret == PATH_DOWN) { ++ hexadecimal_to_ascii(ct->wwn, wwnstr); ++ ++ /* ++ * Check for inactive snapshot LU this way. Must ++ * fail these. ++ */ ++ if (((sbb[2]&0xf) == 5) && (sbb[12] == 0x25) && ++ (sbb[13]==1)) { ++ /* ++ * Do this so that we can fail even the ++ * passive paths which will return ++ * 02/04/03 not 05/25/01 on read. ++ */ ++ SET_INACTIVE_SNAP(c); ++ MSG(c, "emc_clariion_checker: Active " ++ "path to inactive snapshot WWN %s.", ++ wwnstr); ++ } else ++ MSG(c, "emc_clariion_checker: Read " ++ "error for WWN %s. Sense data are " ++ "0x%x/0x%x/0x%x.", wwnstr, ++ sbb[2]&0xf, sbb[12], sbb[13]); ++ } else { ++ MSG(c, "emc_clariion_checker: Active path is " ++ "healthy."); ++ /* ++ * Remove the path from the set of paths to inactive ++ * snapshot LUs if it was in this list since the ++ * snapshot is no longer inactive. ++ */ ++ CLR_INACTIVE_SNAP(c); ++ } ++ } else { ++ if (IS_INACTIVE_SNAP(c)) { ++ hexadecimal_to_ascii(ct->wwn, wwnstr); ++ MSG(c, "emc_clariion_checker: Passive " ++ "path to inactive snapshot WWN %s.", ++ wwnstr); ++ ret = PATH_DOWN; ++ } else { ++ MSG(c, ++ "emc_clariion_checker: Passive path is healthy."); ++ ret = PATH_UP; /* not ghost */ ++ } ++ } ++ ++ return ret; + } diff --git a/libcheckers/hp_sw.c b/libcheckers/hp_sw.c index 509e9c4..b9731ff 100644 --- a/libcheckers/hp_sw.c +++ b/libcheckers/hp_sw.c -@@ -19,7 +19,6 @@ #define TUR_CMD_LEN 6 +@@ -19,7 +19,6 @@ #define INQUIRY_CMDLEN 6 #define INQUIRY_CMD 0x12 #define SENSE_BUFF_LEN 32 @@ -360,38 +727,325 @@ index 509e9c4..b9731ff 100644 io_hdr.pack_id = 0; if (ioctl(fd, SG_IO, &io_hdr) < 0) +diff --git a/libcheckers/libsg.c b/libcheckers/libsg.c +new file mode 100644 +index 0000000..f426aaf +--- /dev/null ++++ b/libcheckers/libsg.c +@@ -0,0 +1,79 @@ ++/* ++ * Copyright (c) 2004, 2005 Christophe Varoqui ++ */ ++#include ++#include ++#include ++ ++#include "checkers.h" ++#include "libsg.h" ++#include "../libmultipath/sg_include.h" ++ ++int ++sg_read (int sg_fd, unsigned char * buff, unsigned char * senseBuff) ++{ ++ /* defaults */ ++ int blocks = 1; ++ long long start_block = 0; ++ int bs = 512; ++ int cdbsz = 10; ++ int * diop = NULL; ++ ++ unsigned char rdCmd[cdbsz]; ++ unsigned char *sbb = senseBuff; ++ struct sg_io_hdr io_hdr; ++ int res; ++ int rd_opcode[] = {0x8, 0x28, 0xa8, 0x88}; ++ int sz_ind; ++ int retry_count = 3; ++ ++ memset(rdCmd, 0, cdbsz); ++ sz_ind = 1; ++ rdCmd[0] = rd_opcode[sz_ind]; ++ rdCmd[2] = (unsigned char)((start_block >> 24) & 0xff); ++ rdCmd[3] = (unsigned char)((start_block >> 16) & 0xff); ++ rdCmd[4] = (unsigned char)((start_block >> 8) & 0xff); ++ rdCmd[5] = (unsigned char)(start_block & 0xff); ++ rdCmd[7] = (unsigned char)((blocks >> 8) & 0xff); ++ rdCmd[8] = (unsigned char)(blocks & 0xff); ++ ++ memset(&io_hdr, 0, sizeof(struct sg_io_hdr)); ++ io_hdr.interface_id = 'S'; ++ io_hdr.cmd_len = cdbsz; ++ io_hdr.cmdp = rdCmd; ++ io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; ++ io_hdr.dxfer_len = bs * blocks; ++ io_hdr.dxferp = buff; ++ io_hdr.mx_sb_len = SENSE_BUFF_LEN; ++ io_hdr.sbp = senseBuff; ++ io_hdr.timeout = DEF_TIMEOUT; ++ io_hdr.pack_id = (int)start_block; ++ if (diop && *diop) ++ io_hdr.flags |= SG_FLAG_DIRECT_IO; ++ ++retry: ++ memset(senseBuff, 0, SENSE_BUFF_LEN); ++ while (((res = ioctl(sg_fd, SG_IO, &io_hdr)) < 0) && (EINTR == errno)); ++ ++ if (res < 0) { ++ if (ENOMEM == errno) { ++ return PATH_UP; ++ } ++ return PATH_DOWN; ++ } ++ ++ if ((0 == io_hdr.status) && ++ (0 == io_hdr.host_status) && ++ (0 == io_hdr.driver_status)) { ++ return PATH_UP; ++ } else { ++ /* ++ * Retry if UNIT_ATTENTION check condition. ++ */ ++ if ((sbb[2]&0xf) == 6) { ++ if (--retry_count) ++ goto retry; ++ } ++ return PATH_DOWN; ++ } ++} +diff --git a/libcheckers/libsg.h b/libcheckers/libsg.h +new file mode 100644 +index 0000000..97c4491 +--- /dev/null ++++ b/libcheckers/libsg.h +@@ -0,0 +1,8 @@ ++#ifndef _LIBSG_H ++#define _LIBSG_H ++ ++#define SENSE_BUFF_LEN 32 ++ ++int sg_read (int sg_fd, unsigned char * buff, unsigned char * senseBuff); ++ ++#endif /* _LIBSG_H */ +diff --git a/libcheckers/rdac.c b/libcheckers/rdac.c +new file mode 100644 +index 0000000..f430488 +--- /dev/null ++++ b/libcheckers/rdac.c +@@ -0,0 +1,109 @@ ++/* ++ * Copyright (c) 2005 Christophe Varoqui ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "checkers.h" ++ ++#include "../libmultipath/sg_include.h" ++ ++#define INQUIRY_CMDLEN 6 ++#define INQUIRY_CMD 0x12 ++#define SENSE_BUFF_LEN 32 ++#define RDAC_DEF_TIMEOUT 60000 ++#define SCSI_CHECK_CONDITION 0x2 ++#define SCSI_COMMAND_TERMINATED 0x22 ++#define SG_ERR_DRIVER_SENSE 0x08 ++#define RECOVERED_ERROR 0x01 ++ ++#define MSG_RDAC_UP "rdac checker reports path is up" ++#define MSG_RDAC_DOWN "rdac checker reports path is down" ++#define MSG_RDAC_GHOST "rdac checker reports path is ghost" ++ ++struct rdac_checker_context { ++ void * dummy; ++}; ++ ++int rdac_init (struct checker * c) ++{ ++ return 0; ++} ++ ++void rdac_free (struct checker * c) ++{ ++ return; ++} ++ ++static int ++do_inq(int sg_fd, unsigned int pg_op, void *resp, int mx_resp_len) ++{ ++ unsigned char inqCmdBlk[INQUIRY_CMDLEN] = { INQUIRY_CMD, 1, 0, 0, 0, 0 }; ++ unsigned char sense_b[SENSE_BUFF_LEN]; ++ struct sg_io_hdr io_hdr; ++ ++ inqCmdBlk[2] = (unsigned char) pg_op; ++ inqCmdBlk[4] = (unsigned char) (mx_resp_len & 0xff); ++ memset(&io_hdr, 0, sizeof (struct sg_io_hdr)); ++ ++ io_hdr.interface_id = 'S'; ++ io_hdr.cmd_len = sizeof (inqCmdBlk); ++ io_hdr.mx_sb_len = sizeof (sense_b); ++ io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; ++ io_hdr.dxfer_len = mx_resp_len; ++ io_hdr.dxferp = resp; ++ io_hdr.cmdp = inqCmdBlk; ++ io_hdr.sbp = sense_b; ++ io_hdr.timeout = RDAC_DEF_TIMEOUT; ++ ++ if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) ++ return 1; ++ ++ /* treat SG_ERR here to get rid of sg_err.[ch] */ ++ io_hdr.status &= 0x7e; ++ if ((0 == io_hdr.status) && (0 == io_hdr.host_status) && ++ (0 == io_hdr.driver_status)) ++ return 0; ++ if ((SCSI_CHECK_CONDITION == io_hdr.status) || ++ (SCSI_COMMAND_TERMINATED == io_hdr.status) || ++ (SG_ERR_DRIVER_SENSE == (0xf & io_hdr.driver_status))) { ++ if (io_hdr.sbp && (io_hdr.sb_len_wr > 2)) { ++ int sense_key; ++ unsigned char * sense_buffer = io_hdr.sbp; ++ if (sense_buffer[0] & 0x2) ++ sense_key = sense_buffer[1] & 0xf; ++ else ++ sense_key = sense_buffer[2] & 0xf; ++ if (RECOVERED_ERROR == sense_key) ++ return 0; ++ } ++ } ++ return 1; ++} ++ ++struct volume_access_inq ++{ ++ char dontcare0[8]; ++ char avtcvp; ++ char dontcare1[39]; ++}; ++ ++extern int ++rdac(struct checker * c) ++{ ++ struct volume_access_inq inq; ++ ++ if (0 != do_inq(c->fd, 0xC9, &inq, sizeof(struct volume_access_inq))) { ++ MSG(c, MSG_RDAC_DOWN); ++ return PATH_DOWN; ++ } ++ ++ return ((inq.avtcvp & 0x1) ? PATH_UP : PATH_GHOST); ++} +diff --git a/libcheckers/rdac.h b/libcheckers/rdac.h +new file mode 100644 +index 0000000..d7bf812 +--- /dev/null ++++ b/libcheckers/rdac.h +@@ -0,0 +1,8 @@ ++#ifndef _RDAC_H ++#define _RDAC_H ++ ++int rdac(struct checker *); ++int rdac_init(struct checker *); ++void rdac_free(struct checker *); ++ ++#endif /* _RDAC_H */ diff --git a/libcheckers/readsector0.c b/libcheckers/readsector0.c -index e368fb4..dd18528 100644 +index e368fb4..3cddfa8 100644 --- a/libcheckers/readsector0.c +++ b/libcheckers/readsector0.c -@@ -16,7 +16,6 @@ #include "checkers.h" - #include "../libmultipath/sg_include.h" +@@ -2,21 +2,9 @@ + * Copyright (c) 2004, 2005 Christophe Varoqui + */ + #include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include - #define SENSE_BUFF_LEN 32 + #include "checkers.h" +- +-#include "../libmultipath/sg_include.h" +- +-#define SENSE_BUFF_LEN 32 -#define DEF_TIMEOUT 60000 ++#include "libsg.h" #define MSG_READSECTOR0_UP "readsector0 checker reports path is up" #define MSG_READSECTOR0_DOWN "readsector0 checker reports path is down" -@@ -35,8 +34,8 @@ void readsector0_free (struct checker * +@@ -35,72 +23,14 @@ void readsector0_free (struct checker * return; } -static int -sg_read (int sg_fd, unsigned char * buff) -+int -+sg_read (int sg_fd, unsigned char * buff, unsigned char * senseBuff) - { - /* defaults */ - int blocks = 1; -@@ -46,7 +45,6 @@ sg_read (int sg_fd, unsigned char * buff - int * diop = NULL; - - unsigned char rdCmd[cdbsz]; +-{ +- /* defaults */ +- int blocks = 1; +- long long start_block = 0; +- int bs = 512; +- int cdbsz = 10; +- int * diop = NULL; +- +- unsigned char rdCmd[cdbsz]; - unsigned char senseBuff[SENSE_BUFF_LEN]; - struct sg_io_hdr io_hdr; - int res; - int rd_opcode[] = {0x8, 0x28, 0xa8, 0x88}; -@@ -98,9 +96,10 @@ extern int +- struct sg_io_hdr io_hdr; +- int res; +- int rd_opcode[] = {0x8, 0x28, 0xa8, 0x88}; +- int sz_ind; +- +- memset(rdCmd, 0, cdbsz); +- sz_ind = 1; +- rdCmd[0] = rd_opcode[sz_ind]; +- rdCmd[2] = (unsigned char)((start_block >> 24) & 0xff); +- rdCmd[3] = (unsigned char)((start_block >> 16) & 0xff); +- rdCmd[4] = (unsigned char)((start_block >> 8) & 0xff); +- rdCmd[5] = (unsigned char)(start_block & 0xff); +- rdCmd[7] = (unsigned char)((blocks >> 8) & 0xff); +- rdCmd[8] = (unsigned char)(blocks & 0xff); +- +- memset(&io_hdr, 0, sizeof(struct sg_io_hdr)); +- io_hdr.interface_id = 'S'; +- io_hdr.cmd_len = cdbsz; +- io_hdr.cmdp = rdCmd; +- io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; +- io_hdr.dxfer_len = bs * blocks; +- io_hdr.dxferp = buff; +- io_hdr.mx_sb_len = SENSE_BUFF_LEN; +- io_hdr.sbp = senseBuff; +- io_hdr.timeout = DEF_TIMEOUT; +- io_hdr.pack_id = (int)start_block; +- if (diop && *diop) +- io_hdr.flags |= SG_FLAG_DIRECT_IO; +- +- while (((res = ioctl(sg_fd, SG_IO, &io_hdr)) < 0) && (EINTR == errno)); +- +- if (res < 0) { +- if (ENOMEM == errno) { +- return PATH_UP; +- } +- return PATH_DOWN; +- } +- +- if ((0 == io_hdr.status) && +- (0 == io_hdr.host_status) && +- (0 == io_hdr.driver_status)) { +- return PATH_UP; +- } else { +- return PATH_DOWN; +- } +-} +- + extern int readsector0 (struct checker * c) { unsigned char buf[512]; @@ -417,7 +1071,7 @@ index d40a273..e79bc0a 100644 if (ioctl(c->fd, SG_IO, &io_hdr) < 0) { MSG(c, MSG_TUR_DOWN); diff --git a/libmultipath/Makefile b/libmultipath/Makefile -index 8a14b04..5d8c586 100644 +index 8a14b04..ef561a8 100644 --- a/libmultipath/Makefile +++ b/libmultipath/Makefile @@ -6,7 +6,7 @@ BUILD = glibc @@ -429,7 +1083,7 @@ index 8a14b04..5d8c586 100644 OBJS = memory.o parser.o vector.o devmapper.o callout.o \ hwtable.o blacklist.o util.o dmparser.o config.o \ -@@ -18,6 +18,7 @@ OBJS = memory.o parser.o vector.o devmap +@@ -18,12 +18,19 @@ OBJS = memory.o parser.o vector.o devmap PREVBUILD = $(shell nm debug.o 2> /dev/null|grep log_safe) ifeq ($(strip $(DAEMON)),1) @@ -437,24 +1091,538 @@ index 8a14b04..5d8c586 100644 CFLAGS += -DDAEMON CLEAN = $(shell if [ "x$(PREVBUILD)" = "x" ]; then echo clean; fi) else -diff --git a/libmultipath/callout.c b/libmultipath/callout.c -index 46b89e6..b0bc503 100644 ---- a/libmultipath/callout.c -+++ b/libmultipath/callout.c -@@ -78,7 +78,7 @@ int execute_program(char *path, char *va - /* dup write side of pipe to STDOUT */ - dup(fds[1]); + CLEAN = $(shell if [ ! "x$(PREVBUILD)" = "x" ]; then echo clean; fi) + endif -- retval = execv(argv[0], argv); -+ retval = execvp(argv[0], argv); ++LIBDM_API_FLUSH = $(shell objdump -T /lib/libdevmapper.so.* | grep -c dm_task_no_flush) ++ ++ifeq ($(strip $(LIBDM_API_FLUSH)),1) ++ CFLAGS += -DLIBDM_API_FLUSH ++endif ++ + all: $(BUILD) + + prepare: $(CLEAN) +diff --git a/libmultipath/alias.c b/libmultipath/alias.c +index 6d103d7..02ca087 100644 +--- a/libmultipath/alias.c ++++ b/libmultipath/alias.c +@@ -166,28 +166,14 @@ fail: + + + static int +-lookup_binding(int fd, char *map_wwid, char **map_alias) ++lookup_binding(FILE *f, char *map_wwid, char **map_alias) + { + char buf[LINE_MAX]; +- FILE *f; + unsigned int line_nr = 0; +- int scan_fd; + int id = 0; + + *map_alias = NULL; +- scan_fd = dup(fd); +- if (scan_fd < 0) { +- condlog(0, "Cannot dup bindings file descriptor : %s", +- strerror(errno)); +- return -1; +- } +- f = fdopen(scan_fd, "r"); +- if (!f) { +- condlog(0, "cannot fdopen on bindings file descriptor : %s", +- strerror(errno)); +- close(scan_fd); +- return -1; +- } ++ + while (fgets(buf, LINE_MAX, f)) { + char *c, *alias, *wwid; + int curr_id; +@@ -210,43 +196,27 @@ lookup_binding(int fd, char *map_wwid, c + } + if (strcmp(wwid, map_wwid) == 0){ + condlog(3, "Found matching wwid [%s] in bindings file." +- "\nSetting alias to %s", wwid, alias); ++ " Setting alias to %s", wwid, alias); + *map_alias = strdup(alias); + if (*map_alias == NULL) + condlog(0, "Cannot copy alias from bindings " + "file : %s", strerror(errno)); +- fclose(f); + return id; + } + } + condlog(3, "No matching wwid [%s] in bindings file.", map_wwid); +- fclose(f); + return id; + } + + static int +-rlookup_binding(int fd, char **map_wwid, char *map_alias) ++rlookup_binding(FILE *f, char **map_wwid, char *map_alias) + { + char buf[LINE_MAX]; +- FILE *f; + unsigned int line_nr = 0; +- int scan_fd; + int id = 0; + + *map_wwid = NULL; +- scan_fd = dup(fd); +- if (scan_fd < 0) { +- condlog(0, "Cannot dup bindings file descriptor : %s", +- strerror(errno)); +- return -1; +- } +- f = fdopen(scan_fd, "r"); +- if (!f) { +- condlog(0, "cannot fdopen on bindings file descriptor : %s", +- strerror(errno)); +- close(scan_fd); +- return -1; +- } ++ + while (fgets(buf, LINE_MAX, f)) { + char *c, *alias, *wwid; + int curr_id; +@@ -274,12 +244,10 @@ rlookup_binding(int fd, char **map_wwid, + if (*map_wwid == NULL) + condlog(0, "Cannot copy alias from bindings " + "file : %s", strerror(errno)); +- fclose(f); + return id; + } + } + condlog(3, "No matching alias [%s] in bindings file.", map_alias); +- fclose(f); + return id; + } + +@@ -327,7 +295,8 @@ char * + get_user_friendly_alias(char *wwid, char *file) + { + char *alias; +- int fd, id; ++ int fd, scan_fd, id; ++ FILE *f; + + if (!wwid || *wwid == '\0') { + condlog(3, "Cannot find binding for empty WWID"); +@@ -337,14 +306,37 @@ get_user_friendly_alias(char *wwid, char + fd = open_bindings_file(file); + if (fd < 0) + return NULL; +- id = lookup_binding(fd, wwid, &alias); ++ ++ scan_fd = dup(fd); ++ if (scan_fd < 0) { ++ condlog(0, "Cannot dup bindings file descriptor : %s", ++ strerror(errno)); ++ close(fd); ++ return NULL; ++ } ++ ++ f = fdopen(scan_fd, "r"); ++ if (!f) { ++ condlog(0, "cannot fdopen on bindings file descriptor : %s", ++ strerror(errno)); ++ close(scan_fd); ++ close(fd); ++ return NULL; ++ } ++ ++ id = lookup_binding(f, wwid, &alias); + if (id < 0) { ++ fclose(f); ++ close(scan_fd); + close(fd); + return NULL; + } ++ + if (!alias) + alias = allocate_binding(fd, wwid, id); + ++ fclose(f); ++ close(scan_fd); + close(fd); + return alias; + } +@@ -353,7 +345,8 @@ char * + get_user_friendly_wwid(char *alias, char *file) + { + char *wwid; +- int fd, id; ++ int fd, scan_fd, id; ++ FILE *f; + + if (!alias || *alias == '\0') { + condlog(3, "Cannot find binding for empty alias"); +@@ -363,12 +356,34 @@ get_user_friendly_wwid(char *alias, char + fd = open_bindings_file(file); + if (fd < 0) + return NULL; +- id = rlookup_binding(fd, &wwid, alias); ++ ++ scan_fd = dup(fd); ++ if (scan_fd < 0) { ++ condlog(0, "Cannot dup bindings file descriptor : %s", ++ strerror(errno)); ++ close(fd); ++ return NULL; ++ } ++ ++ f = fdopen(scan_fd, "r"); ++ if (!f) { ++ condlog(0, "cannot fdopen on bindings file descriptor : %s", ++ strerror(errno)); ++ close(scan_fd); ++ close(fd); ++ return NULL; ++ } ++ ++ id = rlookup_binding(f, &wwid, alias); + if (id < 0) { ++ fclose(f); ++ close(scan_fd); + close(fd); + return NULL; + } + ++ fclose(f); ++ close(scan_fd); + close(fd); + return wwid; + } +diff --git a/libmultipath/blacklist.c b/libmultipath/blacklist.c +index 6a8a681..5f7b2f5 100644 +--- a/libmultipath/blacklist.c ++++ b/libmultipath/blacklist.c +@@ -2,7 +2,6 @@ + * Copyright (c) 2004, 2005 Christophe Varoqui + */ + #include +- + #include + + #include "memory.h" +@@ -14,7 +13,7 @@ + #include "blacklist.h" + + extern int +-store_ble (vector blist, char * str) ++store_ble (vector blist, char * str, int origin) + { + struct blentry * ble; + +@@ -36,6 +35,7 @@ store_ble (vector blist, char * str) + goto out1; + + ble->str = str; ++ ble->origin = origin; + vector_set_slot(blist, ble); + return 0; + out1: +@@ -63,7 +63,7 @@ alloc_ble_device (vector blist) + } + + extern int +-set_ble_device (vector blist, char * vendor, char * product) ++set_ble_device (vector blist, char * vendor, char * product, int origin) + { + struct blentry_device * ble; + +@@ -91,6 +91,7 @@ set_ble_device (vector blist, char * ven + } + ble->product = product; + } ++ ble->origin = origin; + return 0; + } + +@@ -105,19 +106,19 @@ setup_default_blist (struct config * con + str = STRDUP("^(ram|raw|loop|fd|md|dm-|sr|scd|st)[0-9]*"); + if (!str) + return 1; +- if (store_ble(conf->blist_devnode, str)) ++ if (store_ble(conf->blist_devnode, str, ORIGIN_DEFAULT)) + return 1; + + str = STRDUP("^hd[a-z]"); + if (!str) + return 1; +- if (store_ble(conf->blist_devnode, str)) ++ if (store_ble(conf->blist_devnode, str, ORIGIN_DEFAULT)) + return 1; + + str = STRDUP("^cciss!c[0-9]d[0-9]*"); + if (!str) + return 1; +- if (store_ble(conf->blist_devnode, str)) ++ if (store_ble(conf->blist_devnode, str, ORIGIN_DEFAULT)) + return 1; + + vector_foreach_slot (conf->hwtable, hwe, i) { +@@ -128,7 +129,8 @@ setup_default_blist (struct config * con + VECTOR_SIZE(conf->blist_device) -1); + if (set_ble_device(conf->blist_device, + STRDUP(hwe->vendor), +- STRDUP(hwe->bl_product))) { ++ STRDUP(hwe->bl_product), ++ ORIGIN_DEFAULT)) { + FREE(ble); + return 1; + } +@@ -139,52 +141,183 @@ setup_default_blist (struct config * con + } + + int +-blacklist (vector blist, char * str) ++_blacklist_exceptions (vector elist, char * str) ++{ ++ int i; ++ struct blentry * ele; ++ ++ vector_foreach_slot (elist, ele, i) { ++ if (!regexec(&ele->regex, str, 0, NULL, 0)) ++ return 1; ++ } ++ return 0; ++} ++ ++int ++_blacklist (vector blist, char * str) + { + int i; + struct blentry * ble; + + vector_foreach_slot (blist, ble, i) { +- if (!regexec(&ble->regex, str, 0, NULL, 0)) { +- condlog(3, "%s: blacklisted", str); ++ if (!regexec(&ble->regex, str, 0, NULL, 0)) + return 1; +- } + } + return 0; + } + + int +-blacklist_device (vector blist, char * vendor, char * product) ++_blacklist_exceptions_device(vector elist, char * vendor, char * product) ++{ ++ int i; ++ struct blentry_device * ble; ++ ++ vector_foreach_slot (elist, ble, i) { ++ if (!regexec(&ble->vendor_reg, vendor, 0, NULL, 0) && ++ !regexec(&ble->product_reg, product, 0, NULL, 0)) ++ return 1; ++ } ++ return 0; ++} ++ ++int ++_blacklist_device (vector blist, char * vendor, char * product) + { + int i; + struct blentry_device * ble; + + vector_foreach_slot (blist, ble, i) { + if (!regexec(&ble->vendor_reg, vendor, 0, NULL, 0) && +- !regexec(&ble->product_reg, product, 0, NULL, 0)) { +- condlog(3, "%s:%s: blacklisted", vendor, product); ++ !regexec(&ble->product_reg, product, 0, NULL, 0)) + return 1; +- } + } + return 0; + } + ++#define LOG_BLIST(M) \ ++ if (vendor && product) \ ++ condlog(3, "%s: (%s:%s) %s", dev, vendor, product, (M)); \ ++ else if (wwid) \ ++ condlog(3, "%s: (%s) %s", dev, wwid, (M)); \ ++ else \ ++ condlog(3, "%s: %s", dev, (M)) ++ ++void ++log_filter (char *dev, char *vendor, char *product, char *wwid, int r) ++{ ++ /* ++ * Try to sort from most likely to least. ++ */ ++ switch (r) { ++ case MATCH_NOTHING: ++ break; ++ case MATCH_DEVICE_BLIST: ++ LOG_BLIST("vendor/product blacklisted"); ++ break; ++ case MATCH_WWID_BLIST: ++ LOG_BLIST("wwid blacklisted"); ++ break; ++ case MATCH_DEVNODE_BLIST: ++ LOG_BLIST("device node name blacklisted"); ++ break; ++ case MATCH_DEVICE_BLIST_EXCEPT: ++ LOG_BLIST("vendor/product whitelisted"); ++ break; ++ case MATCH_WWID_BLIST_EXCEPT: ++ LOG_BLIST("wwid whitelisted"); ++ break; ++ case MATCH_DEVNODE_BLIST_EXCEPT: ++ LOG_BLIST("device node name whitelisted"); ++ break; ++ } ++} ++ + int +-blacklist_path (struct config * conf, struct path * pp) ++_filter_device (vector blist, vector elist, char * vendor, char * product) + { +- if (blacklist(conf->blist_devnode, pp->dev)) +- return 1; ++ 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; ++} + +- if (blacklist(conf->blist_wwid, pp->wwid)) +- return 1; ++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, r); ++ return r; ++} + +- if (pp->vendor_id && pp->product_id && +- blacklist_device(conf->blist_device, pp->vendor_id, pp->product_id)) +- return 1; ++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; ++} ++ ++int ++filter_devnode (vector blist, vector elist, char * dev) ++{ ++ int r = _filter_devnode(blist, elist, dev); ++ log_filter(dev, NULL, NULL, NULL, r); ++ return r; ++} ++ ++int ++_filter_wwid (vector blist, vector elist, char * wwid) ++{ ++ 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 ++filter_wwid (vector blist, vector elist, char * wwid) ++{ ++ int r = _filter_wwid(blist, elist, wwid); ++ log_filter(NULL, NULL, NULL, wwid, r); ++ return r; ++} ++ ++int ++_filter_path (struct config * conf, struct path * pp) ++{ ++ int r; ++ ++ r = _filter_devnode(conf->blist_devnode, conf->elist_devnode,pp->dev); ++ if (r) ++ return r; ++ r = _filter_wwid(conf->blist_devnode, conf->elist_devnode, pp->wwid); ++ if (r) ++ return r; ++ r = _filter_device(conf->blist_device, conf->elist_device, ++ pp->vendor_id, pp->product_id); ++ if (r) ++ return r; + return 0; + } + ++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, r); ++ return r; ++} ++ + void + free_blacklist (vector blist) + { +diff --git a/libmultipath/blacklist.h b/libmultipath/blacklist.h +index 9e2b63c..cdbebef 100644 +--- a/libmultipath/blacklist.h ++++ b/libmultipath/blacklist.h +@@ -3,9 +3,18 @@ + + #include "regex.h" + ++#define MATCH_NOTHING 0 ++#define MATCH_WWID_BLIST 1 ++#define MATCH_DEVICE_BLIST 2 ++#define MATCH_DEVNODE_BLIST 3 ++#define MATCH_WWID_BLIST_EXCEPT -MATCH_WWID_BLIST ++#define MATCH_DEVICE_BLIST_EXCEPT -MATCH_DEVICE_BLIST ++#define MATCH_DEVNODE_BLIST_EXCEPT -MATCH_DEVNODE_BLIST ++ + struct blentry { + char * str; + regex_t regex; ++ int origin; + }; + + struct blentry_device { +@@ -13,15 +22,17 @@ struct blentry_device { + char * product; + regex_t vendor_reg; + regex_t product_reg; ++ int origin; + }; + + int setup_default_blist (struct config *); + int alloc_ble_device (vector); +-int blacklist (vector, char *); +-int blacklist_device (vector, char *, char *); +-int blacklist_path (struct config *, struct path *); +-int store_ble (vector, char *); +-int set_ble_device (vector, char *, char *); ++int filter_devnode (vector, vector, char *); ++int filter_wwid (vector, vector, char *); ++int filter_device (vector, vector, char *, char *); ++int filter_path (struct config *, 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); - exit(-1); - case -1: diff --git a/libmultipath/config.c b/libmultipath/config.c -index 1068755..528e475 100644 +index 1068755..a39af8a 100644 --- a/libmultipath/config.c +++ b/libmultipath/config.c -@@ -20,26 +20,61 @@ #include "config.h" +@@ -20,26 +20,61 @@ #include "blacklist.h" #include "defaults.h" @@ -570,8 +1738,15 @@ index 1068755..528e475 100644 if (dhwe->getuid && !(hwe->getuid = set_param_str(dhwe->getuid))) goto out; -@@ -305,7 +335,7 @@ free_config (struct config * conf) +@@ -303,9 +333,14 @@ free_config (struct config * conf) + free_blacklist(conf->blist_devnode); + free_blacklist(conf->blist_wwid); free_blacklist_device(conf->blist_device); ++ ++ free_blacklist(conf->elist_devnode); ++ free_blacklist(conf->elist_wwid); ++ free_blacklist_device(conf->elist_device); ++ free_mptable(conf->mptable); free_hwtable(conf->hwtable); - @@ -579,7 +1754,7 @@ index 1068755..528e475 100644 FREE(conf); } -@@ -332,6 +362,7 @@ load_config (char * file) +@@ -332,6 +367,7 @@ load_config (char * file) * read the config file */ if (filepresent(file)) { @@ -587,11 +1762,48 @@ index 1068755..528e475 100644 if (init_data(file, init_keywords)) { condlog(0, "error parsing config file"); goto out; +@@ -372,6 +408,26 @@ load_config (char * file) + if (setup_default_blist(conf)) + goto out; + ++ if (conf->elist_devnode == NULL) { ++ conf->elist_devnode = vector_alloc(); ++ ++ if (!conf->elist_devnode) ++ goto out; ++ } ++ if (conf->elist_wwid == NULL) { ++ conf->elist_wwid = vector_alloc(); ++ ++ if (!conf->elist_wwid) ++ goto out; ++ } ++ ++ if (conf->elist_device == NULL) { ++ conf->elist_device = vector_alloc(); ++ ++ if (!conf->elist_device) ++ goto out; ++ } ++ + if (conf->mptable == NULL) { + conf->mptable = vector_alloc(); + diff --git a/libmultipath/config.h b/libmultipath/config.h -index 6e5633a..0c3b640 100644 +index 6e5633a..7caa11d 100644 --- a/libmultipath/config.h +++ b/libmultipath/config.h -@@ -11,6 +11,7 @@ enum devtypes { +@@ -1,6 +1,9 @@ + #ifndef _CONFIG_H + #define _CONFIG_H + ++#define ORIGIN_DEFAULT 0 ++#define ORIGIN_CONFIG 1 ++ + enum devtypes { + DEV_NONE, + DEV_DEVT, +@@ -11,6 +14,7 @@ enum devtypes { struct hwentry { char * vendor; char * product; @@ -599,7 +1811,23 @@ index 6e5633a..0c3b640 100644 char * getuid; char * getprio; char * features; -@@ -48,7 +49,7 @@ struct config { +@@ -23,6 +27,7 @@ struct hwentry { + int rr_weight; + int no_path_retry; + int minio; ++ int pg_timeout; + struct checker * checker; + char * bl_product; + }; +@@ -38,6 +43,7 @@ struct mpentry { + int rr_weight; + int no_path_retry; + int minio; ++ int pg_timeout; + }; + + struct config { +@@ -48,7 +54,7 @@ struct config { int with_sysfs; int pgpolicy; struct checker * checker; @@ -608,7 +1836,15 @@ index 6e5633a..0c3b640 100644 int minio; int checkint; int max_checkint; -@@ -67,6 +68,7 @@ struct config { +@@ -57,6 +63,7 @@ struct config { + int rr_weight; + int no_path_retry; + int user_friendly_names; ++ int pg_timeout; + + char * dev; + char * udev_dir; +@@ -67,17 +74,21 @@ struct config { char * hwhandler; char * bindings_file; @@ -616,7 +1852,13 @@ index 6e5633a..0c3b640 100644 vector mptable; vector hwtable; -@@ -77,7 +79,7 @@ struct config { + vector blist_devnode; + vector blist_wwid; + vector blist_device; ++ vector elist_devnode; ++ vector elist_wwid; ++ vector elist_device; + }; struct config * conf; @@ -626,10 +1868,18 @@ index 6e5633a..0c3b640 100644 char * get_mpe_wwid (char * alias); diff --git a/libmultipath/configure.c b/libmultipath/configure.c -index 1ba4356..9d17022 100644 +index 1ba4356..9632fb4 100644 --- a/libmultipath/configure.c +++ b/libmultipath/configure.c -@@ -145,11 +145,6 @@ select_action (struct multipath * mpp, v +@@ -60,6 +60,7 @@ setup_map (struct multipath * mpp) + select_rr_weight(mpp); + select_minio(mpp); + select_no_path_retry(mpp); ++ select_pg_timeout(mpp); + + /* + * assign paths to path groups -- start with no groups and all paths +@@ -145,11 +146,6 @@ select_action (struct multipath * mpp, v mpp->action = ACT_RENAME; return; } @@ -641,7 +1891,19 @@ index 1ba4356..9d17022 100644 mpp->action = ACT_CREATE; condlog(3, "%s: set ACT_CREATE (map does not exist)", mpp->alias); -@@ -286,11 +281,13 @@ lock_multipath (struct multipath * mpp, +@@ -179,8 +175,9 @@ select_action (struct multipath * mpp, v + mpp->alias); + return; + } +- if (!mpp->no_path_retry && /* let features be handled by the daemon */ +- strncmp(cmpp->features, mpp->features, strlen(mpp->features))) { ++ if (!mpp->no_path_retry && !mpp->pg_timeout && ++ (strlen(cmpp->features) != strlen(mpp->features) || ++ strcmp(cmpp->features, mpp->features))) { + mpp->action = ACT_RELOAD; + condlog(3, "%s: set ACT_RELOAD (features change)", + mpp->alias); +@@ -286,11 +283,13 @@ lock_multipath (struct multipath * mpp, /* * Return value: @@ -659,7 +1921,7 @@ index 1ba4356..9d17022 100644 extern int domap (struct multipath * mpp) { -@@ -299,15 +296,15 @@ domap (struct multipath * mpp) +@@ -299,15 +298,15 @@ domap (struct multipath * mpp) /* * last chance to quit before touching the devmaps */ @@ -678,7 +1940,7 @@ index 1ba4356..9d17022 100644 case ACT_SWITCHPG: dm_switchgroup(mpp->alias, mpp->bestpg); -@@ -317,13 +314,13 @@ domap (struct multipath * mpp) +@@ -317,13 +316,13 @@ domap (struct multipath * mpp) * retry. */ reinstate_paths(mpp); @@ -694,7 +1956,7 @@ index 1ba4356..9d17022 100644 } dm_shut_log(); -@@ -377,9 +374,9 @@ #else +@@ -377,9 +376,9 @@ domap (struct multipath * mpp) condlog(2, "%s: load table [0 %llu %s %s]", mpp->alias, mpp->size, DEFAULT_TARGET, mpp->params); #endif @@ -706,7 +1968,16 @@ index 1ba4356..9d17022 100644 } static int -@@ -444,7 +441,7 @@ coalesce_paths (struct vectors * vecs, v +@@ -423,7 +422,7 @@ coalesce_paths (struct vectors * vecs, v + + /* 1. if path has no unique id or wwid blacklisted */ + if (memcmp(empty_buff, pp1->wwid, WWID_SIZE) == 0 || +- blacklist_path(conf, pp1)) ++ filter_path(conf, pp1) > 0) + continue; + + /* 2. if path already coalesced */ +@@ -444,7 +443,7 @@ coalesce_paths (struct vectors * vecs, v if ((mpp = add_map_with_path(vecs, pp1, 0)) == NULL) return 1; @@ -715,7 +1986,7 @@ index 1ba4356..9d17022 100644 mpp->action = ACT_REJECT; if (!mpp->paths) { -@@ -471,7 +468,7 @@ coalesce_paths (struct vectors * vecs, v +@@ -471,7 +470,7 @@ coalesce_paths (struct vectors * vecs, v mpp->size); mpp->action = ACT_REJECT; } @@ -724,7 +1995,7 @@ index 1ba4356..9d17022 100644 mpp->action = ACT_REJECT; } verify_paths(mpp, vecs, NULL); -@@ -486,19 +483,18 @@ coalesce_paths (struct vectors * vecs, v +@@ -486,19 +485,18 @@ coalesce_paths (struct vectors * vecs, v r = domap(mpp); @@ -752,7 +2023,20 @@ index 1ba4356..9d17022 100644 if (mpp->no_path_retry != NO_PATH_RETRY_UNDEF) { if (mpp->no_path_retry == NO_PATH_RETRY_FAIL) -@@ -547,11 +543,11 @@ coalesce_paths (struct vectors * vecs, v +@@ -506,6 +504,12 @@ coalesce_paths (struct vectors * vecs, v + else + dm_queue_if_no_path(mpp->alias, 1); + } ++ if (mpp->pg_timeout != PGTIMEOUT_UNDEF) { ++ if (mpp->pg_timeout == -PGTIMEOUT_NONE) ++ dm_set_pg_timeout(mpp->alias, 0); ++ else ++ dm_set_pg_timeout(mpp->alias, mpp->pg_timeout); ++ } + + if (newmp) { + if (mpp->action != ACT_REJECT) { +@@ -547,11 +551,11 @@ coalesce_paths (struct vectors * vecs, v } extern char * @@ -766,7 +2050,7 @@ index 1ba4356..9d17022 100644 if (dev_type == DEV_NONE) return NULL; -@@ -606,6 +602,12 @@ get_refwwid (char * dev, int dev_type, v +@@ -606,6 +610,12 @@ get_refwwid (char * dev, int dev_type, v goto out; } if (dev_type == DEV_DEVMAP) { @@ -794,7 +2078,7 @@ diff --git a/libmultipath/debug.c b/libmultipath/debug.c index 099ab52..fa06cbd 100644 --- a/libmultipath/debug.c +++ b/libmultipath/debug.c -@@ -29,17 +29,16 @@ #if DAEMON +@@ -29,17 +29,16 @@ void dlog (int sink, int prio, char * fm struct tm *tb = localtime(&t); char buff[16]; @@ -818,7 +2102,7 @@ diff --git a/libmultipath/debug.h b/libmultipath/debug.h index e7612a9..74ed531 100644 --- a/libmultipath/debug.h +++ b/libmultipath/debug.h -@@ -11,11 +11,11 @@ #include "log_pthread.h" +@@ -11,11 +11,11 @@ void dlog (int sink, int prio, char * fm int logsink; #define condlog(prio, fmt, args...) \ @@ -832,11 +2116,23 @@ index e7612a9..74ed531 100644 + dlog(0, prio, fmt "\n", ##args) #endif /* DAEMON */ +diff --git a/libmultipath/defaults.h b/libmultipath/defaults.h +index ab65492..8deeb0f 100644 +--- a/libmultipath/defaults.h ++++ b/libmultipath/defaults.h +@@ -9,6 +9,7 @@ + #define DEFAULT_FAILBACK -FAILBACK_MANUAL + #define DEFAULT_RR_WEIGHT RR_WEIGHT_NONE + #define DEFAULT_NO_PATH_RETRY NO_PATH_RETRY_UNDEF ++#define DEFAULT_PGTIMEOUT -PGTIMEOUT_NONE + #define DEFAULT_USER_FRIENDLY_NAMES 0 + + #define DEFAULT_CHECKINT 5 diff --git a/libmultipath/devmapper.c b/libmultipath/devmapper.c -index 4328036..b8571ad 100644 +index 4328036..dece079 100644 --- a/libmultipath/devmapper.c +++ b/libmultipath/devmapper.c -@@ -11,6 +11,7 @@ #include +@@ -11,6 +11,7 @@ #include #include #include @@ -844,26 +2140,125 @@ index 4328036..b8571ad 100644 #include -@@ -23,6 +24,10 @@ #include "devmapper.h" +@@ -23,6 +24,9 @@ #define MAX_WAIT 5 #define LOOPS_PER_SEC 5 +#define UUID_PREFIX "mpath-" +#define UUID_PREFIX_LEN 6 -+ + static void dm_dummy_log (int level, const char *file, int line, const char *f, ...) { -@@ -100,6 +105,7 @@ dm_simplecmd (int task, const char *name +@@ -41,13 +45,35 @@ dm_shut_log (void) + dm_log_init(&dm_dummy_log); + } + +-extern int +-dm_prereq (char * str, int x, int y, int z) ++static int ++dm_libprereq (void) ++{ ++ char version[64]; ++ int v[3]; ++ int minv[3] = {1, 2, 11}; ++ ++ dm_get_library_version(version, sizeof(version)); ++ condlog(3, "libdevmapper version %s", version); ++ sscanf(version, "%d.%d.%d ", &v[0], &v[1], &v[2]); ++ ++ if ((v[0] > minv[0]) || ++ ((v[0] == minv[0]) && (v[1] > minv[1])) || ++ ((v[0] == minv[0]) && (v[1] == minv[1]) && (v[2] >= minv[2]))) ++ return 0; ++ condlog(0, "libdevmapper version must be >= %d.%.2d.%.2d", ++ minv[0], minv[1], minv[2]); ++ return 1; ++} ++ ++static int ++dm_drvprereq (char * str) + { + int r = 2; + struct dm_task *dmt; + struct dm_versions *target; + struct dm_versions *last_target; ++ int minv[3] = {1, 0, 3}; ++ unsigned int *v; + + if (!(dmt = dm_task_create(DM_DEVICE_LIST_VERSIONS))) + return 3; +@@ -58,37 +84,44 @@ dm_prereq (char * str, int x, int y, int + condlog(0, "Can not communicate with kernel DM"); + goto out; + } +- + target = dm_task_get_versions(dmt); + + do { + last_target = target; +- + if (!strncmp(str, target->name, strlen(str))) { +- r--; +- +- if (target->version[0] >= x && +- target->version[1] >= y && +- target->version[2] >= z) +- r--; +- ++ r = 1; + break; + } +- + target = (void *) target + target->next; + } while (last_target != target); + +- if (r == 2) ++ if (r == 2) { + condlog(0, "DM multipath kernel driver not loaded"); +- else if (r == 1) +- condlog(0, "DM multipath kernel driver version too old"); +- ++ goto out; ++ } ++ v = target->version; ++ if ((v[0] > minv[0]) || ++ ((v[0] == minv[0]) && (v[1] > minv[1])) || ++ ((v[0] == minv[0]) && (v[1] == minv[1]) && (v[2] >= minv[2]))) { ++ r = 0; ++ goto out; ++ } ++ condlog(0, "DM multipath kernel driver must be >= %u.%.2u.%.2u", ++ minv[0], minv[1], minv[2]); + out: + dm_task_destroy(dmt); + return r; + } + + extern int ++dm_prereq (char * str) ++{ ++ if (dm_libprereq()) ++ return 1; ++ return dm_drvprereq(str); ++} ++ ++extern int + dm_simplecmd (int task, const char *name) { + int r = 0; + struct dm_task *dmt; +@@ -100,6 +133,10 @@ dm_simplecmd (int task, const char *name goto out; dm_task_no_open_count(dmt); + dm_task_skip_lockfs(dmt); /* for DM_DEVICE_RESUME */ ++#ifdef LIBDM_API_FLUSH ++ dm_task_no_flush(dmt); /* for DM_DEVICE_SUSPEND/RESUME */ ++#endif r = dm_task_run (dmt); -@@ -113,6 +119,7 @@ dm_addmap (int task, const char *name, c +@@ -113,6 +150,7 @@ dm_addmap (int task, const char *name, c const char *params, unsigned long long size, const char *uuid) { int r = 0; struct dm_task *dmt; @@ -871,7 +2266,7 @@ index 4328036..b8571ad 100644 if (!(dmt = dm_task_create (task))) return 0; -@@ -123,13 +130,26 @@ dm_addmap (int task, const char *name, c +@@ -123,13 +161,26 @@ dm_addmap (int task, const char *name, c if (!dm_task_add_target (dmt, 0, size, target, params)) goto addout; @@ -900,7 +2295,7 @@ index 4328036..b8571ad 100644 addout: dm_task_destroy (dmt); return r; -@@ -203,6 +223,7 @@ dm_get_uuid(char *name, char *uuid) +@@ -203,6 +254,7 @@ dm_get_uuid(char *name, char *uuid) { struct dm_task *dmt; const char *uuidtmp; @@ -908,7 +2303,7 @@ index 4328036..b8571ad 100644 dmt = dm_task_create(DM_DEVICE_INFO); if (!dmt) -@@ -215,15 +236,19 @@ dm_get_uuid(char *name, char *uuid) +@@ -215,15 +267,19 @@ dm_get_uuid(char *name, char *uuid) goto uuidout; uuidtmp = dm_task_get_uuid(dmt); @@ -932,7 +2327,24 @@ index 4328036..b8571ad 100644 } extern int -@@ -591,6 +616,7 @@ dm_get_maps (vector mp, char * type) +@@ -509,6 +565,16 @@ dm_queue_if_no_path(char *mapname, int e + return dm_message(mapname, message); + } + ++int ++dm_set_pg_timeout(char *mapname, int timeout_val) ++{ ++ char message[24]; ++ ++ if (snprintf(message, 24, "set_pg_timeout %d", timeout_val) >= 24) ++ return 1; ++ return dm_message(mapname, message); ++} ++ + static int + dm_groupmsg (char * msg, char * mapname, int index) + { +@@ -591,6 +657,7 @@ dm_get_maps (vector mp, char * type) goto out1; dm_get_uuid(names->name, mpp->wwid); @@ -940,11 +2352,394 @@ index 4328036..b8571ad 100644 } if (!vector_alloc_slot(mp)) +diff --git a/libmultipath/devmapper.h b/libmultipath/devmapper.h +index c7879a7..59afd01 100644 +--- a/libmultipath/devmapper.h ++++ b/libmultipath/devmapper.h +@@ -1,6 +1,6 @@ + void dm_shut_log(void); + void dm_restore_log(void); +-int dm_prereq (char *, int, int, int); ++int dm_prereq (char *); + int dm_simplecmd (int, const char *); + int dm_addmap (int, const char *, const char *, const char *, + unsigned long long, const char *uuid); +@@ -13,6 +13,7 @@ int dm_flush_maps (char *); + int dm_fail_path(char * mapname, char * path); + int dm_reinstate_path(char * mapname, char * path); + int dm_queue_if_no_path(char *mapname, int enable); ++int dm_set_pg_timeout(char *mapname, int timeout_val); + int dm_switchgroup(char * mapname, int index); + int dm_enablegroup(char * mapname, int index); + int dm_disablegroup(char * mapname, int index); +diff --git a/libmultipath/dict.c b/libmultipath/dict.c +index 9ca228a..c705cc6 100644 +--- a/libmultipath/dict.c ++++ b/libmultipath/dict.c +@@ -201,6 +201,32 @@ def_no_path_retry_handler(vector strvec) + } + + static int ++def_pg_timeout_handler(vector strvec) ++{ ++ int pg_timeout; ++ char * buff; ++ ++ buff = set_value(strvec); ++ ++ if (!buff) ++ return 1; ++ ++ if (strlen(buff) == 4 && !strcmp(buff, "none")) ++ conf->pg_timeout = -PGTIMEOUT_NONE; ++ else if (sscanf(buff, "%d", &pg_timeout) == 1 && pg_timeout >= 0) { ++ if (pg_timeout == 0) ++ conf->pg_timeout = -PGTIMEOUT_NONE; ++ else ++ conf->pg_timeout = pg_timeout; ++ } ++ else ++ conf->pg_timeout = PGTIMEOUT_UNDEF; ++ ++ FREE(buff); ++ return 0; ++} ++ ++static int + names_handler(vector strvec) + { + char * buff; +@@ -238,6 +264,19 @@ blacklist_handler(vector strvec) + } + + static int ++blacklist_exceptions_handler(vector strvec) ++{ ++ conf->elist_devnode = vector_alloc(); ++ conf->elist_wwid = vector_alloc(); ++ conf->elist_device = vector_alloc(); ++ ++ if (!conf->elist_devnode || !conf->elist_wwid || !conf->blist_device) ++ return 1; ++ ++ return 0; ++} ++ ++static int + ble_devnode_handler(vector strvec) + { + char * buff; +@@ -247,7 +286,20 @@ ble_devnode_handler(vector strvec) + if (!buff) + return 1; + +- return store_ble(conf->blist_devnode, buff); ++ return store_ble(conf->blist_devnode, buff, ORIGIN_CONFIG); ++} ++ ++static int ++ble_except_devnode_handler(vector strvec) ++{ ++ char * buff; ++ ++ buff = set_value(strvec); ++ ++ if (!buff) ++ return 1; ++ ++ return store_ble(conf->elist_devnode, buff, ORIGIN_CONFIG); + } + + static int +@@ -260,7 +312,20 @@ ble_wwid_handler(vector strvec) + if (!buff) + return 1; + +- return store_ble(conf->blist_wwid, buff); ++ return store_ble(conf->blist_wwid, buff, ORIGIN_CONFIG); ++} ++ ++static int ++ble_except_wwid_handler(vector strvec) ++{ ++ char * buff; ++ ++ buff = set_value(strvec); ++ ++ if (!buff) ++ return 1; ++ ++ return store_ble(conf->elist_wwid, buff, ORIGIN_CONFIG); + } + + static int +@@ -270,6 +335,12 @@ ble_device_handler(vector strvec) + } + + static int ++ble_except_device_handler(vector strvec) ++{ ++ return alloc_ble_device(conf->elist_device); ++} ++ ++static int + ble_vendor_handler(vector strvec) + { + char * buff; +@@ -279,7 +350,20 @@ ble_vendor_handler(vector strvec) + if (!buff) + return 1; + +- return set_ble_device(conf->blist_device, buff, NULL); ++ return set_ble_device(conf->blist_device, buff, NULL, ORIGIN_CONFIG); ++} ++ ++static int ++ble_except_vendor_handler(vector strvec) ++{ ++ char * buff; ++ ++ buff = set_value(strvec); ++ ++ if (!buff) ++ return 1; ++ ++ return set_ble_device(conf->elist_device, buff, NULL, ORIGIN_CONFIG); + } + + static int +@@ -292,7 +376,20 @@ ble_product_handler(vector strvec) + if (!buff) + return 1; + +- return set_ble_device(conf->blist_device, NULL, buff); ++ return set_ble_device(conf->blist_device, NULL, buff, ORIGIN_CONFIG); ++} ++ ++static int ++ble_except_product_handler(vector strvec) ++{ ++ char * buff; ++ ++ buff = set_value(strvec); ++ ++ if (!buff) ++ return 1; ++ ++ return set_ble_device(conf->elist_device, NULL, buff, ORIGIN_CONFIG); + } + + /* +@@ -585,6 +682,36 @@ hw_minio_handler(vector strvec) + return 0; + } + ++static int ++hw_pg_timeout_handler(vector strvec) ++{ ++ int pg_timeout; ++ struct hwentry *hwe = VECTOR_LAST_SLOT(conf->hwtable); ++ char *buff; ++ ++ if (!hwe) ++ return 1; ++ ++ buff = set_value(strvec); ++ ++ if (!buff) ++ return 1; ++ ++ if (strlen(buff) == 4 && !strcmp(buff, "none")) ++ hwe->pg_timeout = -PGTIMEOUT_NONE; ++ else if (sscanf(buff, "%d", &pg_timeout) == 1 && pg_timeout >= 0) { ++ if (pg_timeout == 0) ++ hwe->pg_timeout = -PGTIMEOUT_NONE; ++ else ++ hwe->pg_timeout = pg_timeout; ++ } ++ else ++ hwe->pg_timeout = PGTIMEOUT_UNDEF; ++ ++ FREE(buff); ++ return 0; ++} ++ + /* + * multipaths block handlers + */ +@@ -777,6 +904,35 @@ mp_minio_handler(vector strvec) + return 0; + } + ++static int ++mp_pg_timeout_handler(vector strvec) ++{ ++ int pg_timeout; ++ struct mpentry *mpe = VECTOR_LAST_SLOT(conf->mptable); ++ char *buff; ++ ++ if (!mpe) ++ return 1; ++ ++ buff = set_value(strvec); ++ ++ if (!buff) ++ return 1; ++ if (strlen(buff) == 4 && !strcmp(buff, "none")) ++ mpe->pg_timeout = -PGTIMEOUT_NONE; ++ else if (sscanf(buff, "%d", &pg_timeout) == 1 && pg_timeout >= 0) { ++ if (pg_timeout == 0) ++ mpe->pg_timeout = -PGTIMEOUT_NONE; ++ else ++ mpe->pg_timeout = pg_timeout; ++ } ++ else ++ mpe->pg_timeout = PGTIMEOUT_UNDEF; ++ ++ FREE(buff); ++ return 0; ++} ++ + /* + * config file keywords printing + */ +@@ -896,6 +1052,22 @@ snprint_mp_rr_min_io (char * buff, int l + } + + static int ++snprint_mp_pg_timeout (char * buff, int len, void * data) ++{ ++ struct mpentry * mpe = (struct mpentry *)data; ++ ++ switch (mpe->pg_timeout) { ++ case PGTIMEOUT_UNDEF: ++ break; ++ case -PGTIMEOUT_NONE: ++ return snprintf(buff, len, "none"); ++ default: ++ return snprintf(buff, len, "%i", mpe->pg_timeout); ++ } ++ return 0; ++} ++ ++static int + snprint_hw_vendor (char * buff, int len, void * data) + { + struct hwentry * hwe = (struct hwentry *)data; +@@ -1097,6 +1269,27 @@ snprint_hw_rr_min_io (char * buff, int l + } + + static int ++snprint_hw_pg_timeout (char * buff, int len, void * data) ++{ ++ struct hwentry * hwe = (struct hwentry *)data; ++ ++ if (!hwe->pg_timeout) ++ return 0; ++ if (hwe->pg_timeout == conf->pg_timeout) ++ return 0; ++ ++ switch (hwe->pg_timeout) { ++ case PGTIMEOUT_UNDEF: ++ break; ++ case -PGTIMEOUT_NONE: ++ return snprintf(buff, len, "none"); ++ default: ++ return snprintf(buff, len, "%i", hwe->pg_timeout); ++ } ++ return 0; ++} ++ ++static int + snprint_hw_path_checker (char * buff, int len, void * data) + { + struct hwentry * hwe = (struct hwentry *)data; +@@ -1268,6 +1461,23 @@ snprint_def_no_path_retry (char * buff, + } + + static int ++snprint_def_pg_timeout (char * buff, int len, void * data) ++{ ++ if (conf->pg_timeout == DEFAULT_PGTIMEOUT) ++ return 0; ++ ++ switch (conf->pg_timeout) { ++ case PGTIMEOUT_UNDEF: ++ break; ++ case -PGTIMEOUT_NONE: ++ return snprintf(buff, len, "none"); ++ default: ++ return snprintf(buff, len, "%i", conf->pg_timeout); ++ } ++ return 0; ++} ++ ++static int + snprint_def_user_friendly_names (char * buff, int len, void * data) + { + if (conf->user_friendly_names == DEFAULT_USER_FRIENDLY_NAMES) +@@ -1320,6 +1530,7 @@ init_keywords(void) + install_keyword("rr_min_io", &def_minio_handler, &snprint_def_rr_min_io); + install_keyword("rr_weight", &def_weight_handler, &snprint_def_rr_weight); + install_keyword("no_path_retry", &def_no_path_retry_handler, &snprint_def_no_path_retry); ++ install_keyword("pg_timeout", &def_pg_timeout_handler, &snprint_def_pg_timeout); + install_keyword("user_friendly_names", &names_handler, &snprint_def_user_friendly_names); + __deprecated install_keyword("default_selector", &def_selector_handler, NULL); + __deprecated install_keyword("default_path_grouping_policy", &def_pgpolicy_handler, NULL); +@@ -1336,6 +1547,14 @@ init_keywords(void) + install_keyword("vendor", &ble_vendor_handler, &snprint_bled_vendor); + install_keyword("product", &ble_product_handler, &snprint_bled_product); + install_sublevel_end(); ++ install_keyword_root("blacklist_exceptions", &blacklist_exceptions_handler); ++ install_keyword("devnode", &ble_except_devnode_handler, &snprint_ble_simple); ++ install_keyword("wwid", &ble_except_wwid_handler, &snprint_ble_simple); ++ install_keyword("device", &ble_except_device_handler, NULL); ++ install_sublevel(); ++ install_keyword("vendor", &ble_except_vendor_handler, &snprint_bled_vendor); ++ install_keyword("product", &ble_except_product_handler, &snprint_bled_product); ++ install_sublevel_end(); + + #if 0 + __deprecated install_keyword_root("devnode_blacklist", &blacklist_handler); +@@ -1365,6 +1584,7 @@ init_keywords(void) + install_keyword("rr_weight", &hw_weight_handler, &snprint_hw_rr_weight); + install_keyword("no_path_retry", &hw_no_path_retry_handler, &snprint_hw_no_path_retry); + install_keyword("rr_min_io", &hw_minio_handler, &snprint_hw_rr_min_io); ++ install_keyword("pg_timeout", &hw_pg_timeout_handler, &snprint_hw_pg_timeout); + install_sublevel_end(); + + install_keyword_root("multipaths", &multipaths_handler); +@@ -1378,5 +1598,6 @@ init_keywords(void) + install_keyword("rr_weight", &mp_weight_handler, &snprint_mp_rr_weight); + install_keyword("no_path_retry", &mp_no_path_retry_handler, &snprint_mp_no_path_retry); + install_keyword("rr_min_io", &mp_minio_handler, &snprint_mp_rr_min_io); ++ install_keyword("pg_timeout", &mp_pg_timeout_handler, &snprint_mp_pg_timeout); + install_sublevel_end(); + } diff --git a/libmultipath/discovery.c b/libmultipath/discovery.c -index cf8289c..f21e5bc 100644 +index cf8289c..a196583 100644 --- a/libmultipath/discovery.c +++ b/libmultipath/discovery.c -@@ -165,7 +165,7 @@ sysfs_get_##fname (char * sysfs_path, ch +@@ -61,7 +61,8 @@ path_discover (vector pathvec, struct co + if (!devname) + return 0; + +- if (blacklist(conf->blist_devnode, devname)) ++ if (filter_devnode(conf->blist_devnode, conf->elist_devnode, ++ devname) > 0) + return 0; + + if(safe_sprintf(path, "%s/block/%s/device", sysfs_path, +@@ -112,7 +113,7 @@ out: + * not multipath(8), ran by udev + */ + #if DAEMON +-#define WAIT_MAX_SECONDS 5 ++#define WAIT_MAX_SECONDS 60 + #define WAIT_LOOP_PER_SECOND 5 + + static int +@@ -165,7 +166,7 @@ sysfs_get_##fname (char * sysfs_path, ch goto out; \ \ strncpy(buff, attr->value, attr->len - 1); \ @@ -953,7 +2748,7 @@ index cf8289c..f21e5bc 100644 sysfs_close_attribute(attr); \ return 0; \ out: \ -@@ -237,7 +237,7 @@ devt2devname (char *devname, char *devt) +@@ -237,7 +238,7 @@ devt2devname (char *devname, char *devt) struct dlist * ls; char attr_path[FILE_NAME_SIZE]; char block_path[FILE_NAME_SIZE]; @@ -962,7 +2757,7 @@ index cf8289c..f21e5bc 100644 struct sysfs_class * class; struct sysfs_class_device * dev; -@@ -295,7 +295,7 @@ do_inq(int sg_fd, int cmddt, int evpd, u +@@ -295,7 +296,7 @@ do_inq(int sg_fd, int cmddt, int evpd, u { INQUIRY_CMD, 0, 0, 0, 0, 0 }; unsigned char sense_b[SENSE_BUFF_LEN]; struct sg_io_hdr io_hdr; @@ -971,7 +2766,7 @@ index cf8289c..f21e5bc 100644 if (cmddt) inqCmdBlk[1] |= 2; if (evpd) -@@ -313,10 +313,10 @@ do_inq(int sg_fd, int cmddt, int evpd, u +@@ -313,10 +314,10 @@ do_inq(int sg_fd, int cmddt, int evpd, u io_hdr.cmdp = inqCmdBlk; io_hdr.sbp = sense_b; io_hdr.timeout = DEF_TIMEOUT; @@ -984,7 +2779,7 @@ index cf8289c..f21e5bc 100644 /* treat SG_ERR here to get rid of sg_err.[ch] */ io_hdr.status &= 0x7e; if ((0 == io_hdr.status) && (0 == io_hdr.host_status) && -@@ -339,24 +339,26 @@ do_inq(int sg_fd, int cmddt, int evpd, u +@@ -339,24 +340,26 @@ do_inq(int sg_fd, int cmddt, int evpd, u return -1; } @@ -1016,7 +2811,7 @@ index cf8289c..f21e5bc 100644 } static int -@@ -440,7 +442,7 @@ scsi_sysfs_pathinfo (struct path * pp) +@@ -440,7 +443,7 @@ scsi_sysfs_pathinfo (struct path * pp) /* * set the hwe configlet pointer */ @@ -1025,7 +2820,7 @@ index cf8289c..f21e5bc 100644 /* * host / bus / target / lun -@@ -524,7 +526,7 @@ ccw_sysfs_pathinfo (struct path * pp) +@@ -524,7 +527,7 @@ ccw_sysfs_pathinfo (struct path * pp) /* * set the hwe configlet pointer */ @@ -1034,7 +2829,7 @@ index cf8289c..f21e5bc 100644 /* * host / bus / target / lun -@@ -597,7 +599,7 @@ static int +@@ -597,7 +600,7 @@ static int scsi_ioctl_pathinfo (struct path * pp, int mask) { if (mask & DI_SERIAL) { @@ -1043,7 +2838,24 @@ index cf8289c..f21e5bc 100644 condlog(3, "%s: serial = %s", pp->dev, pp->serial); } -@@ -636,14 +638,14 @@ get_prio (struct path * pp) +@@ -609,12 +612,15 @@ get_state (struct path * pp) + { + struct checker * c = &pp->checker; + ++ if (!pp->mpp) ++ return 0; ++ + if (!checker_selected(c)) { + select_checker(pp); + if (!checker_selected(c)) + return 1; + checker_set_fd(c, pp->fd); +- if (checker_init(c)) ++ if (checker_init(c, &pp->mpp->mpcontext)) + return 1; + } + pp->state = checker_check(c); +@@ -636,14 +642,14 @@ get_prio (struct path * pp) pp->getprio_selected = 1; } if (!pp->getprio) { @@ -1061,17 +2873,17 @@ index cf8289c..f21e5bc 100644 return 1; } else pp->priority = atoi(prio); -@@ -699,7 +701,12 @@ pathinfo (struct path *pp, vector hwtabl +@@ -699,7 +705,12 @@ pathinfo (struct path *pp, vector hwtabl if (mask & DI_CHECKER && get_state(pp)) goto blank; - if (mask & DI_PRIO && pp->state != PATH_DOWN) + /* -+ * Retrieve path priority for even PATH_DOWN paths if it has never ++ * Retrieve path priority even for not PATH_UP paths if it has never + * been successfully obtained before. + */ + if (mask & DI_PRIO && -+ (pp->state != PATH_DOWN || pp->priority != PRIO_UNDEF)) ++ (pp->state == PATH_UP || pp->priority == PRIO_UNDEF)) get_prio(pp); if (mask & DI_WWID && !strlen(pp->wwid)) @@ -1079,7 +2891,7 @@ diff --git a/libmultipath/discovery.h b/libmultipath/discovery.h index cdc9627..ab62a59 100644 --- a/libmultipath/discovery.h +++ b/libmultipath/discovery.h -@@ -5,7 +5,6 @@ #define SYSFS_PATH_SIZE 255 +@@ -5,7 +5,6 @@ #define INQUIRY_CMDLEN 6 #define INQUIRY_CMD 0x12 #define SENSE_BUFF_LEN 32 @@ -1087,7 +2899,7 @@ index cdc9627..ab62a59 100644 #define RECOVERED_ERROR 0x01 #define MX_ALLOC_LEN 255 #define TUR_CMD_LEN 6 -@@ -14,6 +13,10 @@ #ifndef BLKGETSIZE +@@ -14,6 +13,10 @@ #define BLKGETSIZE _IO(0x12,96) #endif @@ -1128,7 +2940,7 @@ index 2b170c6..631933d 100644 if (shift >= freechar) { fprintf(stderr, "mp->params too small\n"); return 1; -@@ -117,6 +120,7 @@ disassemble_map (vector pathvec, char * +@@ -117,6 +120,7 @@ disassemble_map (vector pathvec, char * int num_pg_args = 0; int num_paths = 0; int num_paths_args = 0; @@ -1136,7 +2948,7 @@ index 2b170c6..631933d 100644 struct path * pp; struct pathgroup * pgp; -@@ -305,12 +309,15 @@ #endif +@@ -305,12 +309,15 @@ disassemble_map (vector pathvec, char * if (k == 0 && !strncmp(mpp->selector, "round-robin", 11)) { p += get_word(p, &word); @@ -1156,10 +2968,10 @@ index 2b170c6..631933d 100644 else p += get_word(p, NULL); diff --git a/libmultipath/hwtable.c b/libmultipath/hwtable.c -index 5c7d625..ac126c0 100644 +index 5c7d625..df6f5aa 100644 --- a/libmultipath/hwtable.c +++ b/libmultipath/hwtable.c -@@ -12,13 +12,34 @@ #include "pgpolicies.h" +@@ -12,13 +12,34 @@ * Tuning suggestions on these parameters should go to * dm-devel@redhat.com * @@ -1277,8 +3089,12 @@ index 5c7d625..ac126c0 100644 * * Maintainer : Christophe Varoqui * Mail : christophe.varoqui@free.fr -@@ -120,7 +173,7 @@ static struct hwentry default_hw[] = { - .checker_name = READSECTOR0, +@@ -117,10 +170,10 @@ static struct hwentry default_hw[] = { + .rr_weight = RR_WEIGHT_NONE, + .no_path_retry = NO_PATH_RETRY_UNDEF, + .minio = DEFAULT_MINIO, +- .checker_name = READSECTOR0, ++ .checker_name = DIRECTIO, }, /* - * EMC / Clariion controler family @@ -1286,16 +3102,7 @@ index 5c7d625..ac126c0 100644 * * Maintainer : Edward Goggin, EMC * Mail : egoggin@emc.com -@@ -128,7 +181,7 @@ static struct hwentry default_hw[] = { - { - .vendor = "EMC", - .product = "SYMMETRIX", -- .getuid = "/sbin/scsi_id -g -u -ppre-spc3-83 -s /block/%n", -+ .getuid = "scsi_id -g -u -ppre-spc3-83 -s /block/%n", - .getprio = NULL, - .features = DEFAULT_FEATURES, - .hwhandler = DEFAULT_HWHANDLER, -@@ -142,10 +195,10 @@ static struct hwentry default_hw[] = { +@@ -142,7 +195,7 @@ static struct hwentry default_hw[] = { }, { .vendor = "DGC", @@ -1303,11 +3110,7 @@ index 5c7d625..ac126c0 100644 + .product = ".*", .bl_product = "LUNZ", .getuid = DEFAULT_GETUID, -- .getprio = "/sbin/mpath_prio_emc /dev/%n", -+ .getprio = "mpath_prio_emc /dev/%n", - .features = "1 queue_if_no_path", - .hwhandler = "1 emc", - .selector = DEFAULT_SELECTOR, + .getprio = "/sbin/mpath_prio_emc /dev/%n", @@ -157,7 +210,7 @@ static struct hwentry default_hw[] = { .checker_name = EMC_CLARIION, }, @@ -1345,7 +3148,7 @@ index 5c7d625..ac126c0 100644 + .vendor = "HITACHI", + .product = "DF.*", + .getuid = DEFAULT_GETUID, -+ .getprio = "mpath_prio_hds_modular %d", ++ .getprio = "/sbin/mpath_prio_hds_modular %d", + .features = DEFAULT_FEATURES, + .hwhandler = DEFAULT_HWHANDLER, + .selector = DEFAULT_SELECTOR, @@ -1365,12 +3168,15 @@ index 5c7d625..ac126c0 100644 * Mail : hare@suse.de */ { -@@ -224,7 +292,23 @@ static struct hwentry default_hw[] = { - .vendor = "IBM", - .product = "1742", - .getuid = DEFAULT_GETUID, -- .getprio = "/sbin/mpath_prio_tpc /dev/%n", -+ .getprio = "mpath_prio_tpc /dev/%n", +@@ -236,6 +304,22 @@ static struct hwentry default_hw[] = { + .checker_name = TUR, + }, + { ++ /* IBM Netfinity Fibre Channel RAID Controller Unit */ ++ .vendor = "IBM", ++ .product = "3526", ++ .getuid = DEFAULT_GETUID, ++ .getprio = "/sbin/mpath_prio_tpc /dev/%n", + .features = DEFAULT_FEATURES, + .hwhandler = DEFAULT_HWHANDLER, + .selector = DEFAULT_SELECTOR, @@ -1382,14 +3188,9 @@ index 5c7d625..ac126c0 100644 + .checker_name = TUR, + }, + { -+ /* IBM Netfinity Fibre Channel RAID Controller Unit */ -+ .vendor = "IBM", -+ .product = "3526", -+ .getuid = DEFAULT_GETUID, -+ .getprio = "mpath_prio_tpc /dev/%n", - .features = DEFAULT_FEATURES, - .hwhandler = DEFAULT_HWHANDLER, - .selector = DEFAULT_SELECTOR, + /* IBM DS4200 / FAStT200 */ + .vendor = "IBM", + .product = "3542", @@ -254,7 +338,7 @@ static struct hwentry default_hw[] = { { /* IBM ESS F20 aka Shark */ @@ -1399,7 +3200,7 @@ index 5c7d625..ac126c0 100644 .getuid = DEFAULT_GETUID, .getprio = NULL, .features = "1 queue_if_no_path", -@@ -268,11 +352,11 @@ static struct hwentry default_hw[] = { +@@ -268,9 +352,9 @@ static struct hwentry default_hw[] = { .checker_name = TUR, }, { @@ -1409,11 +3210,17 @@ index 5c7d625..ac126c0 100644 - .product = "{1750500,2145}", + .product = "1750500", .getuid = DEFAULT_GETUID, -- .getprio = "/sbin/mpath_prio_alua /dev/%n", -+ .getprio = "mpath_prio_alua /dev/%n", + .getprio = "/sbin/mpath_prio_alua /dev/%n", + .features = "1 queue_if_no_path", +@@ -292,7 +376,7 @@ static struct hwentry default_hw[] = { .features = "1 queue_if_no_path", .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, +- .pgpolicy = GROUP_BY_SERIAL, ++ .pgpolicy = MULTIBUS, + .pgfailback = FAILBACK_UNDEF, + .rr_weight = RR_WEIGHT_NONE, + .no_path_retry = NO_PATH_RETRY_UNDEF, @@ -300,10 +384,27 @@ static struct hwentry default_hw[] = { .checker_name = TUR, }, @@ -1422,7 +3229,7 @@ index 5c7d625..ac126c0 100644 + .vendor = "IBM", + .product = "2145", + .getuid = DEFAULT_GETUID, -+ .getprio = "mpath_prio_alua /dev/%n", ++ .getprio = "/sbin/mpath_prio_alua /dev/%n", + .features = "1 queue_if_no_path", + .hwhandler = DEFAULT_HWHANDLER, + .selector = DEFAULT_SELECTOR, @@ -1439,11 +3246,11 @@ index 5c7d625..ac126c0 100644 .product = "S/390 DASD ECKD", - .getuid = "/sbin/dasdview -j /dev/%n", + .bl_product = "S/390.*", -+ .getuid = "dasd_id /dev/%n", ++ .getuid = "/sbin/dasd_id /dev/%n", .getprio = NULL, .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, -@@ -315,29 +416,50 @@ static struct hwentry default_hw[] = { +@@ -315,38 +416,59 @@ static struct hwentry default_hw[] = { .minio = DEFAULT_MINIO, .checker_name = DIRECTIO, }, @@ -1462,8 +3269,7 @@ index 5c7d625..ac126c0 100644 - .product = "LUN", + .product = "LUN.*", .getuid = DEFAULT_GETUID, -- .getprio = "/sbin/mpath_prio_netapp /dev/%n", -+ .getprio = "mpath_prio_netapp /dev/%n", + .getprio = "/sbin/mpath_prio_netapp /dev/%n", .features = "1 queue_if_no_path", .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, @@ -1486,7 +3292,7 @@ index 5c7d625..ac126c0 100644 + .vendor = "IBM", + .product = "Nseries.*", + .getuid = DEFAULT_GETUID, -+ .getprio = "mpath_prio_netapp /dev/%n", ++ .getprio = "/sbin/mpath_prio_netapp /dev/%n", + .features = "1 queue_if_no_path", + .hwhandler = DEFAULT_HWHANDLER, + .selector = DEFAULT_SELECTOR, @@ -1501,32 +3307,18 @@ index 5c7d625..ac126c0 100644 - * Pillar Data controler family + * Pillar Data controller family * - * Maintainer : Christophe Varoqui - * Mail : christophe.varoqui@free.fr -@@ -346,7 +468,7 @@ static struct hwentry default_hw[] = { +- * Maintainer : Christophe Varoqui +- * Mail : christophe.varoqui@free.fr ++ * Maintainer : Srinivasan Ramani ++ * Mail : sramani@pillardata.com + */ + { .vendor = "Pillar", - .product = "Axiom 500", +- .product = "Axiom 500", ++ .product = "Axiom.*", .getuid = DEFAULT_GETUID, - .getprio = "/sbin/mpath_prio_alua %d", -+ .getprio = "mpath_prio_alua %d", - .features = DEFAULT_FEATURES, - .hwhandler = DEFAULT_HWHANDLER, - .selector = DEFAULT_SELECTOR, -@@ -382,7 +504,7 @@ static struct hwentry default_hw[] = { - .vendor = "SGI", - .product = "TP9[45]00", - .getuid = DEFAULT_GETUID, -- .getprio = "/sbin/mpath_prio_tpc /dev/%n", -+ .getprio = "mpath_prio_tpc /dev/%n", - .features = DEFAULT_FEATURES, - .hwhandler = DEFAULT_HWHANDLER, - .selector = DEFAULT_SELECTOR, -@@ -403,7 +525,7 @@ static struct hwentry default_hw[] = { - .vendor = "STK", - .product = "OPENstorage D280", - .getuid = DEFAULT_GETUID, -- .getprio = "/sbin/mpath_prio_tpc /dev/%n", -+ .getprio = "mpath_prio_tpc /dev/%n", ++ .getprio = "/sbin/mpath_prio_alua %n", .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, @@ -1585,7 +3377,7 @@ diff --git a/libmultipath/parser.c b/libmultipath/parser.c index 9b0b5c2..f9c555e 100644 --- a/libmultipath/parser.c +++ b/libmultipath/parser.c -@@ -25,6 +25,13 @@ #include "memory.h" +@@ -25,6 +25,13 @@ /* local vars */ static int sublevel = 0; vector keywords = NULL; @@ -1637,7 +3429,7 @@ diff --git a/libmultipath/pgpolicies.h b/libmultipath/pgpolicies.h index 66c3c29..1f010a3 100644 --- a/libmultipath/pgpolicies.h +++ b/libmultipath/pgpolicies.h -@@ -9,7 +9,7 @@ #endif +@@ -9,7 +9,7 @@ #define POLICY_NAME_SIZE 32 @@ -1647,10 +3439,19 @@ index 66c3c29..1f010a3 100644 IOPOLICY_UNDEF, FAILOVER, diff --git a/libmultipath/print.c b/libmultipath/print.c -index 6cc63e2..260aed1 100644 +index 6cc63e2..e50f37d 100644 --- a/libmultipath/print.c +++ b/libmultipath/print.c -@@ -13,8 +13,8 @@ #include "structs.h" +@@ -5,6 +5,8 @@ + #include + #include + #include ++#include ++#include + + #include + +@@ -13,11 +15,12 @@ #include "structs_vec.h" #include "print.h" #include "dmparser.h" @@ -1660,7 +3461,11 @@ index 6cc63e2..260aed1 100644 #include "pgpolicies.h" #include "defaults.h" #include "parser.h" -@@ -52,16 +52,26 @@ snprint_uint (char * buff, size_t len, u ++#include "blacklist.h" + + #define MAX(x,y) (x > y) ? x : y + #define TAIL (line + len - 1 - c) +@@ -52,16 +55,26 @@ snprint_uint (char * buff, size_t len, u static int snprint_size (char * buff, size_t len, unsigned long long size) { @@ -1694,7 +3499,7 @@ index 6cc63e2..260aed1 100644 static int snprint_name (char * buff, size_t len, struct multipath * mpp) { -@@ -222,6 +232,9 @@ snprint_action (char * buff, size_t len, +@@ -222,6 +235,9 @@ snprint_action (char * buff, size_t len, } } @@ -1704,7 +3509,7 @@ index 6cc63e2..260aed1 100644 static int snprint_path_uuid (char * buff, size_t len, struct path * pp) { -@@ -292,8 +305,8 @@ snprint_dm_path_state (char * buff, size +@@ -292,8 +308,8 @@ snprint_dm_path_state (char * buff, size static int snprint_vpr (char * buff, size_t len, struct path * pp) { @@ -1715,7 +3520,7 @@ index 6cc63e2..260aed1 100644 } static int -@@ -496,7 +509,7 @@ snprint_multipath (char * line, int len, +@@ -496,7 +512,7 @@ snprint_multipath (char * line, int len, char * f = format; /* format string cursor */ int fwd; struct multipath_data * data; @@ -1724,7 +3529,7 @@ index 6cc63e2..260aed1 100644 do { if (!TAIL) -@@ -515,6 +528,7 @@ snprint_multipath (char * line, int len, +@@ -515,6 +531,7 @@ snprint_multipath (char * line, int len, data->snprint(buff, MAX_FIELD_LEN, mpp); PRINT(c, TAIL, buff); PAD(data->width); @@ -1732,7 +3537,7 @@ index 6cc63e2..260aed1 100644 } while (*f++); line[c - line - 1] = '\n'; -@@ -631,7 +645,7 @@ snprint_pathgroup (char * line, int len, +@@ -631,7 +648,7 @@ snprint_pathgroup (char * line, int len, extern void print_multipath_topology (struct multipath * mpp, int verbosity) { @@ -1741,7 +3546,7 @@ index 6cc63e2..260aed1 100644 snprint_multipath_topology(&buff[0], MAX_LINE_LEN * MAX_LINES, mpp, verbosity); -@@ -662,7 +676,10 @@ snprint_multipath_topology (char * buff, +@@ -662,7 +679,10 @@ snprint_multipath_topology (char * buff, c += sprintf(c, "%%n"); if (strncmp(mpp->alias, mpp->wwid, WWID_SIZE)) @@ -1753,11 +3558,264 @@ index 6cc63e2..260aed1 100644 fwd += snprint_multipath(buff + fwd, len - fwd, style, mpp); if (fwd > len) +@@ -833,6 +853,110 @@ snprint_defaults (char * buff, int len) + + } + ++static int ++snprint_blacklist_group (char *buff, int len, int *fwd, vector *vec) ++{ ++ int threshold = MAX_LINE_LEN; ++ struct blentry * ble; ++ int pos; ++ int i; ++ ++ pos = *fwd; ++ if (!VECTOR_SIZE(*vec)) { ++ if ((len - pos - threshold) <= 0) ++ return 0; ++ pos += snprintf(buff + pos, len - pos, " \n"); ++ } else vector_foreach_slot (*vec, ble, i) { ++ if ((len - pos - threshold) <= 0) ++ return 0; ++ if (ble->origin == ORIGIN_CONFIG) ++ pos += snprintf(buff + pos, len - pos, " (config file rule) "); ++ else if (ble->origin == ORIGIN_DEFAULT) ++ pos += snprintf(buff + pos, len - pos, " (default rule) "); ++ pos += snprintf(buff + pos, len - pos, "%s\n", ble->str); ++ } ++ ++ *fwd = pos; ++ return pos; ++} ++ ++static int ++snprint_blacklist_devgroup (char *buff, int len, int *fwd, vector *vec) ++{ ++ int threshold = MAX_LINE_LEN; ++ struct blentry_device * bled; ++ int pos; ++ int i; ++ ++ pos = *fwd; ++ if (!VECTOR_SIZE(*vec)) { ++ if ((len - pos - threshold) <= 0) ++ return 0; ++ pos += snprintf(buff + pos, len - pos, " \n"); ++ } else vector_foreach_slot (*vec, bled, i) { ++ if ((len - pos - threshold) <= 0) ++ return 0; ++ if (bled->origin == ORIGIN_CONFIG) ++ pos += snprintf(buff + pos, len - pos, " (config file rule) "); ++ else if (bled->origin == ORIGIN_DEFAULT) ++ pos += snprintf(buff + pos, len - pos, " (default rule) "); ++ pos += snprintf(buff + pos, len - pos, "%s:%s\n", bled->vendor, bled->product); ++ } ++ ++ *fwd = pos; ++ return pos; ++} ++ ++extern int ++snprint_blacklist_report (char * buff, int len) ++{ ++ int threshold = MAX_LINE_LEN; ++ int fwd = 0; ++ ++ if ((len - fwd - threshold) <= 0) ++ return len; ++ fwd += snprintf(buff + fwd, len - fwd, "device node rules:\n" ++ "- blacklist:\n"); ++ if (!snprint_blacklist_group(buff, len, &fwd, &conf->blist_devnode)) ++ 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_devnode) == 0) ++ return len; ++ ++ if ((len - fwd - threshold) <= 0) ++ return len; ++ fwd += snprintf(buff + fwd, len - fwd, "wwid rules:\n" ++ "- blacklist:\n"); ++ if (snprint_blacklist_group(buff, len, &fwd, &conf->blist_wwid) == 0) ++ 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_wwid) == 0) ++ return len; ++ ++ if ((len - fwd - threshold) <= 0) ++ return len; ++ fwd += snprintf(buff + fwd, len - fwd, "device rules:\n" ++ "- blacklist:\n"); ++ if (snprint_blacklist_devgroup(buff, len, &fwd, &conf->blist_device) == 0) ++ return len; ++ ++ if ((len - fwd - threshold) <= 0) ++ return len; ++ fwd += snprintf(buff + fwd, len - fwd, "- exceptions:\n"); ++ if (snprint_blacklist_devgroup(buff, len, &fwd, &conf->elist_device) == 0) ++ return len; ++ ++ if (fwd > len) ++ return len; ++ return fwd; ++} ++ + extern int + snprint_blacklist (char * buff, int len) + { +@@ -895,12 +1019,126 @@ snprint_blacklist (char * buff, int len) + if (fwd > len) + return len; + } ++ fwd += snprintf(buff + fwd, len - fwd, "}\n"); ++ if (fwd > len) ++ return len; ++ return fwd; ++} ++ ++extern int ++snprint_blacklist_except (char * buff, int len) ++{ ++ int i; ++ struct blentry * ele; ++ struct blentry_device * eled; ++ int fwd = 0; ++ struct keyword *rootkw; ++ struct keyword *kw; ++ ++ rootkw = find_keyword(NULL, "blacklist_exceptions"); ++ if (!rootkw) ++ return 0; ++ ++ fwd += snprintf(buff + fwd, len - fwd, "blacklist_exceptions {\n"); ++ if (fwd > len) ++ return len; ++ ++ vector_foreach_slot (conf->elist_devnode, ele, i) { ++ kw = find_keyword(rootkw->sub, "devnode"); ++ if (!kw) ++ return 0; ++ fwd += snprint_keyword(buff + fwd, len - fwd, "\t%k %v\n", ++ kw, ele); ++ if (fwd > len) ++ return len; ++ } ++ vector_foreach_slot (conf->elist_wwid, ele, i) { ++ kw = find_keyword(rootkw->sub, "wwid"); ++ 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(rootkw->sub, "device"); ++ if (!rootkw) ++ return 0; + ++ vector_foreach_slot (conf->elist_device, eled, i) { ++ fwd += snprintf(buff + fwd, len - fwd, "\tdevice {\n"); ++ if (fwd > len) ++ return len; ++ kw = find_keyword(rootkw->sub, "vendor"); ++ if (!kw) ++ return 0; ++ fwd += snprint_keyword(buff + fwd, len - fwd, "\t\t%k %v\n", ++ kw, eled); ++ if (fwd > len) ++ return len; ++ kw = find_keyword(rootkw->sub, "product"); ++ if (!kw) ++ return 0; ++ fwd += snprint_keyword(buff + fwd, len - fwd, "\t\t%k %v\n", ++ kw, eled); ++ 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; +- ++} ++ ++extern int ++snprint_devices (char * buff, int len, struct vectors *vecs) ++{ ++ struct dlist * ls; ++ struct sysfs_class * class; ++ struct sysfs_class_device * dev; ++ int threshold = MAX_LINE_LEN; ++ int fwd = 0; ++ int r; ++ ++ struct path * pp; ++ ++ if (!(class = sysfs_open_class("block"))) ++ return 0; ++ ++ if (!(ls = sysfs_get_class_devices(class))) { ++ sysfs_close_class(class); ++ return 0; ++ } ++ ++ if ((len - fwd - threshold) <= 0) ++ return len; ++ fwd += snprintf(buff + fwd, len - fwd, "available block devices:\n"); ++ ++ dlist_for_each_data(ls, dev, struct sysfs_class_device) { ++ if ((len - fwd - threshold) <= 0) ++ return len; ++ fwd += snprintf(buff + fwd, len - fwd, " %s", dev->name); ++ pp = find_path_by_dev(vecs->pathvec, dev->name); ++ if (!pp) { ++ r = filter_devnode(conf->blist_devnode, ++ conf->elist_devnode, dev->name); ++ if (r > 0) ++ fwd += snprintf(buff + fwd, len - fwd, ++ " (blacklisted)"); ++ else if (r < 0) ++ fwd += snprintf(buff + fwd, len - fwd, ++ " (whitelisted)"); ++ } ++ fwd += snprintf(buff + fwd, len - fwd, "\n"); ++ } ++ sysfs_close_class(class); ++ ++ if (fwd > len) ++ return len; ++ return fwd; + } + + extern int +diff --git a/libmultipath/print.h b/libmultipath/print.h +index 3dde45d..73c2f63 100644 +--- a/libmultipath/print.h ++++ b/libmultipath/print.h +@@ -42,6 +42,9 @@ int snprint_multipath_topology (char *, + int verbosity); + int snprint_defaults (char *, int); + int snprint_blacklist (char *, int); ++int snprint_blacklist_except (char *, int); ++int snprint_blacklist_report (char *, int); ++int snprint_devices (char *, int, struct vectors *); + int snprint_hwtable (char *, int, vector); + int snprint_mptable (char *, int, vector); + diff --git a/libmultipath/propsel.c b/libmultipath/propsel.c -index 79cee8b..1f0021a 100644 +index 79cee8b..45a3728 100644 --- a/libmultipath/propsel.c +++ b/libmultipath/propsel.c -@@ -15,6 +15,7 @@ #include "debug.h" +@@ -15,6 +15,7 @@ #include "pgpolicies.h" #include "alias.h" #include "defaults.h" @@ -1881,11 +3939,63 @@ index 79cee8b..1f0021a 100644 mp->alias, mp->minio); return 0; } +@@ -319,3 +330,41 @@ select_minio (struct multipath * mp) + return 0; + } + ++extern int ++select_pg_timeout(struct multipath *mp) ++{ ++ if (mp->mpe && mp->mpe->pg_timeout != PGTIMEOUT_UNDEF) { ++ mp->pg_timeout = mp->mpe->pg_timeout; ++ if (mp->pg_timeout > 0) ++ condlog(3, "%s: pg_timeout = %d (multipath setting)", ++ mp->alias, mp->pg_timeout); ++ else ++ condlog(3, "%s: pg_timeout = NONE (multipath setting)", ++ mp->alias); ++ return 0; ++ } ++ if (mp->hwe && mp->hwe->pg_timeout != PGTIMEOUT_UNDEF) { ++ mp->pg_timeout = mp->hwe->pg_timeout; ++ if (mp->pg_timeout > 0) ++ condlog(3, "%s: pg_timeout = %d (controller setting)", ++ mp->alias, mp->pg_timeout); ++ else ++ condlog(3, "%s: pg_timeout = NONE (controller setting)", ++ mp->alias); ++ return 0; ++ } ++ if (conf->pg_timeout != PGTIMEOUT_UNDEF) { ++ mp->pg_timeout = conf->pg_timeout; ++ if (mp->pg_timeout > 0) ++ condlog(3, "%s: pg_timeout = %d (config file default)", ++ mp->alias, mp->pg_timeout); ++ else ++ condlog(3, ++ "%s: pg_timeout = NONE (config file default)", ++ mp->alias); ++ return 0; ++ } ++ mp->pg_timeout = PGTIMEOUT_UNDEF; ++ condlog(3, "pg_timeout = NONE (internal default)"); ++ return 0; ++} +diff --git a/libmultipath/propsel.h b/libmultipath/propsel.h +index f66a598..afd1f88 100644 +--- a/libmultipath/propsel.h ++++ b/libmultipath/propsel.h +@@ -9,4 +9,5 @@ int select_checker(struct path *pp); + int select_getuid (struct path * pp); + int select_getprio (struct path * pp); + int select_no_path_retry(struct multipath *mp); ++int select_pg_timeout(struct multipath *mp); + int select_minio(struct multipath *mp); diff --git a/libmultipath/structs.c b/libmultipath/structs.c -index 024e790..db3f824 100644 +index 024e790..d36eaef 100644 --- a/libmultipath/structs.c +++ b/libmultipath/structs.c -@@ -16,6 +16,7 @@ #include "config.h" +@@ -16,6 +16,7 @@ #include "debug.h" #include "structs_vec.h" #include "blacklist.h" @@ -1901,7 +4011,28 @@ index 024e790..db3f824 100644 } return pp; } -@@ -365,3 +367,10 @@ pathcount (struct multipath * mpp, int s +@@ -115,9 +117,10 @@ alloc_multipath (void) + + mpp = (struct multipath *)MALLOC(sizeof(struct multipath)); + +- if (mpp) ++ if (mpp) { + mpp->bestpg = 1; +- ++ mpp->mpcontext = NULL; ++ } + return mpp; + } + +@@ -178,6 +181,7 @@ free_multipath (struct multipath * mpp, + + free_pathvec(mpp->paths, free_paths); + free_pgvec(mpp->pg, free_paths); ++ FREE_PTR(mpp->mpcontext); + FREE(mpp); + } + +@@ -365,3 +369,10 @@ pathcount (struct multipath * mpp, int s return count; } @@ -1913,7 +4044,7 @@ index 024e790..db3f824 100644 + return VECTOR_SLOT(pgp->paths, 0); +} diff --git a/libmultipath/structs.h b/libmultipath/structs.h -index 2b96cfb..7db7faa 100644 +index 2b96cfb..46dcdee 100644 --- a/libmultipath/structs.h +++ b/libmultipath/structs.h @@ -1,8 +1,8 @@ @@ -1927,7 +4058,7 @@ index 2b96cfb..7db7faa 100644 #define NODE_NAME_SIZE 19 #define PATH_STR_SIZE 16 #define PARAMS_SIZE 1024 -@@ -18,6 +18,9 @@ #define NO_PATH_RETRY_UNDEF 0 +@@ -18,6 +18,9 @@ #define NO_PATH_RETRY_FAIL -1 #define NO_PATH_RETRY_QUEUE -2 @@ -1937,7 +4068,27 @@ index 2b96cfb..7db7faa 100644 enum free_path_switch { KEEP_PATHS, FREE_PATHS -@@ -142,7 +145,7 @@ struct multipath { +@@ -55,6 +58,11 @@ enum pgstates { + PGSTATE_ACTIVE + }; + ++enum pgtimeouts { ++ PGTIMEOUT_UNDEF, ++ PGTIMEOUT_NONE ++}; ++ + struct scsi_idlun { + int dev_id; + int host_unique_id; +@@ -127,6 +135,7 @@ struct multipath { + int no_path_retry; /* number of retries after all paths are down */ + int retry_tick; /* remaining times for retries */ + int minio; ++ int pg_timeout; + unsigned long long size; + vector paths; + vector pg; +@@ -142,7 +151,7 @@ struct multipath { struct mpentry * mpe; struct hwentry * hwe; @@ -1946,7 +4097,17 @@ index 2b96cfb..7db7faa 100644 void * waiter; /* stats */ -@@ -183,10 +186,11 @@ struct multipath * find_mp_by_minor (vec +@@ -151,6 +160,9 @@ struct multipath { + unsigned int stat_map_loads; + unsigned int stat_total_queueing_time; + unsigned int stat_queueing_timeouts; ++ ++ /* checkers shared data */ ++ void * mpcontext; + }; + + struct pathgroup { +@@ -183,10 +195,11 @@ struct multipath * find_mp_by_minor (vec struct path * find_path_by_devt (vector pathvec, char * devt); struct path * find_path_by_dev (vector pathvec, char * dev); @@ -1960,10 +4121,10 @@ index 2b96cfb..7db7faa 100644 -#endif +#endif /* _STRUCTS_H */ diff --git a/libmultipath/structs_vec.c b/libmultipath/structs_vec.c -index 86bf2a5..1a6d8e2 100644 +index 86bf2a5..53b7e17 100644 --- a/libmultipath/structs_vec.c +++ b/libmultipath/structs_vec.c -@@ -14,6 +14,7 @@ #include "dmparser.h" +@@ -14,6 +14,7 @@ #include "config.h" #include "propsel.h" #include "discovery.h" @@ -1991,7 +4152,15 @@ index 86bf2a5..1a6d8e2 100644 orphan_path(pp); } } -@@ -382,3 +383,90 @@ verify_paths(struct multipath * mpp, str +@@ -277,6 +278,7 @@ retry: + select_rr_weight(mpp); + select_pgfailback(mpp); + set_no_path_retry(mpp); ++ select_pg_timeout(mpp); + + return 0; + out: +@@ -382,3 +384,90 @@ verify_paths(struct multipath * mpp, str return count; } @@ -2086,7 +4255,7 @@ diff --git a/libmultipath/structs_vec.h b/libmultipath/structs_vec.h index 348e9e5..81d9eaa 100644 --- a/libmultipath/structs_vec.h +++ b/libmultipath/structs_vec.h -@@ -9,17 +9,6 @@ #endif +@@ -9,17 +9,6 @@ struct vectors { vector mpvec; }; @@ -2104,7 +4273,7 @@ index 348e9e5..81d9eaa 100644 typedef void (stop_waiter_thread_func) (struct multipath *, struct vectors *); typedef int (start_waiter_thread_func) (struct multipath *, struct vectors *); -@@ -44,5 +33,8 @@ struct multipath * add_map_without_path +@@ -44,5 +33,8 @@ struct multipath * add_map_without_path start_waiter_thread_func *start_waiter); struct multipath * add_map_with_path (struct vectors * vecs, struct path * pp, int add_vec); @@ -2113,6 +4282,19 @@ index 348e9e5..81d9eaa 100644 +void update_queue_mode_add_path(struct multipath *mpp); #endif /* _STRUCTS_VEC_H */ +diff --git a/libmultipath/switchgroup.c b/libmultipath/switchgroup.c +index 757543f..9b84bc2 100644 +--- a/libmultipath/switchgroup.c ++++ b/libmultipath/switchgroup.c +@@ -28,7 +28,7 @@ select_path_group (struct multipath * mp + priority = 0; + + vector_foreach_slot (pgp->paths, pp, j) { +- if (pp->state != PATH_DOWN) ++ if (pp->state == PATH_UP) + priority += pp->priority; + } + pgp->priority = priority; diff --git a/libmultipath/util.c b/libmultipath/util.c index 376ca04..911ec55 100644 --- a/libmultipath/util.c @@ -2137,7 +4319,7 @@ diff --git a/libmultipath/util.h b/libmultipath/util.h index 51f052a..e86bae2 100644 --- a/libmultipath/util.h +++ b/libmultipath/util.h -@@ -2,6 +2,7 @@ #ifndef _UTIL_H +@@ -2,6 +2,7 @@ #define _UTIL_H int strcmp_chomp(char *, char *); @@ -2470,10 +4652,33 @@ index 494f09e..9d6a7ea 100644 %{prefix}/usr/share/man/man8/multipath.8.gz %{prefix}/usr/share/man/man8/kpartx.8.gz diff --git a/multipath.conf.annotated b/multipath.conf.annotated -index a1f04b7..f55c367 100644 +index a1f04b7..649ff8c 100644 --- a/multipath.conf.annotated +++ b/multipath.conf.annotated -@@ -237,7 +237,7 @@ # +@@ -142,6 +142,22 @@ + # product MSA[15]00 + # } + #} ++## ++## name : blacklist_exceptions ++## scope : multipath & multipathd ++## desc : list of device names to be treated as multipath candidates ++## even if they are on the blacklist. ++## Note: blacklist exceptions are only valid in the same class. ++## It is not possible to blacklist devices using the devnode keyword ++## and to exclude some devices of them using the wwid keyword. ++## default : - ++## ++#blacklist_exceptions { ++# devnode "^dasd[c-d]+[0-9]*" ++# wwid "IBM.75000000092461.4d00.34" ++# wwid "IBM.75000000092461.4d00.35" ++# wwid "IBM.75000000092461.4d00.36" ++#} + # + ## + ## name : multipaths +@@ -237,7 +253,7 @@ ## ## name : devices ## scope : multipath & multipathd @@ -2482,7 +4687,7 @@ index a1f04b7..f55c367 100644 ## overrides default settings (device_maps block) ## overriden by per multipath settings (multipaths block) ## -@@ -245,7 +245,7 @@ #devices { +@@ -245,7 +261,7 @@ # # # # name : device # # scope : multipath & multipathd @@ -2491,7 +4696,7 @@ index a1f04b7..f55c367 100644 # # # device { # # -@@ -260,7 +260,7 @@ # # +@@ -260,7 +276,7 @@ # # name : path_grouping_policy # # scope : multipath # # desc : path grouping policy to apply to multipath hosted @@ -2500,11 +4705,35 @@ index a1f04b7..f55c367 100644 # # values : failover = 1 path per priority group # # multibus = all valid paths in 1 priority # # group +diff --git a/multipath.conf.synthetic b/multipath.conf.synthetic +index 4a1f5a4..e5e6cd0 100644 +--- a/multipath.conf.synthetic ++++ b/multipath.conf.synthetic +@@ -16,7 +16,7 @@ + # no_path_retry fail + # user_friendly_names no + #} +-#devnode_blacklist { ++#blacklist { + # wwid 26353900f02796769 + # devnode "^(ram|raw|loop|fd|md|dm-|sr|scd|st)[0-9]*" + # devnode "^hd[a-z][[0-9]*]" +@@ -26,6 +26,10 @@ + # product MSA[15]00 + # } + #} ++#blacklist_exceptions { ++# devnode "^dasd[c-d]+[0-9]*" ++# wwid "IBM.75000000092461.4d00.34" ++#} + #multipaths { + # multipath { + # wwid 3600508b4000156d700012000000b0000 diff --git a/multipath/main.c b/multipath/main.c -index 98f7207..accb230 100644 +index 98f7207..67076db 100644 --- a/multipath/main.c +++ b/multipath/main.c -@@ -46,8 +46,7 @@ #include +@@ -46,8 +46,7 @@ #include #include #include @@ -2531,7 +4760,15 @@ index 98f7207..accb230 100644 "\t-b file\t\tbindings file location\n" \ "\t-d\t\tdry run, do not create or update devmaps\n" \ "\t-l\t\tshow multipath topology (sysfs and DM info)\n" \ -@@ -134,7 +134,7 @@ update_paths (struct multipath * mpp) +@@ -128,13 +128,15 @@ update_paths (struct multipath * mpp) + pp->state = PATH_DOWN; + continue; + } ++ pp->mpp = mpp; + pathinfo(pp, conf->hwtable, DI_ALL); + continue; + } ++ pp->mpp = mpp; if (pp->state == PATH_UNCHECKED) pathinfo(pp, conf->hwtable, DI_CHECKER); @@ -2540,7 +4777,42 @@ index 98f7207..accb230 100644 pathinfo(pp, conf->hwtable, DI_PRIO); } } -@@ -281,8 +281,10 @@ configure (void) +@@ -222,7 +224,7 @@ configure (void) + vecs.mpvec = curmp; + + /* +- * if we have a blacklisted device parameter, exit early ++ * dev is "/dev/" . "sysfs block dev" + */ + if (conf->dev) { + if (!strncmp(conf->dev, "/dev/", 5) && +@@ -232,8 +234,12 @@ configure (void) + dev = conf->dev; + } + +- if (dev && blacklist(conf->blist_devnode, dev)) +- goto out; ++ /* ++ * if we have a blacklisted device parameter, exit early ++ */ ++ if (dev && ++ (filter_devnode(conf->blist_devnode, conf->elist_devnode, dev) > 0)) ++ goto out; + + /* + * scope limiting must be translated into a wwid +@@ -247,8 +253,8 @@ configure (void) + goto out; + } + condlog(3, "scope limited to %s", refwwid); +- +- if (blacklist(conf->blist_wwid, refwwid)) ++ if (filter_wwid(conf->blist_wwid, conf->elist_wwid, ++ refwwid) > 0) + goto out; + } + +@@ -281,8 +287,10 @@ configure (void) filter_pathvec(pathvec, refwwid); @@ -2552,7 +4824,7 @@ index 98f7207..accb230 100644 /* * core logic entry point -@@ -305,7 +307,7 @@ main (int argc, char *argv[]) +@@ -305,14 +313,14 @@ main (int argc, char *argv[]) int arg; extern char *optarg; extern int optind; @@ -2561,7 +4833,15 @@ index 98f7207..accb230 100644 if (getuid() != 0) { fprintf(stderr, "need to be root\n"); -@@ -322,7 +324,7 @@ main (int argc, char *argv[]) + exit(1); + } + +- if (dm_prereq(DEFAULT_TARGET, 1, 0, 3)) ++ if (dm_prereq(DEFAULT_TARGET)) + exit(1); + + if (sysfs_get_mnt_path(sysfs_path, FILE_NAME_SIZE)) { +@@ -322,7 +330,7 @@ main (int argc, char *argv[]) if (load_config(DEFAULT_CONFIGFILE)) exit(1); @@ -2570,7 +4850,7 @@ index 98f7207..accb230 100644 switch(arg) { case 1: printf("optarg : %s\n",optarg); break; -@@ -365,6 +367,8 @@ #endif +@@ -365,6 +373,8 @@ main (int argc, char *argv[]) usage(argv[0]); } break; @@ -2676,18 +4956,192 @@ index 7133598..693872b 100644 .B group_by_node_name 1 priority group per target node name. Target node names are fetched in /sys/class/fc_transport/target*/node_name. diff --git a/multipathd/cli.c b/multipathd/cli.c -index 475819b..bd0d03b 100644 +index 475819b..d786eef 100644 --- a/multipathd/cli.c +++ b/multipathd/cli.c -@@ -4,6 +4,7 @@ +@@ -4,6 +4,8 @@ #include #include #include +#include ++#include #include "cli.h" -@@ -305,6 +306,8 @@ genhelp_handler (void) +@@ -72,6 +74,30 @@ add_handler (int fp, int (*fn)(void *, c + return 0; + } + ++static struct handler * ++find_handler (int fp) ++{ ++ int i; ++ struct handler *h; ++ ++ vector_foreach_slot (handlers, h, i) ++ if (h->fingerprint == fp) ++ return h; ++ ++ return NULL; ++} ++ ++int ++set_handler_callback (int fp, int (*fn)(void *, char **, int *, void *)) ++{ ++ struct handler * h = find_handler(fp); ++ ++ if (!h) ++ return 1; ++ h->fn = fn; ++ return 0; ++} ++ + static void + free_key (struct key * kw) + { +@@ -140,33 +166,34 @@ load_keys (void) + r += add_key(keys, "stats", STATS, 0); + r += add_key(keys, "topology", TOPOLOGY, 0); + r += add_key(keys, "config", CONFIG, 0); ++ r += add_key(keys, "blacklist", BLACKLIST, 0); ++ r += add_key(keys, "devices", DEVICES, 0); + + if (r) { + free_keys(keys); + keys = NULL; + return 1; + } +- + return 0; + } + + static struct key * +-find_key (char * str) ++find_key (const char * str) + { + int i; + int len, klen; + struct key * kw = NULL; + struct key * foundkw = NULL; + +- vector_foreach_slot (keys, kw, i) { +- len = strlen(str); +- klen = strlen(kw->str); ++ len = strlen(str); + ++ vector_foreach_slot (keys, kw, i) { + if (strncmp(kw->str, str, len)) + continue; +- else if (len == klen) ++ klen = strlen(kw->str); ++ if (len == klen) + return kw; /* exact match */ +- else if (len < klen) { ++ if (len < klen) { + if (!foundkw) + foundkw = kw; /* shortcut match */ + else +@@ -175,24 +202,16 @@ find_key (char * str) + } + return foundkw; + } +- +-static struct handler * +-find_handler (int fp) +-{ +- int i; +- struct handler *h; +- +- vector_foreach_slot (handlers, h, i) +- if (h->fingerprint == fp) +- return h; + +- return NULL; +-} ++#define E_SYNTAX 1 ++#define E_NOPARM 2 ++#define E_NOMEM 3 + +-static vector +-get_cmdvec (char * cmd) ++static int ++get_cmdvec (char * cmd, vector *v) + { + int fwd = 1; ++ int r = 0; + char * p = cmd; + char * buff; + struct key * kw = NULL; +@@ -200,9 +219,10 @@ get_cmdvec (char * cmd) + vector cmdvec; + + cmdvec = vector_alloc(); ++ *v = cmdvec; + + if (!cmdvec) +- return NULL; ++ return E_NOMEM; + + while (fwd) { + fwd = get_word(p, &buff); +@@ -215,18 +235,19 @@ get_cmdvec (char * cmd) + FREE(buff); + + if (!kw) +- goto out; /* synthax error */ ++ return E_SYNTAX; + + cmdkw = alloc_key(); + +- if (!cmdkw) ++ if (!cmdkw) { ++ r = E_NOMEM; + goto out; +- ++ } + if (!vector_alloc_slot(cmdvec)) { + FREE(cmdkw); ++ r = E_NOMEM; + goto out; + } +- + vector_set_slot(cmdvec, cmdkw); + cmdkw->code = kw->code; + cmdkw->has_param = kw->has_param; +@@ -238,18 +259,18 @@ get_cmdvec (char * cmd) + fwd = get_word(p, &buff); + + if (!buff) +- goto out; ++ return E_NOPARM; + + p += fwd; + cmdkw->param = buff; + } + } +- +- return cmdvec; ++ return 0; + + out: + free_keys(cmdvec); +- return NULL; ++ *v = NULL; ++ return r; + } + + static int +@@ -259,6 +280,9 @@ fingerprint(vector vec) + int fp = 0; + struct key * kw; + ++ if (!vec) ++ return 0; ++ + vector_foreach_slot(vec, kw, i) + fp += kw->code; + +@@ -305,6 +329,8 @@ genhelp_handler (void) return NULL; p = reply; @@ -2696,8 +5150,200 @@ index 475819b..bd0d03b 100644 vector_foreach_slot (handlers, h, i) { fp = h->fingerprint; +@@ -329,9 +355,13 @@ parse_cmd (char * cmd, char ** reply, in + { + int r; + struct handler * h; +- vector cmdvec = get_cmdvec(cmd); ++ vector cmdvec; ++ ++ r = get_cmdvec(cmd, &cmdvec); + +- if (!cmdvec) { ++ if (r) { ++ if (cmdvec) ++ free_keys(cmdvec); + *reply = genhelp_handler(); + *len = strlen(*reply) + 1; + return 0; +@@ -366,3 +396,141 @@ get_keyparam (vector v, int code) + + return NULL; + } ++ ++int ++cli_init (void) { ++ if (load_keys()) ++ return 1; ++ ++ if (alloc_handlers()) ++ return 1; ++ ++ add_handler(LIST+PATHS, NULL); ++ add_handler(LIST+MAPS, NULL); ++ add_handler(LIST+MAPS+STATUS, NULL); ++ add_handler(LIST+MAPS+STATS, NULL); ++ add_handler(LIST+MAPS+TOPOLOGY, NULL); ++ add_handler(LIST+TOPOLOGY, NULL); ++ add_handler(LIST+MAP+TOPOLOGY, NULL); ++ add_handler(LIST+CONFIG, NULL); ++ add_handler(LIST+BLACKLIST, NULL); ++ add_handler(LIST+DEVICES, NULL); ++ add_handler(ADD+PATH, NULL); ++ add_handler(DEL+PATH, NULL); ++ add_handler(ADD+MAP, NULL); ++ add_handler(DEL+MAP, NULL); ++ add_handler(SWITCH+MAP+GROUP, NULL); ++ add_handler(RECONFIGURE, NULL); ++ add_handler(SUSPEND+MAP, NULL); ++ add_handler(RESUME+MAP, NULL); ++ add_handler(REINSTATE+PATH, NULL); ++ add_handler(FAIL+PATH, NULL); ++ ++ return 0; ++} ++ ++static int ++key_match_fingerprint (struct key * kw, int fp) ++{ ++ if (!fp) ++ return 0; ++ ++ return ((fp & kw->code) == kw->code); ++} ++ ++/* ++ * This is the readline completion handler ++ */ ++char * ++key_generator (const char * str, int state) ++{ ++ static int index, len, rlfp, has_param; ++ struct key * kw; ++ int i; ++ struct handler *h; ++ vector v; ++ ++ if (!state) { ++ index = 0; ++ has_param = 0; ++ rlfp = 0; ++ len = strlen(str); ++ int r = get_cmdvec(rl_line_buffer, &v); ++ /* ++ * If a word completion is in progess, we don't want ++ * to take an exact keyword match in the fingerprint. ++ * For ex "show map[tab]" would validate "map" and discard ++ * "maps" as a valid candidate. ++ */ ++ if (v && len) ++ vector_del_slot(v, VECTOR_SIZE(v) - 1); ++ /* ++ * Clean up the mess if we dropped the last slot of a 1-slot ++ * vector ++ */ ++ if (v && !VECTOR_SIZE(v)) { ++ vector_free(v); ++ v = NULL; ++ } ++ /* ++ * If last keyword takes a param, don't even try to guess ++ */ ++ if (r == E_NOPARM) { ++ has_param = 1; ++ return (strdup("(value)")); ++ } ++ /* ++ * Compute a command fingerprint to find out possible completions. ++ * Once done, the vector is useless. Free it. ++ */ ++ if (v) { ++ rlfp = fingerprint(v); ++ free_keys(v); ++ } ++ } ++ /* ++ * No more completions for parameter placeholder. ++ * Brave souls might try to add parameter completion by walking paths and ++ * multipaths vectors. ++ */ ++ if (has_param) ++ return ((char *)NULL); ++ /* ++ * Loop through keywords for completion candidates ++ */ ++ vector_foreach_slot_after (keys, kw, index) { ++ if (!strncmp(kw->str, str, len)) { ++ /* ++ * Discard keywords already in the command line ++ */ ++ if (key_match_fingerprint(kw, rlfp)) { ++ struct key * curkw = find_key(str); ++ if (!curkw || (curkw != kw)) ++ continue; ++ } ++ /* ++ * Discard keywords making syntax errors. ++ * ++ * nfp is the candidate fingerprint we try to ++ * validate against all known command fingerprints. ++ */ ++ int nfp = rlfp | kw->code; ++ vector_foreach_slot(handlers, h, i) { ++ if (!rlfp || ((h->fingerprint & nfp) == nfp)) { ++ /* ++ * At least one full command is ++ * possible with this keyword : ++ * Consider it validated ++ */ ++ index++; ++ return (strdup(kw->str)); ++ } ++ } ++ } ++ } ++ /* ++ * No more candidates ++ */ ++ return ((char *)NULL); ++} ++ +diff --git a/multipathd/cli.h b/multipathd/cli.h +index ef1e3b8..a2397df 100644 +--- a/multipathd/cli.h ++++ b/multipathd/cli.h +@@ -17,6 +17,8 @@ enum { + __STATS, + __TOPOLOGY, + __CONFIG, ++ __BLACKLIST, ++ __DEVICES, + }; + + #define LIST (1 << __LIST) +@@ -37,6 +39,8 @@ enum { + #define STATS (1 << __STATS) + #define TOPOLOGY (1 << __TOPOLOGY) + #define CONFIG (1 << __CONFIG) ++#define BLACKLIST (1 << __BLACKLIST) ++#define DEVICES (1 << __DEVICES) + + #define INITIAL_REPLY_LEN 1000 + +@@ -57,8 +61,11 @@ vector handlers; + + int alloc_handlers (void); + int add_handler (int fp, int (*fn)(void *, char **, int *, void *)); ++int set_handler_callback (int fp, int (*fn)(void *, char **, int *, void *)); + int parse_cmd (char * cmd, char ** reply, int * len, void *); + int load_keys (void); + char * get_keyparam (vector v, int code); + void free_keys (vector vec); + void free_handlers (vector vec); ++int cli_init (void); ++char * key_generator (const char * str, int state); diff --git a/multipathd/cli_handlers.c b/multipathd/cli_handlers.c -index 92d8221..b5d8e00 100644 +index 92d8221..4938e84 100644 --- a/multipathd/cli_handlers.c +++ b/multipathd/cli_handlers.c @@ -71,7 +71,7 @@ show_map_topology (char ** r, int * len, @@ -2719,7 +5365,20 @@ index 92d8221..b5d8e00 100644 reply = MALLOC(maxlen); while (again) { -@@ -183,6 +184,7 @@ cli_list_map_topology (void * v, char ** +@@ -142,6 +143,12 @@ show_config (char ** r, int * len) + reply = REALLOC(reply, maxlen *= 2); + continue; + } ++ c += snprint_blacklist_except(c, reply + maxlen - c); ++ again = ((c - reply) == maxlen); ++ if (again) { ++ reply = REALLOC(reply, maxlen *= 2); ++ continue; ++ } + c += snprint_hwtable(c, reply + maxlen - c, conf->hwtable); + again = ((c - reply) == maxlen); + if (again) { +@@ -183,6 +190,7 @@ cli_list_map_topology (void * v, char ** struct vectors * vecs = (struct vectors *)data; char * param = get_keyparam(v, MAP); @@ -2727,11 +5386,124 @@ index 92d8221..b5d8e00 100644 mpp = find_mp_by_str(vecs->mpvec, param); if (!mpp) +@@ -278,8 +286,8 @@ cli_add_path (void * v, char ** reply, i + + condlog(2, "%s: add path (operator)", param); + +- if (blacklist(conf->blist_devnode, param) || +- (r = ev_add_path(param, vecs)) == 2) { ++ if (filter_devnode(conf->blist_devnode, conf->elist_devnode, ++ param) > 0 || (r = ev_add_path(param, vecs)) == 2) { + *reply = strdup("blacklisted"); + *len = strlen(*reply) + 1; + condlog(2, "%s: path blacklisted", param); +@@ -307,7 +315,7 @@ cli_add_map (void * v, char ** reply, in + + condlog(2, "%s: add map (operator)", param); + +- if (blacklist(conf->blist_wwid, param)) { ++ if (filter_wwid(conf->blist_wwid, conf->elist_wwid, param) > 0) { + *reply = strdup("blacklisted"); + *len = strlen(*reply) + 1; + condlog(2, "%s: map blacklisted", param); +@@ -431,3 +439,79 @@ cli_fail(void * v, char ** reply, int * + + return dm_fail_path(pp->mpp->alias, pp->dev_t); + } ++ ++int ++show_blacklist (char ** r, int * len) ++{ ++ char *c = NULL; ++ char *reply = NULL; ++ unsigned int maxlen = INITIAL_REPLY_LEN; ++ int again = 1; ++ ++ while (again) { ++ reply = MALLOC(maxlen); ++ if (!reply) ++ return 1; ++ ++ c = reply; ++ c += snprint_blacklist_report(c, maxlen); ++ again = ((c - reply) == maxlen); ++ if (again) { ++ maxlen *= 2; ++ FREE(reply); ++ continue; ++ } ++ } ++ ++ *r = reply; ++ *len = (int)(c - reply + 1); ++ ++ return 0; ++} ++ ++int ++cli_list_blacklist (void * v, char ** reply, int * len, void * data) ++{ ++ condlog(3, "list blacklist (operator)"); ++ ++ return show_blacklist(reply, len); ++} ++ ++int ++show_devices (char ** r, int * len, struct vectors *vecs) ++{ ++ char *c = NULL; ++ char *reply = NULL; ++ unsigned int maxlen = INITIAL_REPLY_LEN; ++ int again = 1; ++ ++ while (again) { ++ reply = MALLOC(maxlen); ++ if (!reply) ++ return 1; ++ ++ c = reply; ++ c += snprint_devices(c, maxlen, vecs); ++ again = ((c - reply) == maxlen); ++ if (again) { ++ maxlen *= 2; ++ FREE(reply); ++ continue; ++ } ++ } ++ ++ *r = reply; ++ *len = (int)(c - reply + 1); ++ ++ return 0; ++} ++ ++int ++cli_list_devices (void * v, char ** reply, int * len, void * data) ++{ ++ struct vectors * vecs = (struct vectors *)data; ++ ++ condlog(3, "list devices (operator)"); ++ ++ return show_devices(reply, len, vecs); ++} +diff --git a/multipathd/cli_handlers.h b/multipathd/cli_handlers.h +index 8768724..863694b 100644 +--- a/multipathd/cli_handlers.h ++++ b/multipathd/cli_handlers.h +@@ -5,6 +5,8 @@ int cli_list_maps_stats (void * v, char + int cli_list_map_topology (void * v, char ** reply, int * len, void * data); + int cli_list_maps_topology (void * v, char ** reply, int * len, void * data); + int cli_list_config (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_add_path (void * v, char ** reply, int * len, void * data); + int cli_del_path (void * v, char ** reply, int * len, void * data); + int cli_add_map (void * v, char ** reply, int * len, void * data); diff --git a/multipathd/main.c b/multipathd/main.c -index 55a2c49..0b70714 100644 +index 55a2c49..f2f9a96 100644 --- a/multipathd/main.c +++ b/multipathd/main.c -@@ -55,136 +55,29 @@ #include "uxlsnr.h" +@@ -55,136 +55,29 @@ #include "uxclnt.h" #include "cli.h" #include "cli_handlers.h" @@ -2766,8 +5538,7 @@ index 55a2c49..0b70714 100644 /* - * structs -+ * global copy of vecs for use in sig handlers - */ +- */ -struct vectors * gvecs; /* global copy of vecs for use in sig handlers */ - -static struct event_thread * @@ -2832,7 +5603,8 @@ index 55a2c49..0b70714 100644 - * -1 (FAIL) : fail_if_no_path - * 0 (UNDEF) : nothing - * >0 : queue_if_no_path enabled, turned off after polling n times -- */ ++ * global copy of vecs for use in sig handlers + */ -static void -update_queue_mode_del_path(struct multipath *mpp) -{ @@ -3129,6 +5901,15 @@ index 55a2c49..0b70714 100644 struct multipath *mpp; vector_foreach_slot (mpvec, mpp, i) +@@ -704,7 +376,7 @@ ev_add_path (char * devname, struct vect + condlog(0, "%s: failed to get path uid", devname); + return 1; /* leave path added to pathvec */ + } +- if (blacklist_path(conf, pp)){ ++ if (filter_path(conf, pp)){ + int i = find_slot(vecs->pathvec, (void *)pp); + if (i != -1) + vector_del_slot(vecs->pathvec, i); @@ -863,7 +535,7 @@ ev_remove_path (char * devname, struct v } sync_map_state(mpp); @@ -3148,6 +5929,68 @@ index 55a2c49..0b70714 100644 if (dm_get_maps(vecs->mpvec, "multipath")) return 1; +@@ -995,7 +667,8 @@ uev_trigger (struct uevent * uev, void * + /* + * path add/remove event + */ +- if (blacklist(conf->blist_devnode, devname)) ++ if (filter_devnode(conf->blist_devnode, conf->elist_devnode, ++ devname) > 0) + goto out; + + if (!strncmp(uev->action, "add", 3)) { +@@ -1024,30 +697,29 @@ ueventloop (void * ap) + static void * + uxlsnrloop (void * ap) + { +- if (load_keys()) +- return NULL; +- +- if (alloc_handlers()) ++ if (cli_init()) + return NULL; + +- add_handler(LIST+PATHS, cli_list_paths); +- add_handler(LIST+MAPS, cli_list_maps); +- add_handler(LIST+MAPS+STATUS, cli_list_maps_status); +- add_handler(LIST+MAPS+STATS, cli_list_maps_stats); +- add_handler(LIST+MAPS+TOPOLOGY, cli_list_maps_topology); +- add_handler(LIST+TOPOLOGY, cli_list_maps_topology); +- add_handler(LIST+MAP+TOPOLOGY, cli_list_map_topology); +- add_handler(LIST+CONFIG, cli_list_config); +- add_handler(ADD+PATH, cli_add_path); +- add_handler(DEL+PATH, cli_del_path); +- add_handler(ADD+MAP, cli_add_map); +- add_handler(DEL+MAP, cli_del_map); +- add_handler(SWITCH+MAP+GROUP, cli_switch_group); +- add_handler(RECONFIGURE, cli_reconfigure); +- add_handler(SUSPEND+MAP, cli_suspend); +- add_handler(RESUME+MAP, cli_resume); +- add_handler(REINSTATE+PATH, cli_reinstate); +- add_handler(FAIL+PATH, cli_fail); ++ set_handler_callback(LIST+PATHS, cli_list_paths); ++ set_handler_callback(LIST+MAPS, cli_list_maps); ++ set_handler_callback(LIST+MAPS+STATUS, cli_list_maps_status); ++ set_handler_callback(LIST+MAPS+STATS, cli_list_maps_stats); ++ set_handler_callback(LIST+MAPS+TOPOLOGY, cli_list_maps_topology); ++ set_handler_callback(LIST+TOPOLOGY, cli_list_maps_topology); ++ set_handler_callback(LIST+MAP+TOPOLOGY, cli_list_map_topology); ++ set_handler_callback(LIST+CONFIG, cli_list_config); ++ set_handler_callback(LIST+BLACKLIST, cli_list_blacklist); ++ set_handler_callback(LIST+DEVICES, cli_list_devices); ++ set_handler_callback(ADD+PATH, cli_add_path); ++ set_handler_callback(DEL+PATH, cli_del_path); ++ set_handler_callback(ADD+MAP, cli_add_map); ++ set_handler_callback(DEL+MAP, cli_del_map); ++ set_handler_callback(SWITCH+MAP+GROUP, cli_switch_group); ++ set_handler_callback(RECONFIGURE, cli_reconfigure); ++ set_handler_callback(SUSPEND+MAP, cli_suspend); ++ set_handler_callback(RESUME+MAP, cli_resume); ++ set_handler_callback(REINSTATE+PATH, cli_reinstate); ++ set_handler_callback(FAIL+PATH, cli_fail); + + uxsock_listen(&uxsock_trigger, ap); + @@ -1129,7 +801,7 @@ static void mpvec_garbage_collector (struct vectors * vecs) { @@ -3199,7 +6042,27 @@ index 55a2c49..0b70714 100644 return 1; if (!(mpvec = vector_alloc())) -@@ -1406,6 +1079,7 @@ configure (struct vectors * vecs, int st +@@ -1366,7 +1039,7 @@ configure (struct vectors * vecs, int st + path_discovery(vecs->pathvec, conf, DI_ALL); + + vector_foreach_slot (vecs->pathvec, pp, i){ +- if (blacklist_path(conf, pp)){ ++ if (filter_path(conf, pp)){ + vector_del_slot(vecs->pathvec, i); + free_path(pp); + i--; +@@ -1394,10 +1067,6 @@ configure (struct vectors * vecs, int st + + sync_maps_state(mpvec); + +- if (conf->verbosity > 2) +- vector_foreach_slot(mpvec, mpp, i) +- print_map(mpp); +- + /* + * purge dm of old maps + */ +@@ -1406,6 +1075,7 @@ configure (struct vectors * vecs, int st /* * save new set of maps formed by considering current path state */ @@ -3207,7 +6070,7 @@ index 55a2c49..0b70714 100644 vecs->mpvec = mpvec; /* -@@ -1435,6 +1109,7 @@ reconfigure (struct vectors * vecs) +@@ -1435,6 +1105,7 @@ reconfigure (struct vectors * vecs) if (VECTOR_SIZE(vecs->pathvec)) free_pathvec(vecs->pathvec, FREE_PATHS); @@ -3215,7 +6078,7 @@ index 55a2c49..0b70714 100644 conf = NULL; if (load_config(DEFAULT_CONFIGFILE)) -@@ -1467,24 +1142,10 @@ init_vecs (void) +@@ -1467,24 +1138,10 @@ init_vecs (void) if (!vecs->lock) goto out; @@ -3240,7 +6103,7 @@ index 55a2c49..0b70714 100644 out: FREE(vecs); condlog(0, "failed to init paths"); -@@ -1543,7 +1204,7 @@ signal_init(void) +@@ -1543,7 +1200,7 @@ signal_init(void) signal_set(SIGUSR1, sigusr1); signal_set(SIGINT, sigend); signal_set(SIGTERM, sigend); @@ -3249,7 +6112,7 @@ index 55a2c49..0b70714 100644 } static void -@@ -1551,7 +1212,7 @@ setscheduler (void) +@@ -1551,7 +1208,7 @@ setscheduler (void) { int res; static struct sched_param sched_param = { @@ -3259,18 +6122,25 @@ index 55a2c49..0b70714 100644 res = sched_setscheduler (0, SCHED_RR, &sched_param); diff --git a/multipathd/multipathd.8 b/multipathd/multipathd.8 -index 48b1b04..88ae9c6 100644 +index 48b1b04..480b8ed 100644 --- a/multipathd/multipathd.8 +++ b/multipathd/multipathd.8 -@@ -1,4 +1,4 @@ +@@ -1,22 +1,102 @@ -.TH MULTIPATHD 8 "October 2004" "Linux Administrator's Manual" -+.TH MULTIPATHD 8 "July 2006" "Linux Administrator's Manual" ++.TH MULTIPATHD 8 "November 2006" "Linux Administrator's Manual" .SH NAME multipathd \- multipath daemon - .SH SYNOPSYS -@@ -6,10 +6,10 @@ multipathd \- multipath daemon +-.SH SYNOPSYS ++ ++.SH SYNOPSIS + .B multipathd ++.RB [\| options \|] - This daemon is in charge of checking for failed paths. When this happens, +-This daemon is in charge of checking for failed paths. When this happens, ++.SH DESCRIPTION ++The ++.B multipathd ++daemon is in charge of checking for failed paths. When this happens, it will reconfigure the multipath map the path belongs to, so that this map -regain its maximum performance and redundancy. +regains its maximum performance and redundancy. @@ -3280,7 +6150,116 @@ index 48b1b04..88ae9c6 100644 +In turn, the multipath tool signals the multipathd daemon when it is done with devmap reconfiguration, so that it can refresh its failed path list. ++.SH OPTIONS ++.TP ++.B \-d ++Forground Mode. Don't daemonize, and print all messages to stdout and stderr. ++.TP ++.B -v "level" ++Verbosity level. Print additional information while running multipathd. A level of 0 means only print errors. A level of 3 or greater prints debugging information as well. ++.TP ++.B -k ++multipathd will enter interactive mode. From this mode, the available commands can be viewed by entering "help". When you are finished entering commands, press CTRL-D to quit. ++ ++.SH COMMANDS ++.TP ++The following commands can be used in interactive mode: ++.TP ++.B list|show paths ++Show the paths that multipathd is monitoring, and their state. ++.TP ++.B list|show maps|multipaths ++Show the multipath devices that the multipathd is monitoring. ++.TP ++.B list|show maps|multipaths status ++Show the status of all multipath devices that the multipathd is monitoring. ++.TP ++.B list|show maps|multipaths stats ++Show some statistics of all multipath devices that the multipathd is monitoring. ++.TP ++.B list|show maps|multipaths topology ++Show the current multipath topology. Same as "multipath -ll". ++.TP ++.B list|show topology ++Show the current multipath topology. Same as "multipath -ll". ++.TP ++.B list|show map|multipath $map topology ++Show topology of a single multipath device specified by $map, e.g. 36005076303ffc56200000000000010aa. ++This map could be obtained from "list maps". ++.TP ++.B list|show config ++Show the currently used configuration, derived from default values and values specified within the configuration file /etc/multipath.conf. ++.TP ++.B list|show blacklist ++Show the currently used blacklist rules, derived from default values and values specified within the configuration file /etc/multipath.conf. ++.TP ++.B list|show devices ++Show all available block devices by name including the information if they are blacklisted or not. ++.TP ++.B add path $path ++Add a path to the list of monitored paths. $path is as listed in /sys/block (e.g. sda). ++.TP ++.B remove|del path $path ++Stop monitoring a path. $path is as listed in /sys/block (e.g. sda). ++.TP ++.B add map $map ++Add a multipath device to the list of monitored devices. $map can either be a device-mapper device as listed in /sys/block (e.g. dm-0) or it can be the alias for the multipath device (e.g. mpath1) or the uid of the multipath device (e.g. 36005076303ffc56200000000000010aa). ++.TP ++.B remove|del map $map ++Stop monitoring a multipath device. ++.TP ++.B switch|switchgroup map $map group $group ++Force a multipath device to switch to a specific path group. $group is the path group index, starting with 1. ++.TP ++.B reconfigure ++Reconfigures the multipaths. This should be triggered automatically after any hotplug event. ++.TP ++.B suspend map|multipath $map ++Sets map $map into suspend state. ++.TP ++.B resume map|multipath $map ++Resumes map $map from suspend state. ++.TP ++.B fail path $path ++Sets path $path into failed state. ++.TP ++.B reinstate path $path ++Resumes path $path from failed state. ++ .SH "SEE ALSO" + .BR multipath (8) + .BR kpartx (8) + .BR hotplug (8) + .SH "AUTHORS" +-This man page was assembled by Patrick Caulfield +-for the Debian project. From documentation provided +-by the multipath author Christophe Varoqui, and others. ++.B multipathd ++was developed by Christophe Varoqui, and others. +diff --git a/multipathd/uxclnt.c b/multipathd/uxclnt.c +index ff7b578..009e5cb 100644 +--- a/multipathd/uxclnt.c ++++ b/multipathd/uxclnt.c +@@ -21,6 +21,9 @@ + #include + #include + ++#include ++#include "cli.h" ++ + /* + * process the client + */ +@@ -29,6 +32,9 @@ static void process(int fd) + char *line; + char *reply; + ++ cli_init(); ++ rl_readline_name = "multipathd"; ++ rl_completion_entry_function = key_generator; + while ((line = readline("multipathd> "))) { + size_t len; + size_t llen = strlen(line); diff --git a/path_priority/pp_alua/main.c b/path_priority/pp_alua/main.c index 190fbdc..ba8da99 100644 --- a/path_priority/pp_alua/main.c @@ -3317,7 +6296,7 @@ diff --git a/path_priority/pp_alua/rtpg.c b/path_priority/pp_alua/rtpg.c index 9aea560..701f9d5 100644 --- a/path_priority/pp_alua/rtpg.c +++ b/path_priority/pp_alua/rtpg.c -@@ -21,6 +21,7 @@ #include +@@ -21,6 +21,7 @@ #include #include #include @@ -3325,7 +6304,7 @@ index 9aea560..701f9d5 100644 #define __user #include -@@ -28,7 +29,7 @@ #include +@@ -28,7 +29,7 @@ #include "rtpg.h" #define SENSE_BUFF_LEN 32 @@ -3429,7 +6408,7 @@ index 307a959..ea70f13 100644 * - else, or if anything goes wrong, return 1 as a default prio * */ -@@ -38,7 +38,7 @@ #define FILE_NAME_SIZE 255 +@@ -38,7 +38,7 @@ #define INQUIRY_CMDLEN 6 #define INQUIRY_CMD 0x12 #define SENSE_BUFF_LEN 32 diff --git a/multipath-tools-local-fixes b/multipath-tools-local-fixes new file mode 100644 index 0000000..275c60b --- /dev/null +++ b/multipath-tools-local-fixes @@ -0,0 +1,2036 @@ + Makefile.inc | 1 + + devmap_name/Makefile | 10 +- + kpartx/Makefile | 8 +- + kpartx/devmapper.c | 7 +- + kpartx/kpartx.c | 16 ++- + libcheckers/directio.c | 119 ++++++++---- + libcheckers/emc_clariion.c | 8 +- + libcheckers/libaio.h | 222 +++++++++++++++++++++ + libmultipath/config.c | 5 +- + libmultipath/configure.c | 2 - + libmultipath/debug.c | 2 +- + libmultipath/debug.h | 2 +- + libmultipath/devmapper.c | 60 +++++-- + libmultipath/devmapper.h | 3 +- + libmultipath/dict.c | 2 +- + libmultipath/hwtable.c | 81 +++++--- + libmultipath/log.c | 5 + + libmultipath/log_pthread.c | 2 +- + libmultipath/log_pthread.h | 2 +- + libmultipath/parser.c | 15 +- + libmultipath/parser.h | 1 + + libmultipath/print.c | 2 +- + libmultipath/structs_vec.c | 32 ++-- + libmultipath/waiter.c | 9 +- + multipath/Makefile | 7 +- + multipath/main.c | 58 ++++++- + multipath/multipath.8 | 8 +- + multipath/multipath.conf.5 | 384 +++++++++++++++++++++++++++++++++++++ + multipathd/Makefile | 5 +- + multipathd/cli.c | 2 +- + multipathd/main.c | 9 +- + path_priority/pp_alua/Makefile | 9 +- + path_priority/pp_emc/pp_emc.c | 8 +- + path_priority/pp_hp_sw/Makefile | 25 +++ + path_priority/pp_hp_sw/pp_hp_sw.c | 119 ++++++++++++ + 35 files changed, 1092 insertions(+), 158 deletions(-) + +diff --git a/Makefile.inc b/Makefile.inc +index 4a705aa..fe6cbdf 100644 +--- a/Makefile.inc ++++ b/Makefile.inc +@@ -26,6 +26,7 @@ bindir = $(exec_prefix)/sbin + checkersdir = $(TOPDIR)/libcheckers + multipathdir = $(TOPDIR)/libmultipath + mandir = $(prefix)/usr/share/man/man8 ++man5dir = $(prefix)/usr/share/man/man5 + rcdir = $(prefix)/etc/init.d + + GZIP = /bin/gzip -9 -c +diff --git a/devmap_name/Makefile b/devmap_name/Makefile +index 380c85b..8b0c678 100644 +--- a/devmap_name/Makefile ++++ b/devmap_name/Makefile +@@ -22,21 +22,19 @@ prepare: + + glibc: prepare $(OBJS) + $(CC) $(OBJS) -o $(EXEC) $(LDFLAGS) +- $(GZIP) $(EXEC).8 > $(EXEC).8.gz + + klibc: prepare $(OBJS) + $(CC) -static -o $(EXEC) $(OBJS) +- $(GZIP) $(EXEC).8 > $(EXEC).8.gz + +-install: ++install: $(EXEC) $(EXEC).8 + install -d $(DESTDIR)$(bindir) +- install -s -m 755 $(EXEC) $(DESTDIR)$(bindir)/ ++ install -m 755 $(EXEC) $(DESTDIR)$(bindir)/ + install -d $(DESTDIR)$(mandir) +- install -m 644 $(EXEC).8.gz $(DESTDIR)$(mandir) ++ install -m 644 $(EXEC).8 $(DESTDIR)$(mandir) + + uninstall: + rm $(DESTDIR)$(bindir)/$(EXEC) +- rm $(DESTDIR)$(mandir)/$(EXEC).8.gz ++ rm $(DESTDIR)$(mandir)/$(EXEC).8 + + clean: + rm -f core *.o $(EXEC) *.gz +diff --git a/kpartx/Makefile b/kpartx/Makefile +index bf6e6c1..522a6a0 100644 +--- a/kpartx/Makefile ++++ b/kpartx/Makefile +@@ -27,20 +27,18 @@ prepare: + + glibc: prepare $(OBJS) + $(CC) $(OBJS) -o $(EXEC) $(LDFLAGS) +- $(GZIP) $(EXEC).8 > $(EXEC).8.gz +- ++ + klibc: prepare $(OBJS) + $(CC) -static -o $(EXEC) $(CRT0) $(OBJS) $(KLIBC) $(LIBGCC) +- $(GZIP) $(EXEC).8 > $(EXEC).8.gz + + $(MULTIPATHLIB)-$(BUILD).a: + make -C $(multipathdir) BUILD=$(BUILD) + +-install: ++install: $(EXEC) $(EXEC).8 + install -d $(DESTDIR)$(bindir) + install -s -m 755 $(EXEC) $(DESTDIR)$(bindir) + install -d $(DESTDIR)$(mandir) +- install -m 644 $(EXEC).8.gz $(DESTDIR)$(mandir) ++ install -m 644 $(EXEC).8 $(DESTDIR)$(mandir) + + uninstall: + rm -f $(DESTDIR)$(bindir)/$(EXEC) +diff --git a/kpartx/devmapper.c b/kpartx/devmapper.c +index 5b27487..4b228ed 100644 +--- a/kpartx/devmapper.c ++++ b/kpartx/devmapper.c +@@ -95,19 +95,16 @@ dm_addmap (int task, const char *name, c + } + sprintf(prefixed_uuid, UUID_PREFIX "%s", part, uuid); + if (!dm_task_set_uuid(dmt, prefixed_uuid)) +- goto freeout; ++ goto addout; + } + + dm_task_no_open_count(dmt); + + r = dm_task_run (dmt); + +- freeout: +- if (prefixed_uuid) +- free(prefixed_uuid); +- + addout: + dm_task_destroy (dmt); ++ + return r; + } + +diff --git a/kpartx/kpartx.c b/kpartx/kpartx.c +index 0ee0cb3..b406b95 100644 +--- a/kpartx/kpartx.c ++++ b/kpartx/kpartx.c +@@ -193,6 +193,7 @@ main(int argc, char **argv){ + char * loopdev = NULL; + char * delim = NULL; + char *uuid = NULL; ++ char *mapname = NULL; + int loopro = 0; + int hotplug = 0; + struct stat buf; +@@ -310,12 +311,19 @@ main(int argc, char **argv){ + + off = find_devname_offset(device); + +- if (!loopdev) ++ if (!loopdev) { + uuid = dm_mapuuid((unsigned int)MAJOR(buf.st_rdev), + (unsigned int)MINOR(buf.st_rdev)); ++ mapname = dm_mapname((unsigned int)MAJOR(buf.st_rdev), ++ (unsigned int)MINOR(buf.st_rdev)); ++ } ++ + if (!uuid) + uuid = device + off; + ++ if (!mapname) ++ mapname = device + off; ++ + fd = open(device, O_RDONLY); + + if (fd == -1) { +@@ -365,7 +373,7 @@ main(int argc, char **argv){ + continue; + + printf("%s%s%d : 0 %lu %s %lu\n", +- device + off, delim, j+1, ++ mapname, delim, j+1, + (unsigned long) slices[j].size, device, + (unsigned long) slices[j].start); + } +@@ -374,7 +382,7 @@ main(int argc, char **argv){ + case DELETE: + for (j = 0; j < n; j++) { + if (safe_sprintf(partname, "%s%s%d", +- device + off , delim, j+1)) { ++ mapname, delim, j+1)) { + fprintf(stderr, "partname too small\n"); + exit(1); + } +@@ -407,7 +415,7 @@ main(int argc, char **argv){ + continue; + + if (safe_sprintf(partname, "%s%s%d", +- device + off , delim, j+1)) { ++ mapname, delim, j+1)) { + fprintf(stderr, "partname too small\n"); + exit(1); + } +diff --git a/libcheckers/directio.c b/libcheckers/directio.c +index b53c1c3..2251515 100644 +--- a/libcheckers/directio.c ++++ b/libcheckers/directio.c +@@ -12,28 +12,44 @@ + #include + #include + #include ++#include ++#include + ++#include "libaio.h" + #include "checkers.h" ++#include "../libmultipath/debug.h" + + #define MSG_DIRECTIO_UNKNOWN "directio checker is not available" + #define MSG_DIRECTIO_UP "directio checker reports path is up" + #define MSG_DIRECTIO_DOWN "directio checker reports path is down" + + struct directio_context { +- int blksize; +- unsigned char *buf; +- unsigned char *ptr; ++ int running; ++ int reset_flags; ++ int blksize; ++ unsigned char * buf; ++ unsigned char * ptr; ++ io_context_t ioctx; ++ struct iocb io; + }; + ++ + int directio_init (struct checker * c) + { + unsigned long pgsize = getpagesize(); + struct directio_context * ct; ++ long flags; + + ct = malloc(sizeof(struct directio_context)); + if (!ct) + return 1; +- c->context = (void *)ct; ++ memset(ct, 0, sizeof(struct directio_context)); ++ ++ if (syscall(__NR_io_setup, 1, &ct->ioctx) != 0) { ++ condlog(1, "io_setup failed"); ++ free(ct); ++ return 1; ++ } + + if (ioctl(c->fd, BLKBSZGET, &ct->blksize) < 0) { + MSG(c, "cannot get blocksize, set default"); +@@ -50,11 +66,28 @@ int directio_init (struct checker * c) + ct->buf = (unsigned char *)malloc(ct->blksize + pgsize); + if (!ct->buf) + goto out; +- ct->ptr = (unsigned char *)(((unsigned long)ct->buf + pgsize - 1) & +- (~(pgsize - 1))); + ++ flags = fcntl(c->fd, F_GETFL); ++ if (flags < 0) ++ goto out; ++ if (!(flags & O_DIRECT)) { ++ flags |= O_DIRECT; ++ if (fcntl(c->fd, F_SETFL, flags) < 0) ++ goto out; ++ ct->reset_flags = 1; ++ } ++ ++ ct->ptr = (unsigned char *) (((unsigned long)ct->buf + pgsize - 1) & ++ (~(pgsize - 1))); ++ ++ /* Sucessfully initialized, return the context. */ ++ c->context = (void *) ct; + return 0; ++ + out: ++ if (ct->buf) ++ free(ct->buf); ++ syscall(__NR_io_destroy, ct->ioctx); + free(ct); + return 1; + } +@@ -62,56 +95,63 @@ out: + void directio_free (struct checker * c) + { + struct directio_context * ct = (struct directio_context *)c->context; ++ long flags; + + if (!ct) + return; ++ ++ if (ct->reset_flags) { ++ if ((flags = fcntl(c->fd, F_GETFL)) >= 0) { ++ flags &= ~O_DIRECT; ++ /* No point in checking for errors */ ++ fcntl(c->fd, F_SETFL, flags); ++ } ++ } ++ + if (ct->buf) + free(ct->buf); ++ syscall(__NR_io_destroy, ct->ioctx); + free(ct); + } + + static int +-direct_read (int fd, unsigned char * buff, int size) ++check_state(int fd, struct directio_context *ct) + { +- long flags; +- int reset_flags = 0; +- int res, retval; +- +- flags = fcntl(fd,F_GETFL); +- +- if (flags < 0) { +- return PATH_UNCHECKED; ++ struct timespec timeout = { .tv_sec = 2 }; ++ struct io_event event; ++ struct stat sb; ++ int rc = PATH_UNCHECKED; ++ long r; ++ ++ if (fstat(fd, &sb) == 0) { ++ condlog(4, "directio: called for %x", (unsigned) sb.st_rdev); + } + +- if (!(flags & O_DIRECT)) { +- flags |= O_DIRECT; +- if (fcntl(fd,F_SETFL,flags) < 0) { ++ if (!ct->running) { ++ struct iocb *ios[1] = { &ct->io }; ++ ++ condlog(3, "directio: starting new request"); ++ memset(&ct->io, 0, sizeof(struct iocb)); ++ io_prep_pread(&ct->io, fd, ct->ptr, ct->blksize, 0); ++ if (syscall(__NR_io_submit, ct->ioctx, 1, ios) != 1) { ++ condlog(3, "directio: io_submit error %i", errno); + return PATH_UNCHECKED; + } +- reset_flags = 1; + } ++ ct->running = 1; + +- while ( (res = read(fd,buff,size)) < 0 && errno == EINTR ); +- if (res < 0) { +- if (errno == EINVAL) { +- /* O_DIRECT is not available */ +- retval = PATH_UNCHECKED; +- } else if (errno == ENOMEM) { +- retval = PATH_UP; +- } else { +- retval = PATH_DOWN; +- } ++ r = syscall(__NR_io_getevents, ct->ioctx, 1L, 1L, &event, &timeout); ++ if (r < 1L) { ++ condlog(3, "directio: timeout r=%li errno=%i", r, errno); ++ rc = PATH_DOWN; + } else { +- retval = PATH_UP; +- } +- +- if (reset_flags) { +- flags &= ~O_DIRECT; +- /* No point in checking for errors */ +- fcntl(fd,F_SETFL,flags); ++ condlog(3, "directio: io finished %lu/%lu", event.res, ++ event.res2); ++ ct->running = 0; ++ rc = (event.res == ct->blksize) ? PATH_UP : PATH_DOWN; + } + +- return retval; ++ return rc; + } + + int directio (struct checker * c) +@@ -119,7 +159,10 @@ int directio (struct checker * c) + int ret; + struct directio_context * ct = (struct directio_context *)c->context; + +- ret = direct_read(c->fd, ct->ptr, ct->blksize); ++ if (!ct) ++ return PATH_UNCHECKED; ++ ++ ret = check_state(c->fd, ct); + + switch (ret) + { +diff --git a/libcheckers/emc_clariion.c b/libcheckers/emc_clariion.c +index 384a943..d801b42 100644 +--- a/libcheckers/emc_clariion.c ++++ b/libcheckers/emc_clariion.c +@@ -129,8 +129,12 @@ int emc_clariion(struct checker * c) + + if ( /* Effective initiator type */ + sense_buffer[27] != 0x03 +- /* Failover mode should be set to 1 */ +- || (sense_buffer[28] & 0x07) != 0x04 ++ /* ++ * Failover mode should be set to 1 (PNR failover mode) ++ * or 4 (ALUA failover mode). ++ */ ++ || (((sense_buffer[28] & 0x07) != 0x04) && ++ ((sense_buffer[28] & 0x07) != 0x06)) + /* Arraycommpath should be set to 1 */ + || (sense_buffer[30] & 0x04) != 0x04) { + MSG(c, "emc_clariion_checker: Path not correctly configured " +diff --git a/libcheckers/libaio.h b/libcheckers/libaio.h +new file mode 100644 +index 0000000..6574601 +--- /dev/null ++++ b/libcheckers/libaio.h +@@ -0,0 +1,222 @@ ++/* /usr/include/libaio.h ++ * ++ * Copyright 2000,2001,2002 Red Hat, Inc. ++ * ++ * Written by Benjamin LaHaise ++ * ++ * libaio Linux async I/O interface ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2 of the License, or (at your option) any later version. ++ * ++ * This library 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 ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++#ifndef __LIBAIO_H ++#define __LIBAIO_H ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#include ++#include ++ ++struct timespec; ++struct sockaddr; ++struct iovec; ++struct iocb; ++ ++typedef struct io_context *io_context_t; ++ ++typedef enum io_iocb_cmd { ++ IO_CMD_PREAD = 0, ++ IO_CMD_PWRITE = 1, ++ ++ IO_CMD_FSYNC = 2, ++ IO_CMD_FDSYNC = 3, ++ ++ IO_CMD_POLL = 5, ++ IO_CMD_NOOP = 6, ++} io_iocb_cmd_t; ++ ++#if defined(__i386__) /* little endian, 32 bits */ ++#define PADDED(x, y) x; unsigned y ++#define PADDEDptr(x, y) x; unsigned y ++#define PADDEDul(x, y) unsigned long x; unsigned y ++#elif defined(__ia64__) || defined(__x86_64__) || defined(__alpha__) ++#define PADDED(x, y) x, y ++#define PADDEDptr(x, y) x ++#define PADDEDul(x, y) unsigned long x ++#elif defined(__powerpc64__) /* big endian, 64 bits */ ++#define PADDED(x, y) unsigned y; x ++#define PADDEDptr(x,y) x ++#define PADDEDul(x, y) unsigned long x ++#elif defined(__PPC__) /* big endian, 32 bits */ ++#define PADDED(x, y) unsigned y; x ++#define PADDEDptr(x, y) unsigned y; x ++#define PADDEDul(x, y) unsigned y; unsigned long x ++#elif defined(__s390x__) /* big endian, 64 bits */ ++#define PADDED(x, y) unsigned y; x ++#define PADDEDptr(x,y) x ++#define PADDEDul(x, y) unsigned long x ++#elif defined(__s390__) /* big endian, 32 bits */ ++#define PADDED(x, y) unsigned y; x ++#define PADDEDptr(x, y) unsigned y; x ++#define PADDEDul(x, y) unsigned y; unsigned long x ++#else ++#error endian? ++#endif ++ ++struct io_iocb_poll { ++ PADDED(int events, __pad1); ++}; /* result code is the set of result flags or -'ve errno */ ++ ++struct io_iocb_sockaddr { ++ struct sockaddr *addr; ++ int len; ++}; /* result code is the length of the sockaddr, or -'ve errno */ ++ ++struct io_iocb_common { ++ PADDEDptr(void *buf, __pad1); ++ PADDEDul(nbytes, __pad2); ++ long long offset; ++ long long __pad3, __pad4; ++}; /* result code is the amount read or -'ve errno */ ++ ++struct io_iocb_vector { ++ const struct iovec *vec; ++ int nr; ++ long long offset; ++}; /* result code is the amount read or -'ve errno */ ++ ++struct iocb { ++ PADDEDptr(void *data, __pad1); /* Return in the io completion event */ ++ PADDED(unsigned key, __pad2); /* For use in identifying io requests */ ++ ++ short aio_lio_opcode; ++ short aio_reqprio; ++ int aio_fildes; ++ ++ union { ++ struct io_iocb_common c; ++ struct io_iocb_vector v; ++ struct io_iocb_poll poll; ++ struct io_iocb_sockaddr saddr; ++ } u; ++}; ++ ++struct io_event { ++ PADDEDptr(void *data, __pad1); ++ PADDEDptr(struct iocb *obj, __pad2); ++ PADDEDul(res, __pad3); ++ PADDEDul(res2, __pad4); ++}; ++ ++#undef PADDED ++#undef PADDEDptr ++#undef PADDEDul ++ ++typedef void (*io_callback_t)(io_context_t ctx, struct iocb *iocb, long res, long res2); ++ ++/* library wrappers */ ++extern int io_queue_init(int maxevents, io_context_t *ctxp); ++/*extern int io_queue_grow(io_context_t ctx, int new_maxevents);*/ ++extern int io_queue_release(io_context_t ctx); ++/*extern int io_queue_wait(io_context_t ctx, struct timespec *timeout);*/ ++extern int io_queue_run(io_context_t ctx); ++ ++/* Actual syscalls */ ++extern int io_setup(int maxevents, io_context_t *ctxp); ++extern int io_destroy(io_context_t ctx); ++extern int io_submit(io_context_t ctx, long nr, struct iocb *ios[]); ++extern int io_cancel(io_context_t ctx, struct iocb *iocb, struct io_event *evt); ++extern int io_getevents(io_context_t ctx_id, long min_nr, long nr, struct io_event *events, struct timespec *timeout); ++ ++ ++static inline void io_set_callback(struct iocb *iocb, io_callback_t cb) ++{ ++ iocb->data = (void *)cb; ++} ++ ++static inline void io_prep_pread(struct iocb *iocb, int fd, void *buf, size_t count, long long offset) ++{ ++ memset(iocb, 0, sizeof(*iocb)); ++ iocb->aio_fildes = fd; ++ iocb->aio_lio_opcode = IO_CMD_PREAD; ++ iocb->aio_reqprio = 0; ++ iocb->u.c.buf = buf; ++ iocb->u.c.nbytes = count; ++ iocb->u.c.offset = offset; ++} ++ ++static inline void io_prep_pwrite(struct iocb *iocb, int fd, void *buf, size_t count, long long offset) ++{ ++ memset(iocb, 0, sizeof(*iocb)); ++ iocb->aio_fildes = fd; ++ iocb->aio_lio_opcode = IO_CMD_PWRITE; ++ iocb->aio_reqprio = 0; ++ iocb->u.c.buf = buf; ++ iocb->u.c.nbytes = count; ++ iocb->u.c.offset = offset; ++} ++ ++static inline void io_prep_poll(struct iocb *iocb, int fd, int events) ++{ ++ memset(iocb, 0, sizeof(*iocb)); ++ iocb->aio_fildes = fd; ++ iocb->aio_lio_opcode = IO_CMD_POLL; ++ iocb->aio_reqprio = 0; ++ iocb->u.poll.events = events; ++} ++ ++static inline int io_poll(io_context_t ctx, struct iocb *iocb, io_callback_t cb, int fd, int events) ++{ ++ io_prep_poll(iocb, fd, events); ++ io_set_callback(iocb, cb); ++ return io_submit(ctx, 1, &iocb); ++} ++ ++static inline void io_prep_fsync(struct iocb *iocb, int fd) ++{ ++ memset(iocb, 0, sizeof(*iocb)); ++ iocb->aio_fildes = fd; ++ iocb->aio_lio_opcode = IO_CMD_FSYNC; ++ iocb->aio_reqprio = 0; ++} ++ ++static inline int io_fsync(io_context_t ctx, struct iocb *iocb, io_callback_t cb, int fd) ++{ ++ io_prep_fsync(iocb, fd); ++ io_set_callback(iocb, cb); ++ return io_submit(ctx, 1, &iocb); ++} ++ ++static inline void io_prep_fdsync(struct iocb *iocb, int fd) ++{ ++ memset(iocb, 0, sizeof(*iocb)); ++ iocb->aio_fildes = fd; ++ iocb->aio_lio_opcode = IO_CMD_FDSYNC; ++ iocb->aio_reqprio = 0; ++} ++ ++static inline int io_fdsync(io_context_t ctx, struct iocb *iocb, io_callback_t cb, int fd) ++{ ++ io_prep_fdsync(iocb, fd); ++ io_set_callback(iocb, cb); ++ return io_submit(ctx, 1, &iocb); ++} ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif /* __LIBAIO_H */ +diff --git a/libmultipath/config.c b/libmultipath/config.c +index a39af8a..1dfc18c 100644 +--- a/libmultipath/config.c ++++ b/libmultipath/config.c +@@ -366,12 +366,15 @@ load_config (char * file) + /* + * read the config file + */ ++ set_current_keywords(&conf->keywords); ++ alloc_keywords(); + if (filepresent(file)) { +- set_current_keywords(&conf->keywords); + if (init_data(file, init_keywords)) { + condlog(0, "error parsing config file"); + goto out; + } ++ } else { ++ init_keywords(); + } + + /* +diff --git a/libmultipath/configure.c b/libmultipath/configure.c +index 9632fb4..a5bad4c 100644 +--- a/libmultipath/configure.c ++++ b/libmultipath/configure.c +@@ -324,7 +324,6 @@ domap (struct multipath * mpp) + mpp->alias); + return DOMAP_RETRY; + } +- dm_shut_log(); + + if (dm_map_present(mpp->alias)) + break; +@@ -345,7 +344,6 @@ domap (struct multipath * mpp) + } + + lock_multipath(mpp, 0); +- dm_restore_log(); + break; + + case ACT_RELOAD: +diff --git a/libmultipath/debug.c b/libmultipath/debug.c +index fa06cbd..05dfb06 100644 +--- a/libmultipath/debug.c ++++ b/libmultipath/debug.c +@@ -14,7 +14,7 @@ + #include "vector.h" + #include "config.h" + +-void dlog (int sink, int prio, char * fmt, ...) ++void dlog (int sink, int prio, const char * fmt, ...) + { + va_list ap; + int thres; +diff --git a/libmultipath/debug.h b/libmultipath/debug.h +index 74ed531..082fff1 100644 +--- a/libmultipath/debug.h ++++ b/libmultipath/debug.h +@@ -1,4 +1,4 @@ +-void dlog (int sink, int prio, char * fmt, ...) ++void dlog (int sink, int prio, const char * fmt, ...) + __attribute__((format(printf, 3, 4))); + + #if DAEMON +diff --git a/libmultipath/devmapper.c b/libmultipath/devmapper.c +index dece079..d762cc1 100644 +--- a/libmultipath/devmapper.c ++++ b/libmultipath/devmapper.c +@@ -20,6 +20,13 @@ + #include "debug.h" + #include "memory.h" + #include "devmapper.h" ++#include "config.h" ++ ++#if DAEMON ++#include "log_pthread.h" ++#include ++#include ++#endif + + #define MAX_WAIT 5 + #define LOOPS_PER_SEC 5 +@@ -28,21 +35,50 @@ + #define UUID_PREFIX_LEN 6 + + static void +-dm_dummy_log (int level, const char *file, int line, const char *f, ...) ++dm_write_log (int level, const char *file, int line, const char *f, ...) + { +- return; +-} ++ va_list ap; ++ int thres; ++ ++ if (level > 6) ++ level = 6; ++ ++ thres = (conf) ? conf->verbosity : 0; ++ if (thres <= 3 || level > thres) ++ return; ++ ++ va_start(ap, f); ++#if DAEMON ++ if (!logsink) { ++ time_t t = time(NULL); ++ struct tm *tb = localtime(&t); ++ char buff[16]; ++ ++ strftime(buff, sizeof(buff), "%b %d %H:%M:%S", tb); ++ buff[sizeof(buff)-1] = '\0'; ++ ++ fprintf(stdout, "%s | ", buff); ++ fprintf(stdout, "libdevmapper: %s(%i): ", file, line); ++ vfprintf(stdout, f, ap); ++ fprintf(stdout, "\n"); ++ } else { ++ condlog(level, "libdevmapper: %s(%i): ", file, line); ++ log_safe(level + 3, f, ap); ++ } ++#else ++ fprintf(stdout, "libdevmapper: %s(%i): ", file, line); ++ vfprintf(stdout, f, ap); ++ fprintf(stdout, "\n"); ++#endif ++ va_end(ap); + +-void +-dm_restore_log (void) +-{ +- dm_log_init(NULL); ++ return; + } + +-void +-dm_shut_log (void) +-{ +- dm_log_init(&dm_dummy_log); ++extern void ++dm_init(void) { ++ dm_log_init(&dm_write_log); ++ dm_log_init_verbose(conf ? conf->verbosity + 3 : 0); + } + + static int +@@ -764,9 +800,7 @@ dm_mapname(int major, int minor) + * daemon uev_trigger -> uev_add_map + */ + while (--loop) { +- dm_shut_log(); + r = dm_task_run(dmt); +- dm_restore_log(); + + if (r) + break; +diff --git a/libmultipath/devmapper.h b/libmultipath/devmapper.h +index 59afd01..8438034 100644 +--- a/libmultipath/devmapper.h ++++ b/libmultipath/devmapper.h +@@ -1,5 +1,4 @@ +-void dm_shut_log(void); +-void dm_restore_log(void); ++void dm_init(void); + int dm_prereq (char *); + int dm_simplecmd (int, const char *); + int dm_addmap (int, const char *, const char *, const char *, +diff --git a/libmultipath/dict.c b/libmultipath/dict.c +index c705cc6..4572a7d 100644 +--- a/libmultipath/dict.c ++++ b/libmultipath/dict.c +@@ -270,7 +270,7 @@ blacklist_exceptions_handler(vector strv + conf->elist_wwid = vector_alloc(); + conf->elist_device = vector_alloc(); + +- if (!conf->elist_devnode || !conf->elist_wwid || !conf->blist_device) ++ if (!conf->elist_devnode || !conf->elist_wwid || !conf->elist_device) + return 1; + + return 0; +diff --git a/libmultipath/hwtable.c b/libmultipath/hwtable.c +index df6f5aa..d5b227f 100644 +--- a/libmultipath/hwtable.c ++++ b/libmultipath/hwtable.c +@@ -63,11 +63,11 @@ static struct hwentry default_hw[] = { + .vendor = "DEC", + .product = "HSG80", + .getuid = DEFAULT_GETUID, +- .getprio = NULL, +- .features = DEFAULT_FEATURES, +- .hwhandler = "1 hp_sw", ++ .getprio = "mpath_prio_hp_sw /dev/%n", ++ .features = "1 queue_if_no_path", ++ .hwhandler = DEFAULT_HWHANDLER, + .selector = DEFAULT_SELECTOR, +- .pgpolicy = GROUP_BY_SERIAL, ++ .pgpolicy = GROUP_BY_PRIO, + .pgfailback = FAILBACK_UNDEF, + .rr_weight = RR_WEIGHT_NONE, + .no_path_retry = NO_PATH_RETRY_UNDEF, +@@ -75,61 +75,62 @@ static struct hwentry default_hw[] = { + .checker_name = HP_SW, + }, + { +- .vendor = "(COMPAQ|HP)", +- .product = "(MSA|HSV)1.*", ++ .vendor = "HP", ++ .product = "A6189A", + .getuid = DEFAULT_GETUID, + .getprio = NULL, + .features = DEFAULT_FEATURES, +- .hwhandler = "1 hp_sw", ++ .hwhandler = DEFAULT_HWHANDLER, + .selector = DEFAULT_SELECTOR, + .pgpolicy = MULTIBUS, + .pgfailback = FAILBACK_UNDEF, + .rr_weight = RR_WEIGHT_NONE, + .no_path_retry = NO_PATH_RETRY_UNDEF, + .minio = DEFAULT_MINIO, +- .checker_name = HP_SW, ++ .checker_name = READSECTOR0, + }, + { +- .vendor = "HP", +- .product = "A6189A", ++ /* MSA 1000/MSA1500 EVA 3000/5000 with old firmware */ ++ .vendor = "(COMPAQ|HP)", ++ .product = "(MSA|HSV)1.0.*", + .getuid = DEFAULT_GETUID, +- .getprio = NULL, +- .features = DEFAULT_FEATURES, ++ .getprio = "mpath_prio_hp_sw /dev/%n", ++ .features = "1 queue_if_no_path", + .hwhandler = DEFAULT_HWHANDLER, + .selector = DEFAULT_SELECTOR, +- .pgpolicy = MULTIBUS, ++ .pgpolicy = GROUP_BY_PRIO, + .pgfailback = FAILBACK_UNDEF, + .rr_weight = RR_WEIGHT_NONE, + .no_path_retry = NO_PATH_RETRY_UNDEF, + .minio = DEFAULT_MINIO, +- .checker_name = READSECTOR0, ++ .checker_name = HP_SW, + }, + { ++ /* MSA 1000/1500 with new firmware */ + .vendor = "HP", +- .product = "HSV20.*", +- .revision = "[123].*", ++ .product = "MSA VOLUME", + .getuid = DEFAULT_GETUID, +- .getprio = NULL, ++ .getprio = "mpath_prio_alua /dev/%n", + .features = DEFAULT_FEATURES, + .hwhandler = DEFAULT_HWHANDLER, + .selector = DEFAULT_SELECTOR, +- .pgpolicy = MULTIBUS, +- .pgfailback = FAILBACK_UNDEF, ++ .pgpolicy = GROUP_BY_PRIO, ++ .pgfailback = -FAILBACK_IMMEDIATE, + .rr_weight = RR_WEIGHT_NONE, + .no_path_retry = NO_PATH_RETRY_UNDEF, + .minio = DEFAULT_MINIO, +- .checker_name = HP_SW, ++ .checker_name = TUR, + }, + { +- .vendor = "HP", +- .product = "HSV20.*", +- .revision = "[^123].*", ++ /* EVA 3000/5000 with new firmware */ ++ .vendor = "(COMPAQ|HP)", ++ .product = "(MSA|HSV)1.1.*", + .getuid = DEFAULT_GETUID, +- .getprio = "/sbin/mpath_prio_alua /dev/%n", ++ .getprio = "mpath_prio_alua /dev/%n", + .features = DEFAULT_FEATURES, + .hwhandler = DEFAULT_HWHANDLER, + .selector = DEFAULT_SELECTOR, +- .pgpolicy = MULTIBUS, ++ .pgpolicy = GROUP_BY_PRIO, + .pgfailback = -FAILBACK_IMMEDIATE, + .rr_weight = RR_WEIGHT_NONE, + .no_path_retry = NO_PATH_RETRY_UNDEF, +@@ -137,15 +138,16 @@ static struct hwentry default_hw[] = { + .checker_name = TUR, + }, + { ++ /* EVA 4000/6000/8000 */ + .vendor = "HP", +- .product = "HSV21.*", ++ .product = "HSV2.*", + .getuid = DEFAULT_GETUID, +- .getprio = "/sbin/mpath_prio_alua /dev/%n", ++ .getprio = "mpath_prio_alua /dev/%n", + .features = DEFAULT_FEATURES, + .hwhandler = DEFAULT_HWHANDLER, + .selector = DEFAULT_SELECTOR, + .pgpolicy = GROUP_BY_PRIO, +- .pgfailback = FAILBACK_UNDEF, ++ .pgfailback = -FAILBACK_IMMEDIATE, + .rr_weight = RR_WEIGHT_NONE, + .no_path_retry = NO_PATH_RETRY_UNDEF, + .minio = DEFAULT_MINIO, +@@ -404,9 +406,9 @@ static struct hwentry default_hw[] = { + .vendor = "IBM", + .product = "S/390 DASD ECKD", + .bl_product = "S/390.*", +- .getuid = "/sbin/dasd_id /dev/%n", ++ .getuid = "dasdinfo -u -b %n", + .getprio = NULL, +- .features = DEFAULT_FEATURES, ++ .features = "1 queue_if_no_path", + .hwhandler = DEFAULT_HWHANDLER, + .selector = DEFAULT_SELECTOR, + .pgpolicy = MULTIBUS, +@@ -511,9 +513,24 @@ static struct hwentry default_hw[] = { + .pgpolicy = GROUP_BY_PRIO, + .pgfailback = -FAILBACK_IMMEDIATE, + .rr_weight = RR_WEIGHT_NONE, +- .no_path_retry = NO_PATH_RETRY_UNDEF, ++ .no_path_retry = NO_PATH_RETRY_QUEUE, + .minio = DEFAULT_MINIO, +- .checker_name = TUR, ++ .checker_name = RDAC, ++ }, ++ { ++ .vendor = "SGI", ++ .product = "IS.*", ++ .getuid = DEFAULT_GETUID, ++ .getprio = "mpath_prio_tpc /dev/%n", ++ .features = DEFAULT_FEATURES, ++ .hwhandler = DEFAULT_HWHANDLER, ++ .selector = DEFAULT_SELECTOR, ++ .pgpolicy = GROUP_BY_PRIO, ++ .pgfailback = -FAILBACK_IMMEDIATE, ++ .rr_weight = RR_WEIGHT_NONE, ++ .no_path_retry = NO_PATH_RETRY_QUEUE, ++ .minio = DEFAULT_MINIO, ++ .checker_name = RDAC, + }, + /* + * STK arrays +diff --git a/libmultipath/log.c b/libmultipath/log.c +index 8b339d7..90e4d1f 100644 +--- a/libmultipath/log.c ++++ b/libmultipath/log.c +@@ -118,6 +118,11 @@ int log_enqueue (int prio, const char * + /* not enough space on tail : rewind */ + if (la->head <= la->tail && len > (la->end - la->tail)) { + logdbg(stderr, "enqueue: rewind tail to %p\n", la->tail); ++ if (la->head == la->start ) { ++ logdbg(stderr, "enqueue: can not rewind tail, drop msg\n"); ++ la->tail = lastmsg; ++ return 1; /* can't reuse */ ++ } + la->tail = la->start; + + if (la->empty) +diff --git a/libmultipath/log_pthread.c b/libmultipath/log_pthread.c +index f98cfa4..5a82b6a 100644 +--- a/libmultipath/log_pthread.c ++++ b/libmultipath/log_pthread.c +@@ -12,7 +12,7 @@ + #include "log_pthread.h" + #include "log.h" + +-void log_safe (int prio, char * fmt, va_list ap) ++void log_safe (int prio, const char * fmt, va_list ap) + { + pthread_mutex_lock(logq_lock); + //va_start(ap, fmt); +diff --git a/libmultipath/log_pthread.h b/libmultipath/log_pthread.h +index 7c902c7..2b18f59 100644 +--- a/libmultipath/log_pthread.h ++++ b/libmultipath/log_pthread.h +@@ -7,7 +7,7 @@ pthread_mutex_t *logq_lock; + pthread_mutex_t *logev_lock; + pthread_cond_t *logev_cond; + +-void log_safe(int prio, char * fmt, va_list ap); ++void log_safe(int prio, const char * fmt, va_list ap); + void log_thread_start(void); + void log_thread_stop(void); + +diff --git a/libmultipath/parser.c b/libmultipath/parser.c +index f9c555e..5302970 100644 +--- a/libmultipath/parser.c ++++ b/libmultipath/parser.c +@@ -455,16 +455,23 @@ process_stream(vector keywords) + return r; + } + ++int alloc_keywords(void) ++{ ++ if (!keywords) ++ keywords = vector_alloc(); ++ ++ if (!keywords) ++ return 1; ++ ++ return 0; ++} ++ + /* Data initialization */ + int + init_data(char *conf_file, void (*init_keywords) (void)) + { + int r; + +- if (!keywords) +- keywords = vector_alloc(); +- if (!keywords) +- return 1; + stream = fopen(conf_file, "r"); + if (!stream) { + syslog(LOG_WARNING, "Configuration file open problem"); +diff --git a/libmultipath/parser.h b/libmultipath/parser.h +index 95d4e6f..8496684 100644 +--- a/libmultipath/parser.h ++++ b/libmultipath/parser.h +@@ -74,6 +74,7 @@ extern vector read_value_block(void); + extern int alloc_value_block(vector strvec, void (*alloc_func) (vector)); + extern void *set_value(vector strvec); + extern int process_stream(vector keywords); ++extern int alloc_keywords(void); + extern int init_data(char *conf_file, void (*init_keywords) (void)); + extern struct keyword * find_keyword(vector v, char * name); + void set_current_keywords (vector *k); +diff --git a/libmultipath/print.c b/libmultipath/print.c +index e50f37d..dc8af48 100644 +--- a/libmultipath/print.c ++++ b/libmultipath/print.c +@@ -734,7 +734,7 @@ snprint_hwentry (char * buff, int len, s + if (fwd > len) + return len; + iterate_sub_keywords(rootkw, kw, i) { +- fwd += snprint_keyword(buff + fwd, len - fwd, "\t\t%k %v\n", ++ fwd += snprint_keyword(buff + fwd, len - fwd, "\t\t%k \"%v\"\n", + kw, hwe); + if (fwd > len) + return len; +diff --git a/libmultipath/structs_vec.c b/libmultipath/structs_vec.c +index 53b7e17..a4a996a 100644 +--- a/libmultipath/structs_vec.c ++++ b/libmultipath/structs_vec.c +@@ -16,7 +16,6 @@ + #include "discovery.h" + #include "waiter.h" + +- + /* + * creates or updates mpp->paths reading mpp->pg + */ +@@ -118,6 +117,8 @@ remove_map (struct multipath * mpp, stru + { + int i; + ++ condlog(4, "%s: remove multipath map", mpp->alias); ++ + /* + * stop the DM event waiter thread + */ +@@ -245,8 +246,17 @@ extern int + setup_multipath (struct vectors * vecs, struct multipath * mpp) + { + retry: +- if (dm_get_info(mpp->alias, &mpp->dmi)) ++ if (dm_get_info(mpp->alias, &mpp->dmi)) { ++ /* Error accessing table */ ++ condlog(3, "%s: cannot access table", mpp->alias); ++ goto out; ++ } ++ ++ if (!dm_map_present(mpp->alias)) { ++ /* Table has been removed */ ++ condlog(3, "%s: table does not exist", mpp->alias); + goto out; ++ } + + set_multipath_wwid(mpp); + mpp->mpe = find_mpe(mpp->wwid); +@@ -270,6 +280,7 @@ retry: + #endif + goto retry; + } ++ condlog(0, "%s: failed to setup multipath", mpp->alias); + goto out; + } + +@@ -282,7 +293,6 @@ retry: + + return 0; + out: +- condlog(0, "%s: failed to setup multipath", mpp->alias); + remove_map(mpp, vecs, NULL, 1); + return 1; + } +@@ -390,18 +400,19 @@ int update_multipath (struct vectors *ve + struct pathgroup *pgp; + struct path *pp; + int i, j; +- int r = 1; + + mpp = find_mp_by_alias(vecs->mpvec, mapname); + +- if (!mpp) +- goto out; ++ if (!mpp) { ++ condlog(3, "%s: multipath map not found\n", mapname); ++ return 2; ++ } + + free_pgvec(mpp->pg, KEEP_PATHS); + mpp->pg = NULL; + + if (setup_multipath(vecs, mpp)) +- goto out; /* mpp freed in setup_multipath */ ++ return 1; /* mpp freed in setup_multipath */ + + /* + * compare checkers states with DM states +@@ -429,11 +440,8 @@ int update_multipath (struct vectors *ve + } + } + } +- r = 0; +-out: +- if (r) +- condlog(0, "failed to update multipath"); +- return r; ++ ++ return 0; + } + + /* +diff --git a/libmultipath/waiter.c b/libmultipath/waiter.c +index 75ed90c..d7af0d1 100644 +--- a/libmultipath/waiter.c ++++ b/libmultipath/waiter.c +@@ -117,15 +117,11 @@ int waiteventloop (struct event_thread * + /* accept wait interruption */ + set = unblock_signals(); + +- /* interruption spits messages */ +- dm_shut_log(); +- + /* wait */ + r = dm_task_run(waiter->dmt); + + /* wait is over : event or interrupt */ + pthread_sigmask(SIG_SETMASK, &set, NULL); +- //dm_restore_log(); + + if (!r) /* wait interrupted by signal */ + return -1; +@@ -157,8 +153,11 @@ int waiteventloop (struct event_thread * + r = update_multipath(waiter->vecs, waiter->mapname); + lock_cleanup_pop(waiter->vecs->lock); + +- if (r) ++ if (r) { ++ condlog(2, "%s: event checker exit", ++ waiter->mapname); + return -1; /* stop the thread */ ++ } + + event_nr = dm_geteventnr(waiter->mapname); + +diff --git a/multipath/Makefile b/multipath/Makefile +index 646dfc2..947d481 100644 +--- a/multipath/Makefile ++++ b/multipath/Makefile +@@ -25,11 +25,9 @@ prepare: + + glibc: prepare $(OBJS) + $(CC) $(OBJS) -o $(EXEC) $(LDFLAGS) +- $(GZIP) $(EXEC).8 > $(EXEC).8.gz + + klibc: prepare $(OBJS) + $(CC) -static -o $(EXEC) $(CRT0) $(OBJS) $(KLIBC) $(LIBGCC) +- $(GZIP) $(EXEC).8 > $(EXEC).8.gz + + $(CHECKERSLIB)-$(BUILD).a: + make -C $(checkersdir) BUILD=$(BUILD) $(BUILD) +@@ -43,12 +41,13 @@ install: + install -d $(DESTDIR)/etc/udev/rules.d + install -m 644 multipath.rules $(DESTDIR)/etc/udev/rules.d/ + install -d $(DESTDIR)$(mandir) +- install -m 644 $(EXEC).8.gz $(DESTDIR)$(mandir) ++ install -m 644 $(EXEC).8 $(DESTDIR)$(mandir) ++ install -m 644 multipath.conf.5 $(DESTDIR)$(man5dir) + + uninstall: + rm $(DESTDIR)/etc/udev/rules.d/multipath.rules + rm $(DESTDIR)$(bindir)/$(EXEC) +- rm $(DESTDIR)$(mandir)/$(EXEC).8.gz ++ rm $(DESTDIR)$(mandir)/$(EXEC).8 + + clean: + rm -f core *.o $(EXEC) *.gz +diff --git a/multipath/main.c b/multipath/main.c +index 67076db..c3d0dac 100644 +--- a/multipath/main.c ++++ b/multipath/main.c +@@ -72,7 +72,7 @@ static void + usage (char * progname) + { + fprintf (stderr, VERSION_STRING); +- fprintf (stderr, "Usage: %s\t[-v level] [-d] [-h|-l|-ll|-f|-F]\n", ++ fprintf (stderr, "Usage: %s\t[-v level] [-d] [-h|-l|-ll|-f|-F|-t]\n", + progname); + fprintf (stderr, + "\t\t\t[-p failover|multibus|group_by_serial|group_by_prio]\n" \ +@@ -90,6 +90,7 @@ usage (char * progname) + "\t-ll\t\tshow multipath topology (maximum info)\n" \ + "\t-f\t\tflush a multipath device map\n" \ + "\t-F\t\tflush all multipath device maps\n" \ ++ "\t-t\t\tprint internal hardware table\n" \ + "\t-p policy\tforce all maps to specified policy :\n" \ + "\t failover\t\t1 path per priority group\n" \ + "\t multibus\t\tall paths in 1 priority group\n" \ +@@ -307,6 +308,55 @@ out: + return r; + } + ++static int ++dump_config (void) ++{ ++ char * c; ++ char * reply; ++ unsigned int maxlen = 256; ++ int again = 1; ++ ++ reply = MALLOC(maxlen); ++ ++ while (again) { ++ if (!reply) ++ return 1; ++ c = reply; ++ c += snprint_defaults(c, reply + maxlen - c); ++ again = ((c - reply) == maxlen); ++ if (again) { ++ reply = REALLOC(reply, maxlen *= 2); ++ continue; ++ } ++ c += snprint_blacklist(c, reply + maxlen - c); ++ again = ((c - reply) == maxlen); ++ if (again) { ++ reply = REALLOC(reply, maxlen *= 2); ++ continue; ++ } ++ c += snprint_blacklist_except(c, reply + maxlen - c); ++ again = ((c - reply) == maxlen); ++ if (again) { ++ reply = REALLOC(reply, maxlen *= 2); ++ continue; ++ } ++ c += snprint_hwtable(c, reply + maxlen - c, conf->hwtable); ++ again = ((c - reply) == maxlen); ++ if (again) { ++ reply = REALLOC(reply, maxlen *= 2); ++ continue; ++ } ++ c += snprint_mptable(c, reply + maxlen - c, conf->mptable); ++ again = ((c - reply) == maxlen); ++ if (again) ++ reply = REALLOC(reply, maxlen *= 2); ++ } ++ ++ printf("%s", reply); ++ FREE(reply); ++ return 0; ++} ++ + int + main (int argc, char *argv[]) + { +@@ -330,7 +380,7 @@ main (int argc, char *argv[]) + if (load_config(DEFAULT_CONFIGFILE)) + exit(1); + +- while ((arg = getopt(argc, argv, ":dhl::FfM:v:p:b:")) != EOF ) { ++ while ((arg = getopt(argc, argv, ":dhl::FfM:v:p:b:t")) != EOF ) { + switch(arg) { + case 1: printf("optarg : %s\n",optarg); + break; +@@ -373,6 +423,9 @@ main (int argc, char *argv[]) + usage(argv[0]); + } + break; ++ case 't': ++ dump_config(); ++ goto out; + case 'h': + usage(argv[0]); + case ':': +@@ -401,6 +454,7 @@ main (int argc, char *argv[]) + conf->dev_type = DEV_DEVMAP; + + } ++ dm_init(); + + if (conf->remove == FLUSH_ONE) { + if (conf->dev_type == DEV_DEVMAP) +diff --git a/multipath/multipath.8 b/multipath/multipath.8 +index 693872b..e72cc45 100644 +--- a/multipath/multipath.8 ++++ b/multipath/multipath.8 +@@ -6,7 +6,7 @@ multipath \- Device mapper target autoco + .RB [\| \-v\ \c + .IR verbosity \|] + .RB [\| \-d \|] +-.RB [\| \-h | \-l | \-ll | \-f | \-F \|] ++.RB [\| \-h | \-l | \-ll | \-f | \-t | \-F \|] + .RB [\| \-p\ \c + .BR failover | multibus | group_by_serial | group_by_prio | group_by_node_name \|] + .RB [\| device \|] +@@ -47,6 +47,9 @@ flush a multipath device map specified a + .B \-F + flush all unused multipath device maps + .TP ++.B \-t ++print internal hardware table to stdout ++.TP + .BI \-p " policy" + force maps to specified policy: + .RS 1.2i +@@ -76,6 +79,9 @@ is in the /dev/sdb (as shown by udev in + .I device + may alternatively be a multipath mapname + .SH "SEE ALSO" ++.BR multipathd (8), ++.BR multipath.conf (5), ++.BR kpartx (8), + .BR udev (8), + .BR dmsetup (8) + .BR hotplug (8) +diff --git a/multipath/multipath.conf.5 b/multipath/multipath.conf.5 +new file mode 100644 +index 0000000..1c1c0db +--- /dev/null ++++ b/multipath/multipath.conf.5 +@@ -0,0 +1,384 @@ ++.TH MULTIPATH.CONF 5 "30 November 2006" ++.SH NAME ++multipath.conf \- multipath daemon configuration file ++.SH DESCRIPTION ++.B "multipath.conf" ++is the configuration file for the multipath daemon. It is used to ++overwrite the built-in configuration table of \fBmultipathd\fP. ++Any line whose first non-white-space character is a '#' is considered ++a comment line. Empty lines are ignored. ++.SH SYNTAX ++The configuration file contains entries of the form: ++.RS ++.nf ++.ft B ++.sp ++
{ ++.RS ++.ft B ++ ++.I "..." ++.ft B ++ { ++.RS ++.ft B ++ ++.I "..." ++.RE ++} ++.RE ++} ++.ft R ++.fi ++.RE ++.LP ++Each \fIsection\fP contains one or more attributes or subsections. The ++recognized keywords for attributes or subsections depend on the ++section in which they occor. ++.LP ++The following \fIsection\fP keywords are recognized: ++.TP 17 ++.B defaults ++This section defines default values for attributes which are used ++whenever no specific setting is given. ++.TP ++.B blacklist ++This section defines which devices should be excluded from the ++multipath topology discovery. ++.TP ++.B blacklist_exceptions ++This section defines which devices should be included in the ++multipath topology discovery, despite being listed in the ++.I blacklist ++section. ++.TP ++.B multipaths ++This section defines the multipath topologies. They are indexed by a ++\fIWorld Wide Identifier\fR(wwid), which is the result of the ++\fIgetuid_callout\fR program. ++.TP ++.B devices ++This section defines the device-specific settings. ++.RE ++.LP ++.SH "defaults section" ++The ++.B defaults ++section recognizes the following keywords: ++.TP 17 ++.B polling_interval ++interval between two path checks in seconds; default is ++.I 5 ++.TP ++.B udev_dir ++directory where udev creates its device nodes; default is ++.I /dev ++.TP ++.B selector ++The default path selector algorithm to use; they are offered by the ++kernel multipath target. The only currently implemented is ++.I "round-robin 0" ++.TP ++.B path_grouping_policy ++The default path grouping policy to apply to unspecified ++multipaths. Possible values are ++.RS ++.TP 12 ++.B failover ++1 path per priority group ++.TP ++.B multibus ++all paths in 1 priority group ++.TP ++.B group_by_serial ++1 priority group per serial number ++.TP ++.B group_by_prio ++1 priority group per priority value. Priorities are determined by ++callout programs specified as a global, per-controller or ++per-multipath option in the configuration file. ++.TP ++.B group_by_node_name ++1 priority group per target node name. Target node names are fetched ++in /sys/class/fc_transport/target*/node_name. ++.TP ++Default value is \fImultibus\fR. ++.RE ++.TP ++.B getuid_callout ++The default program and args to callout to obtain a unique path ++identifier. Should be specified with an absolute path. Default value ++is ++.I /sbin/scsi_id -g -u -s ++.TP ++.B prio_callout ++The default program and args to callout to obtain a path priority ++value. The specified program will be executed and should return a ++numeric value specifying the relative priority of this path. Higher ++number have a higher priority. A '%n' in the command line will be expanded ++to the device name, a '%b' will be expanded to the device number in ++.I major:minor ++format. ++.I "none" ++is a valid value. Currently the following path priority programs are ++implemented: ++.RS ++.TP 12 ++.B mpath_prio_emc /dev/%n ++Generate the path priority for EMC arrays ++.TP ++.B mpath_prio_alua /dev/%n ++Generate the path priority based on the SCSI-3 ALUA settings. ++.TP ++.B mpath_prio_netapp /dev/%n ++Generate the path priority for NetApp arrays. ++.TP ++.B mpath_prio_tpc /dev/%n ++Generate the path priority for LSI/Engenio RDAC controller. ++.TP ++.B mpath_prio_hp_sw /dev/%n ++Generate the path priority for Compaq/HP controller in ++active/standby mode. ++.TP ++.B mpath_prio_hds_modular %b ++Generate the path priority for Hitachi HDS Modular storage arrays. ++.TP ++Default value is \fBnone\fR. ++.RE ++.TP ++.B features ++Specify any device-mapper features to be used. The most common of ++these features is ++.I "1 queue_if_no_path" ++Note that this can also be set via the ++.I no_path_retry ++keyword. ++.TP ++.B path_checker ++The default method used to determine the paths' state. Possible values ++are ++.RS ++.TP 12 ++.B readsector0 ++Read the first sector of the device ++.TP ++.B tur ++Issue a ++.I TEST UNIT READY ++command to the device. ++.TP ++.B emc_clariion ++Query the EMC Clariion specific EVPD page 0xC0 to determine the path ++state. ++.TP ++.B hp_sw ++Check the path state for HP storage arrays with Active/Standby firmware. ++.TP ++.B rdac ++Check the path state for LSI/Engenio RDAC storage controller. ++.TP ++.B directio ++Read the first sector with direct I/O. ++.TP ++Default value is \fIreadsector0\fR. ++.RE ++.TP ++.B failback ++Tell the daemon to manage path group failback, or not to. 0 or ++.I immediate ++means immediate failback, values >0 means deferred failback (in ++seconds). ++.I manual ++means no failback. Default value is ++.I manual ++.TP ++.B rr_min_io ++The number of IO to route to a path before switching to the next in ++the same path group. Default is ++.I 1000 ++.TP ++.B rr_weight ++If set to \fIpriorities\fR the multipath configurator will assign ++path weights as "path prio * rr_min_io". Possible values are ++.I priorities ++or ++.I uniform ++. Default is ++.I uniform ++.TP ++.B no_path_retry ++Specify the number of retries until disable queueing, or ++.I fail ++for immediate failure (no queueing), ++.I queue ++for never stop queueing. Default is 0. ++.TP ++.B user_friendly_names ++If set to ++.I yes ++, using the bindings file ++.I /var/lib/multipath/bindings ++to assign a persistent and unique alias to the multipath, in the form of mpath. ++If set to ++.I no ++use the WWID as the alias. In either case this be will ++be overriden by any specific aliases in the \fImultipaths\fR section. ++Default is ++.I no ++. ++.SH "blacklist section" ++The ++.I blacklist ++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. ++.LP ++The following keywords are recognized: ++.TP 17 ++.B wwid ++The \fIWorld Wide Identification\fR of a device. ++.TP ++.B devnode ++Regular expression of the device nodes to be excluded. ++.TP ++.B device ++Subsection for the device description. This subsection recognizes the ++.I vendor ++and ++.I product ++keywords. For a full description of these keywords please see the ++.I devices ++section description. ++.SH "blacklist_exceptions section" ++The ++.I blacklist_exceptions ++section is used to revert the actions of the ++.I blacklist ++section, ie to include specific device in the ++multipath topology. This allows to selectively include devices which ++would normally be excluded via the ++.I blacklist ++section. ++.LP ++The following keywords are recognized: ++.TP 17 ++.B wwid ++The \fIWorld Wide Identification\fR of a device. ++.TP ++.B devnode ++Regular expression of the device nodes to be excluded. ++.TP ++.B device ++Subsection for the device description. This subsection recognizes the ++.I vendor ++and ++.I product ++keywords. For a full description of these keywords please see the ++.I devices ++section description. ++.SH "multipaths section" ++The only recognized attribute for the ++.B multipaths ++section is the ++.I multipath ++subsection. ++.LP ++The ++.B multipath ++subsection recognizes the following attributes: ++.TP 17 ++.B wwid ++Index of the container. Mandatory for this subsection. ++.TP ++.B alias ++(Optional) symbolic name for the multipath map. ++.LP ++The following attributes are optional; if not set the default values ++are taken from the ++.I defaults ++section: ++.sp 1 ++.PD .1v ++.RS ++.TP 18 ++.B path_grouping_policy ++.TP ++.B path_checker ++.TP ++.B path_selector ++.TP ++.B failback ++.TP ++.B no_path_retry ++.TP ++.B rr_min_io ++.RE ++.PD ++.LP ++.SH "devices section" ++The only recognized attribute for the ++.B devices ++section is the ++.I device ++subsection. ++.LP ++The ++.I device ++subsection recognizes the following attributes: ++.TP 17 ++.B vendor ++(Mandatory) Vendor identifier ++.TP ++.B product ++(Mandatory) Product identifier ++.TP ++.B product_blacklist ++Product strings to blacklist for this vendor ++.TP ++.B hardware_handler ++(Optional) The hardware handler to use for this device type. ++The following hardware handler are implemented: ++.RS ++.TP 12 ++.B 1 emc ++Hardware handler for EMC storage arrays. ++.RE ++.LP ++The following attributes are optional; if not set the default values ++are taken from the ++.I defaults ++section: ++.sp 1 ++.PD .1v ++.RS ++.TP 18 ++.B path_grouping_policy ++.TP ++.B getuid_callout ++.TP ++.B path_selector ++.TP ++.B path_checker ++.TP ++.B features ++.TP ++.B prio_callout ++.TP ++.B failback ++.TP ++.B rr_weight ++.TP ++.B no_path_retry ++.TP ++.B rr_min_io ++.RE ++.PD ++.LP ++.SH "SEE ALSO" ++.BR udev (8), ++.BR dmsetup (8) ++.BR multipath (8) ++.BR multipathd (8) ++.SH AUTHORS ++.B multipath ++was developed by Christophe Varoqui, and others. +diff --git a/multipathd/Makefile b/multipathd/Makefile +index 8ad25ee..da351dc 100644 +--- a/multipathd/Makefile ++++ b/multipathd/Makefile +@@ -35,7 +35,6 @@ klibc: + + $(EXEC): clean $(OBJS) + $(CC) $(OBJS) -o $(EXEC) $(LDFLAGS) +- $(GZIP) $(EXEC).8 > $(EXEC).8.gz + + $(CHECKERSLIB)-glibc.a: + $(MAKE) -C $(checkersdir) BUILD=glibc glibc +@@ -48,12 +47,12 @@ install: + install -s -m 755 $(EXEC) $(DESTDIR)$(bindir) + install -d $(DESTDIR)$(rcdir) + install -d $(DESTDIR)$(mandir) +- install -m 644 $(EXEC).8.gz $(DESTDIR)$(mandir) ++ install -m 644 $(EXEC).8 $(DESTDIR)$(mandir) + + uninstall: + rm -f $(DESTDIR)$(bindir)/$(EXEC) + rm -f $(DESTDIR)$(rcdir)/$(EXEC) +- rm -f $(DESTDIR)$(mandir)/$(EXEC).8.gz ++ rm -f $(DESTDIR)$(mandir)/$(EXEC).8 + + clean: + $(MAKE) -C $(multipathdir) prepare DAEMON=1 +diff --git a/multipathd/cli.c b/multipathd/cli.c +index d786eef..587514f 100644 +--- a/multipathd/cli.c ++++ b/multipathd/cli.c +@@ -498,7 +498,7 @@ key_generator (const char * str, int sta + /* + * Loop through keywords for completion candidates + */ +- vector_foreach_slot_after (keys, kw, index) { ++ vector_foreach_slot (keys, kw, index) { + if (!strncmp(kw->str, str, len)) { + /* + * Discard keywords already in the command line +diff --git a/multipathd/main.c b/multipathd/main.c +index f2f9a96..94b0b95 100644 +--- a/multipathd/main.c ++++ b/multipathd/main.c +@@ -225,6 +225,8 @@ ev_add_map (char * devname, struct vecto + int map_present; + int r = 1; + ++ /* libsysfs seems to forget to terminate the string... */ ++ memset(dev_t, 0, BLK_DEV_SIZE); + if (sscanf(devname, "dm-%d", &minor) == 1 && + !sysfs_get_dev(sysfs_path, devname, dev_t, BLK_DEV_SIZE) && + sscanf(dev_t, "%d:%d", &major, &minor) == 2) +@@ -646,10 +648,12 @@ uev_trigger (struct uevent * uev, void * + lock(vecs->lock); + + /* +- * device map add/remove event ++ * device map event ++ * Add events are ignored here as the tables ++ * are not fully initialised then. + */ + if (!strncmp(devname, "dm-", 3)) { +- if (!strncmp(uev->action, "add", 3)) { ++ if (!strncmp(uev->action, "change", 6)) { + r = uev_add_map(devname, vecs); + goto out; + } +@@ -1397,6 +1401,7 @@ main (int argc, char *argv[]) + int err; + + logsink = 1; ++ dm_init(); + + if (getuid() != 0) { + fprintf(stderr, "need to be root\n"); +diff --git a/path_priority/pp_alua/Makefile b/path_priority/pp_alua/Makefile +index 983ffe3..6c58529 100644 +--- a/path_priority/pp_alua/Makefile ++++ b/path_priority/pp_alua/Makefile +@@ -35,20 +35,17 @@ glibc: $(OBJS) + klibc: $(OBJS) + $(CC) -static -o $(EXEC) $(OBJS) + +-install: $(BUILD) $(EXEC).8.gz ++install: $(EXEC) $(EXEC).8 + $(INSTALL) -s -m 755 $(EXEC) $(DESTDIR)$(bindir)/$(EXEC) +- $(INSTALL) -m 644 $(EXEC).8.gz $(DESTDIR)$(mandir)/$(EXEC).8.gz ++ $(INSTALL) -m 644 $(EXEC).8 $(DESTDIR)$(mandir)/$(EXEC).8 + + uninstall: + rm $(DESTDIR)$(bindir)/$(EXEC) +- rm $(DESTDIR)$(mandir)/$(EXEC).8.gz ++ rm $(DESTDIR)$(mandir)/$(EXEC).8 + + clean: + rm -f *.o *.gz $(EXEC) + +-$(EXEC).8.gz: $(EXEC).8 +- $(GZIP) $< >$@ +- + main.o: main.c rtpg.h spc3.h + + rtpg.o: rtpg.c rtpg.h spc3.h +diff --git a/path_priority/pp_emc/pp_emc.c b/path_priority/pp_emc/pp_emc.c +index dd58424..4031720 100644 +--- a/path_priority/pp_emc/pp_emc.c ++++ b/path_priority/pp_emc/pp_emc.c +@@ -60,8 +60,12 @@ int emc_clariion_prio(const char *dev) + + if ( /* Effective initiator type */ + sense_buffer[27] != 0x03 +- /* Failover mode should be set to 1 */ +- || (sense_buffer[28] & 0x07) != 0x04 ++ /* ++ * Failover mode should be set to 1 (PNR failover mode) ++ * or 4 (ALUA failover mode). ++ */ ++ || (((sense_buffer[28] & 0x07) != 0x04) && ++ ((sense_buffer[28] & 0x07) != 0x06)) + /* Arraycommpath should be set to 1 */ + || (sense_buffer[30] & 0x04) != 0x04) { + fprintf(stderr, "Path not correctly configured for failover"); +diff --git a/path_priority/pp_hp_sw/Makefile b/path_priority/pp_hp_sw/Makefile +new file mode 100644 +index 0000000..e7debf5 +--- /dev/null ++++ b/path_priority/pp_hp_sw/Makefile +@@ -0,0 +1,25 @@ ++EXEC = mpath_prio_hp_sw ++BUILD = glibc ++OBJS = pp_hp_sw.o ++ ++TOPDIR = ../.. ++include $(TOPDIR)/Makefile.inc ++ ++all: $(BUILD) ++ ++glibc: $(OBJS) ++ $(CC) -o $(EXEC) $(OBJS) $(LDFLAGS) ++ ++klibc: $(OBJS) ++ $(CC) -static -o $(EXEC) $(OBJS) ++ ++install: $(EXEC) ++ install -m 755 $(EXEC) $(DESTDIR)$(bindir)/$(EXEC) ++ ++uninstall: ++ rm $(DESTDIR)$(bindir)/$(EXEC) ++clean: ++ rm -f *.o $(EXEC) ++ ++%.o: %.c ++ $(CC) $(CFLAGS) -c -o $@ $< +diff --git a/path_priority/pp_hp_sw/pp_hp_sw.c b/path_priority/pp_hp_sw/pp_hp_sw.c +new file mode 100644 +index 0000000..e4a18b1 +--- /dev/null ++++ b/path_priority/pp_hp_sw/pp_hp_sw.c +@@ -0,0 +1,119 @@ ++/* ++ * Path priority checker for HP active/standby controller ++ * ++ * Check the path state and sort them into groups. ++ * There is actually a preferred path in the controller; ++ * we should ask HP on how to retrieve that information. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define TUR_CMD_LEN 6 ++#define SCSI_CHECK_CONDITION 0x2 ++#define SCSI_COMMAND_TERMINATED 0x22 ++#define SG_ERR_DRIVER_SENSE 0x08 ++#define RECOVERED_ERROR 0x01 ++#define NOT_READY 0x02 ++#define UNIT_ATTENTION 0x06 ++ ++#define HP_PATH_ACTIVE 0x04 ++#define HP_PATH_STANDBY 0x02 ++#define HP_PATH_FAILED 0x00 ++ ++#include "../../libmultipath/sg_include.h" ++ ++int hp_sw_prio(const char *dev) ++{ ++ unsigned char turCmdBlk[TUR_CMD_LEN] = { 0x00, 0, 0, 0, 0, 0 }; ++ unsigned char sb[128]; ++ struct sg_io_hdr io_hdr; ++ int ret = HP_PATH_FAILED; ++ int fd; ++ ++ fd = open(dev, O_RDWR|O_NONBLOCK); ++ ++ if (fd <= 0) { ++ fprintf(stderr, "Opening the device failed.\n"); ++ goto out; ++ } ++ ++ memset(&io_hdr, 0, sizeof (struct sg_io_hdr)); ++ io_hdr.interface_id = 'S'; ++ io_hdr.cmd_len = sizeof (turCmdBlk); ++ io_hdr.mx_sb_len = sizeof (sb); ++ io_hdr.dxfer_direction = SG_DXFER_NONE; ++ io_hdr.cmdp = turCmdBlk; ++ io_hdr.sbp = sb; ++ io_hdr.timeout = 60000; ++ io_hdr.pack_id = 0; ++ retry: ++ if (ioctl(fd, SG_IO, &io_hdr) < 0) { ++ fprintf(stderr, "sending tur command failed\n"); ++ goto out; ++ } ++ io_hdr.status &= 0x7e; ++ if ((0 == io_hdr.status) && (0 == io_hdr.host_status) && ++ (0 == io_hdr.driver_status)) { ++ /* Command completed normally, path is active */ ++ ret = HP_PATH_ACTIVE; ++ } ++ ++ if ((SCSI_CHECK_CONDITION == io_hdr.status) || ++ (SCSI_COMMAND_TERMINATED == io_hdr.status) || ++ (SG_ERR_DRIVER_SENSE == (0xf & io_hdr.driver_status))) { ++ if (io_hdr.sbp && (io_hdr.sb_len_wr > 2)) { ++ int sense_key, asc, asq; ++ unsigned char * sense_buffer = io_hdr.sbp; ++ if (sense_buffer[0] & 0x2) { ++ sense_key = sense_buffer[1] & 0xf; ++ asc = sense_buffer[2]; ++ asq = sense_buffer[3]; ++ } else { ++ sense_key = sense_buffer[2] & 0xf; ++ asc = sense_buffer[12]; ++ asq = sense_buffer[13]; ++ } ++ if(RECOVERED_ERROR == sense_key) ++ ret = HP_PATH_ACTIVE; ++ if(NOT_READY == sense_key) { ++ if (asc == 0x04 && asq == 0x02) { ++ /* This is a standby path */ ++ ret = HP_PATH_STANDBY; ++ } ++ } ++ if(UNIT_ATTENTION == sense_key) { ++ if (asc == 0x29) { ++ /* Retry for device reset */ ++ goto retry; ++ } ++ } ++ } ++ } ++ ++ close(fd); ++ ++out: ++ return(ret); ++} ++ ++int ++main (int argc, char **argv) ++{ ++ int prio; ++ if (argc != 2) { ++ fprintf(stderr, "Arguments wrong!\n"); ++ prio = 0; ++ } else ++ prio = hp_sw_prio(argv[1]); ++ ++ printf("%d\n", prio); ++ exit(0); ++} ++ diff --git a/multipath-tools-no-gz-for-manpage b/multipath-tools-no-gz-for-manpage deleted file mode 100644 index e07c7ea..0000000 --- a/multipath-tools-no-gz-for-manpage +++ /dev/null @@ -1,140 +0,0 @@ -diff --git a/devmap_name/Makefile b/devmap_name/Makefile -index 380c85b..5551c9b 100644 ---- a/devmap_name/Makefile -+++ b/devmap_name/Makefile -@@ -22,21 +22,19 @@ prepare: - - glibc: prepare $(OBJS) - $(CC) $(OBJS) -o $(EXEC) $(LDFLAGS) -- $(GZIP) $(EXEC).8 > $(EXEC).8.gz - - klibc: prepare $(OBJS) - $(CC) -static -o $(EXEC) $(OBJS) -- $(GZIP) $(EXEC).8 > $(EXEC).8.gz - - install: - install -d $(DESTDIR)$(bindir) - install -s -m 755 $(EXEC) $(DESTDIR)$(bindir)/ - install -d $(DESTDIR)$(mandir) -- install -m 644 $(EXEC).8.gz $(DESTDIR)$(mandir) -+ install -m 644 $(EXEC).8 $(DESTDIR)$(mandir) - - uninstall: - rm $(DESTDIR)$(bindir)/$(EXEC) -- rm $(DESTDIR)$(mandir)/$(EXEC).8.gz -+ rm $(DESTDIR)$(mandir)/$(EXEC).8 - - clean: - rm -f core *.o $(EXEC) *.gz -diff --git a/kpartx/Makefile b/kpartx/Makefile -index bf6e6c1..691ecbe 100644 ---- a/kpartx/Makefile -+++ b/kpartx/Makefile -@@ -27,11 +27,9 @@ prepare: - - glibc: prepare $(OBJS) - $(CC) $(OBJS) -o $(EXEC) $(LDFLAGS) -- $(GZIP) $(EXEC).8 > $(EXEC).8.gz -- -+ - klibc: prepare $(OBJS) - $(CC) -static -o $(EXEC) $(CRT0) $(OBJS) $(KLIBC) $(LIBGCC) -- $(GZIP) $(EXEC).8 > $(EXEC).8.gz - - $(MULTIPATHLIB)-$(BUILD).a: - make -C $(multipathdir) BUILD=$(BUILD) -@@ -40,7 +38,7 @@ install: - install -d $(DESTDIR)$(bindir) - install -s -m 755 $(EXEC) $(DESTDIR)$(bindir) - install -d $(DESTDIR)$(mandir) -- install -m 644 $(EXEC).8.gz $(DESTDIR)$(mandir) -+ install -m 644 $(EXEC).8 $(DESTDIR)$(mandir) - - uninstall: - rm -f $(DESTDIR)$(bindir)/$(EXEC) -diff --git a/multipath/Makefile b/multipath/Makefile -index 646dfc2..add1972 100644 ---- a/multipath/Makefile -+++ b/multipath/Makefile -@@ -25,11 +25,9 @@ prepare: - - glibc: prepare $(OBJS) - $(CC) $(OBJS) -o $(EXEC) $(LDFLAGS) -- $(GZIP) $(EXEC).8 > $(EXEC).8.gz - - klibc: prepare $(OBJS) - $(CC) -static -o $(EXEC) $(CRT0) $(OBJS) $(KLIBC) $(LIBGCC) -- $(GZIP) $(EXEC).8 > $(EXEC).8.gz - - $(CHECKERSLIB)-$(BUILD).a: - make -C $(checkersdir) BUILD=$(BUILD) $(BUILD) -@@ -43,12 +41,12 @@ install: - install -d $(DESTDIR)/etc/udev/rules.d - install -m 644 multipath.rules $(DESTDIR)/etc/udev/rules.d/ - install -d $(DESTDIR)$(mandir) -- install -m 644 $(EXEC).8.gz $(DESTDIR)$(mandir) -+ install -m 644 $(EXEC).8 $(DESTDIR)$(mandir) - - uninstall: - rm $(DESTDIR)/etc/udev/rules.d/multipath.rules - rm $(DESTDIR)$(bindir)/$(EXEC) -- rm $(DESTDIR)$(mandir)/$(EXEC).8.gz -+ rm $(DESTDIR)$(mandir)/$(EXEC).8 - - clean: - rm -f core *.o $(EXEC) *.gz -diff --git a/multipathd/Makefile b/multipathd/Makefile -index 8ad25ee..da351dc 100644 ---- a/multipathd/Makefile -+++ b/multipathd/Makefile -@@ -35,7 +35,6 @@ klibc: - - $(EXEC): clean $(OBJS) - $(CC) $(OBJS) -o $(EXEC) $(LDFLAGS) -- $(GZIP) $(EXEC).8 > $(EXEC).8.gz - - $(CHECKERSLIB)-glibc.a: - $(MAKE) -C $(checkersdir) BUILD=glibc glibc -@@ -48,12 +47,12 @@ install: - install -s -m 755 $(EXEC) $(DESTDIR)$(bindir) - install -d $(DESTDIR)$(rcdir) - install -d $(DESTDIR)$(mandir) -- install -m 644 $(EXEC).8.gz $(DESTDIR)$(mandir) -+ install -m 644 $(EXEC).8 $(DESTDIR)$(mandir) - - uninstall: - rm -f $(DESTDIR)$(bindir)/$(EXEC) - rm -f $(DESTDIR)$(rcdir)/$(EXEC) -- rm -f $(DESTDIR)$(mandir)/$(EXEC).8.gz -+ rm -f $(DESTDIR)$(mandir)/$(EXEC).8 - - clean: - $(MAKE) -C $(multipathdir) prepare DAEMON=1 -diff --git a/path_priority/pp_alua/Makefile b/path_priority/pp_alua/Makefile -index 983ffe3..c38990e 100644 ---- a/path_priority/pp_alua/Makefile -+++ b/path_priority/pp_alua/Makefile -@@ -35,20 +35,17 @@ glibc: $(OBJS) - klibc: $(OBJS) - $(CC) -static -o $(EXEC) $(OBJS) - --install: $(BUILD) $(EXEC).8.gz -+install: $(BUILD) $(EXEC).8 - $(INSTALL) -s -m 755 $(EXEC) $(DESTDIR)$(bindir)/$(EXEC) -- $(INSTALL) -m 644 $(EXEC).8.gz $(DESTDIR)$(mandir)/$(EXEC).8.gz -+ $(INSTALL) -m 644 $(EXEC).8 $(DESTDIR)$(mandir)/$(EXEC).8 - - uninstall: - rm $(DESTDIR)$(bindir)/$(EXEC) -- rm $(DESTDIR)$(mandir)/$(EXEC).8.gz -+ rm $(DESTDIR)$(mandir)/$(EXEC).8 - - clean: - rm -f *.o *.gz $(EXEC) - --$(EXEC).8.gz: $(EXEC).8 -- $(GZIP) $< >$@ -- - main.o: main.c rtpg.h spc3.h - - rtpg.o: rtpg.c rtpg.h spc3.h diff --git a/multipath-tools-online-device.patch b/multipath-tools-online-device.patch deleted file mode 100644 index b8970a3..0000000 --- a/multipath-tools-online-device.patch +++ /dev/null @@ -1,107 +0,0 @@ -diff --git a/libmultipath/discovery.c b/libmultipath/discovery.c ---- a/libmultipath/discovery.c -+++ b/libmultipath/discovery.c -@@ -209,6 +209,44 @@ sysfs_get_size (char * sysfs_path, char - return 0; - } - -+int -+sysfs_get_online (char * sysfs_path, char * dev) -+{ -+ char attr_path[SYSFS_PATH_SIZE]; -+ char attr_buff[SYSFS_PATH_SIZE]; -+ long r; -+ char *p; -+ -+ if (safe_sprintf(attr_path, "%s/block/%s/online", sysfs_path, dev)) -+ return -1; -+ -+ if (0 > sysfs_read_attribute_value(attr_path, attr_buff, sizeof(attr_buff))) -+ return -1; -+ -+ r = strtol(attr_buff,&p, 10); -+ -+ if (attr_buff != p && r > 0) -+ return 1; -+ -+ return 0; -+} -+ -+int writeattr (char *path, const char *value) -+{ -+ struct sysfs_attribute *attr; -+ int retval; -+ -+ attr = sysfs_open_attribute(path); -+ if (!attr) -+ return -1; -+ -+ retval = sysfs_write_attribute(attr, value, strlen(value)); -+ -+ sysfs_close_attribute(attr); -+ -+ return retval > 0? -1 : 0; -+} -+ - /* - * udev might be slow creating node files : wait - */ -@@ -565,6 +603,30 @@ sysfs_pathinfo(struct path * curpath) - return 0; - } - -+extern int -+online_device(struct path *curpath) -+{ -+ int online; -+ char attr_path[FILE_NAME_SIZE]; -+ -+ online = sysfs_get_online(sysfs_path, curpath->dev); -+ if (online > 0) -+ return 0; -+ else if(online < 0) -+ return 1; -+ -+ if(safe_sprintf(attr_path, "%s/block/%s/device/online", -+ sysfs_path, curpath->dev)) { -+ condlog(0, "attr_path too small"); -+ return 1; -+ } -+ -+ condlog(1,"%s: setting device online", curpath->dev); -+ writeattr(attr_path, "1"); -+ -+ return 0; -+} -+ - static int - apply_format (char * string, char * cmd, struct path * pp) - { -diff --git a/libmultipath/discovery.h b/libmultipath/discovery.h ---- a/libmultipath/discovery.h -+++ b/libmultipath/discovery.h -@@ -28,6 +28,7 @@ int sysfs_get_dev (char * sysfs_path, ch - - int sysfs_get_size (char * sysfs_path, char * dev, unsigned long long *); - int path_discovery (vector pathvec, struct config * conf, int flag); -+int online_device (struct path *curpath); - - void basename (char *, char *); - int get_serial (char * buff, int fd); -diff --git a/multipathd/main.c b/multipathd/main.c ---- a/multipathd/main.c -+++ b/multipathd/main.c -@@ -1185,6 +1185,13 @@ checkerloop (void *ap) - condlog(0, "%s: checkfn is void", pp->dev); - continue; - } -+ -+ /* -+ * Set the device online for checkers -+ * to run successfully -+ */ -+ online_device(pp); -+ - newstate = pp->checkfn(pp->fd, checker_msg, - &pp->checker_context); - diff --git a/multipath-tools-strip.patch b/multipath-tools-strip.patch deleted file mode 100644 index c90c069..0000000 --- a/multipath-tools-strip.patch +++ /dev/null @@ -1,121 +0,0 @@ ---- devmap_name/Makefile -+++ devmap_name/Makefile -@@ -28,7 +28,7 @@ - - install: - install -d $(DESTDIR)$(bindir) -- install -s -m 755 $(EXEC) $(DESTDIR)$(bindir)/ -+ install -m 755 $(EXEC) $(DESTDIR)$(bindir)/ - install -d $(DESTDIR)$(mandir) - install -m 644 $(EXEC).8 $(DESTDIR)$(mandir) - ---- kpartx/Makefile -+++ kpartx/Makefile -@@ -36,7 +36,7 @@ - - install: - install -d $(DESTDIR)$(bindir) -- install -s -m 755 $(EXEC) $(DESTDIR)$(bindir) -+ install -m 755 $(EXEC) $(DESTDIR)$(bindir) - install -d $(DESTDIR)$(mandir) - install -m 644 $(EXEC).8 $(DESTDIR)$(mandir) - ---- multipath/Makefile -+++ multipath/Makefile -@@ -37,7 +37,7 @@ - - install: - install -d $(DESTDIR)$(bindir) -- install -s -m 755 $(EXEC) $(DESTDIR)$(bindir)/ -+ install -m 755 $(EXEC) $(DESTDIR)$(bindir)/ - install -d $(DESTDIR)/etc/udev/rules.d - install -m 644 multipath.rules $(DESTDIR)/etc/udev/rules.d/ - install -d $(DESTDIR)$(mandir) ---- multipathd/Makefile -+++ multipathd/Makefile -@@ -44,7 +44,7 @@ - - install: - install -d $(DESTDIR)$(bindir) -- install -s -m 755 $(EXEC) $(DESTDIR)$(bindir) -+ install -m 755 $(EXEC) $(DESTDIR)$(bindir) - install -d $(DESTDIR)$(rcdir) - install -d $(DESTDIR)$(mandir) - install -m 644 $(EXEC).8 $(DESTDIR)$(mandir) ---- path_priority/pp_alua/Makefile -+++ path_priority/pp_alua/Makefile -@@ -36,7 +36,7 @@ - $(CC) -static -o $(EXEC) $(OBJS) - - install: $(BUILD) $(EXEC).8 -- $(INSTALL) -s -m 755 $(EXEC) $(DESTDIR)$(bindir)/$(EXEC) -+ $(INSTALL) -m 755 $(EXEC) $(DESTDIR)$(bindir)/$(EXEC) - $(INSTALL) -m 644 $(EXEC).8 $(DESTDIR)$(mandir)/$(EXEC).8 - - uninstall: ---- path_priority/pp_balance_units/Makefile -+++ path_priority/pp_balance_units/Makefile -@@ -35,7 +35,7 @@ - - install: - install -d $(DESTDIR)$(bindir) -- install -s -m 755 $(EXEC) $(DESTDIR)$(bindir)/ -+ install -m 755 $(EXEC) $(DESTDIR)$(bindir)/ - - uninstall: - rm $(DESTDIR)$(bindir)/$(EXEC) ---- path_priority/pp_emc/Makefile -+++ path_priority/pp_emc/Makefile -@@ -14,7 +14,7 @@ - $(CC) -static -o $(EXEC) $(OBJS) - - install: $(EXEC) -- install -s -m 755 $(EXEC) $(DESTDIR)$(bindir)/$(EXEC) -+ install -m 755 $(EXEC) $(DESTDIR)$(bindir)/$(EXEC) - - uninstall: - rm $(DESTDIR)$(bindir)/$(EXEC) ---- path_priority/pp_hds_modular/Makefile -+++ path_priority/pp_hds_modular/Makefile -@@ -14,7 +14,7 @@ - $(CC) -static -o $(EXEC) $(OBJS) - - install: $(EXEC) -- install -s -m 755 $(EXEC) $(DESTDIR)$(bindir)/$(EXEC) -+ install -m 755 $(EXEC) $(DESTDIR)$(bindir)/$(EXEC) - - uninstall: - rm $(DESTDIR)$(bindir)/$(EXEC) ---- path_priority/pp_netapp/Makefile -+++ path_priority/pp_netapp/Makefile -@@ -14,7 +14,7 @@ - $(CC) -static -o $(EXEC) $(OBJS) - - install: $(EXEC) -- install -s -m 755 $(EXEC) $(DESTDIR)$(bindir)/$(EXEC) -+ install -m 755 $(EXEC) $(DESTDIR)$(bindir)/$(EXEC) - - uninstall: - rm $(DESTDIR)$(bindir)/$(EXEC) ---- path_priority/pp_random/Makefile -+++ path_priority/pp_random/Makefile -@@ -14,7 +14,7 @@ - $(CC) -static -o $(EXEC) $(OBJS) - - install: $(EXEC) -- install -s -m 755 $(EXEC) $(DESTDIR)$(bindir)/$(EXEC) -+ install -m 755 $(EXEC) $(DESTDIR)$(bindir)/$(EXEC) - - uninstall: - rm $(DESTDIR)$(bindir)/$(EXEC) ---- path_priority/pp_tpc/Makefile -+++ path_priority/pp_tpc/Makefile -@@ -14,7 +14,7 @@ - $(CC) -static -o $(EXEC) $(OBJS) - - install: $(EXEC) -- install -s -m 755 $(EXEC) $(DESTDIR)$(bindir)/$(EXEC) -+ install -m 755 $(EXEC) $(DESTDIR)$(bindir)/$(EXEC) - - uninstall: - rm $(DESTDIR)$(bindir)/$(EXEC) diff --git a/multipath-tools-suse-update b/multipath-tools-suse-update new file mode 100644 index 0000000..e8acdca --- /dev/null +++ b/multipath-tools-suse-update @@ -0,0 +1,3096 @@ + Makefile.inc | 1 - + devmap_name/Makefile | 5 +- + kpartx/Makefile | 10 +- + kpartx/devmapper.c | 29 ++ + kpartx/devmapper.h | 1 + + kpartx/dos.c | 7 +- + kpartx/kpartx.c | 125 ++++++++-- + kpartx/kpartx.h | 3 + + kpartx/kpartx_id | 110 ++++++++ + libcheckers/Makefile | 4 +- + libmultipath/Makefile | 6 +- + libmultipath/config.h | 1 + + libmultipath/configure.c | 4 +- + libmultipath/discovery.c | 422 ++++++++++++------------------- + libmultipath/discovery.h | 7 +- + libmultipath/list.h | 289 +++++++++++++++++++++ + libmultipath/print.c | 47 ++-- + libmultipath/structs.h | 15 +- + libmultipath/structs_vec.c | 7 +- + libmultipath/sysfs.c | 405 +++++++++++++++++++++++++++++ + libmultipath/sysfs.h | 20 ++ + libmultipath/uevent.c | 99 +++++--- + libmultipath/util.c | 52 ++++ + libmultipath/util.h | 4 +- + multipath/Makefile | 10 +- + multipath/main.c | 11 +- + multipath/multipath.init.suse | 104 ++++++++ + multipathd/71-multipath.rules | 27 ++ + multipathd/Makefile | 7 +- + multipathd/cli_handlers.c | 17 ++- + multipathd/main.c | 92 +++---- + multipathd/main.h | 2 +- + multipathd/multipathd.init.suse | 155 +++++++++++ + path_priority/pp_alua/Makefile | 3 +- + path_priority/pp_balance_units/Makefile | 6 +- + path_priority/pp_emc/Makefile | 2 +- + path_priority/pp_hds_modular/Makefile | 2 +- + path_priority/pp_netapp/Makefile | 2 +- + path_priority/pp_random/Makefile | 2 +- + path_priority/pp_tpc/Makefile | 2 +- + 40 files changed, 1685 insertions(+), 432 deletions(-) + +diff --git a/Makefile.inc b/Makefile.inc +index fe6cbdf..786565c 100644 +--- a/Makefile.inc ++++ b/Makefile.inc +@@ -17,7 +17,6 @@ ifeq ($(strip $(BUILD)),klibc) + CC = klcc + klibcdir = /usr/lib/klibc + libdm = $(klibcdir)/lib/libdevmapper.a +- libsysfs = $(klibcdir)/lib/libsysfs.a + endif + + prefix = +diff --git a/devmap_name/Makefile b/devmap_name/Makefile +index 8b0c678..57051d9 100644 +--- a/devmap_name/Makefile ++++ b/devmap_name/Makefile +@@ -18,7 +18,7 @@ EXEC = devmap_name + all: $(BUILD) + + prepare: +- rm -f core *.o *.gz ++ rm -f core *.o + + glibc: prepare $(OBJS) + $(CC) $(OBJS) -o $(EXEC) $(LDFLAGS) +@@ -34,7 +34,6 @@ install: $(EXEC) $(EXEC).8 + + uninstall: + rm $(DESTDIR)$(bindir)/$(EXEC) +- rm $(DESTDIR)$(mandir)/$(EXEC).8 + + clean: +- rm -f core *.o $(EXEC) *.gz ++ rm -f core *.o $(EXEC) +diff --git a/kpartx/Makefile b/kpartx/Makefile +index 522a6a0..9832467 100644 +--- a/kpartx/Makefile ++++ b/kpartx/Makefile +@@ -23,7 +23,7 @@ EXEC = kpartx + all: $(BUILD) + + prepare: +- rm -f core *.o *.gz ++ rm -f core *.o + + glibc: prepare $(OBJS) + $(CC) $(OBJS) -o $(EXEC) $(LDFLAGS) +@@ -34,15 +34,15 @@ klibc: prepare $(OBJS) + $(MULTIPATHLIB)-$(BUILD).a: + make -C $(multipathdir) BUILD=$(BUILD) + +-install: $(EXEC) $(EXEC).8 ++install: $(EXEC) kpartx_id $(EXEC).8 + install -d $(DESTDIR)$(bindir) +- install -s -m 755 $(EXEC) $(DESTDIR)$(bindir) ++ install -m 755 $(EXEC) $(DESTDIR)$(bindir) ++ install -m 755 kpartx_id $(DESTDIR)$(bindir) + install -d $(DESTDIR)$(mandir) + install -m 644 $(EXEC).8 $(DESTDIR)$(mandir) + + uninstall: + rm -f $(DESTDIR)$(bindir)/$(EXEC) +- rm -f $(DESTDIR)$(mandir)/$(EXEC).8.gz + + clean: +- rm -f core *.o $(EXEC) *.gz ++ rm -f core *.o $(EXEC) +diff --git a/kpartx/devmapper.c b/kpartx/devmapper.c +index 4b228ed..e5da022 100644 +--- a/kpartx/devmapper.c ++++ b/kpartx/devmapper.c +@@ -219,3 +219,32 @@ out: + dm_task_destroy(dmt); + return uuid; + } ++ ++int ++dm_devn (char * mapname, int *major, int *minor) ++{ ++ int r = 1; ++ struct dm_task *dmt; ++ struct dm_info info; ++ ++ if (!(dmt = dm_task_create(DM_DEVICE_INFO))) ++ return 0; ++ ++ if (!dm_task_set_name(dmt, mapname)) ++ goto out; ++ ++ if (!dm_task_run(dmt)) ++ goto out; ++ ++ if (!dm_task_get_info(dmt, &info)) ++ goto out; ++ ++ *major = info.major; ++ *minor = info.minor; ++ ++ r = 0; ++out: ++ dm_task_destroy(dmt); ++ return r; ++} ++ +diff --git a/kpartx/devmapper.h b/kpartx/devmapper.h +index e20e456..ccdbead 100644 +--- a/kpartx/devmapper.h ++++ b/kpartx/devmapper.h +@@ -6,3 +6,4 @@ int dm_map_present (char *); + char * dm_mapname(int major, int minor); + dev_t dm_get_first_dep(char *devname); + char * dm_mapuuid(int major, int minor); ++int dm_devn (char * mapname, int *major, int *minor); +diff --git a/kpartx/dos.c b/kpartx/dos.c +index a707423..1691105 100644 +--- a/kpartx/dos.c ++++ b/kpartx/dos.c +@@ -16,7 +16,7 @@ is_extended(int type) { + } + + static int +-read_extended_partition(int fd, struct partition *ep, ++read_extended_partition(int fd, struct partition *ep, int en, + struct slice *sp, int ns) + { + struct partition p; +@@ -53,6 +53,7 @@ read_extended_partition(int fd, struct p + if (n < ns) { + sp[n].start = here + le32_to_cpu(p.start_sect); + sp[n].size = le32_to_cpu(p.nr_sects); ++ sp[n].container = en + 1; + n++; + } else { + fprintf(stderr, +@@ -97,9 +98,7 @@ read_dos_pt(int fd, struct slice all, st + break; + } + if (is_extended(p.sys_type)) { +- n += read_extended_partition(fd, &p, sp+n, ns-n); +- /* hide the extended partition itself */ +- sp[i].size = 0; ++ n += read_extended_partition(fd, &p, i, sp+n, ns-n); + } + } + return n; +diff --git a/kpartx/kpartx.c b/kpartx/kpartx.c +index b406b95..c48a59c 100644 +--- a/kpartx/kpartx.c ++++ b/kpartx/kpartx.c +@@ -182,7 +182,7 @@ get_hotplug_device(void) + + int + main(int argc, char **argv){ +- int fd, i, j, k, n, op, off, arg; ++ int fd, i, j, k, m, n, op, off, arg, c, d; + struct slice all; + struct pt *ptp; + enum action what = LIST; +@@ -353,30 +353,47 @@ main(int argc, char **argv){ + else + continue; + +- /* +- * test for overlap, as in the case of an extended partition +- * zero their size to avoid mapping +- */ +- for (j=0; j slices[j].start && +- slices[k].start < slices[j].start + +- slices[j].size) +- slices[j].size = 0; +- } +- } +- + switch(what) { + case LIST: +- for (j = 0; j < n; j++) { ++ for (j = 0, c = 0, m = 0; j < n; j++) { + if (slices[j].size == 0) + continue; ++ if (slices[j].container > 0) { ++ c++; ++ continue; ++ } ++ ++ slices[j].minor = m++; + + printf("%s%s%d : 0 %lu %s %lu\n", + mapname, delim, j+1, + (unsigned long) slices[j].size, device, + (unsigned long) slices[j].start); + } ++ /* Loop to resolve contained slices */ ++ d = c; ++ while (c) { ++ for (j = 0; j < n; j++) { ++ if (slices[j].size == 0) ++ continue; ++ if (slices[j].minor > 0) ++ continue; ++ if (slices[j].container == 0) ++ continue; ++ slices[j].minor = m++; ++ ++ printf("%s%s%d : 0 %lu /dev/dm-%d %lu\n", ++ mapname, delim, j+1, ++ (unsigned long) slices[j].size, ++ slices[j].minor, ++ (unsigned long) slices[j].start); ++ c--; ++ } ++ /* Terminate loop if nothing more to resolve */ ++ if (d == c) ++ break; ++ } ++ + break; + + case DELETE: +@@ -410,10 +427,16 @@ main(int argc, char **argv){ + break; + + case ADD: +- for (j=0; j 0) { ++ c++; ++ continue; ++ } ++ + if (safe_sprintf(partname, "%s%s%d", + mapname, delim, j+1)) { + fprintf(stderr, "partname too small\n"); +@@ -437,10 +460,74 @@ main(int argc, char **argv){ + dm_simplecmd(DM_DEVICE_RESUME, + partname); + ++ dm_devn(partname, &slices[j].major, ++ &slices[j].minor); ++ + if (verbose) +- printf("add map %s : 0 %lu %s %s\n", +- partname, slices[j].size, +- DM_TARGET, params); ++ printf("add map %s (%d:%d): 0 %lu %s %s\n", ++ partname, slices[j].major, ++ slices[j].minor, slices[j].size, ++ DM_TARGET, params); ++ } ++ /* Loop to resolve contained slices */ ++ d = c; ++ while (c) { ++ for (j = 0; j < n; j++) { ++ int k = slices[j].container - 1; ++ ++ if (slices[j].size == 0) ++ continue; ++ ++ /* Skip all existing slices */ ++ if (slices[j].minor > 0) ++ continue; ++ ++ /* Skip all simple slices */ ++ if (k < 0) ++ continue; ++ ++ /* Check container slice */ ++ if (slices[k].size == 0) ++ fprintf(stderr, "Invalid slice %d\n", ++ k); ++ ++ if (safe_sprintf(partname, "%s%s%d", ++ mapname, delim, j+1)) { ++ fprintf(stderr, "partname too small\n"); ++ exit(1); ++ } ++ strip_slash(partname); ++ ++ if (safe_sprintf(params, "%d:%d %lu", ++ slices[k].major, ++ slices[k].minor, ++ (unsigned long)slices[j].start)) { ++ fprintf(stderr, "params too small\n"); ++ exit(1); ++ } ++ ++ op = (dm_map_present(partname) ? ++ DM_DEVICE_RELOAD : DM_DEVICE_CREATE); ++ ++ dm_addmap(op, partname, DM_TARGET, params, ++ slices[j].size, uuid, j+1); ++ ++ if (op == DM_DEVICE_RELOAD) ++ dm_simplecmd(DM_DEVICE_RESUME, ++ partname); ++ ++ dm_devn(partname, &slices[j].major, ++ &slices[j].minor); ++ ++ if (verbose) ++ printf("add map %s : 0 %lu %s %s\n", ++ partname, slices[j].size, ++ DM_TARGET, params); ++ c--; ++ } ++ /* Terminate loop */ ++ if (d == c) ++ break; + } + break; + +diff --git a/kpartx/kpartx.h b/kpartx/kpartx.h +index 6a715de..b49c543 100644 +--- a/kpartx/kpartx.h ++++ b/kpartx/kpartx.h +@@ -22,6 +22,9 @@ + struct slice { + unsigned long start; + unsigned long size; ++ int container; ++ int major; ++ int minor; + }; + + typedef int (ptreader)(int fd, struct slice all, struct slice *sp, int ns); +diff --git a/kpartx/kpartx_id b/kpartx/kpartx_id +new file mode 100644 +index 0000000..c599e5d +--- /dev/null ++++ b/kpartx/kpartx_id +@@ -0,0 +1,110 @@ ++#!/bin/bash ++# ++# kpartx_id ++# ++# Generates ID information for device-mapper tables. ++# ++# Copyright (C) 2006 SUSE Linux Products GmbH ++# Author: ++# Hannes Reinecke ++# ++# ++# 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 version 2 of the License. ++# ++# This script generates ID information used to generate persistent symlinks. ++# It relies on the UUID strings generated by the various programs; the name ++# of the tables are of no consequence. ++# ++# Please note that dmraid does not provide the UUIDs (yet); a patch has been ++# sent upstream but has not been accepted yet. ++# ++ ++DMSETUP=/sbin/dmsetup ++ ++MAJOR=$1 ++MINOR=$2 ++ ++if [ -z "$MAJOR" -o -z "$MINOR" ]; then ++ echo "usage: $0 major minor" ++ exit 1; ++fi ++ ++# Device-mapper not installed; not an error ++if [ ! -x $DMSETUP ] ; then ++ exit 0 ++fi ++ ++# Get the table info ++tblinfo=$($DMSETUP info -c --noheadings -o name,uuid -j $MAJOR -m $MINOR) ++if [ $? -ne 0 ] || [ -z "$tblinfo" ]; then ++ exit $? ++fi ++ ++set -- $(IFS=":"; echo $tblinfo) ++tblname=$1 ++tbluuid=$2 ++ ++if [ -z "$tbluuid" ] ; then ++ exit 0 ++fi ++ ++# Table UUIDs are always '-'. ++dmuuid=${tbluuid#*-} ++dmtbl=${tbluuid%%-*} ++dmpart=${dmtbl#part} ++# kpartx types are 'part' ++if [ "$dmpart" == "$dmtbl" ] ; then ++ dmpart= ++else ++ dmtbl=part ++fi ++ ++# Set the name of the table. We're only interested in dmraid, ++# multipath, and kpartx tables; everything else is ignored. ++if [ "$dmtbl" == "part" ] ; then ++ # The name of the kpartx table is the name of the parent table ++ dmname=$($DMSETUP info -c --noheadings -o name -u $dmuuid) ++ # We need the dependencies of the parent table to figure out ++ # the type if the parent is a multipath table ++ case "$dmparent" in ++ mpath-*) ++ dmdeps=$($DMSETUP deps -u $dmuuid) ++ ;; ++ esac ++elif [ "$dmtbl" == "mpath" ] ; then ++ dmname=$tblname ++ # We need the dependencies of the table to figure out the type ++ dmdeps=$($DMSETUP deps -u $tbluuid) ++elif [ "$dmtbl" == "dmraid" ] ; then ++ dmname=$tblname ++fi ++ ++if [ -z "$dmname" ] ; then ++ exit 0 ++fi ++ ++echo "ID_DM_TABLE=$dmtbl" ++echo "ID_DM_NAME=$dmname" ++[ -n "$dmpart" ] && echo "ID_DM_PART=$dmpart" ++ ++# Figure out the type of the map. For non-multipath maps it's ++# always 'raid'. ++if [ -n "$dmdeps" ] ; then ++ case "$dmdeps" in ++ *\(94,*) ++ echo "ID_DM_TYPE=dasd" ++ ;; ++ *\(9*) ++ echo "ID_DM_TYPE=raid" ++ ;; ++ *) ++ echo "ID_DM_TYPE=scsi" ++ ;; ++ esac ++else ++ echo "ID_DM_TYPE=raid" ++fi ++ ++exit 0 +diff --git a/libcheckers/Makefile b/libcheckers/Makefile +index 6340a68..bdd423f 100644 +--- a/libcheckers/Makefile ++++ b/libcheckers/Makefile +@@ -11,7 +11,7 @@ OBJS = libsg.o checkers.o readsector0.o + all: $(BUILD) + + prepare: +- @file *-$(BUILD).a >/dev/null 2>&1 || rm -f core *.o *.gz ++ @file *-$(BUILD).a >/dev/null 2>&1 || rm -f core *.o + + klibc: prepare $(OBJS) + ar rs libcheckers-klibc.a *.o +@@ -24,4 +24,4 @@ install: + uninstall: + + clean: +- rm -f core *.a *.o *.gz ++ rm -f core *.a *.o +diff --git a/libmultipath/Makefile b/libmultipath/Makefile +index ef561a8..a6ad89b 100644 +--- a/libmultipath/Makefile ++++ b/libmultipath/Makefile +@@ -13,7 +13,7 @@ OBJS = memory.o parser.o vector.o devmap + structs.o discovery.o propsel.o dict.o \ + pgpolicies.o debug.o regex.o defaults.o uevent.o \ + switchgroup.o uxsock.o print.o alias.o log_pthread.o \ +- log.o configure.o structs_vec.o ++ log.o configure.o structs_vec.o sysfs.o + + PREVBUILD = $(shell nm debug.o 2> /dev/null|grep log_safe) + +@@ -34,7 +34,7 @@ endif + all: $(BUILD) + + prepare: $(CLEAN) +- @file *-$(BUILD).a >/dev/null 2>&1 || rm -f core *.o *.gz ++ @file *-$(BUILD).a >/dev/null 2>&1 || rm -f core *.o + @rm -f *-$(BUILD).a + + klibc: $(OBJS) +@@ -48,4 +48,4 @@ install: + uninstall: + + clean: +- rm -f core *.a *.o *.gz ++ rm -f core *.a *.o +diff --git a/libmultipath/config.h b/libmultipath/config.h +index 7caa11d..a25b3ad 100644 +--- a/libmultipath/config.h ++++ b/libmultipath/config.h +@@ -66,6 +66,7 @@ struct config { + int pg_timeout; + + char * dev; ++ char * sysfs_dir; + char * udev_dir; + char * selector; + char * getuid; +diff --git a/libmultipath/configure.c b/libmultipath/configure.c +index a5bad4c..3cd6041 100644 +--- a/libmultipath/configure.c ++++ b/libmultipath/configure.c +@@ -325,8 +325,10 @@ domap (struct multipath * mpp) + return DOMAP_RETRY; + } + +- if (dm_map_present(mpp->alias)) ++ if (dm_map_present(mpp->alias)) { ++ condlog(3, "%s: map already present", mpp->alias); + break; ++ } + + r = dm_addmap(DM_DEVICE_CREATE, mpp->alias, DEFAULT_TARGET, + mpp->params, mpp->size, mpp->wwid); +diff --git a/libmultipath/discovery.c b/libmultipath/discovery.c +index a196583..52e0621 100644 +--- a/libmultipath/discovery.c ++++ b/libmultipath/discovery.c +@@ -8,9 +8,8 @@ + #include + #include + #include ++#include + #include +-#include +-#include + + #include + +@@ -24,6 +23,7 @@ + #include "debug.h" + #include "propsel.h" + #include "sg_include.h" ++#include "sysfs.h" + #include "discovery.h" + + struct path * +@@ -87,127 +87,117 @@ path_discover (vector pathvec, struct co + int + path_discovery (vector pathvec, struct config * conf, int flag) + { +- struct dlist * ls; +- struct sysfs_class * class; +- struct sysfs_class_device * dev; +- int r = 1; +- +- if (!(class = sysfs_open_class("block"))) ++ DIR *blkdir; ++ struct dirent *blkdev; ++ struct stat statbuf; ++ char devpath[PATH_MAX]; ++ char *devptr; ++ int r = 0; ++ ++ if (!(blkdir = opendir("/sys/block"))) + return 1; + +- if (!(ls = sysfs_get_class_devices(class))) +- goto out; ++ strcpy(devpath,"/sys/block"); ++ while ((blkdev = readdir(blkdir)) != NULL) { ++ if ((strcmp(blkdev->d_name,".") == 0) || ++ (strcmp(blkdev->d_name,"..") == 0)) ++ continue; + +- r = 0; ++ devptr = devpath + 10; ++ *devptr = '\0'; ++ strcat(devptr,"/"); ++ strcat(devptr,blkdev->d_name); ++ if (stat(devpath, &statbuf) < 0) ++ continue; + +- dlist_for_each_data(ls, dev, struct sysfs_class_device) +- r += path_discover(pathvec, conf, dev->name, flag); ++ if (S_ISDIR(statbuf.st_mode) == 0) ++ continue; + +-out: +- sysfs_close_class(class); +- return r; +-} ++ condlog(4, "Discover device %s", devpath); + +-/* +- * the daemon can race udev upon path add, +- * not multipath(8), ran by udev +- */ +-#if DAEMON +-#define WAIT_MAX_SECONDS 60 +-#define WAIT_LOOP_PER_SECOND 5 +- +-static int +-wait_for_file (char * filename) +-{ +- int loop; +- struct stat stats; +- +- loop = WAIT_MAX_SECONDS * WAIT_LOOP_PER_SECOND; +- +- while (--loop) { +- if (stat(filename, &stats) == 0) +- return 0; +- +- if (errno != ENOENT) +- return 1; +- +- usleep(1000 * 1000 / WAIT_LOOP_PER_SECOND); ++ r += path_discover(pathvec, conf, blkdev->d_name, flag); + } +- return 1; +-} +-#else +-static int +-wait_for_file (char * filename) +-{ +- return 0; ++ closedir(blkdir); ++ condlog(4, "Discovery status %d", r); ++ return r; + } +-#endif + +-#define declare_sysfs_get_str(fname, fmt) \ ++#define declare_sysfs_get_str(fname) \ + extern int \ +-sysfs_get_##fname (char * sysfs_path, char * dev, char * buff, int len) \ ++sysfs_get_##fname (struct sysfs_device * dev, char * buff, size_t len) \ + { \ +- struct sysfs_attribute * attr; \ +- char attr_path[SYSFS_PATH_SIZE]; \ ++ char *attr; \ + \ +- if (safe_sprintf(attr_path, fmt, sysfs_path, dev)) \ ++ attr = sysfs_attr_get_value(dev->devpath, #fname); \ ++ if (!attr) \ + return 1; \ + \ +- if (wait_for_file(attr_path)) \ +- return 1; \ +-\ +- if (!(attr = sysfs_open_attribute(attr_path))) \ +- return 1; \ +-\ +- if (0 > sysfs_read_attribute(attr)) \ +- goto out; \ +-\ +- if (attr->len < 2 || attr->len - 1 > len) \ +- goto out; \ +-\ +- strncpy(buff, attr->value, attr->len - 1); \ +- strchop(buff); \ +- sysfs_close_attribute(attr); \ ++ if (strlcpy(buff, attr, len) != strlen(attr)) \ ++ return 2; \ + return 0; \ +-out: \ +- sysfs_close_attribute(attr); \ +- return 1; \ + } + +-declare_sysfs_get_str(devtype, "%s/block/%s/device/devtype"); +-declare_sysfs_get_str(cutype, "%s/block/%s/device/cutype"); +-declare_sysfs_get_str(vendor, "%s/block/%s/device/vendor"); +-declare_sysfs_get_str(model, "%s/block/%s/device/model"); +-declare_sysfs_get_str(rev, "%s/block/%s/device/rev"); +-declare_sysfs_get_str(dev, "%s/block/%s/dev"); ++declare_sysfs_get_str(devtype); ++declare_sysfs_get_str(cutype); ++declare_sysfs_get_str(vendor); ++declare_sysfs_get_str(model); ++declare_sysfs_get_str(rev); + + int +-sysfs_get_size (char * sysfs_path, char * dev, unsigned long long * size) ++sysfs_get_dev (struct sysfs_device * dev, char * buff, size_t len) + { +- struct sysfs_attribute * attr; +- char attr_path[SYSFS_PATH_SIZE]; +- int r; ++ char *attr; + +- if (safe_sprintf(attr_path, "%s/block/%s/size", sysfs_path, dev)) ++ attr = sysfs_attr_get_value(dev->devpath, "dev"); ++ if (!attr) { ++ condlog(3, "%s: no 'dev' attribute in sysfs", dev->kernel); + return 1; ++ } ++ if (strlcpy(buff, attr, len) != strlen(attr)) { ++ condlog(3, "%s: overflow in 'dev' attribute", dev->kernel); ++ return 2; ++ } ++ return 0; ++} + +- attr = sysfs_open_attribute(attr_path); ++int ++sysfs_get_size (struct sysfs_device * dev, unsigned long long * size) ++{ ++ char *attr; ++ int r; + ++ attr = sysfs_attr_get_value(dev->devpath, "size"); + if (!attr) + return 1; + +- if (0 > sysfs_read_attribute(attr)) +- goto out; +- +- r = sscanf(attr->value, "%llu\n", size); +- sysfs_close_attribute(attr); ++ r = sscanf(attr, "%llu\n", size); + + if (r != 1) + return 1; + + return 0; +-out: +- sysfs_close_attribute(attr); ++} ++ ++int ++sysfs_get_fc_nodename (struct sysfs_device * dev, char * node, ++ unsigned int host, unsigned int channel, ++ unsigned int target) ++{ ++ char attr_path[SYSFS_PATH_SIZE], *attr; ++ ++ if (safe_sprintf(attr_path, ++ "/class/fc_transport/target%i:%i:%i", ++ host, channel, target)) { ++ condlog(0, "attr_path too small"); ++ return 1; ++ } ++ ++ attr = sysfs_attr_get_value(attr_path, "node_name"); ++ if (attr) { ++ strlcpy(node, attr, strlen(attr)); ++ return 0; ++ } ++ + return 1; + } + +@@ -224,68 +214,52 @@ opennode (char * dev, int mode) + return -1; + } + +- if (wait_for_file(devpath)) { +- condlog(3, "failed to open %s", devpath); +- return -1; +- } +- + return open(devpath, mode); + } + + extern int + devt2devname (char *devname, char *devt) + { +- struct dlist * ls; +- char attr_path[FILE_NAME_SIZE]; ++ FILE *fd; ++ unsigned int tmpmaj, tmpmin, major, minor; ++ char dev[FILE_NAME_SIZE]; + char block_path[FILE_NAME_SIZE]; +- struct sysfs_attribute * attr = NULL; +- struct sysfs_class * class; +- struct sysfs_class_device * dev; ++ struct stat statbuf; + +- if(safe_sprintf(block_path, "%s/block", sysfs_path)) { +- condlog(0, "block_path too small"); ++ if (sscanf(devt, "%u:%u", &major, &minor) != 2) { ++ condlog(0, "Invalid device number %s", devt); + return 1; + } +- if (!(class = sysfs_open_class("block"))) +- return 1; + +- if (!(ls = sysfs_get_class_devices(class))) +- goto err; ++ if ((fd = fopen("/proc/partitions", "r")) < 0) { ++ condlog(0, "Cannot open /proc/partitions"); ++ return 1; ++ } ++ ++ while (!feof(fd)) { ++ if (fscanf(fd," %u %u %*d %s",&tmpmaj, &tmpmin, dev) != 3) ++ continue; + +- dlist_for_each_data(ls, dev, struct sysfs_class_device) { +- if(safe_sprintf(attr_path, "%s/%s/dev", +- block_path, dev->name)) { +- condlog(0, "attr_path too small"); +- goto err; +- } +- if (!(attr = sysfs_open_attribute(attr_path))) +- goto err; +- +- if (sysfs_read_attribute(attr)) +- goto err1; +- +- /* discard newline */ +- if (attr->len > 1) attr->len--; +- +- if (strlen(devt) == attr->len && +- strncmp(attr->value, devt, attr->len) == 0) { +- if(safe_sprintf(attr_path, "%s/%s", +- block_path, dev->name)) { +- condlog(0, "attr_path too small"); +- goto err1; +- } +- sysfs_get_name_from_path(attr_path, devname, +- FILE_NAME_SIZE); +- sysfs_close_attribute(attr); +- sysfs_close_class(class); +- return 0; ++ if ((major == tmpmaj) && (minor == tmpmin)) { ++ sprintf(block_path, "/sys/block/%s", dev); ++ break; + } + } +-err1: +- sysfs_close_attribute(attr); +-err: +- sysfs_close_class(class); +- return 1; ++ fclose(fd); ++ ++ if (strncpy(block_path,"/sys/block", 10)) ++ return 1; ++ ++ if (stat(block_path, &statbuf) < 0) { ++ condlog(0, "No sysfs entry for %s\n", block_path); ++ return 1; ++ } ++ ++ if (S_ISDIR(statbuf.st_mode) == 0) { ++ condlog(0, "sysfs entry %s is not a directory\n", block_path); ++ return 1; ++ } ++ return 0; + } + + static int +@@ -363,79 +337,21 @@ get_serial (char * str, int maxlen, int + } + + static int +-sysfs_get_bus (char * sysfs_path, struct path * pp) +-{ +- struct sysfs_device *sdev; +- char attr_path[FILE_NAME_SIZE]; +- char attr_buff[FILE_NAME_SIZE]; +- +- pp->bus = SYSFS_BUS_UNDEF; +- +- /* +- * This is ugly : we should be able to do a simple +- * get_link("%s/block/%s/device/bus", ...) but it just +- * won't work +- */ +- if(safe_sprintf(attr_path, "%s/block/%s/device", +- sysfs_path, pp->dev)) { +- condlog(0, "attr_path too small"); +- return 1; +- } +- +- if (0 > sysfs_get_link(attr_path, attr_buff, sizeof(attr_buff))) +- return 1; +- +-#if DAEMON +- int loop = WAIT_MAX_SECONDS * WAIT_LOOP_PER_SECOND; +- +- while (loop--) { +- sdev = sysfs_open_device_path(attr_buff); +- +- if (strlen(sdev->bus)) +- break; +- +- sysfs_close_device(sdev); +- usleep(1000 * 1000 / WAIT_LOOP_PER_SECOND); +- } +-#else +- sdev = sysfs_open_device_path(attr_buff); +-#endif +- +- if (!strncmp(sdev->bus, "scsi", 4)) +- pp->bus = SYSFS_BUS_SCSI; +- else if (!strncmp(sdev->bus, "ide", 3)) +- pp->bus = SYSFS_BUS_IDE; +- else if (!strncmp(sdev->bus, "ccw", 3)) +- pp->bus = SYSFS_BUS_CCW; +- else +- return 1; +- +- sysfs_close_device(sdev); +- +- return 0; +-} +- +-static int +-scsi_sysfs_pathinfo (struct path * pp) ++scsi_sysfs_pathinfo (struct path * pp, struct sysfs_device * parent) + { + char attr_path[FILE_NAME_SIZE]; +- char attr_buff[FILE_NAME_SIZE]; +- struct sysfs_attribute * attr; + +- if (sysfs_get_vendor(sysfs_path, pp->dev, +- pp->vendor_id, SCSI_VENDOR_SIZE)) ++ if (sysfs_get_vendor(parent, pp->vendor_id, SCSI_VENDOR_SIZE)) + return 1; + + condlog(3, "%s: vendor = %s", pp->dev, pp->vendor_id); + +- if (sysfs_get_model(sysfs_path, pp->dev, +- pp->product_id, SCSI_PRODUCT_SIZE)) ++ if (sysfs_get_model(parent, pp->product_id, SCSI_PRODUCT_SIZE)) + return 1; + + condlog(3, "%s: product = %s", pp->dev, pp->product_id); + +- if (sysfs_get_rev(sysfs_path, pp->dev, +- pp->rev, SCSI_REV_SIZE)) ++ if (sysfs_get_rev(parent, pp->rev, SCSI_REV_SIZE)) + return 1; + + condlog(3, "%s: rev = %s", pp->dev, pp->rev); +@@ -448,15 +364,7 @@ scsi_sysfs_pathinfo (struct path * pp) + /* + * host / bus / target / lun + */ +- if(safe_sprintf(attr_path, "%s/block/%s/device", +- sysfs_path, pp->dev)) { +- condlog(0, "attr_path too small"); +- return 1; +- } +- if (0 > sysfs_get_link(attr_path, attr_buff, sizeof(attr_buff))) +- return 1; +- +- basename(attr_buff, attr_path); ++ basename(parent->devpath, attr_path); + + sscanf(attr_path, "%i:%i:%i:%i", + &pp->sg_id.host_no, +@@ -473,35 +381,19 @@ scsi_sysfs_pathinfo (struct path * pp) + /* + * target node name + */ +- if(safe_sprintf(attr_path, +- "%s/class/fc_transport/target%i:%i:%i/node_name", +- sysfs_path, +- pp->sg_id.host_no, +- pp->sg_id.channel, +- pp->sg_id.scsi_id)) { +- condlog(0, "attr_path too small"); +- return 1; ++ if(!sysfs_get_fc_nodename(parent, pp->tgt_node_name, ++ pp->sg_id.host_no, ++ pp->sg_id.channel, ++ pp->sg_id.scsi_id)) { ++ condlog(3, "%s: tgt_node_name = %s", ++ pp->dev, pp->tgt_node_name); + } +- if (!(attr = sysfs_open_attribute(attr_path))) +- return 0; +- +- if (sysfs_read_attribute(attr)) +- goto err; +- +- if (attr->len > 0) +- strncpy(pp->tgt_node_name, attr->value, attr->len - 1); +- +- condlog(3, "%s: tgt_node_name = %s", +- pp->dev, pp->tgt_node_name); + + return 0; +-err: +- sysfs_close_attribute(attr); +- return 1; + } + + static int +-ccw_sysfs_pathinfo (struct path * pp) ++ccw_sysfs_pathinfo (struct path * pp, struct sysfs_device * parent) + { + char attr_path[FILE_NAME_SIZE]; + char attr_buff[FILE_NAME_SIZE]; +@@ -510,8 +402,7 @@ ccw_sysfs_pathinfo (struct path * pp) + + condlog(3, "%s: vendor = %s", pp->dev, pp->vendor_id); + +- if (sysfs_get_devtype(sysfs_path, pp->dev, +- attr_buff, FILE_NAME_SIZE)) ++ if (sysfs_get_devtype(parent, attr_buff, FILE_NAME_SIZE)) + return 1; + + if (!strncmp(attr_buff, "3370", 4)) { +@@ -531,16 +422,8 @@ ccw_sysfs_pathinfo (struct path * pp) + + /* + * host / bus / target / lun +- */ +- if(safe_sprintf(attr_path, "%s/block/%s/device", +- sysfs_path, pp->dev)) { +- condlog(0, "attr_path too small"); +- return 1; +- } +- if (0 > sysfs_get_link(attr_path, attr_buff, sizeof(attr_buff))) +- return 1; +- +- basename(attr_buff, attr_path); ++ */ ++ basename(parent->devpath, attr_path); + pp->sg_id.lun = 0; + sscanf(attr_path, "%i.%i.%x", + &pp->sg_id.host_no, +@@ -557,20 +440,20 @@ ccw_sysfs_pathinfo (struct path * pp) + } + + static int +-common_sysfs_pathinfo (struct path * pp) ++common_sysfs_pathinfo (struct path * pp, struct sysfs_device *dev) + { +- if (sysfs_get_bus(sysfs_path, pp)) +- return 1; +- +- condlog(3, "%s: bus = %i", pp->dev, pp->bus); ++ char *attr; + +- if (sysfs_get_dev(sysfs_path, pp->dev, +- pp->dev_t, BLK_DEV_SIZE)) ++ attr = sysfs_attr_get_value(dev->devpath, "dev"); ++ if (!attr) { ++ condlog(3, "%s: no 'dev' attribute in sysfs", pp->dev); + return 1; ++ } ++ strlcpy(pp->dev_t, attr, BLK_DEV_SIZE); + + condlog(3, "%s: dev_t = %s", pp->dev, pp->dev_t); + +- if (sysfs_get_size(sysfs_path, pp->dev, &pp->size)) ++ if (sysfs_get_size(dev, &pp->size)) + return 1; + + condlog(3, "%s: size = %llu", pp->dev, pp->size); +@@ -578,19 +461,46 @@ common_sysfs_pathinfo (struct path * pp) + return 0; + } + ++struct sysfs_device *sysfs_device_from_path(struct path *pp) ++{ ++ char sysdev[FILE_NAME_SIZE]; ++ ++ strlcpy(sysdev,"/block/", FILE_NAME_SIZE); ++ strlcat(sysdev,pp->dev, FILE_NAME_SIZE); ++ ++ return sysfs_device_get(sysdev); ++} ++ + extern int + sysfs_pathinfo(struct path * pp) + { +- if (common_sysfs_pathinfo(pp)) ++ struct sysfs_device *parent; ++ ++ pp->sysdev = sysfs_device_from_path(pp); ++ if (!pp->sysdev) { ++ condlog(1, "%s: failed to get sysfs information", pp->dev); + return 1; ++ } ++ ++ parent = sysfs_device_get_parent(pp->sysdev); ++ ++ if (common_sysfs_pathinfo(pp, pp->sysdev)) ++ return 1; ++ ++ condlog(3, "%s: subsystem = %s", pp->dev, parent->subsystem); ++ ++ if (!strncmp(parent->subsystem, "scsi",4)) ++ pp->bus = SYSFS_BUS_SCSI; ++ if (!strncmp(parent->subsystem, "ccw",3)) ++ pp->bus = SYSFS_BUS_CCW; + + if (pp->bus == SYSFS_BUS_UNDEF) + return 0; + else if (pp->bus == SYSFS_BUS_SCSI) { +- if (scsi_sysfs_pathinfo(pp)) ++ if (scsi_sysfs_pathinfo(pp, parent)) + return 1; + } else if (pp->bus == SYSFS_BUS_CCW) { +- if (ccw_sysfs_pathinfo(pp)) ++ if (ccw_sysfs_pathinfo(pp, parent)) + return 1; + } + return 0; +diff --git a/libmultipath/discovery.h b/libmultipath/discovery.h +index ab62a59..c7cf7e8 100644 +--- a/libmultipath/discovery.h ++++ b/libmultipath/discovery.h +@@ -24,12 +24,7 @@ + #define SCSI_COMMAND_TERMINATED 0x22 + #define SG_ERR_DRIVER_SENSE 0x08 + +-int sysfs_get_vendor (char * sysfs_path, char * dev, char * buff, int len); +-int sysfs_get_model (char * sysfs_path, char * dev, char * buff, int len); +-int sysfs_get_rev (char * sysfs_path, char * dev, char * buff, int len); +-int sysfs_get_dev (char * sysfs_path, char * dev, char * buff, int len); +- +-int sysfs_get_size (char * sysfs_path, char * dev, unsigned long long *); ++int sysfs_get_dev (struct sysfs_device * dev, char * buff, size_t len); + int path_discovery (vector pathvec, struct config * conf, int flag); + + void basename (char *, char *); +diff --git a/libmultipath/list.h b/libmultipath/list.h +new file mode 100644 +index 0000000..8626630 +--- /dev/null ++++ b/libmultipath/list.h +@@ -0,0 +1,289 @@ ++/* ++ * Copied from the Linux kernel source tree, version 2.6.0-test1. ++ * ++ * Licensed under the GPL v2 as per the whole kernel source tree. ++ * ++ */ ++ ++#ifndef _LIST_H ++#define _LIST_H ++ ++/** ++ * container_of - cast a member of a structure out to the containing structure ++ * ++ * @ptr: the pointer to the member. ++ * @type: the type of the container struct this is embedded in. ++ * @member: the name of the member within the struct. ++ * ++ */ ++#define container_of(ptr, type, member) ({ \ ++ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ ++ (type *)( (char *)__mptr - offsetof(type,member) );}) ++ ++/* ++ * These are non-NULL pointers that will result in page faults ++ * under normal circumstances, used to verify that nobody uses ++ * non-initialized list entries. ++ */ ++#define LIST_POISON1 ((void *) 0x00100100) ++#define LIST_POISON2 ((void *) 0x00200200) ++ ++/* ++ * Simple doubly linked list implementation. ++ * ++ * Some of the internal functions ("__xxx") are useful when ++ * manipulating whole lists rather than single entries, as ++ * sometimes we already know the next/prev entries and we can ++ * generate better code by using them directly rather than ++ * using the generic single-entry routines. ++ */ ++ ++struct list_head { ++ struct list_head *next, *prev; ++}; ++ ++#define LIST_HEAD_INIT(name) { &(name), &(name) } ++ ++#define LIST_HEAD(name) \ ++ struct list_head name = LIST_HEAD_INIT(name) ++ ++#define INIT_LIST_HEAD(ptr) do { \ ++ (ptr)->next = (ptr); (ptr)->prev = (ptr); \ ++} while (0) ++ ++/* ++ * Insert a new entry between two known consecutive entries. ++ * ++ * This is only for internal list manipulation where we know ++ * the prev/next entries already! ++ */ ++static inline void __list_add(struct list_head *new, ++ struct list_head *prev, ++ struct list_head *next) ++{ ++ next->prev = new; ++ new->next = next; ++ new->prev = prev; ++ prev->next = new; ++} ++ ++/** ++ * list_add - add a new entry ++ * @new: new entry to be added ++ * @head: list head to add it after ++ * ++ * Insert a new entry after the specified head. ++ * This is good for implementing stacks. ++ */ ++static inline void list_add(struct list_head *new, struct list_head *head) ++{ ++ __list_add(new, head, head->next); ++} ++ ++/** ++ * list_add_tail - add a new entry ++ * @new: new entry to be added ++ * @head: list head to add it before ++ * ++ * Insert a new entry before the specified head. ++ * This is useful for implementing queues. ++ */ ++static inline void list_add_tail(struct list_head *new, struct list_head *head) ++{ ++ __list_add(new, head->prev, head); ++} ++ ++/* ++ * Delete a list entry by making the prev/next entries ++ * point to each other. ++ * ++ * This is only for internal list manipulation where we know ++ * the prev/next entries already! ++ */ ++static inline void __list_del(struct list_head * prev, struct list_head * next) ++{ ++ next->prev = prev; ++ prev->next = next; ++} ++ ++/** ++ * list_del - deletes entry from list. ++ * @entry: the element to delete from the list. ++ * Note: list_empty on entry does not return true after this, the entry is ++ * in an undefined state. ++ */ ++static inline void list_del(struct list_head *entry) ++{ ++ __list_del(entry->prev, entry->next); ++ entry->next = LIST_POISON1; ++ entry->prev = LIST_POISON2; ++} ++ ++/** ++ * list_del_init - deletes entry from list and reinitialize it. ++ * @entry: the element to delete from the list. ++ */ ++static inline void list_del_init(struct list_head *entry) ++{ ++ __list_del(entry->prev, entry->next); ++ INIT_LIST_HEAD(entry); ++} ++ ++/** ++ * list_move - delete from one list and add as another's head ++ * @list: the entry to move ++ * @head: the head that will precede our entry ++ */ ++static inline void list_move(struct list_head *list, struct list_head *head) ++{ ++ __list_del(list->prev, list->next); ++ list_add(list, head); ++} ++ ++/** ++ * list_move_tail - delete from one list and add as another's tail ++ * @list: the entry to move ++ * @head: the head that will follow our entry ++ */ ++static inline void list_move_tail(struct list_head *list, ++ struct list_head *head) ++{ ++ __list_del(list->prev, list->next); ++ list_add_tail(list, head); ++} ++ ++/** ++ * list_empty - tests whether a list is empty ++ * @head: the list to test. ++ */ ++static inline int list_empty(struct list_head *head) ++{ ++ return head->next == head; ++} ++ ++static inline void __list_splice(struct list_head *list, ++ struct list_head *head) ++{ ++ struct list_head *first = list->next; ++ struct list_head *last = list->prev; ++ struct list_head *at = head->next; ++ ++ first->prev = head; ++ head->next = first; ++ ++ last->next = at; ++ at->prev = last; ++} ++ ++/** ++ * list_splice - join two lists ++ * @list: the new list to add. ++ * @head: the place to add it in the first list. ++ */ ++static inline void list_splice(struct list_head *list, struct list_head *head) ++{ ++ if (!list_empty(list)) ++ __list_splice(list, head); ++} ++ ++/** ++ * list_splice_init - join two lists and reinitialise the emptied list. ++ * @list: the new list to add. ++ * @head: the place to add it in the first list. ++ * ++ * The list at @list is reinitialised ++ */ ++static inline void list_splice_init(struct list_head *list, ++ struct list_head *head) ++{ ++ if (!list_empty(list)) { ++ __list_splice(list, head); ++ INIT_LIST_HEAD(list); ++ } ++} ++ ++/** ++ * list_entry - get the struct for this entry ++ * @ptr: the &struct list_head pointer. ++ * @type: the type of the struct this is embedded in. ++ * @member: the name of the list_struct within the struct. ++ */ ++#define list_entry(ptr, type, member) \ ++ container_of(ptr, type, member) ++ ++/** ++ * list_for_each - iterate over a list ++ * @pos: the &struct list_head to use as a loop counter. ++ * @head: the head for your list. ++ */ ++#define list_for_each(pos, head) \ ++ for (pos = (head)->next; pos != (head); \ ++ pos = pos->next) ++ ++/** ++ * __list_for_each - iterate over a list ++ * @pos: the &struct list_head to use as a loop counter. ++ * @head: the head for your list. ++ * ++ * This variant differs from list_for_each() in that it's the ++ * simplest possible list iteration code. ++ * Use this for code that knows the list to be very short (empty ++ * or 1 entry) most of the time. ++ */ ++#define __list_for_each(pos, head) \ ++ for (pos = (head)->next; pos != (head); pos = pos->next) ++ ++/** ++ * list_for_each_prev - iterate over a list backwards ++ * @pos: the &struct list_head to use as a loop counter. ++ * @head: the head for your list. ++ */ ++#define list_for_each_prev(pos, head) \ ++ for (pos = (head)->prev; pos != (head); pos = pos->prev) ++ ++/** ++ * list_for_each_safe - iterate over a list safe against removal of list entry ++ * @pos: the &struct list_head to use as a loop counter. ++ * @n: another &struct list_head to use as temporary storage ++ * @head: the head for your list. ++ */ ++#define list_for_each_safe(pos, n, head) \ ++ for (pos = (head)->next, n = pos->next; pos != (head); \ ++ pos = n, n = pos->next) ++ ++/** ++ * list_for_each_entry - iterate over list of given type ++ * @pos: the type * to use as a loop counter. ++ * @head: the head for your list. ++ * @member: the name of the list_struct within the struct. ++ */ ++#define list_for_each_entry(pos, head, member) \ ++ for (pos = list_entry((head)->next, typeof(*pos), member); \ ++ &pos->member != (head); \ ++ pos = list_entry(pos->member.next, typeof(*pos), member)) ++ ++/** ++ * list_for_each_entry_reverse - iterate backwards over list of given type. ++ * @pos: the type * to use as a loop counter. ++ * @head: the head for your list. ++ * @member: the name of the list_struct within the struct. ++ */ ++#define list_for_each_entry_reverse(pos, head, member) \ ++ for (pos = list_entry((head)->prev, typeof(*pos), member); \ ++ &pos->member != (head); \ ++ pos = list_entry(pos->member.prev, typeof(*pos), member)) ++ ++/** ++ * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry ++ * @pos: the type * to use as a loop counter. ++ * @n: another type * to use as temporary storage ++ * @head: the head for your list. ++ * @member: the name of the list_struct within the struct. ++ */ ++#define list_for_each_entry_safe(pos, n, head, member) \ ++ for (pos = list_entry((head)->next, typeof(*pos), member), \ ++ n = list_entry(pos->member.next, typeof(*pos), member); \ ++ &pos->member != (head); \ ++ pos = n, n = list_entry(n->member.next, typeof(*n), member)) ++ ++#endif /* _LIST_H */ +diff --git a/libmultipath/print.c b/libmultipath/print.c +index dc8af48..1d80e48 100644 +--- a/libmultipath/print.c ++++ b/libmultipath/print.c +@@ -5,8 +5,8 @@ + #include + #include + #include +-#include +-#include ++#include ++#include + + #include + +@@ -1096,35 +1096,46 @@ snprint_blacklist_except (char * buff, i + extern int + snprint_devices (char * buff, int len, struct vectors *vecs) + { +- struct dlist * ls; +- struct sysfs_class * class; +- struct sysfs_class_device * dev; ++ DIR *blkdir; ++ struct dirent *blkdev; ++ struct stat statbuf; ++ char devpath[PATH_MAX]; ++ char *devptr; + int threshold = MAX_LINE_LEN; + int fwd = 0; + int r; + + struct path * pp; + +- if (!(class = sysfs_open_class("block"))) +- return 0; +- +- if (!(ls = sysfs_get_class_devices(class))) { +- sysfs_close_class(class); +- return 0; +- } ++ if (!(blkdir = opendir("/sys/block"))) ++ return 1; + + if ((len - fwd - threshold) <= 0) + return len; + fwd += snprintf(buff + fwd, len - fwd, "available block devices:\n"); + +- dlist_for_each_data(ls, dev, struct sysfs_class_device) { ++ strcpy(devpath,"/sys/block"); ++ devptr = devpath + 10; ++ while ((blkdev = readdir(blkdir)) != NULL) { ++ if ((strcmp(blkdev->d_name,".") == 0) || ++ (strcmp(blkdev->d_name,"..") == 0)) ++ continue; ++ ++ strcat(devptr,blkdev->d_name); ++ if (stat(devptr, &statbuf) < 0) ++ continue; ++ ++ if (S_ISDIR(statbuf.st_mode) == 0) ++ continue; ++ + if ((len - fwd - threshold) <= 0) + return len; +- fwd += snprintf(buff + fwd, len - fwd, " %s", dev->name); +- pp = find_path_by_dev(vecs->pathvec, dev->name); ++ ++ fwd += snprintf(buff + fwd, len - fwd, " %s", devpath); ++ pp = find_path_by_dev(vecs->pathvec, devpath); + if (!pp) { + r = filter_devnode(conf->blist_devnode, +- conf->elist_devnode, dev->name); ++ conf->elist_devnode, devpath); + if (r > 0) + fwd += snprintf(buff + fwd, len - fwd, + " (blacklisted)"); +@@ -1133,8 +1144,8 @@ snprint_devices (char * buff, int len, s + " (whitelisted)"); + } + fwd += snprintf(buff + fwd, len - fwd, "\n"); +- } +- sysfs_close_class(class); ++ } ++ closedir(blkdir); + + if (fwd > len) + return len; +diff --git a/libmultipath/structs.h b/libmultipath/structs.h +index 46dcdee..75322aa 100644 +--- a/libmultipath/structs.h ++++ b/libmultipath/structs.h +@@ -9,6 +9,9 @@ + #define FILE_NAME_SIZE 256 + #define CALLOUT_MAX_SIZE 128 + #define BLK_DEV_SIZE 33 ++#define PATH_SIZE 512 ++#define NAME_SIZE 128 ++ + + #define SCSI_VENDOR_SIZE 9 + #define SCSI_PRODUCT_SIZE 17 +@@ -86,9 +89,19 @@ struct scsi_dev { + int host_no; + }; + ++struct sysfs_device { ++ struct sysfs_device *parent; /* parent device */ ++ char devpath[PATH_SIZE]; ++ char subsystem[NAME_SIZE]; /* $class, $bus, drivers, module */ ++ char kernel[NAME_SIZE]; /* device instance name */ ++ char kernel_number[NAME_SIZE]; ++ char driver[NAME_SIZE]; /* device driver name */ ++}; ++ + struct path { + char dev[FILE_NAME_SIZE]; + char dev_t[BLK_DEV_SIZE]; ++ struct sysfs_device *sysdev; + struct scsi_idlun scsi_id; + struct sg_id sg_id; + char wwid[WWID_SIZE]; +@@ -200,6 +213,6 @@ struct path * first_path (struct multipa + int pathcountgr (struct pathgroup *, int); + int pathcount (struct multipath *, int); + +-char sysfs_path[FILE_NAME_SIZE]; ++extern char sysfs_path[PATH_SIZE]; + + #endif /* _STRUCTS_H */ +diff --git a/libmultipath/structs_vec.c b/libmultipath/structs_vec.c +index a4a996a..1cc6028 100644 +--- a/libmultipath/structs_vec.c ++++ b/libmultipath/structs_vec.c +@@ -13,6 +13,7 @@ + #include "dmparser.h" + #include "config.h" + #include "propsel.h" ++#include "sysfs.h" + #include "discovery.h" + #include "waiter.h" + +@@ -373,10 +374,10 @@ verify_paths(struct multipath * mpp, str + /* + * see if path is in sysfs + */ +- if (!pp->dev || sysfs_get_dev(sysfs_path, +- pp->dev, pp->dev_t, BLK_DEV_SIZE)) { ++ if (!pp->sysdev || sysfs_get_dev(pp->sysdev, ++ pp->dev_t, BLK_DEV_SIZE)) { + condlog(0, "%s: failed to access path %s", mpp->alias, +- pp->dev ? pp->dev : pp->dev_t); ++ pp->sysdev ? pp->sysdev->devpath : pp->dev_t); + count++; + vector_del_slot(mpp->paths, i); + i--; +diff --git a/libmultipath/sysfs.c b/libmultipath/sysfs.c +new file mode 100644 +index 0000000..e24aa34 +--- /dev/null ++++ b/libmultipath/sysfs.c +@@ -0,0 +1,405 @@ ++/* ++ * Copyright (C) 2005-2006 Kay Sievers ++ * ++ * 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 version 2 of the License. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ++ * ++ */ ++ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "checkers.h" ++#include "vector.h" ++#include "structs.h" ++#include "sysfs.h" ++#include "list.h" ++#include "util.h" ++ ++char sysfs_path[PATH_SIZE]; ++ ++/* attribute value cache */ ++static LIST_HEAD(attr_list); ++struct sysfs_attr { ++ struct list_head node; ++ char path[PATH_SIZE]; ++ char *value; /* points to value_local if value is cached */ ++ char value_local[NAME_SIZE]; ++}; ++ ++int sysfs_init(char *path, size_t len) ++{ ++ if (path) { ++ strlcpy(sysfs_path, path, len); ++ remove_trailing_chars(sysfs_path, '/'); ++ } else ++ strlcpy(sysfs_path, "/sys", len); ++ dbg("sysfs_path='%s'", sysfs_path); ++ ++ INIT_LIST_HEAD(&attr_list); ++ return 0; ++} ++ ++void sysfs_cleanup(void) ++{ ++ struct sysfs_attr *attr_loop; ++ struct sysfs_attr *attr_temp; ++ ++ list_for_each_entry_safe(attr_loop, attr_temp, &attr_list, node) { ++ list_del(&attr_loop->node); ++ free(attr_loop); ++ } ++ ++} ++ ++void sysfs_device_set_values(struct sysfs_device *dev, const char *devpath, ++ const char *subsystem, const char *driver) ++{ ++ char *pos; ++ ++ strlcpy(dev->devpath, devpath, sizeof(dev->devpath)); ++ if (subsystem != NULL) ++ strlcpy(dev->subsystem, subsystem, sizeof(dev->subsystem)); ++ if (driver != NULL) ++ strlcpy(dev->driver, driver, sizeof(dev->driver)); ++ ++ /* set kernel name */ ++ pos = strrchr(dev->devpath, '/'); ++ if (pos == NULL) ++ return; ++ strlcpy(dev->kernel, &pos[1], sizeof(dev->kernel)); ++ dbg("kernel='%s'", dev->kernel); ++ ++ /* some devices have '!' in their name, change that to '/' */ ++ pos = dev->kernel; ++ while (pos[0] != '\0') { ++ if (pos[0] == '!') ++ pos[0] = '/'; ++ pos++; ++ } ++ ++ /* get kernel number */ ++ pos = &dev->kernel[strlen(dev->kernel)]; ++ while (isdigit(pos[-1])) ++ pos--; ++ strlcpy(dev->kernel_number, pos, sizeof(dev->kernel_number)); ++ dbg("kernel_number='%s'", dev->kernel_number); ++} ++ ++int sysfs_resolve_link(char *devpath, size_t size) ++{ ++ char link_path[PATH_SIZE]; ++ char link_target[PATH_SIZE]; ++ int len; ++ int i; ++ int back; ++ ++ strlcpy(link_path, sysfs_path, sizeof(link_path)); ++ strlcat(link_path, devpath, sizeof(link_path)); ++ len = readlink(link_path, link_target, sizeof(link_target)); ++ if (len <= 0) ++ return -1; ++ link_target[len] = '\0'; ++ dbg("path link '%s' points to '%s'", devpath, link_target); ++ ++ for (back = 0; strncmp(&link_target[back * 3], "../", 3) == 0; back++) ++ ; ++ dbg("base '%s', tail '%s', back %i", devpath, &link_target[back * 3], back); ++ for (i = 0; i <= back; i++) { ++ char *pos = strrchr(devpath, '/'); ++ ++ if (pos == NULL) ++ return -1; ++ pos[0] = '\0'; ++ } ++ dbg("after moving back '%s'", devpath); ++ strlcat(devpath, "/", size); ++ strlcat(devpath, &link_target[back * 3], size); ++ return 0; ++} ++ ++struct sysfs_device *sysfs_device_get(const char *devpath) ++{ ++ char path[PATH_SIZE]; ++ char devpath_real[PATH_SIZE]; ++ struct sysfs_device *dev; ++ struct stat statbuf; ++ char link_path[PATH_SIZE]; ++ char link_target[PATH_SIZE]; ++ int len; ++ char *pos; ++ ++ dbg("open '%s'", devpath); ++ strlcpy(devpath_real, devpath, sizeof(devpath_real)); ++ remove_trailing_chars(devpath_real, '/'); ++ ++ /* if we got a link, resolve it to the real device */ ++ strlcpy(path, sysfs_path, sizeof(path)); ++ strlcat(path, devpath_real, sizeof(path)); ++ if (lstat(path, &statbuf) != 0) { ++ dbg("stat '%s' failed: %s", path, strerror(errno)); ++ return NULL; ++ } ++ if (S_ISLNK(statbuf.st_mode)) { ++ if (sysfs_resolve_link(devpath_real, sizeof(devpath_real)) != 0) ++ return NULL; ++ ++ } ++ ++ /* it is a new device */ ++ dbg("new device '%s'", devpath_real); ++ dev = malloc(sizeof(struct sysfs_device)); ++ if (dev == NULL) ++ return NULL; ++ memset(dev, 0x00, sizeof(struct sysfs_device)); ++ ++ sysfs_device_set_values(dev, devpath_real, NULL, NULL); ++ ++ /* get subsystem name */ ++ strlcpy(link_path, sysfs_path, sizeof(link_path)); ++ strlcat(link_path, dev->devpath, sizeof(link_path)); ++ strlcat(link_path, "/subsystem", sizeof(link_path)); ++ len = readlink(link_path, link_target, sizeof(link_target)); ++ if (len > 0) { ++ /* get subsystem from "subsystem" link */ ++ link_target[len] = '\0'; ++ dbg("subsystem link '%s' points to '%s'", link_path, link_target); ++ pos = strrchr(link_target, '/'); ++ if (pos != NULL) ++ strlcpy(dev->subsystem, &pos[1], sizeof(dev->subsystem)); ++ } else if (strncmp(dev->devpath, "/class/", 7) == 0) { ++ /* get subsystem from class dir */ ++ strlcpy(dev->subsystem, &dev->devpath[7], sizeof(dev->subsystem)); ++ pos = strchr(dev->subsystem, '/'); ++ if (pos != NULL) ++ pos[0] = '\0'; ++ else ++ dev->subsystem[0] = '\0'; ++ } else if (strncmp(dev->devpath, "/block/", 7) == 0) { ++ strlcpy(dev->subsystem, "block", sizeof(dev->subsystem)); ++ } else if (strncmp(dev->devpath, "/devices/", 9) == 0) { ++ /* get subsystem from "bus" link */ ++ strlcpy(link_path, sysfs_path, sizeof(link_path)); ++ strlcat(link_path, dev->devpath, sizeof(link_path)); ++ strlcat(link_path, "/bus", sizeof(link_path)); ++ len = readlink(link_path, link_target, sizeof(link_target)); ++ if (len > 0) { ++ link_target[len] = '\0'; ++ dbg("bus link '%s' points to '%s'", link_path, link_target); ++ pos = strrchr(link_target, '/'); ++ if (pos != NULL) ++ strlcpy(dev->subsystem, &pos[1], sizeof(dev->subsystem)); ++ } ++ } else if (strstr(dev->devpath, "/drivers/") != NULL) { ++ strlcpy(dev->subsystem, "drivers", sizeof(dev->subsystem)); ++ } else if (strncmp(dev->devpath, "/module/", 8) == 0) { ++ strlcpy(dev->subsystem, "module", sizeof(dev->subsystem)); ++ } ++ ++ /* get driver name */ ++ strlcpy(link_path, sysfs_path, sizeof(link_path)); ++ strlcat(link_path, dev->devpath, sizeof(link_path)); ++ strlcat(link_path, "/driver", sizeof(link_path)); ++ len = readlink(link_path, link_target, sizeof(link_target)); ++ if (len > 0) { ++ link_target[len] = '\0'; ++ dbg("driver link '%s' points to '%s'", link_path, link_target); ++ pos = strrchr(link_target, '/'); ++ if (pos != NULL) ++ strlcpy(dev->driver, &pos[1], sizeof(dev->driver)); ++ } ++ ++ return dev; ++} ++ ++struct sysfs_device *sysfs_device_get_parent(struct sysfs_device *dev) ++{ ++ char parent_devpath[PATH_SIZE]; ++ char *pos; ++ ++ dbg("open '%s'", dev->devpath); ++ ++ /* look if we already know the parent */ ++ if (dev->parent != NULL) ++ return dev->parent; ++ ++ /* requesting a parent is only valid for devices */ ++ if ((strncmp(dev->devpath, "/devices/", 9) != 0) && ++ (strncmp(dev->devpath, "/subsystem/", 11) != 0) && ++ (strncmp(dev->devpath, "/class/", 7) != 0) && ++ (strncmp(dev->devpath, "/block/", 7) != 0)) ++ return NULL; ++ ++ strlcpy(parent_devpath, dev->devpath, sizeof(parent_devpath)); ++ dbg("'%s'", parent_devpath); ++ ++ /* strip last element */ ++ pos = strrchr(parent_devpath, '/'); ++ if (pos == NULL || pos == parent_devpath) ++ return NULL; ++ pos[0] = '\0'; ++ ++ /* are we at the top level of /devices */ ++ if (strcmp(parent_devpath, "/devices") == 0) { ++ dbg("/devices top level"); ++ return NULL; ++ } ++ ++ /* at the subsystems top level we want to follow the old-style "device" link */ ++ if (strncmp(parent_devpath, "/subsystem", 10) == 0) { ++ pos = strrchr(parent_devpath, '/'); ++ if (pos == &parent_devpath[10] || pos == parent_devpath || strcmp(pos, "/devices") == 0) { ++ dbg("/subsystem top level, look for device link"); ++ goto device_link; ++ } ++ } ++ if (strncmp(parent_devpath, "/class", 6) == 0) { ++ pos = strrchr(parent_devpath, '/'); ++ if (pos == &parent_devpath[6] || pos == parent_devpath) { ++ dbg("/class top level, look for device link"); ++ goto device_link; ++ } ++ } ++ if (strcmp(parent_devpath, "/block") == 0) { ++ dbg("/block top level, look for device link"); ++ goto device_link; ++ } ++ ++ /* get parent and remember it */ ++ dev->parent = sysfs_device_get(parent_devpath); ++ return dev->parent; ++ ++device_link: ++ strlcpy(parent_devpath, dev->devpath, sizeof(parent_devpath)); ++ strlcat(parent_devpath, "/device", sizeof(parent_devpath)); ++ if (sysfs_resolve_link(parent_devpath, sizeof(parent_devpath)) != 0) ++ return NULL; ++ ++ /* get parent and remember it */ ++ dev->parent = sysfs_device_get(parent_devpath); ++ return dev->parent; ++} ++ ++struct sysfs_device *sysfs_device_get_parent_with_subsystem(struct sysfs_device *dev, const char *subsystem) ++{ ++ struct sysfs_device *dev_parent; ++ ++ dev_parent = sysfs_device_get_parent(dev); ++ while (dev_parent != NULL) { ++ if (strcmp(dev_parent->subsystem, subsystem) == 0) ++ return dev_parent; ++ dev_parent = sysfs_device_get_parent(dev_parent); ++ } ++ return NULL; ++} ++ ++char *sysfs_attr_get_value(const char *devpath, const char *attr_name) ++{ ++ char path_full[PATH_SIZE]; ++ const char *path; ++ char value[NAME_SIZE]; ++ struct sysfs_attr *attr_loop; ++ struct sysfs_attr *attr; ++ struct stat statbuf; ++ int fd; ++ ssize_t size; ++ size_t sysfs_len; ++ ++ dbg("open '%s'/'%s'", devpath, attr_name); ++ sysfs_len = strlcpy(path_full, sysfs_path, sizeof(path_full)); ++ path = &path_full[sysfs_len]; ++ strlcat(path_full, devpath, sizeof(path_full)); ++ strlcat(path_full, "/", sizeof(path_full)); ++ strlcat(path_full, attr_name, sizeof(path_full)); ++ ++ /* look for attribute in cache */ ++ list_for_each_entry(attr_loop, &attr_list, node) { ++ if (strcmp(attr_loop->path, path) == 0) { ++ dbg("found in cache '%s'", attr_loop->path); ++ return attr_loop->value; ++ } ++ } ++ ++ /* store attribute in cache (also negatives are kept in cache) */ ++ dbg("new uncached attribute '%s'", path_full); ++ attr = malloc(sizeof(struct sysfs_attr)); ++ if (attr == NULL) ++ return NULL; ++ memset(attr, 0x00, sizeof(struct sysfs_attr)); ++ strlcpy(attr->path, path, sizeof(attr->path)); ++ dbg("add to cache '%s'", path_full); ++ list_add(&attr->node, &attr_list); ++ ++ if (lstat(path_full, &statbuf) != 0) { ++ dbg("stat '%s' failed: %s", path_full, strerror(errno)); ++ goto out; ++ } ++ ++ if (S_ISLNK(statbuf.st_mode)) { ++ /* links return the last element of the target path */ ++ char link_target[PATH_SIZE]; ++ int len; ++ const char *pos; ++ ++ len = readlink(path_full, link_target, sizeof(link_target)); ++ if (len > 0) { ++ link_target[len] = '\0'; ++ pos = strrchr(link_target, '/'); ++ if (pos != NULL) { ++ dbg("cache '%s' with link value '%s'", path_full, value); ++ strlcpy(attr->value_local, &pos[1], sizeof(attr->value_local)); ++ attr->value = attr->value_local; ++ } ++ } ++ goto out; ++ } ++ ++ /* skip directories */ ++ if (S_ISDIR(statbuf.st_mode)) ++ goto out; ++ ++ /* skip non-readable files */ ++ if ((statbuf.st_mode & S_IRUSR) == 0) ++ goto out; ++ ++ /* read attribute value */ ++ fd = open(path_full, O_RDONLY); ++ if (fd < 0) { ++ dbg("attribute '%s' does not exist", path_full); ++ goto out; ++ } ++ size = read(fd, value, sizeof(value)); ++ close(fd); ++ if (size < 0) ++ goto out; ++ if (size == sizeof(value)) ++ goto out; ++ ++ /* got a valid value, store and return it */ ++ value[size] = '\0'; ++ remove_trailing_chars(value, '\n'); ++ dbg("cache '%s' with attribute value '%s'", path_full, value); ++ strlcpy(attr->value_local, value, sizeof(attr->value_local)); ++ attr->value = attr->value_local; ++ ++out: ++ return attr->value; ++} +diff --git a/libmultipath/sysfs.h b/libmultipath/sysfs.h +new file mode 100644 +index 0000000..4ac30db +--- /dev/null ++++ b/libmultipath/sysfs.h +@@ -0,0 +1,20 @@ ++/* ++ * sysfs.h ++ */ ++ ++#ifndef _LIBMULTIPATH_SYSFS_H ++#define _LIBMULTIPATH_SYSFS_H ++ ++#define dbg(format, arg...) do {} while (0) ++ ++int sysfs_init(char *path, size_t len); ++void sysfs_cleanup(void); ++void sysfs_device_set_values(struct sysfs_device *dev, const char *devpath, ++ const char *subsystem, const char *driver); ++struct sysfs_device *sysfs_device_get(const char *devpath); ++struct sysfs_device *sysfs_device_get_parent(struct sysfs_device *dev); ++struct sysfs_device *sysfs_device_get_parent_with_subsystem(struct sysfs_device *dev, const char *subsystem); ++char *sysfs_attr_get_value(const char *devpath, const char *attr_name); ++int sysfs_resolve_link(char *path, size_t size); ++ ++#endif +diff --git a/libmultipath/uevent.c b/libmultipath/uevent.c +index 6482698..a4028d8 100644 +--- a/libmultipath/uevent.c ++++ b/libmultipath/uevent.c +@@ -26,12 +26,14 @@ + #include + #include + #include ++#include + #include + #include + #include + #include + #include +-#include ++#include ++#include + #include + #include + #include +@@ -105,6 +107,8 @@ int uevent_listen(int (*uev_trigger)(str + { + int sock; + struct sockaddr_nl snl; ++ struct sockaddr_un sun; ++ socklen_t addrlen; + int retval; + int rcvbufsz = 128*1024; + int rcvsz = 0; +@@ -131,43 +135,72 @@ int uevent_listen(int (*uev_trigger)(str + pthread_attr_setstacksize(&attr, 64 * 1024); + pthread_create(&uevq_thr, &attr, uevq_thread, NULL); + +- memset(&snl, 0x00, sizeof(struct sockaddr_nl)); +- snl.nl_family = AF_NETLINK; +- snl.nl_pid = getpid(); +- snl.nl_groups = 0xffffffff; +- +- sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT); +- if (sock == -1) { +- condlog(0, "error getting socket, exit"); +- return 1; +- } +- + /* +- * try to avoid dropping uevents, even so, this is not a guarantee, +- * but it does help to change the netlink uevent socket's +- * receive buffer threshold from the default value of 106,496 to +- * the maximum value of 262,142. ++ * First check whether we have a udev socket + */ +- retval = setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &rcvbufsz, +- sizeof(rcvbufsz)); ++ memset(&sun, 0x00, sizeof(struct sockaddr_un)); ++ sun.sun_family = AF_LOCAL; ++ strcpy(&sun.sun_path[1], "/org/kernel/dm/multipath_event"); ++ addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(sun.sun_path+1) + 1; ++ ++ sock = socket(AF_LOCAL, SOCK_DGRAM, 0); ++ if (sock >= 0) { ++ const int feature_on = 1; ++ ++ condlog(3, "reading events from udev socket."); ++ ++ /* the bind takes care of ensuring only one copy running */ ++ retval = bind(sock, (struct sockaddr *) &sun, addrlen); ++ if (retval < 0) { ++ condlog(0, "bind failed, exit"); ++ goto exit; ++ } + +- if (retval < 0) { +- condlog(0, "error setting receive buffer size for socket, exit"); +- exit(1); +- } +- retval = getsockopt(sock, SOL_SOCKET, SO_RCVBUF, &rcvsz, prcvszsz); ++ /* enable receiving of the sender credentials */ ++ setsockopt(sock, SOL_SOCKET, SO_PASSCRED, ++ &feature_on, sizeof(feature_on)); ++ ++ } else { ++ /* Fallback to read kernel netlink events */ ++ memset(&snl, 0x00, sizeof(struct sockaddr_nl)); ++ snl.nl_family = AF_NETLINK; ++ snl.nl_pid = getpid(); ++ snl.nl_groups = 0xffffffff; ++ ++ sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT); ++ if (sock == -1) { ++ condlog(0, "error getting socket, exit"); ++ return 1; ++ } + +- if (retval < 0) { +- condlog(0, "error setting receive buffer size for socket, exit"); +- exit(1); +- } +- condlog(3, "receive buffer size for socket is %u.", rcvsz); ++ condlog(3, "reading events from kernel."); + +- retval = bind(sock, (struct sockaddr *) &snl, +- sizeof(struct sockaddr_nl)); +- if (retval < 0) { +- condlog(0, "bind failed, exit"); +- goto exit; ++ /* ++ * try to avoid dropping uevents, even so, this is not a guarantee, ++ * but it does help to change the netlink uevent socket's ++ * receive buffer threshold from the default value of 106,496 to ++ * the maximum value of 262,142. ++ */ ++ retval = setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &rcvbufsz, ++ sizeof(rcvbufsz)); ++ ++ if (retval < 0) { ++ condlog(0, "error setting receive buffer size for socket, exit"); ++ exit(1); ++ } ++ retval = getsockopt(sock, SOL_SOCKET, SO_RCVBUF, &rcvsz, prcvszsz); ++ if (retval < 0) { ++ condlog(0, "error setting receive buffer size for socket, exit"); ++ exit(1); ++ } ++ condlog(3, "receive buffer size for socket is %u.", rcvsz); ++ ++ retval = bind(sock, (struct sockaddr *) &snl, ++ sizeof(struct sockaddr_nl)); ++ if (retval < 0) { ++ condlog(0, "bind failed, exit"); ++ goto exit; ++ } + } + + while (1) { +diff --git a/libmultipath/util.c b/libmultipath/util.c +index 911ec55..eaf2266 100644 +--- a/libmultipath/util.c ++++ b/libmultipath/util.c +@@ -103,3 +103,55 @@ get_word (char * sentence, char ** word) + return skip + len; + } + ++size_t strlcpy(char *dst, const char *src, size_t size) ++{ ++ size_t bytes = 0; ++ char *q = dst; ++ const char *p = src; ++ char ch; ++ ++ while ((ch = *p++)) { ++ if (bytes+1 < size) ++ *q++ = ch; ++ bytes++; ++ } ++ ++ /* If size == 0 there is no space for a final null... */ ++ if (size) ++ *q = '\0'; ++ return bytes; ++} ++ ++size_t strlcat(char *dst, const char *src, size_t size) ++{ ++ size_t bytes = 0; ++ char *q = dst; ++ const char *p = src; ++ char ch; ++ ++ while (bytes < size && *q) { ++ q++; ++ bytes++; ++ } ++ if (bytes == size) ++ return (bytes + strlen(src)); ++ ++ while ((ch = *p++)) { ++ if (bytes+1 < size) ++ *q++ = ch; ++ bytes++; ++ } ++ ++ *q = '\0'; ++ return bytes; ++} ++ ++void remove_trailing_chars(char *path, char c) ++{ ++ size_t len; ++ ++ len = strlen(path); ++ while (len > 0 && path[len-1] == c) ++ path[--len] = '\0'; ++} ++ +diff --git a/libmultipath/util.h b/libmultipath/util.h +index e86bae2..d0df8aa 100644 +--- a/libmultipath/util.h ++++ b/libmultipath/util.h +@@ -6,7 +6,9 @@ void strchop(char *); + void basename (char * src, char * dst); + int filepresent (char * run); + int get_word (char * sentence, char ** word); +- ++size_t strlcpy(char *dst, const char *src, size_t size); ++size_t strlcat(char *dst, const char *src, size_t size); ++void remove_trailing_chars(char *path, char c); + + #define safe_sprintf(var, format, args...) \ + snprintf(var, sizeof(var), format, ##args) >= sizeof(var) +diff --git a/multipath/Makefile b/multipath/Makefile +index 947d481..a31afe2 100644 +--- a/multipath/Makefile ++++ b/multipath/Makefile +@@ -12,7 +12,7 @@ CFLAGS += -I$(multipathdir) -I$(checkers + ifeq ($(strip $(BUILD)),klibc) + OBJS += $(libdm) $(libsysfs) + else +- LDFLAGS += -ldevmapper -lsysfs ++ LDFLAGS += -ldevmapper + endif + + EXEC = multipath +@@ -21,7 +21,7 @@ all: $(BUILD) + + prepare: + make -C $(multipathdir) prepare +- rm -f core *.o *.gz ++ rm -f core *.o + + glibc: prepare $(OBJS) + $(CC) $(OBJS) -o $(EXEC) $(LDFLAGS) +@@ -37,17 +37,17 @@ $(MULTIPATHLIB)-$(BUILD).a: + + install: + install -d $(DESTDIR)$(bindir) +- install -s -m 755 $(EXEC) $(DESTDIR)$(bindir)/ ++ install -m 755 $(EXEC) $(DESTDIR)$(bindir)/ + install -d $(DESTDIR)/etc/udev/rules.d + install -m 644 multipath.rules $(DESTDIR)/etc/udev/rules.d/ + install -d $(DESTDIR)$(mandir) + install -m 644 $(EXEC).8 $(DESTDIR)$(mandir) ++ install -d $(DESTDIR)$(man5dir) + install -m 644 multipath.conf.5 $(DESTDIR)$(man5dir) + + uninstall: + rm $(DESTDIR)/etc/udev/rules.d/multipath.rules + rm $(DESTDIR)$(bindir)/$(EXEC) +- rm $(DESTDIR)$(mandir)/$(EXEC).8 + + clean: +- rm -f core *.o $(EXEC) *.gz ++ rm -f core *.o $(EXEC) +diff --git a/multipath/main.c b/multipath/main.c +index c3d0dac..e2d7f41 100644 +--- a/multipath/main.c ++++ b/multipath/main.c +@@ -25,7 +25,6 @@ + #include + #include + #include +-#include + + #include + #include +@@ -37,6 +36,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -373,13 +373,13 @@ main (int argc, char *argv[]) + if (dm_prereq(DEFAULT_TARGET)) + exit(1); + +- if (sysfs_get_mnt_path(sysfs_path, FILE_NAME_SIZE)) { +- condlog(0, "multipath tools need sysfs mounted"); +- exit(1); +- } + if (load_config(DEFAULT_CONFIGFILE)) + exit(1); + ++ if (sysfs_init(conf->sysfs_dir, FILE_NAME_SIZE)) { ++ condlog(0, "multipath tools need sysfs mounted"); ++ exit(1); ++ } + while ((arg = getopt(argc, argv, ":dhl::FfM:v:p:b:t")) != EOF ) { + switch(arg) { + case 1: printf("optarg : %s\n",optarg); +@@ -472,6 +472,7 @@ main (int argc, char *argv[]) + condlog(3, "restart multipath configuration process"); + + out: ++ sysfs_cleanup(); + free_config(conf); + dm_lib_release(); + dm_lib_exit(); +diff --git a/multipath/multipath.init.suse b/multipath/multipath.init.suse +new file mode 100755 +index 0000000..34a128c +--- /dev/null ++++ b/multipath/multipath.init.suse +@@ -0,0 +1,104 @@ ++#! /bin/sh ++# Copyright (c) 2005 SuSE GmbH Nuernberg, Germany. ++# ++# Author: Hannes Reinecke ++# ++# init.d/boot.multipath ++# ++### BEGIN INIT INFO ++# Provides: boot.multipath ++# Required-Start: boot.device-mapper boot.udev ++# Required-Stop: ++# Default-Start: B ++# Default-Stop: ++# Description: Create multipath device targets ++### END INIT INFO ++ ++PATH=/bin:/usr/bin:/sbin:/usr/sbin ++PROGRAM=/sbin/multipath ++ ++# Set the maximum number of open files ++MAX_OPEN_FDS=4096 ++ ++test -x $PROGRAM || exit 5 ++ ++# Shell functions sourced from /etc/rc.status: ++# rc_check check and set local and overall rc status ++# rc_status check and set local and overall rc status ++# rc_status -v ditto but be verbose in local rc status ++# rc_status -v -r ditto and clear the local rc status ++# rc_failed set local and overall rc status to failed ++# rc_reset clear local rc status (overall remains) ++# rc_exit exit appropriate to overall rc status ++. /etc/rc.status ++ ++# First reset status of this service ++rc_reset ++ ++# Return values acc. to LSB for all commands but status: ++# 0 - success ++# 1 - misc error ++# 2 - invalid or excess args ++# 3 - unimplemented feature (e.g. reload) ++# 4 - insufficient privilege ++# 5 - program not installed ++# 6 - program not configured ++# 7 - program is not running ++# ++# Note that starting an already running service, stopping ++# or restarting a not-running service as well as the restart ++# with force-reload (in case signalling is not supported) are ++# considered a success. ++ ++case "$1" in ++ start) ++ echo -n "Creating multipath targets" ++ # Check whether multipath daemon is already running ++ if /sbin/multipathd -k"list paths" > /dev/null 2>&1 ; then ++ echo -n " (multipathd running)" ++ rc_status -v ++ rc_exit ++ fi ++ ++ # Load prerequisite module ++ modprobe dm-multipath ++ ++ # Be a chicken and flush all existing maps ++ $PROGRAM -F ++ ++ # Clear /dev/disk/by-name/ prior to start-up; multipath will ++ # recreate them. ++ rm -f /dev/disk/by-name/* 2>&1 >/dev/null ++ ++ # Set the maximum number of open files ++ if [ -n "$MAX_OPEN_FDS" ] ; then ++ ulimit -n $MAX_OPEN_FDS ++ fi ++ ++ # Start the program directly as checkproc doesn't work here ++ $PROGRAM -v 0 ++ ++ # Create all partitions which might have been missing ++ /sbin/dmsetup ls --target multipath --exec "/sbin/kpartx -a -p -part" ++ ++ # Remember status and be verbose ++ rc_status -v ++ sleep 1 ++ ;; ++ stop) ++ # Remove all partition mappings ++ if dmsetup ls | grep -q -- -part; then ++ /sbin/dmsetup ls --target multipath --exec "/sbin/kpartx -d -p -part" ++ fi ++ ++ # And remove the multipath mappings themselves ++ for map in $(/sbin/dmsetup ls --target multipath | cut -f 1); do ++ /sbin/dmsetup remove $map ++ done ++ ;; ++ *) ++ echo "Usage: $0 {start|stop}" ++ exit 1 ++ ;; ++esac ++rc_exit +diff --git a/multipathd/71-multipath.rules b/multipathd/71-multipath.rules +new file mode 100644 +index 0000000..756ebe7 +--- /dev/null ++++ b/multipathd/71-multipath.rules +@@ -0,0 +1,27 @@ ++# ++# persistent links for device-mapper devices ++# only hardware-backed device-mapper devices (ie multipath, dmraid, ++# and kpartx) have meaningful persistent device names ++# ++ ++KERNEL!="dm-*", GOTO="multipath_end" ++ACTION=="add|remove", GOTO="multipath_end" ++ ++ACTION=="change", IMPORT{program}=="/sbin/kpartx_id %M %m" ++ ++# Create persistent links for tables ++ACTION=="change", ENV{ID_DM_TABLE}=="mpath|dmraid", ENV{ID_DM_TYPE}=="?*", \ ++ SYMLINK+="disk/by-id/$env{ID_DM_TYPE}-$env{ID_DM_NAME}" ++ ++# Create dm tables for partitions ++ACTION=="change", ENV{ID_DM_TABLE}=="mpath|dmraid", \ ++ RUN+="/sbin/kpartx -a -p _part /dev/mapper/$env{ID_DM_NAME}" ++ ++# Create persistent links for partitions ++ACTION=="change", ENV{ID_DM_TABLE}=="part", ENV{ID_DM_TYPE}=="?*", \ ++ SYMLINK+="disk/by-id/$env{ID_DM_TYPE}-$env{ID_DM_NAME}-part$env{ID_DM_PART}" ++ ++# socket for uevents ++RUN+="socket:/org/kernel/dm/multipath_event" ++LABEL="multipath_end" ++ +diff --git a/multipathd/Makefile b/multipathd/Makefile +index da351dc..bbab8da 100644 +--- a/multipathd/Makefile ++++ b/multipathd/Makefile +@@ -7,7 +7,7 @@ include ../Makefile.inc + # basic flags setting + # + CFLAGS += -DDAEMON -I$(multipathdir) -I$(checkersdir) +-LDFLAGS = -lpthread -ldevmapper -lsysfs -lreadline -lncurses ++LDFLAGS = -lpthread -ldevmapper -lreadline -lncurses + + # + # debuging stuff +@@ -44,7 +44,7 @@ $(MULTIPATHLIB)-glibc.a: + + install: + install -d $(DESTDIR)$(bindir) +- install -s -m 755 $(EXEC) $(DESTDIR)$(bindir) ++ install -m 755 $(EXEC) $(DESTDIR)$(bindir) + install -d $(DESTDIR)$(rcdir) + install -d $(DESTDIR)$(mandir) + install -m 644 $(EXEC).8 $(DESTDIR)$(mandir) +@@ -52,9 +52,8 @@ install: + uninstall: + rm -f $(DESTDIR)$(bindir)/$(EXEC) + rm -f $(DESTDIR)$(rcdir)/$(EXEC) +- rm -f $(DESTDIR)$(mandir)/$(EXEC).8 + + clean: + $(MAKE) -C $(multipathdir) prepare DAEMON=1 +- rm -f core *.o $(EXEC) *.gz ++ rm -f core *.o $(EXEC) + +diff --git a/multipathd/cli_handlers.c b/multipathd/cli_handlers.c +index 4938e84..7bae02a 100644 +--- a/multipathd/cli_handlers.c ++++ b/multipathd/cli_handlers.c +@@ -13,6 +13,7 @@ + #include + #include + #include ++#include + + #include "main.h" + #include "cli.h" +@@ -312,6 +313,9 @@ cli_add_map (void * v, char ** reply, in + { + struct vectors * vecs = (struct vectors *)data; + char * param = get_keyparam(v, MAP); ++ int minor; ++ char dev_path[PATH_SIZE]; ++ struct sysfs_device *sysdev; + + condlog(2, "%s: add map (operator)", param); + +@@ -321,7 +325,18 @@ cli_add_map (void * v, char ** reply, in + condlog(2, "%s: map blacklisted", param); + return 0; + } +- return ev_add_map(param, vecs); ++ minor = dm_get_minor(param); ++ if (minor < 0) { ++ condlog(2, "%s: not a device mapper table", param); ++ return 0; ++ } ++ sprintf(dev_path,"/block/dm-%d", minor); ++ sysdev = sysfs_device_get(dev_path); ++ if (!sysdev) { ++ condlog(2, "%s: not found in sysfs", param); ++ return 0; ++ } ++ return ev_add_map(sysdev, vecs); + } + + int +diff --git a/multipathd/main.c b/multipathd/main.c +index 94b0b95..a173da3 100644 +--- a/multipathd/main.c ++++ b/multipathd/main.c +@@ -14,12 +14,6 @@ + #include + + /* +- * libsysfs +- */ +-#include +-#include +- +-/* + * libcheckers + */ + #include +@@ -40,6 +34,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -208,31 +203,29 @@ flush_map(struct multipath * mpp, struct + } + + static int +-uev_add_map (char * devname, struct vectors * vecs) ++uev_add_map (struct sysfs_device * dev, struct vectors * vecs) + { +- condlog(2, "%s: add map (uevent)", devname); +- return ev_add_map(devname, vecs); ++ condlog(2, "%s: add map (uevent)", dev->kernel); ++ return ev_add_map(dev, vecs); + } + + int +-ev_add_map (char * devname, struct vectors * vecs) ++ev_add_map (struct sysfs_device * dev, struct vectors * vecs) + { +- int major, minor; +- char dev_t[BLK_DEV_SIZE]; + char * alias; ++ char *dev_t; ++ int major, minor; + char * refwwid; + struct multipath * mpp; + int map_present; + int r = 1; + +- /* libsysfs seems to forget to terminate the string... */ +- memset(dev_t, 0, BLK_DEV_SIZE); +- if (sscanf(devname, "dm-%d", &minor) == 1 && +- !sysfs_get_dev(sysfs_path, devname, dev_t, BLK_DEV_SIZE) && +- sscanf(dev_t, "%d:%d", &major, &minor) == 2) +- alias = dm_mapname(major, minor); +- else +- alias = STRDUP(devname); ++ dev_t = sysfs_attr_get_value(dev->devpath, "dev"); ++ ++ if (!dev_t || sscanf(dev_t, "%d:%d", &major, &minor) != 2) ++ return 1; ++ ++ alias = dm_mapname(major, minor); + + if (!alias) + return 1; +@@ -241,7 +234,6 @@ ev_add_map (char * devname, struct vecto + + if (map_present && dm_type(alias, DEFAULT_TARGET) <= 0) { + condlog(4, "%s: not a multipath map", alias); +- FREE(alias); + return 0; + } + +@@ -254,8 +246,7 @@ ev_add_map (char * devname, struct vecto + * of uev_add_path + */ + condlog(0, "%s: devmap already registered", +- devname); +- FREE(alias); ++ dev->kernel); + return 0; + } + +@@ -265,10 +256,10 @@ ev_add_map (char * devname, struct vecto + if (map_present && (mpp = add_map_without_path(vecs, minor, alias, + start_waiter_thread))) { + sync_map_state(mpp); +- condlog(3, "%s: devmap %s added", alias, devname); ++ condlog(3, "%s: devmap %s added", alias, dev->kernel); + return 0; + } +- refwwid = get_refwwid(devname, DEV_DEVMAP, vecs->pathvec); ++ refwwid = get_refwwid(dev->kernel, DEV_DEVMAP, vecs->pathvec); + + if (refwwid) { + r = coalesce_paths(vecs, NULL, refwwid); +@@ -276,20 +267,19 @@ ev_add_map (char * devname, struct vecto + } + + if (!r) +- condlog(3, "%s: devmap %s added", alias, devname); ++ condlog(3, "%s: devmap %s added", alias, dev->kernel); + else +- condlog(0, "%s: uev_add_map %s failed", alias, devname); ++ condlog(0, "%s: uev_add_map %s failed", alias, dev->kernel); + + FREE(refwwid); +- FREE(alias); + return r; + } + + static int +-uev_remove_map (char * devname, struct vectors * vecs) ++uev_remove_map (struct sysfs_device * dev, struct vectors * vecs) + { +- condlog(2, "%s: remove map (uevent)", devname); +- return ev_remove_map(devname, vecs); ++ condlog(2, "%s: remove map (uevent)", dev->kernel); ++ return ev_remove_map(dev->kernel, vecs); + } + + int +@@ -310,13 +300,13 @@ ev_remove_map (char * devname, struct ve + } + + static int +-uev_umount_map (char * devname, struct vectors * vecs) ++uev_umount_map (struct sysfs_device * dev, struct vectors * vecs) + { + struct multipath * mpp; + +- condlog(2, "%s: umount map (uevent)", devname); ++ condlog(2, "%s: umount map (uevent)", dev->kernel); + +- mpp = find_mp_by_str(vecs->mpvec, devname); ++ mpp = find_mp_by_str(vecs->mpvec, dev->kernel); + + if (!mpp) + return 0; +@@ -331,10 +321,10 @@ uev_umount_map (char * devname, struct v + } + + static int +-uev_add_path (char * devname, struct vectors * vecs) ++uev_add_path (struct sysfs_device * dev, struct vectors * vecs) + { +- condlog(2, "%s: add path (uevent)", devname); +- return (ev_add_path(devname, vecs) != 1)? 0 : 1; ++ condlog(2, "%s: add path (uevent)", dev->kernel); ++ return (ev_add_path(dev->kernel, vecs) != 1)? 0 : 1; + } + + +@@ -450,10 +440,10 @@ out: + } + + static int +-uev_remove_path (char * devname, struct vectors * vecs) ++uev_remove_path (struct sysfs_device * dev, struct vectors * vecs) + { +- condlog(2, "%s: remove path (uevent)", devname); +- return ev_remove_path(devname, vecs); ++ condlog(2, "%s: remove path (uevent)", dev->kernel); ++ return ev_remove_path(dev->kernel, vecs); + } + + int +@@ -636,7 +626,7 @@ int + uev_trigger (struct uevent * uev, void * trigger_data) + { + int r = 0; +- char devname[32]; ++ struct sysfs_device *sysdev; + struct vectors * vecs; + + vecs = (struct vectors *)trigger_data; +@@ -644,7 +634,7 @@ uev_trigger (struct uevent * uev, void * + if (uev_discard(uev->devpath)) + return 0; + +- basename(uev->devpath, devname); ++ sysdev = sysfs_device_get(uev->devpath); + lock(vecs->lock); + + /* +@@ -652,17 +642,17 @@ uev_trigger (struct uevent * uev, void * + * Add events are ignored here as the tables + * are not fully initialised then. + */ +- if (!strncmp(devname, "dm-", 3)) { ++ if (!strncmp(sysdev->kernel, "dm-", 3)) { + if (!strncmp(uev->action, "change", 6)) { +- r = uev_add_map(devname, vecs); ++ r = uev_add_map(sysdev, vecs); + goto out; + } + if (!strncmp(uev->action, "remove", 6)) { +- r = uev_remove_map(devname, vecs); ++ r = uev_remove_map(sysdev, vecs); + goto out; + } + if (!strncmp(uev->action, "umount", 6)) { +- r = uev_umount_map(devname, vecs); ++ r = uev_umount_map(sysdev, vecs); + goto out; + } + goto out; +@@ -672,15 +662,15 @@ uev_trigger (struct uevent * uev, void * + * path add/remove event + */ + if (filter_devnode(conf->blist_devnode, conf->elist_devnode, +- devname) > 0) ++ sysdev->kernel) > 0) + goto out; + + if (!strncmp(uev->action, "add", 3)) { +- r = uev_add_path(devname, vecs); ++ r = uev_add_path(sysdev, vecs); + goto out; + } + if (!strncmp(uev->action, "remove", 6)) { +- r = uev_remove_path(devname, vecs); ++ r = uev_remove_path(sysdev, vecs); + goto out; + } + +@@ -1278,7 +1268,7 @@ child (void * param) + if (!vecs) + exit(1); + +- if (sysfs_get_mnt_path(sysfs_path, FILE_NAME_SIZE)) { ++ if (sysfs_init(conf->sysfs_dir, FILE_NAME_SIZE)) { + condlog(0, "can not find sysfs mount point"); + exit(1); + } +@@ -1315,6 +1305,8 @@ child (void * param) + pthread_cancel(uevent_thr); + pthread_cancel(uxlsnr_thr); + ++ sysfs_cleanup(); ++ + free_keys(keys); + keys = NULL; + free_handlers(handlers); +diff --git a/multipathd/main.h b/multipathd/main.h +index d0cce3a..1a6dc55 100644 +--- a/multipathd/main.h ++++ b/multipathd/main.h +@@ -7,7 +7,7 @@ + int reconfigure (struct vectors *); + int ev_add_path (char *, struct vectors *); + int ev_remove_path (char *, struct vectors *); +-int ev_add_map (char *, struct vectors *); ++int ev_add_map (struct sysfs_device *, struct vectors *); + int ev_remove_map (char *, struct vectors *); + + #endif /* MAIN_H */ +diff --git a/multipathd/multipathd.init.suse b/multipathd/multipathd.init.suse +new file mode 100755 +index 0000000..a297956 +--- /dev/null ++++ b/multipathd/multipathd.init.suse +@@ -0,0 +1,155 @@ ++#! /bin/sh ++# Copyright (c) 1995-2001 SuSE GmbH Nuernberg, Germany. ++# ++# Author: Thorsten Kukuk ++# ++# init.d/routed ++# ++# and symbolic its link ++# ++# /usr/sbin/rcrouted ++# ++### BEGIN INIT INFO ++# Provides: multipathd ++# Required-Start: $syslog ++# Required-Stop: ++# Default-Start: 3 5 ++# Default-Stop: 0 1 2 4 6 ++# Description: Starts multipath daemon ++### END INIT INFO ++ ++PATH=/bin:/usr/bin:/sbin:/usr/sbin ++DAEMON=/sbin/multipathd ++PIDFILE=/var/run/multipathd.pid ++ ++# Set the maximum number of open files ++MAX_OPEN_FDS=4096 ++ ++test -x $DAEMON || exit 5 ++ ++# Shell functions sourced from /etc/rc.status: ++# rc_check check and set local and overall rc status ++# rc_status check and set local and overall rc status ++# rc_status -v ditto but be verbose in local rc status ++# rc_status -v -r ditto and clear the local rc status ++# rc_failed set local and overall rc status to failed ++# rc_reset clear local rc status (overall remains) ++# rc_exit exit appropriate to overall rc status ++. /etc/rc.status ++ ++# First reset status of this service ++rc_reset ++ ++# Return values acc. to LSB for all commands but status: ++# 0 - success ++# 1 - misc error ++# 2 - invalid or excess args ++# 3 - unimplemented feature (e.g. reload) ++# 4 - insufficient privilege ++# 5 - program not installed ++# 6 - program not configured ++# 7 - program is not running ++# ++# Note that starting an already running service, stopping ++# or restarting a not-running service as well as the restart ++# with force-reload (in case signalling is not supported) are ++# considered a success. ++ ++case "$1" in ++ start) ++ echo -n "Starting multipathd" ++ ++ modprobe dm-multipath ++ ++ # Set the maximum number of open files ++ if [ -n "$MAX_OPEN_FDS" ] ; then ++ ulimit -n $MAX_OPEN_FDS ++ fi ++ ++ if [ -f $PIDFILE ]; then ++ PID="$(cat $PIDFILE)" ++ PROCNAME="$(ps -o cmd --no-headers $PID)" ++ fi ++ ++ if [ "$PROCNAME" != "$DAEMON" ]; then ++ $DAEMON ++ fi ++ ++ # Remember status and be verbose ++ rc_status -v ++ sleep 1 ++ ;; ++ stop) ++ echo -n "Shutting down multipathd" ++ # Because of the way how multipathd sets up its own namespace ++ # and chroots to it, killproc cannot be used with this process. ++ # So implement a cruder version: ++ if [ -f $PIDFILE ]; then ++ PID="$(cat $PIDFILE)" ++ PROCNAME="$(ps -o cmd --no-headers $PID)" ++ fi ++ ++ if [ "$PROCNAME" == "$DAEMON" ]; then ++ kill -TERM $PID ++ fi ++ ++ # Remember status and be verbose ++ rc_status -v ++ ;; ++ try-restart) ++ ## Stop the service and if this succeeds (i.e. the ++ ## service was running before), start it again. ++ $0 status >/dev/null && $0 restart ++ ++ # Remember status and be quiet ++ rc_status ++ ;; ++ restart|force-reload) ++ ## Stop the service and regardless of whether it was ++ ## running or not, start it again. ++ $0 stop ++ $0 start ++ ++ # Remember status and be quiet ++ rc_status ++ ;; ++ reload) ++ ## Like force-reload, but if daemon does not support ++ ## signalling, do nothing (!) ++ ++ # If it does not support reload: ++ exit 3 ++ ;; ++ status) ++ echo -n "Checking for multipathd: " ++ ++ # Status has a slightly different for the status command: ++ # 0 - service running ++ # 1 - service dead, but /var/run/ pid file exists ++ # 2 - service dead, but /var/lock/ lock file exists ++ # 3 - service not running ++ ++ if [ -f $PIDFILE ]; then ++ PID="$(cat $PIDFILE)" ++ PROCNAME="$(ps -o cmd --no-headers $PID)" ++ if [ "$PROCNAME" == "$DAEMON" ]; then ++ (exit 0) ++ else ++ (exit 1) ++ fi ++ else ++ (exit 3) ++ fi ++ ++ rc_status -v ++ ;; ++ probe) ++ ## Optional: Probe for the necessity of a reload, ++ ## give out the argument which is required for a reload. ++ ;; ++ *) ++ echo "Usage: $0 {start|stop|status|try-restart|restart|force-reload|reload|probe}" ++ exit 1 ++ ;; ++esac ++rc_exit +diff --git a/path_priority/pp_alua/Makefile b/path_priority/pp_alua/Makefile +index 6c58529..ce78455 100644 +--- a/path_priority/pp_alua/Makefile ++++ b/path_priority/pp_alua/Makefile +@@ -41,10 +41,9 @@ install: $(EXEC) $(EXEC).8 + + uninstall: + rm $(DESTDIR)$(bindir)/$(EXEC) +- rm $(DESTDIR)$(mandir)/$(EXEC).8 + + clean: +- rm -f *.o *.gz $(EXEC) ++ rm -f *.o $(EXEC) + + main.o: main.c rtpg.h spc3.h + +diff --git a/path_priority/pp_balance_units/Makefile b/path_priority/pp_balance_units/Makefile +index bed7fb0..b0fee28 100644 +--- a/path_priority/pp_balance_units/Makefile ++++ b/path_priority/pp_balance_units/Makefile +@@ -22,7 +22,7 @@ EXEC = mpath_prio_balance_units + all: $(BUILD) + + prepare: +- rm -f core *.o *.gz ++ rm -f core *.o + + glibc: prepare $(OBJS) + $(CC) -o $(EXEC) $(OBJS) $(LDFLAGS) +@@ -35,10 +35,10 @@ $(MULTIPATHLIB)-$(BUILD).a: + + install: + install -d $(DESTDIR)$(bindir) +- install -s -m 755 $(EXEC) $(DESTDIR)$(bindir)/ ++ install -m 755 $(EXEC) $(DESTDIR)$(bindir)/ + + uninstall: + rm $(DESTDIR)$(bindir)/$(EXEC) + + clean: +- rm -f core *.o $(EXEC) *.gz ++ rm -f core *.o $(EXEC) +diff --git a/path_priority/pp_emc/Makefile b/path_priority/pp_emc/Makefile +index 651bdcd..8c6e922 100644 +--- a/path_priority/pp_emc/Makefile ++++ b/path_priority/pp_emc/Makefile +@@ -14,7 +14,7 @@ klibc: $(OBJS) + $(CC) -static -o $(EXEC) $(OBJS) + + install: $(EXEC) +- install -s -m 755 $(EXEC) $(DESTDIR)$(bindir)/$(EXEC) ++ install -m 755 $(EXEC) $(DESTDIR)$(bindir)/$(EXEC) + + uninstall: + rm $(DESTDIR)$(bindir)/$(EXEC) +diff --git a/path_priority/pp_hds_modular/Makefile b/path_priority/pp_hds_modular/Makefile +index a0249a5..1c276e6 100644 +--- a/path_priority/pp_hds_modular/Makefile ++++ b/path_priority/pp_hds_modular/Makefile +@@ -14,7 +14,7 @@ klibc: $(OBJS) + $(CC) -static -o $(EXEC) $(OBJS) + + install: $(EXEC) +- install -s -m 755 $(EXEC) $(DESTDIR)$(bindir)/$(EXEC) ++ install -m 755 $(EXEC) $(DESTDIR)$(bindir)/$(EXEC) + + uninstall: + rm $(DESTDIR)$(bindir)/$(EXEC) +diff --git a/path_priority/pp_netapp/Makefile b/path_priority/pp_netapp/Makefile +index 9e7d3a3..2d571b0 100644 +--- a/path_priority/pp_netapp/Makefile ++++ b/path_priority/pp_netapp/Makefile +@@ -14,7 +14,7 @@ klibc: $(OBJS) + $(CC) -static -o $(EXEC) $(OBJS) + + install: $(EXEC) +- install -s -m 755 $(EXEC) $(DESTDIR)$(bindir)/$(EXEC) ++ install -m 755 $(EXEC) $(DESTDIR)$(bindir)/$(EXEC) + + uninstall: + rm $(DESTDIR)$(bindir)/$(EXEC) +diff --git a/path_priority/pp_random/Makefile b/path_priority/pp_random/Makefile +index 85f42a2..ca7974e 100644 +--- a/path_priority/pp_random/Makefile ++++ b/path_priority/pp_random/Makefile +@@ -14,7 +14,7 @@ klibc: $(OBJS) + $(CC) -static -o $(EXEC) $(OBJS) + + install: $(EXEC) +- install -s -m 755 $(EXEC) $(DESTDIR)$(bindir)/$(EXEC) ++ install -m 755 $(EXEC) $(DESTDIR)$(bindir)/$(EXEC) + + uninstall: + rm $(DESTDIR)$(bindir)/$(EXEC) +diff --git a/path_priority/pp_tpc/Makefile b/path_priority/pp_tpc/Makefile +index 86841dd..624f76d 100644 +--- a/path_priority/pp_tpc/Makefile ++++ b/path_priority/pp_tpc/Makefile +@@ -14,7 +14,7 @@ klibc: $(OBJS) + $(CC) -static -o $(EXEC) $(OBJS) + + install: $(EXEC) +- install -s -m 755 $(EXEC) $(DESTDIR)$(bindir)/$(EXEC) ++ install -m 755 $(EXEC) $(DESTDIR)$(bindir)/$(EXEC) + + uninstall: + rm $(DESTDIR)$(bindir)/$(EXEC) diff --git a/multipath-tools.changes b/multipath-tools.changes index b54107b..6df02f9 100644 --- a/multipath-tools.changes +++ b/multipath-tools.changes @@ -1,3 +1,16 @@ +------------------------------------------------------------------- +Mon May 14 16:20:55 CEST 2007 - hare@suse.de + +- Merge in latest fixes from upstream +- Add all SuSE specific files to git repository. + +------------------------------------------------------------------- +Fri May 11 16:06:16 CEST 2007 - hare@suse.de + +- Include latest changes from upstream +- Remove libsysfs (242766) +- Handle extended partitions for kpartx + ------------------------------------------------------------------- Mon Dec 4 16:51:13 CET 2006 - dmueller@suse.de diff --git a/multipath-tools.spec b/multipath-tools.spec index cc8f892..0f3b712 100644 --- a/multipath-tools.spec +++ b/multipath-tools.spec @@ -1,7 +1,7 @@ # # spec file for package multipath-tools (Version 0.4.7) # -# Copyright (c) 2006 SUSE LINUX Products GmbH, Nuernberg, Germany. +# Copyright (c) 2007 SUSE LINUX Products GmbH, Nuernberg, Germany. # This file and all modifications and additions to the pristine # package are under the same license as the package itself. # @@ -11,7 +11,7 @@ # norootforbuild Name: multipath-tools -BuildRequires: device-mapper-devel readline-devel sysfsutils +BuildRequires: device-mapper-devel readline-devel URL: http://christophe.varoqui.free.fr/ License: BSD License and BSD-like, GNU General Public License (GPL) Group: System/Base @@ -21,20 +21,13 @@ PreReq: %insserv_prereq %endif Autoreqprov: on Version: 0.4.7 -Release: 29 +Release: 48 Summary: Tools to Manage Multipathed Devices with the device-mapper Source: multipath-tools-%{version}.tar.bz2 -Source1: multipathd -Source3: 71-multipath.rules -Source4: boot.multipath -Source5: mpath_id -Source6: kpartx_id -Source7: 72-multipath-compat.rules BuildRoot: %{_tmppath}/%{name}-%{version}-build Patch0: %{name}-git-update.patch -Patch10: %{name}-online-device.patch -Patch11: %{name}-no-gz-for-manpage -Patch15: %{name}-strip.patch +Patch1: %{name}-local-fixes +Patch10: %{name}-suse-update %description This package provides the tools to manage multipathed devices by @@ -60,8 +53,8 @@ Authors: %prep %setup -n multipath-tools-%{version} %patch0 -p1 -%patch11 -p1 -%patch15 +%patch1 -p1 +%patch10 -p1 %build make OPTFLAGS="$RPM_OPT_FLAGS" BUILD=glibc @@ -71,13 +64,10 @@ mkdir -p $RPM_BUILD_ROOT/sbin mkdir -p $RPM_BUILD_ROOT%{_mandir}/man8 make DESTDIR=$RPM_BUILD_ROOT install rm $RPM_BUILD_ROOT/etc/udev/rules.d/multipath.rules -install -m 644 %{SOURCE3} $RPM_BUILD_ROOT/etc/udev/rules.d -install -m 644 %{SOURCE7} $RPM_BUILD_ROOT/etc/udev/rules.d +install -m 644 multipathd/71-multipath.rules $RPM_BUILD_ROOT/etc/udev/rules.d mkdir -p $RPM_BUILD_ROOT/etc/init.d -install -m 744 %{SOURCE1} $RPM_BUILD_ROOT/etc/init.d -install -m 744 %{SOURCE4} $RPM_BUILD_ROOT/etc/init.d -install -m 755 %{SOURCE5} $RPM_BUILD_ROOT/sbin -install -m 755 %{SOURCE6} $RPM_BUILD_ROOT/sbin +install -m 744 multipath/multipath.init.suse $RPM_BUILD_ROOT/etc/init.d/boot.multipath +install -m 744 multipathd/multipathd.init.suse $RPM_BUILD_ROOT/etc/init.d/multipathd mkdir -p $RPM_BUILD_ROOT/var/cache/multipath/ %clean @@ -100,7 +90,6 @@ mkdir -p $RPM_BUILD_ROOT/var/cache/multipath/ /sbin/devmap_name /sbin/multipath /sbin/kpartx -/sbin/mpath_id /sbin/kpartx_id /sbin/multipathd /sbin/mpath_prio_netapp @@ -110,14 +99,23 @@ mkdir -p $RPM_BUILD_ROOT/var/cache/multipath/ /sbin/mpath_prio_emc /sbin/mpath_prio_tpc /sbin/mpath_prio_hds_modular +/sbin/mpath_prio_hp_sw %attr (0700, root, root) /var/cache/multipath %{_mandir}/man8/devmap_name.8* %{_mandir}/man8/multipath.8* +%{_mandir}/man5/multipath.conf.5* %{_mandir}/man8/kpartx.8* %{_mandir}/man8/multipathd.8* %{_mandir}/man8/mpath_prio_alua.8* -%changelog -n multipath-tools +%changelog +* Mon May 14 2007 - hare@suse.de +- Merge in latest fixes from upstream +- Add all SuSE specific files to git repository. +* Fri May 11 2007 - hare@suse.de +- Include latest changes from upstream +- Remove libsysfs (242766) +- Handle extended partitions for kpartx * Mon Dec 04 2006 - dmueller@suse.de - don't build as root * Fri Nov 17 2006 - hare@suse.de diff --git a/multipathd b/multipathd deleted file mode 100644 index 9aae27d..0000000 --- a/multipathd +++ /dev/null @@ -1,155 +0,0 @@ -#! /bin/sh -# Copyright (c) 1995-2001 SuSE GmbH Nuernberg, Germany. -# -# Author: Thorsten Kukuk -# -# init.d/routed -# -# and symbolic its link -# -# /usr/sbin/rcrouted -# -### BEGIN INIT INFO -# Provides: multipathd -# Required-Start: $syslog -# Required-Stop: -# Default-Start: 3 5 -# Default-Stop: 0 1 2 4 6 -# Description: Starts multipath daemon -### END INIT INFO - -PATH=/bin:/usr/bin:/sbin:/usr/sbin -DAEMON=/sbin/multipathd -PIDFILE=/var/run/multipathd.pid - -# Set the maximum number of open files -MAX_OPEN_FDS=4096 - -test -x $DAEMON || exit 5 - -# Shell functions sourced from /etc/rc.status: -# rc_check check and set local and overall rc status -# rc_status check and set local and overall rc status -# rc_status -v ditto but be verbose in local rc status -# rc_status -v -r ditto and clear the local rc status -# rc_failed set local and overall rc status to failed -# rc_reset clear local rc status (overall remains) -# rc_exit exit appropriate to overall rc status -. /etc/rc.status - -# First reset status of this service -rc_reset - -# Return values acc. to LSB for all commands but status: -# 0 - success -# 1 - misc error -# 2 - invalid or excess args -# 3 - unimplemented feature (e.g. reload) -# 4 - insufficient privilege -# 5 - program not installed -# 6 - program not configured -# 7 - program is not running -# -# Note that starting an already running service, stopping -# or restarting a not-running service as well as the restart -# with force-reload (in case signalling is not supported) are -# considered a success. - -case "$1" in - start) - echo -n "Starting multipathd" - - modprobe dm-multipath - - # Set the maximum number of open files - if [ -n "$MAX_OPEN_FDS" ] ; then - ulimit -n $MAX_OPEN_FDS - fi - - if [ -f $PIDFILE ]; then - PID="$(cat $PIDFILE)" - PROCNAME="$(ps -o cmd --no-headers $PID)" - fi - - if [ "$PROCNAME" != "$DAEMON" ]; then - $DAEMON - fi - - # Remember status and be verbose - rc_status -v - sleep 1 - ;; - stop) - echo -n "Shutting down multipathd" - # Because of the way how multipathd sets up its own namespace - # and chroots to it, killproc cannot be used with this process. - # So implement a cruder version: - if [ -f $PIDFILE ]; then - PID="$(cat $PIDFILE)" - PROCNAME="$(ps -o cmd --no-headers $PID)" - fi - - if [ "$PROCNAME" == "$DAEMON" ]; then - kill -TERM $PID - fi - - # Remember status and be verbose - rc_status -v - ;; - try-restart) - ## Stop the service and if this succeeds (i.e. the - ## service was running before), start it again. - $0 status >/dev/null && $0 restart - - # Remember status and be quiet - rc_status - ;; - restart|force-reload) - ## Stop the service and regardless of whether it was - ## running or not, start it again. - $0 stop - $0 start - - # Remember status and be quiet - rc_status - ;; - reload) - ## Like force-reload, but if daemon does not support - ## signalling, do nothing (!) - - # If it does not support reload: - exit 3 - ;; - status) - echo -n "Checking for multipathd: " - - # Status has a slightly different for the status command: - # 0 - service running - # 1 - service dead, but /var/run/ pid file exists - # 2 - service dead, but /var/lock/ lock file exists - # 3 - service not running - - if [ -f $PIDFILE ]; then - PID="$(cat $PIDFILE)" - PROCNAME="$(ps -o cmd --no-headers $PID)" - if [ "$PROCNAME" == "$DAEMON" ]; then - (exit 0) - else - (exit 1) - fi - else - (exit 3) - fi - - rc_status -v - ;; - probe) - ## Optional: Probe for the necessity of a reload, - ## give out the argument which is required for a reload. - ;; - *) - echo "Usage: $0 {start|stop|status|try-restart|restart|force-reload|reload|probe}" - exit 1 - ;; -esac -rc_exit