r462 - multipath-tools/trunk/debian/patches

Guido Guenther agx at alioth.debian.org
Sat Jun 9 15:59:27 UTC 2007


Author: agx
Date: Sat Jun  9 15:59:27 2007
New Revision: 462

Log:
forward to upstream git as of 178b93111d54828a89ad280c0aaaea0812343a6a
and rediff the patches


Added:
   multipath-tools/trunk/debian/patches/git-178b93111d54828a89ad280c0aaaea0812343a6a.diff
Modified:
   multipath-tools/trunk/debian/patches/Makefile-cleanups.diff
   multipath-tools/trunk/debian/patches/scsi_id.diff
   multipath-tools/trunk/debian/patches/series

Modified: multipath-tools/trunk/debian/patches/Makefile-cleanups.diff
==============================================================================
--- multipath-tools/trunk/debian/patches/Makefile-cleanups.diff	(original)
+++ multipath-tools/trunk/debian/patches/Makefile-cleanups.diff	Sat Jun  9 15:59:27 2007
@@ -1,8 +1,10 @@
 * create non existing dirs
 * dont't strip binaries
 
---- multipath-tools-0.4.7.orig/path_priority/pp_random/Makefile
-+++ multipath-tools-0.4.7/path_priority/pp_random/Makefile
+Index: multipath-tools.debian-git/path_priority/pp_random/Makefile
+===================================================================
+--- multipath-tools.debian-git.orig/path_priority/pp_random/Makefile	2007-06-08 12:03:28.000000000 +0200
++++ multipath-tools.debian-git/path_priority/pp_random/Makefile	2007-06-08 12:05:12.000000000 +0200
 @@ -14,7 +14,8 @@
  	$(CC) -static -o $(EXEC) $(OBJS)
  
@@ -13,19 +15,23 @@
  
  uninstall:
  	rm $(DESTDIR)$(bindir)/$(EXEC)
---- multipath-tools-0.4.7.orig/path_priority/pp_alua/Makefile
-+++ multipath-tools-0.4.7/path_priority/pp_alua/Makefile
+Index: multipath-tools.debian-git/path_priority/pp_alua/Makefile
+===================================================================
+--- multipath-tools.debian-git.orig/path_priority/pp_alua/Makefile	2007-06-08 12:05:00.000000000 +0200
++++ multipath-tools.debian-git/path_priority/pp_alua/Makefile	2007-06-08 12:05:12.000000000 +0200
 @@ -36,7 +36,7 @@
  	$(CC) -static -o $(EXEC) $(OBJS)
  
- install: $(BUILD) $(EXEC).8.gz
+ install: $(EXEC) $(EXEC).8.gz
 -	$(INSTALL) -s -m 755 $(EXEC) $(DESTDIR)$(bindir)/$(EXEC)
 +	$(INSTALL) -m 755 $(EXEC) $(DESTDIR)$(bindir)/$(EXEC)
  	$(INSTALL) -m 644 $(EXEC).8.gz $(DESTDIR)$(mandir)/$(EXEC).8.gz
  
  uninstall:
---- multipath-tools-0.4.7.orig/multipath/Makefile
-+++ multipath-tools-0.4.7/multipath/Makefile
+Index: multipath-tools.debian-git/multipath/Makefile
+===================================================================
+--- multipath-tools.debian-git.orig/multipath/Makefile	2007-06-08 12:05:00.000000000 +0200
++++ multipath-tools.debian-git/multipath/Makefile	2007-06-08 12:05:12.000000000 +0200
 @@ -39,9 +39,9 @@
  
  install:
@@ -38,4 +44,19 @@
 +	install -m 644 multipath.rules $(DESTDIR)/etc/udev/
  	install -d $(DESTDIR)$(mandir)
  	install -m 644 $(EXEC).8.gz $(DESTDIR)$(mandir)
+ 	install -d $(DESTDIR)$(man5dir)
+Index: multipath-tools.debian-git/kpartx/Makefile
+===================================================================
+--- multipath-tools.debian-git.orig/kpartx/Makefile	2007-06-08 12:05:21.000000000 +0200
++++ multipath-tools.debian-git/kpartx/Makefile	2007-06-08 12:05:34.000000000 +0200
+@@ -40,8 +40,8 @@
+ 	install -d $(DESTDIR)$(bindir)
+ 	install -s -m 755 $(EXEC) $(DESTDIR)$(bindir)
+ 	install -m 755 kpartx_id $(DESTDIR)$(bindir)
+-	install -d $(DESTDIR)/etc/udev/rules.d
+-	install -m 644 kpartx.rules $(DESTDIR)/etc/udev/rules.d/
++	install -d $(DESTDIR)/etc/udev/
++	install -m 644 kpartx.rules $(DESTDIR)/etc/udev/
+ 	install -d $(DESTDIR)$(mandir)
+ 	install -m 644 $(EXEC).8.gz $(DESTDIR)$(mandir)
  

Added: multipath-tools/trunk/debian/patches/git-178b93111d54828a89ad280c0aaaea0812343a6a.diff
==============================================================================
--- (empty file)
+++ multipath-tools/trunk/debian/patches/git-178b93111d54828a89ad280c0aaaea0812343a6a.diff	Sat Jun  9 15:59:27 2007
@@ -0,0 +1,9962 @@
+diff --git a/AUTHOR b/AUTHOR
+diff --git a/COPYING b/COPYING
+diff --git a/ChangeLog b/ChangeLog
+diff --git a/FAQ b/FAQ
+diff --git a/Makefile b/Makefile
+index 83ae2fe..aacede3 100644
+--- a/Makefile
++++ b/Makefile
+@@ -22,7 +22,11 @@ export KRNLOBJ
+ 
+ BUILDDIRS = $(shell find . -mindepth 2 -name Makefile -exec dirname {} \; | grep -v ^lib)
+ 
++ifeq   ($(MULTIPATH_VERSION),)
+ VERSION = $(shell basename ${PWD} | cut -d'-' -f3)
++else
++VERSION = $(MULTIPATH_VERSION)
++endif
+ 
+ all: recurse
+ 
+diff --git a/Makefile.inc b/Makefile.inc
+index 4a705aa..7eb3dfb 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      = 
+@@ -26,6 +25,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/README b/README
+diff --git a/TODO b/TODO
+diff --git a/devmap_name/Makefile b/devmap_name/Makefile
+index 380c85b..d8d8b09 100644
+--- a/devmap_name/Makefile
++++ b/devmap_name/Makefile
+@@ -28,9 +28,9 @@ 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)
+ 
+diff --git a/devmap_name/devmap_name.8 b/devmap_name/devmap_name.8
+index f4f03c3..86d0931 100644
+--- a/devmap_name/devmap_name.8
++++ b/devmap_name/devmap_name.8
+@@ -1,4 +1,4 @@
+-.TH DEVMAP_NAME 8 "February 2004" "" "Linux Administrator's Manual"
++.TH DEVMAP_NAME 8 "July 2006" "" "Linux Administrator's Manual"
+ .SH NAME
+ devmap_name \- Query device-mapper name
+ .SH SYNOPSIS
+diff --git a/devmap_name/devmap_name.c b/devmap_name/devmap_name.c
+diff --git a/getuid/usb_id b/getuid/usb_id
+diff --git a/kpartx/ChangeLog b/kpartx/ChangeLog
+diff --git a/kpartx/Makefile b/kpartx/Makefile
+index bf6e6c1..fd6ab8f 100644
+--- a/kpartx/Makefile
++++ b/kpartx/Makefile
+@@ -36,9 +36,12 @@ klibc: prepare $(OBJS)
+ $(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 -m 755 kpartx_id $(DESTDIR)$(bindir)
++	install -d $(DESTDIR)/etc/udev/rules.d
++	install -m 644 kpartx.rules $(DESTDIR)/etc/udev/rules.d/
+ 	install -d $(DESTDIR)$(mandir)
+ 	install -m 644 $(EXEC).8.gz $(DESTDIR)$(mandir)
+ 
+diff --git a/kpartx/README b/kpartx/README
+diff --git a/kpartx/bsd.c b/kpartx/bsd.c
+diff --git a/kpartx/byteorder.h b/kpartx/byteorder.h
+diff --git a/kpartx/crc32.c b/kpartx/crc32.c
+diff --git a/kpartx/crc32.h b/kpartx/crc32.h
+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, struct slice *sp, int ns)
+ 			/* 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, struct slice *sp, int ns)
+ 		        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, struct slice *sp, int ns)
+ 		/*
+ 		 * 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/dasd.h b/kpartx/dasd.h
+diff --git a/kpartx/devmapper.c b/kpartx/devmapper.c
+index 1253941..4b228ed 100644
+--- a/kpartx/devmapper.c
++++ b/kpartx/devmapper.c
+@@ -7,6 +7,10 @@
+ #include <libdevmapper.h>
+ #include <ctype.h>
+ #include <linux/kdev_t.h>
++#include <errno.h>
++
++#define UUID_PREFIX "part%d-"
++#define MAX_PREFIX_LEN 8
+ 
+ extern int
+ dm_prereq (char * str, int x, int y, int z)
+@@ -68,9 +72,10 @@ dm_simplecmd (int task, const char *name) {
+ 
+ extern int
+ dm_addmap (int task, const char *name, const char *target,
+-	   const char *params, unsigned long size) {
++	   const char *params, unsigned long size, const char *uuid, int part) {
+ 	int r = 0;
+ 	struct dm_task *dmt;
++	char *prefixed_uuid;
+ 
+ 	if (!(dmt = dm_task_create (task)))
+ 		return 0;
+@@ -81,12 +86,25 @@ dm_addmap (int task, const char *name, const char *target,
+ 	if (!dm_task_add_target (dmt, 0, size, target, params))
+ 		goto addout;
+ 
++	if (task == DM_DEVICE_CREATE && uuid) {
++		prefixed_uuid = malloc(MAX_PREFIX_LEN + strlen(uuid) + 1);
++		if (!prefixed_uuid) {
++			fprintf(stderr, "cannot create prefixed uuid : %s\n",
++				strerror(errno));
++			goto addout;
++		}
++		sprintf(prefixed_uuid, UUID_PREFIX "%s", part, uuid);
++		if (!dm_task_set_uuid(dmt, prefixed_uuid))
++			goto addout;
++	}
++
+ 	dm_task_no_open_count(dmt);
+ 
+ 	r = dm_task_run (dmt);
+ 
+ 	addout:
+ 	dm_task_destroy (dmt);
++
+ 	return r;
+ }
+ 
+@@ -178,3 +196,26 @@ out:
+ 	return ret;
+ }
+ 
++char *
++dm_mapuuid(int major, int minor)
++{
++	struct dm_task *dmt;
++	char *tmp, *uuid = NULL;
++
++	if (!(dmt = dm_task_create(DM_DEVICE_INFO)))
++		return NULL;
++
++	dm_task_no_open_count(dmt);
++	dm_task_set_major(dmt, major);
++	dm_task_set_minor(dmt, minor);
++
++	if (!dm_task_run(dmt))
++		goto out;
++
++	tmp = dm_task_get_uuid(dmt);
++	if (tmp[0] != '\0')
++		uuid = strdup(tmp);
++out:
++	dm_task_destroy(dmt);
++	return uuid;
++}
+diff --git a/kpartx/devmapper.h b/kpartx/devmapper.h
+index 9607476..e20e456 100644
+--- a/kpartx/devmapper.h
++++ b/kpartx/devmapper.h
+@@ -1,6 +1,8 @@
+ int dm_prereq (char *, int, int, int);
+ int dm_simplecmd (int, const char *);
+-int dm_addmap (int, const char *, const char *, const char *, unsigned long);
++int dm_addmap (int, const char *, const char *, const char *, unsigned long,
++	       char *, int);
+ 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);
+diff --git a/kpartx/dos.c b/kpartx/dos.c
+diff --git a/kpartx/dos.h b/kpartx/dos.h
+diff --git a/kpartx/efi.h b/kpartx/efi.h
+diff --git a/kpartx/gpt.c b/kpartx/gpt.c
+diff --git a/kpartx/gpt.h b/kpartx/gpt.h
+diff --git a/kpartx/kpartx.8 b/kpartx/kpartx.8
+index 259ce3f..87b07ce 100644
+--- a/kpartx/kpartx.8
++++ b/kpartx/kpartx.8
+@@ -1,4 +1,4 @@
+-.TH KPARTX 8 "February 2004" "" "Linux Administrator's Manual"
++.TH KPARTX 8 "July 2006" "" "Linux Administrator's Manual"
+ .SH NAME
+ kpartx \- Create device maps from partition tables
+ .SH SYNOPSIS
+diff --git a/kpartx/kpartx.c b/kpartx/kpartx.c
+index 2198302..b406b95 100644
+--- a/kpartx/kpartx.c
++++ b/kpartx/kpartx.c
+@@ -192,6 +192,8 @@ main(int argc, char **argv){
+ 	char partname[PARTNAME_SIZE], params[PARTNAME_SIZE + 16];
+ 	char * loopdev = NULL;
+ 	char * delim = NULL;
++	char *uuid = NULL;
++	char *mapname = NULL;
+ 	int loopro = 0;
+ 	int hotplug = 0;
+ 	struct stat buf;
+@@ -284,11 +286,6 @@ main(int argc, char **argv){
+ 	}
+ 
+ 	if (S_ISREG (buf.st_mode)) {
+-		loopdev = malloc(LO_NAME_SIZE * sizeof(char));
+-		
+-		if (!loopdev)
+-			exit(1);
+-
+ 		/* already looped file ? */
+ 		loopdev = find_loop_by_file(device);
+ 
+@@ -313,6 +310,20 @@ main(int argc, char **argv){
+ 	}
+ 	
+ 	off = find_devname_offset(device);
++
++	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) {
+@@ -362,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);
+ 			}
+@@ -371,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);
+ 				}
+@@ -404,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);
+ 				}
+@@ -420,7 +431,7 @@ main(int argc, char **argv){
+ 					DM_DEVICE_RELOAD : DM_DEVICE_CREATE);
+ 
+ 				dm_addmap(op, partname, DM_TARGET, params,
+-					  slices[j].size);
++					  slices[j].size, uuid, j+1);
+ 
+ 				if (op == DM_DEVICE_RELOAD)
+ 					dm_simplecmd(DM_DEVICE_RESUME,
+diff --git a/kpartx/kpartx.h b/kpartx/kpartx.h
+diff --git a/kpartx/kpartx.rules b/kpartx/kpartx.rules
+new file mode 100644
+index 0000000..9bd7db7
+--- /dev/null
++++ b/kpartx/kpartx.rules
+@@ -0,0 +1,36 @@
++#
++# 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="kpartx_end"
++ACTION=="remove", GOTO="kpartx_end"
++
++ENV{DM_TABLE_STATE}!="LIVE", GOTO="kpartx_end"
++
++ENV{DM_UUID}=="?*", IMPORT{program}=="/sbin/kpartx_id %M %m $env{DM_UUID}"
++
++OPTIONS="link_priority=50"
++
++# Create persistent links for multipath tables
++ENV{DM_UUID}=="mpath-*", \
++	SYMLINK+="disk/by-id/$env{DM_TYPE}-$env{DM_NAME}"
++
++# Create persistent links for dmraid tables
++ENV{DM_UUID}=="mpath-*", \
++        SYMLINK+="disk/by-id/$env{DM_TYPE}-$env{DM_NAME}"
++
++# Create persistent links for partitions
++ENV{DM_PART}=="?*", \
++        SYMLINK+="disk/by-id/$env{DM_TYPE}-$env{DM_NAME}-part$env{DM_PART}"
++
++# Create dm tables for partitions
++ENV{DM_STATE}=="ACTIVE", ENV{DM_UUID}=="mpath-*", \
++        RUN+="/sbin/kpartx -a -p -part /dev/$kernel"
++ENV{DM_STATE}=="ACTIVE", ENV{DM_UUID}=="dmraid-*", \
++        RUN+="/sbin/kpartx -a -p -part /dev/$kernel"
++
++LABEL="kpartx_end"
++
++
+diff --git a/kpartx/kpartx_id b/kpartx/kpartx_id
+new file mode 100644
+index 0000000..e876b98
+--- /dev/null
++++ b/kpartx/kpartx_id
+@@ -0,0 +1,93 @@
++#!/bin/bash
++#
++# kpartx_id
++#
++# Generates ID information for device-mapper tables.
++#
++# Copyright (C) 2006 SUSE Linux Products GmbH
++# Author:
++#       Hannes Reinecke <hare at suse.de>
++#
++#
++#       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
++UUID=$3
++
++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
++
++
++# Table UUIDs are always '<type>-<uuid>'.
++dmuuid=${UUID#*-}
++dmtbl=${UUID%%-*}
++dmpart=${dmtbl#part}
++# kpartx types are 'part<num>'
++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)
++    echo "DM_NAME=$dmname"
++    # We need the dependencies of the parent table to figure out
++    # the type if the parent is a multipath table
++    case "$dmuuid" 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 $UUID)
++elif [ "$dmtbl" == "dmraid" ] ; then
++    dmname=$tblname
++fi
++
++[ -n "$dmpart" ] && echo "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 "DM_TYPE=dasd"
++	    ;;
++	*\(9*)
++            echo "DM_TYPE=raid"
++	    ;;
++	*)
++            echo "DM_TYPE=scsi"
++	    ;;
++    esac
++else
++    echo "DM_TYPE=raid"
++fi
++
++exit 0
+diff --git a/kpartx/lopart.c b/kpartx/lopart.c
+diff --git a/kpartx/lopart.h b/kpartx/lopart.h
+diff --git a/kpartx/mac.c b/kpartx/mac.c
+diff --git a/kpartx/mac.h b/kpartx/mac.h
+diff --git a/kpartx/solaris.c b/kpartx/solaris.c
+diff --git a/kpartx/sysmacros.h b/kpartx/sysmacros.h
+diff --git a/kpartx/unixware.c b/kpartx/unixware.c
+diff --git a/kpartx/xstrncpy.c b/kpartx/xstrncpy.c
+diff --git a/kpartx/xstrncpy.h b/kpartx/xstrncpy.h
+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 * name)
+ 	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..d4dad9c 100644
+--- a/libcheckers/checkers.h
++++ b/libcheckers/checkers.h
+@@ -2,7 +2,44 @@
+ #define _CHECKERS_H
+ 
+ /*
+- * path states
++ *
++ * Userspace (multipath/multipathd) path states
++ *
++ * PATH_WILD:
++ * - Use: None of the checkers (returned if we don't have an fd)
++ * - Description: Corner case where "fd <= 0" for path fd (see checker_check())
++ *
++ * PATH_UNCHECKED:
++ * - Use: Only in directio checker
++ * - Description: set when fcntl(F_GETFL) fails to return flags or O_DIRECT
++ *   not include in flags, or O_DIRECT read fails
++ * - Notes:
++ *   - multipathd: uses it to skip over paths in sync_map_state()
++ *   - multipath: used in update_paths(); if state==PATH_UNCHECKED, call
++ *     pathinfo()
++ *
++ * PATH_DOWN:
++ * - Use: All checkers (directio, emc_clariion, hp_sw, readsector0, tur)
++ * - Description: Either a) SG_IO ioctl failed, or b) check condition on some
++ *   SG_IO ioctls that succeed (tur, readsector0 checkers); path is down and
++ *   you shouldn't try to send commands to it
++ *
++ * PATH_UP:
++ * - Use: All checkers (directio, emc_clariion, hp_sw, readsector0, tur)
++ * - Description: Path is up and I/O can be sent to it
++ *
++ * PATH_SHAKY:
++ * - Use: Only emc_clariion
++ * - Description: Indicates path not available for "normal" operations
++ *
++ * PATH_GHOST:
++ * - Use: Only hp_sw
++ * - Description: Indicates a "passive/standby" path on active/passive HP
++ *   arrays.  These paths will return valid answers to certain SCSI commands
++ *   (tur, read_capacity, inquiry, start_stop), but will fail I/O commands.
++ *   The path needs an initialization command to be sent to it in order for
++ *   I/Os to succeed.
++ *
+  */
+ #define PATH_WILD	-1
+ #define PATH_UNCHECKED	0
+@@ -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
+ 
+ /*
++ * Overloaded storage response time can be very long.
++ * SG_IO timouts after DEF_TIMEOUT milliseconds, and checkers interprets this
++ * as a path failure. multipathd then proactively evicts the path from the DM
++ * multipath table in this case.
++ *
++ * This generaly snow balls and ends up in full eviction and IO errors for end
++ * users. Bad. This may also cause SCSI bus resets, causing disruption for all
++ * local and external storage hardware users.
++ * 
++ * Provision a long timeout. Longer than any real-world application would cope
++ * with.
++ */
++#define DEF_TIMEOUT	300000
++
++/*
+  * 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/directio.c b/libcheckers/directio.c
+diff --git a/libcheckers/directio.h b/libcheckers/directio.h
+diff --git a/libcheckers/emc_clariion.c b/libcheckers/emc_clariion.c
+index 1d7b684..d801b42 100644
+--- a/libcheckers/emc_clariion.c
++++ b/libcheckers/emc_clariion.c
+@@ -11,25 +11,76 @@
+ #include <sys/ioctl.h>
+ #include <errno.h>
+ 
+-#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 * c)
+ 
+ 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;
+-	io_hdr.timeout = 60000;
++	io_hdr.timeout = DEF_TIMEOUT;
+ 	io_hdr.pack_id = 0;
+ 	if (ioctl(c->fd, SG_IO, &io_hdr) < 0) {
+ 		MSG(c, "emc_clariion_checker: sending query command failed");
+@@ -69,34 +122,39 @@ 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;
+ 	}
+ 
+ 	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 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;
+ 	}
+ 
+-#if 0
+-	/* This is not actually an error as the failover to this group
+-	 * _would_ bind the path */
+-	if ( /* LUN should at least be bound somewhere */
+-		sense_buffer[4] != 0x00) {
+-		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");
++		return PATH_DOWN;
+ 	}
+-#endif	
+ 	
+ 	/*
+ 	 * store the LUN WWN there and compare that it indeed did not
+@@ -105,7 +163,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 +172,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/emc_clariion.h b/libcheckers/emc_clariion.h
+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 INQUIRY_CMDLEN		6
+ #define INQUIRY_CMD		0x12
+ #define SENSE_BUFF_LEN		32
+-#define DEF_TIMEOUT		60000
+ #define SCSI_CHECK_CONDITION	0x2
+ #define SCSI_COMMAND_TERMINATED	0x22
+ #define SG_ERR_DRIVER_SENSE	0x08
+@@ -111,7 +110,7 @@ do_tur (int fd)
+         io_hdr.dxfer_direction = SG_DXFER_NONE;
+         io_hdr.cmdp = turCmdBlk;
+         io_hdr.sbp = sense_buffer;
+-        io_hdr.timeout = 20000;
++        io_hdr.timeout = DEF_TIMEOUT;
+         io_hdr.pack_id = 0;
+ 
+         if (ioctl(fd, SG_IO, &io_hdr) < 0)
+diff --git a/libcheckers/hp_sw.h b/libcheckers/hp_sw.h
+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 <string.h>
++#include <sys/ioctl.h>
++#include <errno.h>
++
++#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 <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++#include <sys/types.h>
++#include <sys/stat.h>
++#include <unistd.h>
++#include <fcntl.h>
++#include <sys/ioctl.h>
++#include <errno.h>
++
++#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..3cddfa8 100644
+--- a/libcheckers/readsector0.c
++++ b/libcheckers/readsector0.c
+@@ -2,21 +2,9 @@
+  * Copyright (c) 2004, 2005 Christophe Varoqui
+  */
+ #include <stdio.h>
+-#include <stdlib.h>
+-#include <string.h>
+-#include <sys/types.h>
+-#include <sys/stat.h>
+-#include <unistd.h>
+-#include <fcntl.h>
+-#include <sys/ioctl.h>
+-#include <errno.h>
+ 
+ #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,72 +23,14 @@ void readsector0_free (struct checker * c)
+ 	return;
+ }
+ 
+-static int
+-sg_read (int sg_fd, unsigned char * buff)
+-{
+-	/* 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};
+-	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];
++	unsigned char sbuf[SENSE_BUFF_LEN];
+ 	int ret;
+ 
+-	ret = sg_read(c->fd, &buf[0]);
++	ret = sg_read(c->fd, &buf[0], &sbuf[0]);
+ 
+ 	switch (ret)
+ 	{
+diff --git a/libcheckers/readsector0.h b/libcheckers/readsector0.h
+diff --git a/libcheckers/tur.c b/libcheckers/tur.c
+index d40a273..e79bc0a 100644
+--- a/libcheckers/tur.c
++++ b/libcheckers/tur.c
+@@ -51,7 +51,7 @@ tur (struct checker * c)
+         io_hdr.dxfer_direction = SG_DXFER_NONE;
+         io_hdr.cmdp = turCmdBlk;
+         io_hdr.sbp = sense_buffer;
+-        io_hdr.timeout = 20000;
++        io_hdr.timeout = DEF_TIMEOUT;
+         io_hdr.pack_id = 0;
+         if (ioctl(c->fd, SG_IO, &io_hdr) < 0) {
+ 		MSG(c, MSG_TUR_DOWN);
+diff --git a/libcheckers/tur.h b/libcheckers/tur.h
+diff --git a/libmultipath/Makefile b/libmultipath/Makefile
+index 8a14b04..511f5ad 100644
+--- a/libmultipath/Makefile
++++ b/libmultipath/Makefile
+@@ -6,24 +6,31 @@ BUILD = glibc
+ 
+ include ../Makefile.inc
+ 
+-CFLAGS = -I$(checkersdir)
++CFLAGS += -I$(checkersdir)
+ 
+ OBJS = memory.o parser.o vector.o devmapper.o callout.o \
+        hwtable.o blacklist.o util.o dmparser.o config.o \
+        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)
+ 
+ ifeq ($(strip $(DAEMON)),1)
++	OBJS += lock.o waiter.o
+ 	CFLAGS += -DDAEMON
+ 	CLEAN = $(shell if [ "x$(PREVBUILD)" = "x" ]; then echo clean; fi)
+ else
+ 	CLEAN = $(shell if [ ! "x$(PREVBUILD)" = "x" ]; then echo clean; fi)
+ endif
+ 
++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, char **map_alias)
+ 		}
+ 		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, char *map_alias)
+ 			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 *file)
+ 	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 *file)
+ 	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/alias.h b/libmultipath/alias.h
+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 <stdio.h>
+-
+ #include <checkers.h>
+ 
+ #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 * vendor, char * product)
+ 		}
+ 		ble->product = product;
+ 	}
++	ble->origin = origin;
+ 	return 0;
+ }
+ 
+@@ -105,19 +106,19 @@ setup_default_blist (struct config * conf)
+ 	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 * conf)
+ 					  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 * conf)
+ }
+ 
+ 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);
+ 
+diff --git a/libmultipath/callout.c b/libmultipath/callout.c
+diff --git a/libmultipath/callout.h b/libmultipath/callout.h
+diff --git a/libmultipath/config.c b/libmultipath/config.c
+index 1068755..a39af8a 100644
+--- a/libmultipath/config.c
++++ b/libmultipath/config.c
+@@ -20,26 +20,61 @@
+ #include "blacklist.h"
+ #include "defaults.h"
+ 
++static struct hwentry *
++find_hwe_strmatch (vector hwtable, char * vendor, char * product, char * revision)
++{
++	int i;
++	struct hwentry *hwe, *ret = NULL;
++
++	vector_foreach_slot (hwtable, hwe, i) {
++		if (hwe->vendor && vendor && strcmp(hwe->vendor, vendor))
++			continue;
++
++		if (hwe->product && product && strcmp(hwe->product, product))
++			continue;
++
++		if (hwe->revision && revision && strcmp(hwe->revision, revision))
++			continue;
++
++		ret = hwe;
++		break;
++	}
++	return ret;
++}
++
+ struct hwentry *
+-find_hwe (vector hwtable, char * vendor, char * product)
++find_hwe (vector hwtable, char * vendor, char * product, char * revision)
+ {
+ 	int i;
+ 	struct hwentry *hwe, *ret = NULL;
+-	regex_t vre, pre;
++	regex_t vre, pre, rre;
+ 
+ 	vector_foreach_slot (hwtable, hwe, i) {
+-		if (regcomp(&vre, hwe->vendor, REG_EXTENDED|REG_NOSUB))
++		if (hwe->vendor &&
++		    regcomp(&vre, hwe->vendor, REG_EXTENDED|REG_NOSUB))
++			break;
++		if (hwe->product &&
++		    regcomp(&pre, hwe->product, REG_EXTENDED|REG_NOSUB)) {
++			regfree(&vre);
+ 			break;
+-		if (regcomp(&pre, hwe->product, REG_EXTENDED|REG_NOSUB)) {
++		}
++		if (hwe->revision &&
++		    regcomp(&rre, hwe->revision, REG_EXTENDED|REG_NOSUB)) {
+ 			regfree(&vre);
++			regfree(&pre);
+ 			break;
+ 		}
+-		if (!regexec(&vre, vendor, 0, NULL, 0) &&
+-		    !regexec(&pre, product, 0, NULL, 0))
++		if ((!hwe->vendor || !regexec(&vre, vendor, 0, NULL, 0)) &&
++		    (!hwe->product || !regexec(&pre, product, 0, NULL, 0)) &&
++		    (!hwe->revision || !regexec(&rre, revision, 0, NULL, 0)))
+ 			ret = hwe;
+-		
+-		regfree(&pre);
+-		regfree(&vre);
++
++		if (hwe->revision)
++			regfree(&rre);
++		if (hwe->product)
++			regfree(&pre);
++		if (hwe->vendor)
++			regfree(&vre);
+ 
+ 		if (ret)
+ 			break;
+@@ -91,6 +126,9 @@ free_hwe (struct hwentry * hwe)
+ 	if (hwe->product)
+ 		FREE(hwe->product);
+ 
++	if (hwe->revision)
++		FREE(hwe->revision);
++
+ 	if (hwe->selector)
+ 		FREE(hwe->selector);
+ 
+@@ -204,23 +242,12 @@ set_param_str(char * str)
+ 	return dst;
+ }
+ 
+-static int
+-dup_hwe (vector hwtable, char * vendor, char * product)
+-{
+-	struct hwentry * hwe = find_hwe(hwtable, vendor, product);
+-
+-	if (hwe)
+-		return 1;
+-
+-	return 0;
+-}
+-
+ int
+ store_hwe (vector hwtable, struct hwentry * dhwe)
+ {
+ 	struct hwentry * hwe;
+ 
+-	if (dup_hwe(hwtable, dhwe->vendor, dhwe->product))
++	if (find_hwe_strmatch(hwtable, dhwe->vendor, dhwe->product, dhwe->revision))
+ 		return 0;
+ 	
+ 	if (!(hwe = alloc_hwe()))
+@@ -232,6 +259,9 @@ store_hwe (vector hwtable, struct hwentry * dhwe)
+ 	if (!dhwe->product || !(hwe->product = set_param_str(dhwe->product)))
+ 		goto out;
+ 	
++	if (dhwe->revision && !(hwe->revision = set_param_str(dhwe->revision)))
++		goto out;
++	
+ 	if (dhwe->getuid && !(hwe->getuid = set_param_str(dhwe->getuid)))
+ 		goto out;
+ 
+@@ -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);
+-
++	free_keywords(conf->keywords);
+ 	FREE(conf);
+ }
+ 
+@@ -332,6 +367,7 @@ load_config (char * file)
+ 	 * read the config file
+ 	 */
+ 	if (filepresent(file)) {
++		set_current_keywords(&conf->keywords);
+ 		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..a25b3ad 100644
+--- a/libmultipath/config.h
++++ b/libmultipath/config.h
+@@ -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;
++	char * revision;
+ 	char * getuid;
+ 	char * getprio;
+ 	char * features;
+@@ -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;
+-	int dev_type;
++	enum devtypes dev_type;
+ 	int minio;
+ 	int checkint;
+ 	int max_checkint;
+@@ -57,8 +63,10 @@ struct config {
+ 	int rr_weight;
+ 	int no_path_retry;
+ 	int user_friendly_names;
++	int pg_timeout;
+ 
+ 	char * dev;
++	char * sysfs_dir;
+ 	char * udev_dir;
+ 	char * selector;
+ 	char * getuid;
+@@ -67,17 +75,21 @@ struct config {
+ 	char * hwhandler;
+ 	char * bindings_file;
+ 
++	vector keywords;
+ 	vector mptable;
+ 	vector hwtable;
+ 
+ 	vector blist_devnode;
+ 	vector blist_wwid;
+ 	vector blist_device;
++	vector elist_devnode;
++	vector elist_wwid;
++	vector elist_device;
+ };
+ 
+ struct config * conf;
+ 
+-struct hwentry * find_hwe (vector hwtable, char * vendor, char * product);
++struct hwentry * find_hwe (vector hwtable, char * vendor, char * product, char *revision);
+ struct mpentry * find_mpe (char * wwid);
+ char * get_mpe_wwid (char * alias);
+ 
+diff --git a/libmultipath/configure.c b/libmultipath/configure.c
+index 1ba4356..3cd6041 100644
+--- a/libmultipath/configure.c
++++ b/libmultipath/configure.c
+@@ -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, vector curmp)
+ 			mpp->action = ACT_RENAME;
+ 			return;
+ 		}
+-		else {
+-			condlog(3, "%s: set ACT_CREATE (map does not exist)",
+-				mpp->alias);
+-			mpp->action = ACT_CREATE;
+-		}
+ 		mpp->action = ACT_CREATE;
+ 		condlog(3, "%s: set ACT_CREATE (map does not exist)",
+ 			mpp->alias);
+@@ -179,8 +175,9 @@ select_action (struct multipath * mpp, vector curmp)
+ 			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, int lock)
+ 
+ /*
+  * Return value:
+- *  -1: Retry
+- *   0: DM_DEVICE_CREATE or DM_DEVICE_RELOAD failed, or dry_run mode.
+- *   1: DM_DEVICE_CREATE or DM_DEVICE_RELOAD succeeded.
+- *   2: Map is already existing.
+  */
++#define DOMAP_RETRY	-1
++#define DOMAP_FAIL	0
++#define DOMAP_OK	1
++#define DOMAP_EXIST	2
++#define DOMAP_DRY	3
++
+ extern int
+ domap (struct multipath * mpp)
+ {
+@@ -299,15 +298,15 @@ domap (struct multipath * mpp)
+ 	/*
+ 	 * last chance to quit before touching the devmaps
+ 	 */
+-	if (conf->dry_run) {
++	if (conf->dry_run && mpp->action != ACT_NOTHING) {
+ 		print_multipath_topology(mpp, conf->verbosity);
+-		return 0;
++		return DOMAP_DRY;
+ 	}
+ 
+ 	switch (mpp->action) {
+ 	case ACT_REJECT:
+ 	case ACT_NOTHING:
+-		return 2;
++		return DOMAP_EXIST;
+ 
+ 	case ACT_SWITCHPG:
+ 		dm_switchgroup(mpp->alias, mpp->bestpg);
+@@ -317,18 +316,19 @@ domap (struct multipath * mpp)
+ 		 * retry.
+ 		 */
+ 		reinstate_paths(mpp);
+-		return 2;
++		return DOMAP_EXIST;
+ 
+ 	case ACT_CREATE:
+ 		if (lock_multipath(mpp, 1)) {
+ 			condlog(3, "%s: failed to create map (in use)",
+ 				mpp->alias);
+-			return -1;
++			return DOMAP_RETRY;
+ 		}
+-		dm_shut_log();
+ 
+-		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);
+@@ -346,7 +346,6 @@ domap (struct multipath * mpp)
+ 		}
+ 
+ 		lock_multipath(mpp, 0);
+-		dm_restore_log();
+ 		break;
+ 
+ 	case ACT_RELOAD:
+@@ -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
++		return DOMAP_OK;
+ 	}
+-
+-	return r;
++	return DOMAP_FAIL;
+ }
+ 
+ static int
+@@ -423,7 +422,7 @@ coalesce_paths (struct vectors * vecs, vector newmp, char * refwwid)
+ 
+ 		/* 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, vector newmp, char * refwwid)
+ 		if ((mpp = add_map_with_path(vecs, pp1, 0)) == NULL)
+ 			return 1;
+ 
+-		if (pp1->priority < 0)
++		if (pp1->priority == PRIO_UNDEF)
+ 			mpp->action = ACT_REJECT;
+ 
+ 		if (!mpp->paths) {
+@@ -471,7 +470,7 @@ coalesce_paths (struct vectors * vecs, vector newmp, char * refwwid)
+ 					mpp->size);
+ 				mpp->action = ACT_REJECT;
+ 			}
+-			if (pp2->priority < 0)
++			if (pp2->priority == PRIO_UNDEF)
+ 				mpp->action = ACT_REJECT;
+ 		}
+ 		verify_paths(mpp, vecs, NULL);
+@@ -486,19 +485,18 @@ coalesce_paths (struct vectors * vecs, vector newmp, char * refwwid)
+ 
+ 		r = domap(mpp);
+ 
+-		if (!r) {
+-			condlog(3, "%s: domap (%u) failure "
+-				   "for create/reload map",
+-				mpp->alias, r);
+-			remove_map(mpp, vecs, NULL, 0);
+-			continue;
+-		}
+-		else if (r < 0) {
++		if (r == DOMAP_FAIL || r == DOMAP_RETRY) {
+ 			condlog(3, "%s: domap (%u) failure "
+ 				   "for create/reload map",
+ 				mpp->alias, r);
+-			return r;
++			if (r == DOMAP_FAIL) {
++				remove_map(mpp, vecs, NULL, 0);
++				continue;
++			} else /* if (r == DOMAP_RETRY) */
++				return r;
+ 		}
++		if (r == DOMAP_DRY)
++			continue;
+ 
+ 		if (mpp->no_path_retry != NO_PATH_RETRY_UNDEF) {
+ 			if (mpp->no_path_retry == NO_PATH_RETRY_FAIL)
+@@ -506,6 +504,12 @@ coalesce_paths (struct vectors * vecs, vector newmp, char * refwwid)
+ 			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, vector newmp, char * refwwid)
+ }
+ 
+ extern char *
+-get_refwwid (char * dev, int dev_type, vector pathvec)
++get_refwwid (char * dev, enum devtypes dev_type, vector pathvec)
+ {
+ 	struct path * pp;
+ 	char buff[FILE_NAME_SIZE];
+-	char * refwwid;
++	char * refwwid = NULL, tmpwwid[WWID_SIZE];
+ 
+ 	if (dev_type == DEV_NONE)
+ 		return NULL;
+@@ -606,6 +610,12 @@ get_refwwid (char * dev, int dev_type, vector pathvec)
+ 		goto out;
+ 	}
+ 	if (dev_type == DEV_DEVMAP) {
++
++		if (((dm_get_uuid(dev, tmpwwid)) == 0) && (strlen(tmpwwid))) {
++			refwwid = tmpwwid;
++			goto out;
++		}
++
+ 		/*
+ 		 * may be a binding
+ 		 */
+diff --git a/libmultipath/configure.h b/libmultipath/configure.h
+index 42ee9b0..1cbbe82 100644
+--- a/libmultipath/configure.h
++++ b/libmultipath/configure.h
+@@ -25,5 +25,5 @@ int setup_map (struct multipath * mpp);
+ int domap (struct multipath * mpp);
+ int reinstate_paths (struct multipath *mpp);
+ int coalesce_paths (struct vectors *vecs, vector curmp, char * refwwid);
+-char * get_refwwid (char * dev, int dev_type, vector pathvec);
++char * get_refwwid (char * dev, enum devtypes dev_type, vector pathvec);
+ 
+diff --git a/libmultipath/debug.c b/libmultipath/debug.c
+index 099ab52..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;
+@@ -29,17 +29,16 @@ void dlog (int sink, int prio, char * fmt, ...)
+ 			struct tm *tb = localtime(&t);
+ 			char buff[16];
+ 			
+-			strftime(buff, 16, "%b %d %H:%M:%S", tb); 
++			strftime(buff, sizeof(buff), "%b %d %H:%M:%S", tb);
++			buff[sizeof(buff)-1] = '\0';
+ 
+ 			fprintf(stdout, "%s | ", buff);
+ 			vfprintf(stdout, fmt, ap);
+-			fprintf(stdout, "\n");
+ 		}
+ 		else
+ 			log_safe(prio + 3, fmt, ap);
+ #else
+ 		vfprintf(stdout, fmt, ap);
+-		fprintf(stdout, "\n");
+ #endif
+ 	}
+ 	va_end(ap);
+diff --git a/libmultipath/debug.h b/libmultipath/debug.h
+index e7612a9..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
+@@ -11,11 +11,11 @@ void dlog (int sink, int prio, char * fmt, ...)
+ int logsink;
+ 
+ #define condlog(prio, fmt, args...) \
+-	dlog(logsink, prio, fmt, ##args)
++	dlog(logsink, prio, fmt "\n", ##args)
+ 
+ #else /* DAEMON */
+ 
+ #define condlog(prio, fmt, args...) \
+-	dlog(0, prio, fmt, ##args)
++	dlog(0, prio, fmt "\n", ##args)
+ 
+ #endif /* DAEMON */
+diff --git a/libmultipath/defaults.c b/libmultipath/defaults.c
+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..d6991ba 100644
+--- a/libmultipath/devmapper.c
++++ b/libmultipath/devmapper.c
+@@ -6,11 +6,13 @@
+  */
+ #include <stdio.h>
+ #include <stdlib.h>
++#include <stdarg.h>
+ #include <string.h>
+ #include <libdevmapper.h>
+ #include <ctype.h>
+ #include <linux/kdev_t.h>
+ #include <unistd.h>
++#include <errno.h>
+ 
+ #include <checkers.h>
+ 
+@@ -19,35 +21,96 @@
+ #include "debug.h"
+ #include "memory.h"
+ #include "devmapper.h"
++#include "config.h"
++
++#if DAEMON
++#include "log_pthread.h"
++#include <sys/types.h>
++#include <time.h>
++#endif
+ 
+ #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, ...)
++dm_write_log (int level, const char *file, int line, const char *f, ...)
+ {
++	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);
++
+ 	return;
+ }
+ 
+-void
+-dm_restore_log (void)
+-{
+-	dm_log_init(NULL);
++extern void
++dm_init(void) {
++	dm_log_init(&dm_write_log);
++	dm_log_init_verbose(conf ? conf->verbosity + 3 : 0);
+ }
+ 
+-void
+-dm_shut_log (void)
++static int
++dm_libprereq (void)
+ {
+-	dm_log_init(&dm_dummy_log);
++	char version[64];
++	int v[3];
++	int minv[3] = {1, 2, 8};
++
++	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;
+ }
+ 
+-extern int
+-dm_prereq (char * str, int x, int y, int z)
++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 +121,44 @@ dm_prereq (char * str, int x, int y, int z)
+ 		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 +170,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 +187,7 @@ dm_addmap (int task, const char *name, const char *target,
+ 	   const char *params, unsigned long long size, const char *uuid) {
+ 	int r = 0;
+ 	struct dm_task *dmt;
++	char *prefixed_uuid = NULL;
+ 
+ 	if (!(dmt = dm_task_create (task)))
+ 		return 0;
+@@ -123,13 +198,26 @@ dm_addmap (int task, const char *name, const char *target,
+ 	if (!dm_task_add_target (dmt, 0, size, target, params))
+ 		goto addout;
+ 
+-	if (uuid && !dm_task_set_uuid(dmt, uuid))
+-		goto addout;
++	if (uuid){
++		prefixed_uuid = MALLOC(UUID_PREFIX_LEN + strlen(uuid) + 1);
++		if (!prefixed_uuid) {
++			condlog(0, "cannot create prefixed uuid : %s\n",
++				strerror(errno));
++			goto addout;
++		}
++		sprintf(prefixed_uuid, UUID_PREFIX "%s", uuid);
++		if (!dm_task_set_uuid(dmt, prefixed_uuid))
++			goto freeout;
++	}
+ 
+ 	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;
+@@ -203,6 +291,7 @@ dm_get_uuid(char *name, char *uuid)
+ {
+ 	struct dm_task *dmt;
+ 	const char *uuidtmp;
++	int r = 1;
+ 
+ 	dmt = dm_task_create(DM_DEVICE_INFO);
+ 	if (!dmt)
+@@ -215,15 +304,19 @@ dm_get_uuid(char *name, char *uuid)
+                 goto uuidout;
+ 
+ 	uuidtmp = dm_task_get_uuid(dmt);
+-	if (uuidtmp)
+-		strcpy(uuid, uuidtmp);
++	if (uuidtmp) {
++		if (!strncmp(uuidtmp, UUID_PREFIX, UUID_PREFIX_LEN))
++			strcpy(uuid, uuidtmp + UUID_PREFIX_LEN);
++		else
++			strcpy(uuid, uuidtmp);
++	}
+ 	else
+ 		uuid[0] = '\0';
+ 
++	r = 0;
+ uuidout:
+ 	dm_task_destroy(dmt);
+-
+-	return 0;
++	return r;
+ }
+ 
+ extern int
+@@ -509,6 +602,16 @@ dm_queue_if_no_path(char *mapname, int enable)
+ 	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 +694,7 @@ dm_get_maps (vector mp, char * type)
+ 				goto out1;
+ 
+ 			dm_get_uuid(names->name, mpp->wwid);
++			dm_get_info(names->name, &mpp->dmi);
+ 		}
+ 
+ 		if (!vector_alloc_slot(mp))
+@@ -697,9 +801,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 c7879a7..8438034 100644
+--- a/libmultipath/devmapper.h
++++ b/libmultipath/devmapper.h
+@@ -1,6 +1,5 @@
+-void dm_shut_log(void);
+-void dm_restore_log(void);
+-int dm_prereq (char *, int, int, int);
++void dm_init(void);
++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 +12,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..4572a7d 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->elist_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 len, void * data)
+ }
+ 
+ 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 len, void * data)
+ }
+ 
+ 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, int len, void * data)
+ }
+ 
+ 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/dict.h b/libmultipath/dict.h
+diff --git a/libmultipath/discovery.c b/libmultipath/discovery.c
+index cf8289c..e3d4cd5 100644
+--- a/libmultipath/discovery.c
++++ b/libmultipath/discovery.c
+@@ -8,9 +8,8 @@
+ #include <fcntl.h>
+ #include <sys/ioctl.h>
+ #include <sys/stat.h>
++#include <dirent.h>
+ #include <errno.h>
+-#include <sysfs/dlist.h>
+-#include <sysfs/libsysfs.h>
+ 
+ #include <checkers.h>
+ 
+@@ -24,6 +23,7 @@
+ #include "debug.h"
+ #include "propsel.h"
+ #include "sg_include.h"
++#include "sysfs.h"
+ #include "discovery.h"
+ 
+ struct path *
+@@ -61,7 +61,8 @@ path_discover (vector pathvec, struct config * conf, char * devname, int flag)
+ 	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,
+@@ -86,127 +87,117 @@ path_discover (vector pathvec, struct config * conf, char * devname, int flag)
+ 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;
+-
+-	r = 0;
++	strcpy(devpath,"/sys/block");
++	while ((blkdev = readdir(blkdir)) != NULL) {
++		if ((strcmp(blkdev->d_name,".") == 0) ||
++		    (strcmp(blkdev->d_name,"..") == 0))
++			continue;
+ 
+-	dlist_for_each_data(ls, dev, struct sysfs_class_device)
+-		r += path_discover(pathvec, conf, dev->name, flag);
++		devptr = devpath + 10;
++		*devptr = '\0';
++		strcat(devptr,"/");
++		strcat(devptr,blkdev->d_name);
++		if (stat(devpath, &statbuf) < 0)
++			continue;
+ 
+-out:
+-	sysfs_close_class(class);
+-	return r;
+-}
++		if (S_ISDIR(statbuf.st_mode) == 0)
++			continue;
+ 
+-/*
+- * the daemon can race udev upon path add,
+- * not multipath(8), ran by udev
+- */
+-#if DAEMON
+-#define WAIT_MAX_SECONDS 5
+-#define WAIT_LOOP_PER_SECOND 5
++		condlog(4, "Discover device %s", devpath);
+ 
+-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]; \
+-\
+-	if (safe_sprintf(attr_path, fmt, sysfs_path, dev)) \
+-		return 1; \
+-\
+-	if (wait_for_file(attr_path)) \
+-		return 1; \
++	char *attr; \
+ \
+-	if (!(attr = sysfs_open_attribute(attr_path))) \
++	attr = sysfs_attr_get_value(dev->devpath, #fname); \
++	if (!attr) \
+ 		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); \
+-	buff[attr->len - 1] = '\0'; \
+-	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;
+ }
+ 	
+@@ -223,71 +214,60 @@ 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;
+-	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;
+-
+-	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 ((fd = fopen("/proc/partitions", "r")) < 0) {
++		condlog(0, "Cannot open /proc/partitions");
++		return 1;
++	}
++	
++	while (!feof(fd)) {
++		int r = fscanf(fd,"%u %u %*d %s",&tmpmaj, &tmpmin, dev);
++		if (!r) {
++			fscanf(fd,"%*s\n");
++			continue;
+ 		}
+-		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 (r != 3)
++			continue;
++
++		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 (strncmp(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
++int
+ do_inq(int sg_fd, int cmddt, int evpd, unsigned int pg_op,
+        void *resp, int mx_resp_len, int noisy)
+ {
+@@ -295,7 +275,7 @@ do_inq(int sg_fd, int cmddt, int evpd, unsigned int pg_op,
+             { INQUIRY_CMD, 0, 0, 0, 0, 0 };
+         unsigned char sense_b[SENSE_BUFF_LEN];
+         struct sg_io_hdr io_hdr;
+-                                                                                                                 
++
+         if (cmddt)
+                 inqCmdBlk[1] |= 2;
+         if (evpd)
+@@ -313,10 +293,10 @@ do_inq(int sg_fd, int cmddt, int evpd, unsigned int pg_op,
+         io_hdr.cmdp = inqCmdBlk;
+         io_hdr.sbp = sense_b;
+         io_hdr.timeout = 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) &&
+@@ -339,100 +319,44 @@ do_inq(int sg_fd, int cmddt, int evpd, unsigned int pg_op,
+         return -1;
+ }
+ 
+-int
+-get_serial (char * str, int fd)
++static int
++get_serial (char * str, int maxlen, int fd)
+ {
+         int len = 0;
+         char buff[MX_ALLOC_LEN + 1] = {0};
+ 
+ 	if (fd < 0)
+-                return 0;
++                return 1;
+ 
+ 	if (0 == do_inq(fd, 0, 1, 0x80, buff, MX_ALLOC_LEN, 0)) {
+ 		len = buff[3];
++		if (len >= maxlen)
++			return 1;
+ 		if (len > 0) {
+ 			memcpy(str, buff + 4, len);
+ 			str[len] = '\0';
+ 		}
+-		return 1;
+-	}
+-        return 0;
+-}
+-
+-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);
++		return 0;
+ 	}
+-#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;
++        return 1;
+ }
+ 
+ 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);
+@@ -440,20 +364,12 @@ scsi_sysfs_pathinfo (struct path * pp)
+ 	/*
+ 	 * set the hwe configlet pointer
+ 	 */
+-	pp->hwe = find_hwe(conf->hwtable, pp->vendor_id, pp->product_id);
++	pp->hwe = find_hwe(conf->hwtable, pp->vendor_id, pp->product_id, pp->rev);
+ 
+ 	/*
+ 	 * 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,
+@@ -470,35 +386,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];
+@@ -507,8 +407,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)) {
+@@ -524,20 +423,12 @@ ccw_sysfs_pathinfo (struct path * pp)
+ 	/*
+ 	 * set the hwe configlet pointer
+ 	 */
+-	pp->hwe = find_hwe(conf->hwtable, pp->vendor_id, pp->product_id);
++	pp->hwe = find_hwe(conf->hwtable, pp->vendor_id, pp->product_id, NULL);
+ 
+ 	/*
+ 	 * 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,
+@@ -554,20 +445,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;
++	char *attr;
+ 
+-	condlog(3, "%s: bus = %i", pp->dev, pp->bus);
+-
+-	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);
+@@ -575,19 +466,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;
+@@ -597,7 +515,7 @@ static int
+ scsi_ioctl_pathinfo (struct path * pp, int mask)
+ {
+ 	if (mask & DI_SERIAL) {
+-		get_serial(pp->serial, pp->fd);
++		get_serial(pp->serial, SERIAL_SIZE, pp->fd);
+ 		condlog(3, "%s: serial = %s", pp->dev, pp->serial);
+ 	}
+ 
+@@ -609,12 +527,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 +557,14 @@ get_prio (struct path * pp)
+ 		pp->getprio_selected = 1;
+ 	}
+ 	if (!pp->getprio) {
+-		pp->priority = 1;
++		pp->priority = PRIO_DEFAULT;
+ 	} else if (apply_format(pp->getprio, &buff[0], pp)) {
+ 		condlog(0, "error formatting prio callout command");
+-		pp->priority = -1;
++		pp->priority = PRIO_UNDEF;
+ 		return 1;
+ 	} else if (execute_program(buff, prio, 16)) {
+ 		condlog(0, "error calling out %s", buff);
+-		pp->priority = -1;
++		pp->priority = PRIO_UNDEF;
+ 		return 1;
+ 	} else
+ 		pp->priority = atoi(prio);
+@@ -699,7 +620,12 @@ pathinfo (struct path *pp, vector hwtable, int mask)
+ 	if (mask & DI_CHECKER && get_state(pp))
+ 		goto blank;
+ 	
+-	if (mask & DI_PRIO && pp->state != PATH_DOWN)
++	 /*
++	  * Retrieve path priority even for not PATH_UP paths if it has never
++	  * been successfully obtained before.
++	  */
++	if (mask & DI_PRIO &&
++	    (pp->state == PATH_UP || pp->priority == PRIO_UNDEF))
+ 		get_prio(pp);
+ 
+ 	if (mask & DI_WWID && !strlen(pp->wwid))
+diff --git a/libmultipath/discovery.h b/libmultipath/discovery.h
+index cdc9627..c7cf7e8 100644
+--- a/libmultipath/discovery.h
++++ b/libmultipath/discovery.h
+@@ -5,7 +5,6 @@
+ #define INQUIRY_CMDLEN  6
+ #define INQUIRY_CMD     0x12
+ #define SENSE_BUFF_LEN  32
+-#define DEF_TIMEOUT     60000
+ #define RECOVERED_ERROR 0x01
+ #define MX_ALLOC_LEN    255
+ #define TUR_CMD_LEN     6
+@@ -14,6 +13,10 @@
+ #define BLKGETSIZE      _IO(0x12,96)
+ #endif
+ 
++#ifndef DEF_TIMEOUT
++#define DEF_TIMEOUT	300000
++#endif
++
+ /*
+  * exerpt from sg_err.h
+  */
+@@ -21,16 +24,10 @@
+ #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 *);
+-int get_serial (char * buff, int fd);
+ int do_tur (char *);
+ int devt2devname (char *, char *);
+ int pathinfo (struct path *, vector hwtable, int mask);
+diff --git a/libmultipath/dmparser.c b/libmultipath/dmparser.c
+index 2b170c6..631933d 100644
+--- a/libmultipath/dmparser.c
++++ b/libmultipath/dmparser.c
+@@ -84,11 +84,14 @@ assemble_map (struct multipath * mp)
+ 		freechar -= shift;
+ 
+ 		vector_foreach_slot (pgp->paths, pp, j) {
+-			if (mp->rr_weight == RR_WEIGHT_PRIO && pp->priority)
+-				minio *= pp->priority;
++			int tmp_minio = minio;
++
++			if (mp->rr_weight == RR_WEIGHT_PRIO
++			    && pp->priority > 0)
++				tmp_minio = minio * pp->priority;
+ 
+ 			shift = snprintf(p, freechar, " %s %d",
+-					 pp->dev_t, minio);
++					 pp->dev_t, tmp_minio);
+ 			if (shift >= freechar) {
+ 				fprintf(stderr, "mp->params too small\n");
+ 				return 1;
+@@ -117,6 +120,7 @@ disassemble_map (vector pathvec, char * params, struct multipath * mpp)
+ 	int num_pg_args = 0;
+ 	int num_paths = 0;
+ 	int num_paths_args = 0;
++	int def_minio = 0;
+ 	struct path * pp;
+ 	struct pathgroup * pgp;
+ 
+@@ -305,12 +309,15 @@ disassemble_map (vector pathvec, char * params, struct multipath * mpp)
+ 				if (k == 0 && !strncmp(mpp->selector,
+ 						       "round-robin", 11)) {
+ 					p += get_word(p, &word);
+-					mpp->minio = atoi(word);
++					def_minio = atoi(word);
+ 
+-					if (mpp->rr_weight)
+-						mpp->minio /= mpp->rr_weight;
++					if (mpp->rr_weight == RR_WEIGHT_PRIO
++					    && pp->priority > 0)
++						def_minio /= pp->priority;
+ 
+ 					FREE(word);
++					if (def_minio != mpp->minio)
++						mpp->minio = def_minio;
+ 				}
+ 				else
+ 					p += get_word(p, NULL);
+diff --git a/libmultipath/dmparser.h b/libmultipath/dmparser.h
+diff --git a/libmultipath/hwtable.c b/libmultipath/hwtable.c
+index 5c7d625..2a5f2d5 100644
+--- a/libmultipath/hwtable.c
++++ b/libmultipath/hwtable.c
+@@ -12,13 +12,34 @@
+  * Tuning suggestions on these parameters should go to
+  * dm-devel at redhat.com
+  * 
+- * You are welcome to claim maintainership over a controler
++ * You are welcome to claim maintainership over a controller
+  * family. Please mail the currently enlisted maintainer and
+  * the upstream package maintainer.
+  */
+ static struct hwentry default_hw[] = {
+ 	/*
+-	 * StorageWorks controler family
++	 * Apple controller family
++	 *
++	 * Maintainer : Shyam Sundar
++	 * Mail : g.shyamsundar at yahoo.co.in
++	 */
++	{
++		.vendor        = "APPLE*",
++		.product       = "Xserve RAID ",
++		.getuid        = DEFAULT_GETUID,
++		.getprio       = NULL,
++		.features      = DEFAULT_FEATURES,
++		.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  = DEFAULT_CHECKER,
++	},
++	/*
++	 * StorageWorks controller family
+ 	 *
+ 	 * Maintainer : Christophe Varoqui
+ 	 * Mail : christophe.varoqui at free.fr
+@@ -42,11 +63,11 @@ static struct hwentry default_hw[] = {
+ 		.vendor        = "DEC",
+ 		.product       = "HSG80",
+ 		.getuid        = DEFAULT_GETUID,
+-		.getprio       = NULL,
+-		.features      = DEFAULT_FEATURES,
++		.getprio       = "/sbin/mpath_prio_hp_sw /dev/%n",
++		.features      = "1 queue_if_no_path",
+ 		.hwhandler     = "1 hp_sw",
+ 		.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,
+@@ -54,14 +75,30 @@ 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     = 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  = READSECTOR0,
++	},
++	{
++		/* MSA 1000/MSA1500 EVA 3000/5000 with old firmware */
++		.vendor        = "(COMPAQ|HP)",
++		.product       = "(MSA|HSV)1.0.*",
++		.getuid        = DEFAULT_GETUID,
++		.getprio       = "/sbin/mpath_prio_hp_sw /dev/%n",
++		.features      = "1 queue_if_no_path",
+ 		.hwhandler     = "1 hp_sw",
+ 		.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,
+@@ -69,37 +106,55 @@ static struct hwentry default_hw[] = {
+ 		.checker_name  = HP_SW,
+ 	},
+ 	{
++		/* MSA 1000/1500 with new firmware */
+ 		.vendor        = "HP",
+-		.product       = "HSV2*",
++		.product       = "MSA VOLUME",
+ 		.getuid        = DEFAULT_GETUID,
+-		.getprio       = NULL,
++		.getprio       = "/sbin/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  = READSECTOR0,
++		.checker_name  = TUR,
++	},
++	{
++		/* EVA 3000/5000 with new firmware */
++		.vendor        = "(COMPAQ|HP)",
++		.product       = "(MSA|HSV)1.1.*",
++		.getuid        = DEFAULT_GETUID,
++		.getprio       = "/sbin/mpath_prio_alua /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_UNDEF,
++		.minio         = DEFAULT_MINIO,
++		.checker_name  = TUR,
+ 	},
+ 	{
++		/* EVA 4000/6000/8000 */
+ 		.vendor        = "HP",
+-		.product       = "DF[456]00",
++		.product       = "HSV2.*",
+ 		.getuid        = DEFAULT_GETUID,
+-		.getprio       = NULL,
++		.getprio       = "/sbin/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  = READSECTOR0,
++		.checker_name  = TUR,
+ 	},
+ 	/*
+-	 * DDN controler family
++	 * DDN controller family
+ 	 *
+ 	 * Maintainer : Christophe Varoqui
+ 	 * Mail : christophe.varoqui at free.fr
+@@ -117,10 +172,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
++	 * EMC / Clariion controller family
+ 	 *
+ 	 * Maintainer : Edward Goggin, EMC
+ 	 * Mail : egoggin at emc.com
+@@ -142,7 +197,7 @@ static struct hwentry default_hw[] = {
+ 	},
+ 	{
+ 		.vendor        = "DGC",
+-		.product       = "*",
++		.product       = ".*",
+ 		.bl_product    = "LUNZ",
+ 		.getuid        = DEFAULT_GETUID,
+ 		.getprio       = "/sbin/mpath_prio_emc /dev/%n",
+@@ -157,7 +212,7 @@ static struct hwentry default_hw[] = {
+ 		.checker_name  = EMC_CLARIION,
+ 	},
+ 	/*
+-	 * Fujitsu controler family
++	 * Fujitsu controller family
+ 	 *
+ 	 * Maintainer : Christophe Varoqui
+ 	 * Mail : christophe.varoqui at free.fr
+@@ -178,14 +233,14 @@ static struct hwentry default_hw[] = {
+ 		.checker_name  = READSECTOR0,
+ 	},
+ 	/*
+-	 * Hitachi controler family
++	 * Hitachi controller family
+ 	 *
+-	 * Maintainer : Christophe Varoqui
+-	 * Mail : christophe.varoqui at free.fr
++	 * Maintainer : Matthias Rudolph
++	 * Mail : matthias.rudolph at hds.com
+ 	 */
+ 	{
+-		.vendor        = "HITACHI",
+-		.product       = "{A6189A,OPEN-}",
++		.vendor        = "(HITACHI|HP)",
++		.product       = "OPEN-.*",
+ 		.getuid        = DEFAULT_GETUID,
+ 		.getprio       = NULL,
+ 		.features      = DEFAULT_FEATURES,
+@@ -198,10 +253,25 @@ static struct hwentry default_hw[] = {
+ 		.minio         = DEFAULT_MINIO,
+ 		.checker_name  = READSECTOR0,
+ 	},
++	{
++		.vendor        = "HITACHI",
++		.product       = "DF.*",
++		.getuid        = DEFAULT_GETUID,
++		.getprio       = "/sbin/mpath_prio_hds_modular %d",
++		.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_UNDEF,
++		.minio         = DEFAULT_MINIO,
++		.checker_name  = READSECTOR0,
++	},
+ 	/*
+-	 * IBM controler family
++	 * IBM controller family
+ 	 *
+-	 * Maintainer : Hannes Reinecke, Suse
++	 * Maintainer : Hannes Reinecke, SuSE
+ 	 * Mail : hare at suse.de
+ 	 */
+ 	{
+@@ -236,6 +306,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,
++		.pgpolicy      = GROUP_BY_PRIO,
++		.pgfailback    = -FAILBACK_IMMEDIATE,
++		.rr_weight     = RR_WEIGHT_NONE,
++		.no_path_retry = NO_PATH_RETRY_UNDEF,
++		.minio         = DEFAULT_MINIO,
++		.checker_name  = TUR,
++	},
++	{
+ 		/* IBM DS4200 / FAStT200 */
+ 		.vendor        = "IBM",
+ 		.product       = "3542",
+@@ -254,7 +340,7 @@ static struct hwentry default_hw[] = {
+ 	{
+ 		/* IBM ESS F20 aka Shark */
+ 		.vendor        = "IBM",
+-		.product       = "2105F20",
++		.product       = "2105(800|F20)",
+ 		.getuid        = DEFAULT_GETUID,
+ 		.getprio       = NULL,
+ 		.features      = "1 queue_if_no_path",
+@@ -268,9 +354,9 @@ static struct hwentry default_hw[] = {
+ 		.checker_name  = TUR,
+ 	},
+ 	{
+-		/* IBM DS6000 / SAN Volume Controller */
++		/* IBM DS6000 */
+ 		.vendor        = "IBM",
+-		.product       = "{1750500,2145}",
++		.product       = "1750500",
+ 		.getuid        = DEFAULT_GETUID,
+ 		.getprio       = "/sbin/mpath_prio_alua /dev/%n",
+ 		.features      = "1 queue_if_no_path",
+@@ -292,7 +378,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,12 +386,29 @@ static struct hwentry default_hw[] = {
+ 		.checker_name  = TUR,
+ 	},
+ 	{
++		/* IBM SAN Volume Controller */
++		.vendor        = "IBM",
++		.product       = "2145",
++		.getuid        = DEFAULT_GETUID,
++		.getprio       = "/sbin/mpath_prio_alua /dev/%n",
++		.features      = "1 queue_if_no_path",
++		.hwhandler     = DEFAULT_HWHANDLER,
++		.selector      = DEFAULT_SELECTOR,
++		.pgpolicy      = GROUP_BY_PRIO,
++		.pgfailback    = -FAILBACK_IMMEDIATE,
++		.rr_weight     = RR_WEIGHT_NONE,
++		.no_path_retry = NO_PATH_RETRY_UNDEF,
++		.minio         = DEFAULT_MINIO,
++		.checker_name  = TUR,
++	},
++	{
+ 		/* IBM S/390 ECKD DASD */
+ 		.vendor        = "IBM",
+ 		.product       = "S/390 DASD ECKD",
+-		.getuid        = "/sbin/dasdview -j /dev/%n",
++		.bl_product       = "S/390.*",
++		.getuid        = "/sbin/dasdinfo -u -b %n",
+ 		.getprio       = NULL,
+-		.features      = DEFAULT_FEATURES,
++		.features      = "1 queue_if_no_path",
+ 		.hwhandler     = DEFAULT_HWHANDLER,
+ 		.selector      = DEFAULT_SELECTOR,
+ 		.pgpolicy      = MULTIBUS,
+@@ -315,38 +418,59 @@ static struct hwentry default_hw[] = {
+ 		.minio         = DEFAULT_MINIO,
+ 		.checker_name  = DIRECTIO,
+ 	},
+-	/*
+-	 * NETAPP controler family
++ 	/*
++	 * NETAPP controller family
+ 	 *
+-	 * Maintainer : Igor Feoktistov
+-	 * Mail : igorf at netapp.com
++	 * Maintainer : Dave Wysochanski
++	 * Mail : davidw at netapp.com
+ 	 */
+ 	{
+ 		.vendor        = "NETAPP",
+-		.product       = "LUN",
++		.product       = "LUN.*",
+ 		.getuid        = DEFAULT_GETUID,
+ 		.getprio       = "/sbin/mpath_prio_netapp /dev/%n",
+ 		.features      = "1 queue_if_no_path",
+ 		.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,
++		.minio         = 128,
++		.checker_name  = READSECTOR0,
++	},
++ 	/*
++	 * IBM NSeries (NETAPP) controller family
++	 *
++	 * Maintainer : Dave Wysochanski
++	 * Mail : davidw at netapp.com
++	 */
++	{
++		.vendor        = "IBM",
++		.product       = "Nseries.*",
++		.getuid        = DEFAULT_GETUID,
++		.getprio       = "/sbin/mpath_prio_netapp /dev/%n",
++		.features      = "1 queue_if_no_path",
++		.hwhandler     = DEFAULT_HWHANDLER,
++		.selector      = DEFAULT_SELECTOR,
++		.pgpolicy      = GROUP_BY_PRIO,
++		.pgfailback    = -FAILBACK_IMMEDIATE,
++		.rr_weight     = RR_WEIGHT_NONE,
++		.no_path_retry = NO_PATH_RETRY_UNDEF,
++		.minio         = 128,
+ 		.checker_name  = READSECTOR0,
+ 	},
+ 	/*
+-	 * Pillar Data controler family
++	 * Pillar Data controller family
+ 	 *
+-	 * Maintainer : Christophe Varoqui
+-	 * Mail : christophe.varoqui at free.fr
++	 * Maintainer : Srinivasan Ramani
++	 * Mail : sramani at pillardata.com
+ 	 */
+ 	{
+ 		.vendor        = "Pillar",
+-		.product       = "Axiom 500",
++		.product       = "Axiom.*",
+ 		.getuid        = DEFAULT_GETUID,
+-		.getprio       = "/sbin/mpath_prio_alua %d",
++		.getprio       = "/sbin/mpath_prio_alua %n",
+ 		.features      = DEFAULT_FEATURES,
+ 		.hwhandler     = DEFAULT_HWHANDLER,
+ 		.selector      = DEFAULT_SELECTOR,
+@@ -389,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       = "/sbin/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
+@@ -422,7 +561,7 @@ static struct hwentry default_hw[] = {
+ 	 */
+ 	{
+ 		.vendor        = "SUN",
+-		.product       = "{StorEdge 3510,T4}",
++		.product       = "(StorEdge 3510|T4)",
+ 		.getuid        = DEFAULT_GETUID,
+ 		.getprio       = NULL,
+ 		.features      = DEFAULT_FEATURES,
+diff --git a/libmultipath/hwtable.h b/libmultipath/hwtable.h
+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/lock.c b/libmultipath/lock.c
+new file mode 100644
+index 0000000..0ca8783
+--- /dev/null
++++ b/libmultipath/lock.c
+@@ -0,0 +1,8 @@
++#include <pthread.h>
++#include "lock.h"
++
++void cleanup_lock (void * data)
++{
++	unlock((pthread_mutex_t *)data);
++}
++
+diff --git a/libmultipath/lock.h b/libmultipath/lock.h
+new file mode 100644
+index 0000000..6afecda
+--- /dev/null
++++ b/libmultipath/lock.h
+@@ -0,0 +1,22 @@
++#ifndef _LOCK_H
++#define _LOCK_H
++
++#ifdef LCKDBG
++#define lock(a) \
++	        fprintf(stderr, "%s:%s(%i) lock %p\n", __FILE__, __FUNCTION__, __LINE__, a); \
++        pthread_mutex_lock(a)
++#define unlock(a) \
++	        fprintf(stderr, "%s:%s(%i) unlock %p\n", __FILE__, __FUNCTION__, __LINE__, a); \
++        pthread_mutex_unlock(a)
++#define lock_cleanup_pop(a) \
++	        fprintf(stderr, "%s:%s(%i) unlock %p\n", __FILE__, __FUNCTION__, __LINE__, a); \
++        pthread_cleanup_pop(1);
++#else
++#define lock(a) pthread_mutex_lock(a)
++#define unlock(a) pthread_mutex_unlock(a)
++#define lock_cleanup_pop(a) pthread_cleanup_pop(1);
++#endif
++
++void cleanup_lock (void * data);
++
++#endif /* _LOCK_H */
+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 * fmt, va_list ap)
+ 	/* 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.h b/libmultipath/log.h
+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/memory.c b/libmultipath/memory.c
+diff --git a/libmultipath/memory.h b/libmultipath/memory.h
+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 @@
+ /* local vars */
+ static int sublevel = 0;
+ vector keywords = NULL;
++vector *keywords_addr = NULL;
++
++void set_current_keywords (vector *k)
++{
++	keywords_addr = k;
++	keywords = NULL;
++}
+ 
+ int
+ keyword_alloc(vector keywords, char *string, int (*handler) (vector),
+@@ -53,7 +60,10 @@ keyword_alloc(vector keywords, char *string, int (*handler) (vector),
+ int
+ install_keyword_root(char *string, int (*handler) (vector))
+ {
+-	return keyword_alloc(keywords, string, handler, NULL);
++	int r = keyword_alloc(keywords, string, handler, NULL);
++	if (!r)
++		*keywords_addr = keywords;
++	return r;
+ }
+ 
+ void
+@@ -100,6 +110,9 @@ free_keywords(vector keywords)
+ 	struct keyword *keyword;
+ 	int i;
+ 
++	if (!keywords)
++		return;
++
+ 	for (i = 0; i < VECTOR_SIZE(keywords); i++) {
+ 		keyword = VECTOR_SLOT(keywords, i);
+ 		if (keyword->sub)
+diff --git a/libmultipath/parser.h b/libmultipath/parser.h
+index f0fdd94..95d4e6f 100644
+--- a/libmultipath/parser.h
++++ b/libmultipath/parser.h
+@@ -76,6 +76,7 @@ extern void *set_value(vector strvec);
+ extern int process_stream(vector keywords);
+ 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);
+ int snprint_keyword(char *buff, int len, char *fmt, struct keyword *kw,
+ 		    void *data);
+ 
+diff --git a/libmultipath/pgpolicies.c b/libmultipath/pgpolicies.c
+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 @@
+ 
+ #define POLICY_NAME_SIZE 32
+ 
+-/* Storage controlers capabilities */
++/* Storage controllers capabilities */
+ enum iopolicies { 
+ 	IOPOLICY_UNDEF,
+ 	FAILOVER,
+diff --git a/libmultipath/print.c b/libmultipath/print.c
+index 6cc63e2..b1e7b26 100644
+--- a/libmultipath/print.c
++++ b/libmultipath/print.c
+@@ -5,6 +5,8 @@
+ #include <string.h>
+ #include <libdevmapper.h>
+ #include <stdarg.h>
++#include <sys/stat.h>
++#include <dirent.h>
+ 
+ #include <checkers.h>
+ 
+@@ -13,11 +15,12 @@
+ #include "structs_vec.h"
+ #include "print.h"
+ #include "dmparser.h"
+-#include "configure.h"
+ #include "config.h"
++#include "configure.h"
+ #include "pgpolicies.h"
+ #include "defaults.h"
+ #include "parser.h"
++#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, unsigned int val)
+ static int
+ snprint_size (char * buff, size_t len, unsigned long long size)
+ {
+-	if (size < (1 << 11))
+-		return snprintf(buff, len, "%llu kB", size >> 1);
+-	else if (size < (1 << 21))
+-		return snprintf(buff, len, "%llu MB", size >> 11);
+-	else if (size < (1 << 31))
+-		return snprintf(buff, len, "%llu GB", size >> 21);
++	float s = (float)(size >> 1); /* start with KB */
++	char fmt[6] = {};
++	char units[] = {'K','M','G','T','P'};
++	char *u = units;
++	
++	while (s >= 1024 && *u != 'P') {
++		s = s / 1024;
++		u++;
++	}
++	if (s < 10)
++		snprintf(fmt, 6, "%%.1f%c", *u);
+ 	else
+-		return snprintf(buff, len, "%llu TB", size >> 31);
++		snprintf(fmt, 6, "%%.0f%c", *u);
++
++	return snprintf(buff, len, fmt, s);
+ }
+ 
++/*
++ * multipath info printing functions
++ */
+ static int
+ snprint_name (char * buff, size_t len, struct multipath * mpp)
+ {
+@@ -222,6 +235,9 @@ snprint_action (char * buff, size_t len, struct multipath * mpp)
+ 	}
+ }
+ 
++/*
++ * path info printing functions
++ */
+ static int
+ snprint_path_uuid (char * buff, size_t len, struct path * pp)
+ {
+@@ -292,8 +308,8 @@ snprint_dm_path_state (char * buff, size_t len, struct path * pp)
+ static int
+ snprint_vpr (char * buff, size_t len, struct path * pp)
+ {
+-	return snprintf(buff, len, "%s/%s/%s",
+-		        pp->vendor_id, pp->product_id, pp->rev);
++	return snprintf(buff, len, "%s,%s",
++		        pp->vendor_id, pp->product_id);
+ }
+ 
+ static int
+@@ -496,7 +512,7 @@ snprint_multipath (char * line, int len, char * format,
+ 	char * f = format; /* format string cursor */
+ 	int fwd;
+ 	struct multipath_data * data;
+-	char buff[MAX_FIELD_LEN];
++	char buff[MAX_FIELD_LEN] = {};
+ 
+ 	do {
+ 		if (!TAIL)
+@@ -515,6 +531,7 @@ snprint_multipath (char * line, int len, char * format,
+ 		data->snprint(buff, MAX_FIELD_LEN, mpp);
+ 		PRINT(c, TAIL, buff);
+ 		PAD(data->width);
++		buff[0] = '\0';
+ 	} while (*f++);
+ 
+ 	line[c - line - 1] = '\n';
+@@ -631,7 +648,7 @@ snprint_pathgroup (char * line, int len, char * format,
+ extern void
+ print_multipath_topology (struct multipath * mpp, int verbosity)
+ {
+-	char buff[MAX_LINE_LEN * MAX_LINES];
++	char buff[MAX_LINE_LEN * MAX_LINES] = {};
+ 
+ 	snprint_multipath_topology(&buff[0], MAX_LINE_LEN * MAX_LINES,
+ 				   mpp, verbosity);
+@@ -662,7 +679,10 @@ snprint_multipath_topology (char * buff, int len, struct multipath * mpp,
+ 	c += sprintf(c, "%%n");
+ 	
+ 	if (strncmp(mpp->alias, mpp->wwid, WWID_SIZE))
+-		c += sprintf(c, " (%%w)");
++		c += sprintf(c, " (%%w) ");
++
++	c += sprintf(c, "%%d ");
++	c += snprint_vpr(c, 24, first_path(mpp));
+ 
+ 	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, "        <empty>\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, "        <empty>\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,137 @@ 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)
++{
++	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 (!(blkdir = opendir("/sys/block")))
++		return 1;
++
++	if ((len - fwd - threshold) <= 0)
++		return len;
++	fwd += snprintf(buff + fwd, len - fwd, "available block devices:\n");
++
++	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", devpath);
++		pp = find_path_by_dev(vecs->pathvec, devpath);
++		if (!pp) {
++			r = filter_devnode(conf->blist_devnode,
++					   conf->elist_devnode, devpath);
++			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");
++	}
++	closedir(blkdir);
++
++	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, struct multipath * mpp,
+ 				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..45a3728 100644
+--- a/libmultipath/propsel.c
++++ b/libmultipath/propsel.c
+@@ -15,6 +15,7 @@
+ #include "pgpolicies.h"
+ #include "alias.h"
+ #include "defaults.h"
++#include "devmapper.h"
+ 
+ pgpolicyfn *pgpolicies[] = {
+ 	NULL,
+@@ -41,7 +42,7 @@ select_rr_weight (struct multipath * mp)
+ 	}
+ 	if (mp->hwe && mp->hwe->rr_weight) {
+ 		mp->rr_weight = mp->hwe->rr_weight;
+-		condlog(3, "%s: rr_weight = %i (controler setting)",
++		condlog(3, "%s: rr_weight = %i (controller setting)",
+ 			mp->alias, mp->rr_weight);
+ 		return 0;
+ 	}
+@@ -68,7 +69,7 @@ select_pgfailback (struct multipath * mp)
+ 	}
+ 	if (mp->hwe && mp->hwe->pgfailback != FAILBACK_UNDEF) {
+ 		mp->pgfailback = mp->hwe->pgfailback;
+-		condlog(3, "%s: pgfailback = %i (controler setting)",
++		condlog(3, "%s: pgfailback = %i (controller setting)",
+ 			mp->alias, mp->pgfailback);
+ 		return 0;
+ 	}
+@@ -112,7 +113,7 @@ select_pgpolicy (struct multipath * mp)
+ 		mp->pgpolicyfn = pgpolicies[mp->pgpolicy];
+ 		get_pgpolicy_name(pgpolicy_name, POLICY_NAME_SIZE,
+ 				  mp->pgpolicy);
+-		condlog(3, "%s: pgpolicy = %s (controler setting)",
++		condlog(3, "%s: pgpolicy = %s (controller setting)",
+ 			mp->alias, pgpolicy_name);
+ 		return 0;
+ 	}
+@@ -144,7 +145,7 @@ select_selector (struct multipath * mp)
+ 	}
+ 	if (mp->hwe && mp->hwe->selector) {
+ 		mp->selector = mp->hwe->selector;
+-		condlog(3, "%s: selector = %s (controler setting)",
++		condlog(3, "%s: selector = %s (controller setting)",
+ 			mp->alias, mp->selector);
+ 		return 0;
+ 	}
+@@ -164,6 +165,16 @@ select_alias (struct multipath * mp)
+ 		if (conf->user_friendly_names)
+ 			mp->alias = get_user_friendly_alias(mp->wwid,
+ 					conf->bindings_file);
++		if (mp->alias == NULL){
++			char *alias;
++			if ((alias = MALLOC(WWID_SIZE)) != NULL){
++				if (dm_get_name(mp->wwid, DEFAULT_TARGET,
++						alias) == 1)
++					mp->alias = alias;
++				else
++					FREE(alias);
++			}
++		}
+ 		if (mp->alias == NULL)
+ 			mp->alias = mp->wwid;
+ 	}
+@@ -176,7 +187,7 @@ select_features (struct multipath * mp)
+ {
+ 	if (mp->hwe && mp->hwe->features) {
+ 		mp->features = mp->hwe->features;
+-		condlog(3, "%s: features = %s (controler setting)",
++		condlog(3, "%s: features = %s (controller setting)",
+ 			mp->alias, mp->features);
+ 		return 0;
+ 	}
+@@ -191,7 +202,7 @@ select_hwhandler (struct multipath * mp)
+ {
+ 	if (mp->hwe && mp->hwe->hwhandler) {
+ 		mp->hwhandler = mp->hwe->hwhandler;
+-		condlog(3, "%s: hwhandler = %s (controler setting)",
++		condlog(3, "%s: hwhandler = %s (controller setting)",
+ 			mp->alias, mp->hwhandler);
+ 		return 0;
+ 	}
+@@ -208,7 +219,7 @@ select_checker(struct path *pp)
+ 
+ 	if (pp->hwe && pp->hwe->checker) {
+ 		checker_get(c, pp->hwe->checker);
+-		condlog(3, "%s: path checker = %s (controler setting)",
++		condlog(3, "%s: path checker = %s (controller setting)",
+ 			pp->dev, checker_name(c));
+ 		return 0;
+ 	}
+@@ -229,7 +240,7 @@ select_getuid (struct path * pp)
+ {
+ 	if (pp->hwe && pp->hwe->getuid) {
+ 		pp->getuid = pp->hwe->getuid;
+-		condlog(3, "%s: getuid = %s (controler setting)",
++		condlog(3, "%s: getuid = %s (controller setting)",
+ 			pp->dev, pp->getuid);
+ 		return 0;
+ 	}
+@@ -250,7 +261,7 @@ select_getprio (struct path * pp)
+ {
+ 	if (pp->hwe && pp->hwe->getprio) {
+ 		pp->getprio = pp->hwe->getprio;
+-		condlog(3, "%s: getprio = %s (controler setting)",
++		condlog(3, "%s: getprio = %s (controller setting)",
+ 			pp->dev, pp->getprio);
+ 		return 0;
+ 	}
+@@ -276,7 +287,7 @@ select_no_path_retry(struct multipath *mp)
+ 	}
+ 	if (mp->hwe && mp->hwe->no_path_retry != NO_PATH_RETRY_UNDEF) {
+ 		mp->no_path_retry = mp->hwe->no_path_retry;
+-		condlog(3, "%s: no_path_retry = %i (controler setting)",
++		condlog(3, "%s: no_path_retry = %i (controller setting)",
+ 			mp->alias, mp->no_path_retry);
+ 		return 0;
+ 	}
+@@ -303,7 +314,7 @@ select_minio (struct multipath * mp)
+ 	}
+ 	if (mp->hwe && mp->hwe->minio) {
+ 		mp->minio = mp->hwe->minio;
+-		condlog(3, "%s: minio = %i (controler setting)",
++		condlog(3, "%s: minio = %i (controller setting)",
+ 			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/regex.c b/libmultipath/regex.c
+diff --git a/libmultipath/regex.h b/libmultipath/regex.h
+diff --git a/libmultipath/sg_include.h b/libmultipath/sg_include.h
+diff --git a/libmultipath/structs.c b/libmultipath/structs.c
+index 024e790..d36eaef 100644
+--- a/libmultipath/structs.c
++++ b/libmultipath/structs.c
+@@ -16,6 +16,7 @@
+ #include "debug.h"
+ #include "structs_vec.h"
+ #include "blacklist.h"
++#include "waiter.h"
+ 
+ struct path *
+ alloc_path (void)
+@@ -30,6 +31,7 @@ alloc_path (void)
+ 		pp->sg_id.scsi_id = -1;
+ 		pp->sg_id.lun = -1;
+ 		pp->fd = -1;
++		pp->priority = PRIO_UNDEF;
+ 	}
+ 	return pp;
+ }
+@@ -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, int free_paths)
+ 
+ 	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 state)
+ 
+ 	return count;
+ }
++
++struct path *
++first_path (struct multipath * mpp)
++{
++	struct pathgroup * pgp = VECTOR_SLOT(mpp->pg, 0);
++	return VECTOR_SLOT(pgp->paths, 0);
++}
+diff --git a/libmultipath/structs.h b/libmultipath/structs.h
+index 2b96cfb..75322aa 100644
+--- a/libmultipath/structs.h
++++ b/libmultipath/structs.h
+@@ -1,14 +1,17 @@
+ #ifndef _STRUCTS_H
+ #define _STRUCTS_H
+ 
+-#define WWID_SIZE		64
+-#define SERIAL_SIZE		17
++#define WWID_SIZE		128
++#define SERIAL_SIZE		64
+ #define NODE_NAME_SIZE		19
+ #define PATH_STR_SIZE  		16
+ #define PARAMS_SIZE		1024
+ #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
+@@ -18,6 +21,9 @@
+ #define NO_PATH_RETRY_FAIL	-1
+ #define NO_PATH_RETRY_QUEUE	-2
+ 
++#define PRIO_UNDEF		-1
++#define PRIO_DEFAULT		1
++
+ enum free_path_switch {
+ 	KEEP_PATHS,
+ 	FREE_PATHS
+@@ -55,6 +61,11 @@ enum pgstates {
+ 	PGSTATE_ACTIVE
+ };
+ 
++enum pgtimeouts {
++	PGTIMEOUT_UNDEF,
++	PGTIMEOUT_NONE
++};
++
+ struct scsi_idlun {
+ 	int dev_id;
+ 	int host_unique_id;
+@@ -78,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];
+@@ -127,6 +148,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 +164,7 @@ struct multipath {
+ 	struct mpentry * mpe;
+ 	struct hwentry * hwe;
+ 
+-	/* daemon store a data blob for DM event waiter threads */
++	/* threads */
+ 	void * waiter;
+ 
+ 	/* stats */
+@@ -151,6 +173,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 +208,11 @@ struct multipath * find_mp_by_minor (vector mp, int minor);
+ 	
+ struct path * find_path_by_devt (vector pathvec, char * devt);
+ struct path * find_path_by_dev (vector pathvec, char * dev);
++struct path * first_path (struct multipath * mpp);
+ 
+ int pathcountgr (struct pathgroup *, int);
+ int pathcount (struct multipath *, int);
+ 
+-char sysfs_path[FILE_NAME_SIZE];
++extern char sysfs_path[PATH_SIZE];
+ 
+-#endif
++#endif /* _STRUCTS_H */
+diff --git a/libmultipath/structs_vec.c b/libmultipath/structs_vec.c
+index 86bf2a5..1cc6028 100644
+--- a/libmultipath/structs_vec.c
++++ b/libmultipath/structs_vec.c
+@@ -13,8 +13,9 @@
+ #include "dmparser.h"
+ #include "config.h"
+ #include "propsel.h"
++#include "sysfs.h"
+ #include "discovery.h"
+-
++#include "waiter.h"
+ 
+ /*
+  * creates or updates mpp->paths reading mpp->pg
+@@ -58,8 +59,8 @@ adopt_paths (vector pathvec, struct multipath * mpp)
+ 
+ 	vector_foreach_slot (pathvec, pp, i) {
+ 		if (!strncmp(mpp->wwid, pp->wwid, WWID_SIZE)) {
+-			condlog(3, "%s ownership set to %s",
+-				pp->dev_t, mpp->alias);
++			condlog(3, "%s: ownership set to %s",
++				pp->dev, mpp->alias);
+ 			pp->mpp = mpp;
+ 			
+ 			if (!mpp->paths && !(mpp->paths = vector_alloc()))
+@@ -96,7 +97,7 @@ orphan_paths (vector pathvec, struct multipath * mpp)
+ 
+ 	vector_foreach_slot (pathvec, pp, i) {
+ 		if (pp->mpp == mpp) {
+-			condlog(4, "%s is orphaned", pp->dev_t);
++			condlog(4, "%s: orphaned", pp->dev);
+ 			orphan_path(pp);
+ 		}
+ 	}
+@@ -117,6 +118,8 @@ remove_map (struct multipath * mpp, struct vectors * vecs,
+ {
+ 	int i;
+ 
++	condlog(4, "%s: remove multipath map", mpp->alias);
++
+ 	/*
+ 	 * stop the DM event waiter thread
+ 	 */
+@@ -244,8 +247,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);
+@@ -269,6 +281,7 @@ retry:
+ #endif
+ 			goto retry;
+ 		}
++		condlog(0, "%s: failed to setup multipath", mpp->alias);
+ 		goto out;
+ 	}
+ 
+@@ -277,10 +290,10 @@ retry:
+ 	select_rr_weight(mpp);
+ 	select_pgfailback(mpp);
+ 	set_no_path_retry(mpp);
++	select_pg_timeout(mpp);
+ 
+ 	return 0;
+ out:
+-	condlog(0, "%s: failed to setup multipath", mpp->alias);
+ 	remove_map(mpp, vecs, NULL, 1);
+ 	return 1;
+ }
+@@ -361,10 +374,10 @@ verify_paths(struct multipath * mpp, struct vectors * vecs, vector rpvec)
+ 		/*
+ 		 * 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--;
+@@ -382,3 +395,88 @@ verify_paths(struct multipath * mpp, struct vectors * vecs, vector rpvec)
+ 	return count;
+ }
+ 
++int update_multipath (struct vectors *vecs, char *mapname)
++{
++	struct multipath *mpp;
++	struct pathgroup  *pgp;
++	struct path *pp;
++	int i, j;
++
++	mpp = find_mp_by_alias(vecs->mpvec, mapname);
++
++	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))
++		return 1; /* mpp freed in setup_multipath */
++
++	/*
++	 * compare checkers states with DM states
++	 */
++	vector_foreach_slot (mpp->pg, pgp, i) {
++		vector_foreach_slot (pgp->paths, pp, j) {
++			if (pp->dmstate != PSTATE_FAILED)
++				continue;
++
++			if (pp->state != PATH_DOWN) {
++				int oldstate = pp->state;
++				condlog(2, "%s: mark as failed", pp->dev_t);
++				mpp->stat_path_failures++;
++				pp->state = PATH_DOWN;
++				if (oldstate == PATH_UP ||
++				    oldstate == PATH_GHOST)
++					update_queue_mode_del_path(mpp);
++
++				/*
++				 * if opportune,
++				 * schedule the next check earlier
++				 */
++				if (pp->tick > conf->checkint)
++					pp->tick = conf->checkint;
++			}
++		}
++	}
++
++	return 0;
++}
++
++/*
++ * mpp->no_path_retry:
++ *   -2 (QUEUE) : queue_if_no_path enabled, never turned off
++ *   -1 (FAIL)  : fail_if_no_path
++ *    0 (UNDEF) : nothing
++ *   >0         : queue_if_no_path enabled, turned off after polling n times
++ */
++void update_queue_mode_del_path(struct multipath *mpp)
++{
++	if (--mpp->nr_active == 0 && mpp->no_path_retry > 0) {
++		/*
++		 * Enter retry mode.
++		 * meaning of +1: retry_tick may be decremented in
++		 *                checkerloop before starting retry.
++		 */
++		mpp->stat_queueing_timeouts++;
++		mpp->retry_tick = mpp->no_path_retry * conf->checkint + 1;
++		condlog(1, "%s: Entering recovery mode: max_retries=%d",
++			mpp->alias, mpp->no_path_retry);
++	}
++	condlog(2, "%s: remaining active paths: %d", mpp->alias, mpp->nr_active);
++}
++
++void update_queue_mode_add_path(struct multipath *mpp)
++{
++	if (mpp->nr_active++ == 0 && mpp->no_path_retry > 0) {
++		/* come back to normal mode from retry mode */
++		mpp->retry_tick = 0;
++		dm_queue_if_no_path(mpp->alias, 1);
++		condlog(2, "%s: queue_if_no_path enabled", mpp->alias);
++		condlog(1, "%s: Recovered to normal mode", mpp->alias);
++	}
++	condlog(2, "%s: remaining active paths: %d", mpp->alias, mpp->nr_active);
++}
++
+diff --git a/libmultipath/structs_vec.h b/libmultipath/structs_vec.h
+index 348e9e5..81d9eaa 100644
+--- a/libmultipath/structs_vec.h
++++ b/libmultipath/structs_vec.h
+@@ -9,17 +9,6 @@ struct vectors {
+ 	vector mpvec;
+ };
+ 
+-#if DAEMON
+-struct event_thread {
+-	struct dm_task *dmt;
+-	pthread_t thread;
+-	int event_nr;
+-	char mapname[WWID_SIZE];
+-	struct vectors *vecs;
+-	struct multipath *mpp;
+-};
+-#endif
+-
+ 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 (struct vectors * vecs,
+ 				start_waiter_thread_func *start_waiter);
+ struct multipath * add_map_with_path (struct vectors * vecs,
+ 				struct path * pp, int add_vec);
++int update_multipath (struct vectors *vecs, char *mapname);
++void update_queue_mode_del_path(struct multipath *mpp);
++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 * mpp)
+ 		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/switchgroup.h b/libmultipath/switchgroup.h
+diff --git a/libmultipath/sysfs.c b/libmultipath/sysfs.c
+new file mode 100644
+index 0000000..1fb5436
+--- /dev/null
++++ b/libmultipath/sysfs.c
+@@ -0,0 +1,410 @@
++/*
++ * Copyright (C) 2005-2006 Kay Sievers <kay.sievers at vrfy.org>
++ *
++ *	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 <stdlib.h>
++#include <stdio.h>
++#include <stddef.h>
++#include <unistd.h>
++#include <fcntl.h>
++#include <ctype.h>
++#include <errno.h>
++#include <sys/stat.h>
++#include <string.h>
++
++#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 = NULL;
++	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);
++			attr = attr_loop;
++		}
++	}
++	if (!attr) {
++		/* store attribute 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);
++	} else {
++		/* clear old value */
++		if(attr->value)
++			memset(attr->value, 0x00, sizeof(attr->value));
++	}
++
++	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..cf3af36
+--- /dev/null
++++ b/libmultipath/sysfs.h
+@@ -0,0 +1,24 @@
++/*
++ * sysfs.h
++ */
++
++#ifndef _LIBMULTIPATH_SYSFS_H
++#define _LIBMULTIPATH_SYSFS_H
++
++#ifdef DEBUG
++# define dbg printf
++#else
++# define dbg(format, arg...) do {} while (0)
++#endif
++
++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 <unistd.h>
+ #include <stdio.h>
+ #include <stdlib.h>
++#include <stddef.h>
+ #include <string.h>
+ #include <fcntl.h>
+ #include <time.h>
+ #include <sys/socket.h>
+ #include <sys/user.h>
+-#include <asm/types.h>
++#include <sys/un.h>
++#include <linux/types.h>
+ #include <linux/netlink.h>
+ #include <pthread.h>
+ #include <sys/mman.h>
+@@ -105,6 +107,8 @@ int uevent_listen(int (*uev_trigger)(struct uevent *, void * trigger_data),
+ {
+ 	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)(struct uevent *, void * trigger_data),
+ 	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/uevent.h b/libmultipath/uevent.h
+diff --git a/libmultipath/util.c b/libmultipath/util.c
+index 376ca04..eaf2266 100644
+--- a/libmultipath/util.c
++++ b/libmultipath/util.c
+@@ -30,6 +30,15 @@ strcmp_chomp(char *str1, char *str2)
+ }
+ 
+ void
++strchop(char *str)
++{
++	int i;
++
++	for (i=strlen(str)-1; i >=0 && isspace(str[i]); --i) ;
++	str[++i] = '\0';
++}
++
++void
+ basename (char * str1, char * str2)
+ {
+ 	char *p = str1 + (strlen(str1) - 1);
+@@ -94,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 51f052a..d0df8aa 100644
+--- a/libmultipath/util.h
++++ b/libmultipath/util.h
+@@ -2,10 +2,13 @@
+ #define _UTIL_H
+ 
+ int strcmp_chomp(char *, char *);
++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/libmultipath/uxsock.c b/libmultipath/uxsock.c
+diff --git a/libmultipath/uxsock.h b/libmultipath/uxsock.h
+diff --git a/libmultipath/vector.c b/libmultipath/vector.c
+diff --git a/libmultipath/vector.h b/libmultipath/vector.h
+index 294f0b1..993ba79 100644
+--- a/libmultipath/vector.h
++++ b/libmultipath/vector.h
+@@ -37,6 +37,8 @@ typedef struct _vector *vector;
+ 
+ #define vector_foreach_slot(v,p,i) \
+ 	for (i = 0; i < (v)->allocated && ((p) = (v)->slot[i]); i++)
++#define vector_foreach_slot_after(v,p,i) \
++	for (; i < (v)->allocated && ((p) = (v)->slot[i]); i++)
+ 
+ /* Prototypes */
+ extern vector vector_alloc(void);
+diff --git a/libmultipath/version.h b/libmultipath/version.h
+new file mode 100644
+index 0000000..d577ec9
+--- /dev/null
++++ b/libmultipath/version.h
+@@ -0,0 +1,37 @@
++/*
++ * Soft:        multipath device mapper target autoconfig
++ *
++ * Version:     $Id: main.h,v 0.0.1 2003/09/18 15:13:38 cvaroqui Exp $
++ *
++ * Author:      Christophe Varoqui
++ *
++ *              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.
++ *
++ *              This program is free software; you can redistribute it and/or
++ *              modify it under the terms of the GNU General Public License
++ *              as published by the Free Software Foundation; either version
++ *              2 of the License, or (at your option) any later version.
++ *
++ * Copyright (c) 2006 Christophe Varoqui
++ */
++#ifndef _VERSION_H
++#define _VERSION_H
++
++#define VERSION_CODE 0x000407
++#define DATE_CODE    0x030c06
++
++#define PROG    "multipath-tools"
++
++#define MULTIPATH_VERSION(version)      \
++	(version >> 16) & 0xFF,         \
++	(version >> 8) & 0xFF,          \
++	version & 0xFF
++
++#define VERSION_STRING PROG" v%d.%d.%d (%.2d/%.2d, 20%.2d)\n",  \
++		MULTIPATH_VERSION(VERSION_CODE),                \
++		MULTIPATH_VERSION(DATE_CODE)
++
++#endif /* _VERSION_H */
+diff --git a/libmultipath/waiter.c b/libmultipath/waiter.c
+new file mode 100644
+index 0000000..d7af0d1
+--- /dev/null
++++ b/libmultipath/waiter.c
+@@ -0,0 +1,233 @@
++/*
++ * Copyright (c) 2004, 2005 Christophe Varoqui
++ * Copyright (c) 2005 Kiyoshi Ueda, NEC
++ * Copyright (c) 2005 Benjamin Marzinski, Redhat
++ * Copyright (c) 2005 Edward Goggin, EMC
++ */
++#include <unistd.h>
++#include <libdevmapper.h>
++#include <sys/mman.h>
++#include <pthread.h>
++#include <signal.h>
++
++#include "vector.h"
++#include "memory.h"
++#include "checkers.h"
++#include "structs.h"
++#include "structs_vec.h"
++#include "devmapper.h"
++#include "debug.h"
++#include "lock.h"
++#include "waiter.h"
++
++struct event_thread *alloc_waiter (void)
++{
++
++	struct event_thread *wp;
++
++	wp = (struct event_thread *)MALLOC(sizeof(struct event_thread));
++
++	return wp;
++}
++
++void free_waiter (void *data)
++{
++	struct event_thread *wp = (struct event_thread *)data;
++
++	/*
++	 * indicate in mpp that the wp is already freed storage
++	 */
++	lock(wp->vecs->lock);
++
++	if (wp->mpp)
++		/*
++		 * be careful, mpp may already be freed -- null if so
++		 */
++		wp->mpp->waiter = NULL;
++	else
++		condlog(3, "free_waiter, mpp freed before wp=%p,", wp);
++
++	unlock(wp->vecs->lock);
++
++	if (wp->dmt)
++		dm_task_destroy(wp->dmt);
++
++	FREE(wp);
++}
++
++void stop_waiter_thread (struct multipath *mpp, struct vectors *vecs)
++{
++	struct event_thread *wp = (struct event_thread *)mpp->waiter;
++	
++	if (!wp) {
++		condlog(3, "%s: no waiter thread", mpp->alias);
++		return;
++	}
++	condlog(2, "%s: stop event checker thread", wp->mapname);
++	pthread_kill((pthread_t)wp->thread, SIGUSR1);
++}
++
++static sigset_t unblock_signals(void)
++{
++	sigset_t set, old;
++
++	sigemptyset(&set);
++	sigaddset(&set, SIGHUP);
++	sigaddset(&set, SIGUSR1);
++	pthread_sigmask(SIG_UNBLOCK, &set, &old);
++	return old;
++}
++
++/*
++ * returns the reschedule delay
++ * negative means *stop*
++ */
++int waiteventloop (struct event_thread *waiter)
++{
++	sigset_t set;
++	int event_nr;
++	int r;
++
++	if (!waiter->event_nr)
++		waiter->event_nr = dm_geteventnr(waiter->mapname);
++
++	if (!(waiter->dmt = dm_task_create(DM_DEVICE_WAITEVENT))) {
++		condlog(0, "%s: devmap event #%i dm_task_create error",
++				waiter->mapname, waiter->event_nr);
++		return 1;
++	}
++
++	if (!dm_task_set_name(waiter->dmt, waiter->mapname)) {
++		condlog(0, "%s: devmap event #%i dm_task_set_name error",
++				waiter->mapname, waiter->event_nr);
++		dm_task_destroy(waiter->dmt);
++		return 1;
++	}
++
++	if (waiter->event_nr && !dm_task_set_event_nr(waiter->dmt,
++						      waiter->event_nr)) {
++		condlog(0, "%s: devmap event #%i dm_task_set_event_nr error",
++				waiter->mapname, waiter->event_nr);
++		dm_task_destroy(waiter->dmt);
++		return 1;
++	}
++
++	dm_task_no_open_count(waiter->dmt);
++	
++	/* accept wait interruption */
++	set = unblock_signals();
++
++	/* wait */
++	r = dm_task_run(waiter->dmt);
++
++	/* wait is over : event or interrupt */
++	pthread_sigmask(SIG_SETMASK, &set, NULL);
++
++	if (!r) /* wait interrupted by signal */
++		return -1;
++
++	dm_task_destroy(waiter->dmt);
++	waiter->dmt = NULL;
++	waiter->event_nr++;
++
++	/*
++	 * upon event ...
++	 */
++	while (1) {
++		condlog(3, "%s: devmap event #%i",
++				waiter->mapname, waiter->event_nr);
++
++		/*
++		 * event might be :
++		 *
++		 * 1) a table reload, which means our mpp structure is
++		 *    obsolete : refresh it through update_multipath()
++		 * 2) a path failed by DM : mark as such through
++		 *    update_multipath()
++		 * 3) map has gone away : stop the thread.
++		 * 4) a path reinstate : nothing to do
++		 * 5) a switch group : nothing to do
++		 */
++		pthread_cleanup_push(cleanup_lock, waiter->vecs->lock);
++		lock(waiter->vecs->lock);
++		r = update_multipath(waiter->vecs, waiter->mapname);
++		lock_cleanup_pop(waiter->vecs->lock);
++
++		if (r) {
++			condlog(2, "%s: event checker exit", 
++				waiter->mapname);
++			return -1; /* stop the thread */
++		}
++
++		event_nr = dm_geteventnr(waiter->mapname);
++
++		if (waiter->event_nr == event_nr)
++			return 1; /* upon problem reschedule 1s later */
++
++		waiter->event_nr = event_nr;
++	}
++	return -1; /* never reach there */
++}
++
++void *waitevent (void *et)
++{
++	int r;
++	struct event_thread *waiter;
++
++	mlockall(MCL_CURRENT | MCL_FUTURE);
++
++	waiter = (struct event_thread *)et;
++	pthread_cleanup_push(free_waiter, et);
++
++	while (1) {
++		r = waiteventloop(waiter);
++
++		if (r < 0)
++			break;
++
++		sleep(r);
++	}
++
++	pthread_cleanup_pop(1);
++	return NULL;
++}
++
++int start_waiter_thread (struct multipath *mpp, struct vectors *vecs)
++{
++	pthread_attr_t attr;
++	struct event_thread *wp;
++
++	if (!mpp)
++		return 0;
++
++	if (pthread_attr_init(&attr))
++		goto out;
++
++	pthread_attr_setstacksize(&attr, 32 * 1024);
++	pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
++
++	wp = alloc_waiter();
++
++	if (!wp)
++		goto out;
++
++	mpp->waiter = (void *)wp;
++	strncpy(wp->mapname, mpp->alias, WWID_SIZE);
++	wp->vecs = vecs;
++	wp->mpp = mpp;
++
++	if (pthread_create(&wp->thread, &attr, waitevent, wp)) {
++		condlog(0, "%s: cannot create event checker", wp->mapname);
++		goto out1;
++	}
++	condlog(2, "%s: event checker started", wp->mapname);
++
++	return 0;
++out1:
++	free_waiter(wp);
++	mpp->waiter = NULL;
++out:
++	condlog(0, "failed to start waiter thread");
++	return 1;
++}
++
+diff --git a/libmultipath/waiter.h b/libmultipath/waiter.h
+new file mode 100644
+index 0000000..0223924
+--- /dev/null
++++ b/libmultipath/waiter.h
+@@ -0,0 +1,23 @@
++#ifndef _WAITER_H
++#define _WAITER_H
++
++#if DAEMON
++
++struct event_thread {
++	struct dm_task *dmt;
++	pthread_t thread;
++	int event_nr;
++	char mapname[WWID_SIZE];
++	struct vectors *vecs;
++	struct multipath *mpp;
++};
++
++struct event_thread * alloc_waiter (void);
++void free_waiter (void *data);
++void stop_waiter_thread (struct multipath *mpp, struct vectors *vecs);
++int start_waiter_thread (struct multipath *mpp, struct vectors *vecs);
++int waiteventloop (struct event_thread *waiter);
++void *waitevent (void *et);
++
++#endif /* DAEMON */
++#endif /* _WAITER_H */
+diff --git a/multipath-tools.spec.in b/multipath-tools.spec.in
+index 494f09e..9d6a7ea 100644
+--- a/multipath-tools.spec.in
++++ b/multipath-tools.spec.in
+@@ -50,6 +50,7 @@ rm -rf $RPM_BUILD_ROOT
+ %{prefix}/sbin/mpath_prio_balance_units
+ %{prefix}/sbin/mpath_prio_netapp
+ %{prefix}/sbin/mpath_prio_tpc
++%{prefix}/sbin/mpath_prio_hds_modular
+ %{prefix}/usr/share/man/man8/devmap_name.8.gz
+ %{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..649ff8c 100644
+--- a/multipath.conf.annotated
++++ b/multipath.conf.annotated
+@@ -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
+-## desc  : list of per storage controler settings
++## desc  : list of per storage controller settings
+ ##	  overrides default settings (device_maps block)
+ ##         overriden by per multipath settings (multipaths block)
+ ##
+@@ -245,7 +261,7 @@
+ #	#
+ #	# name  : device
+ #	# scope : multipath & multipathd
+-#	# desc  : settings for this specific storage controler
++#	# desc  : settings for this specific storage controller
+ #	#
+ #	device {
+ #		#
+@@ -260,7 +276,7 @@
+ #		# name    : path_grouping_policy
+ #		# scope   : multipath
+ #		# desc    : path grouping policy to apply to multipath hosted
+-#		#           by this storage controler
++#		#           by this storage controller
+ #		# 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/01_udev b/multipath/01_udev
+diff --git a/multipath/02_multipath b/multipath/02_multipath
+diff --git a/multipath/Makefile b/multipath/Makefile
+index 646dfc2..c4c70cd 100644
+--- a/multipath/Makefile
++++ b/multipath/Makefile
+@@ -12,7 +12,7 @@ CFLAGS += -I$(multipathdir) -I$(checkersdir)
+ ifeq ($(strip $(BUILD)),klibc)
+ 	OBJS += $(libdm) $(libsysfs)
+ else
+-	LDFLAGS += -ldevmapper -lsysfs
++	LDFLAGS += -ldevmapper
+ endif
+ 
+ EXEC = multipath
+@@ -22,14 +22,14 @@ all: $(BUILD)
+ prepare:
+ 	make -C $(multipathdir) prepare
+ 	rm -f core *.o *.gz
++	$(GZIP) $(EXEC).8 > $(EXEC).8.gz
++	$(GZIP) $(EXEC).conf.5 > $(EXEC).conf.5.gz
+ 
+ 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)
+@@ -44,11 +44,14 @@ install:
+ 	install -m 644 multipath.rules $(DESTDIR)/etc/udev/rules.d/
+ 	install -d $(DESTDIR)$(mandir)
+ 	install -m 644 $(EXEC).8.gz $(DESTDIR)$(mandir)
++	install -d $(DESTDIR)$(man5dir)
++	install -m 644 $(EXEC).conf.5.gz $(DESTDIR)$(man5dir)
+ 
+ uninstall:
+ 	rm $(DESTDIR)/etc/udev/rules.d/multipath.rules
+ 	rm $(DESTDIR)$(bindir)/$(EXEC)
+ 	rm $(DESTDIR)$(mandir)/$(EXEC).8.gz
++	rm $(DESTDIR)$(man5dir)/$(EXEC).conf.5.gz
+ 
+ clean:
+ 	rm -f core *.o $(EXEC) *.gz
+diff --git a/multipath/dev_t.h b/multipath/dev_t.h
+diff --git a/multipath/main.c b/multipath/main.c
+index 98f7207..815c307 100644
+--- a/multipath/main.c
++++ b/multipath/main.c
+@@ -25,7 +25,6 @@
+ #include <stdio.h>
+ #include <unistd.h>
+ #include <ctype.h>
+-#include <sysfs/libsysfs.h>
+ 
+ #include <checkers.h>
+ #include <vector.h>
+@@ -37,6 +36,7 @@
+ #include <structs.h>
+ #include <structs_vec.h>
+ #include <dmparser.h>
++#include <sysfs.h>
+ #include <config.h>
+ #include <blacklist.h>
+ #include <discovery.h>
+@@ -46,8 +46,7 @@
+ #include <alias.h>
+ #include <configure.h>
+ #include <pgpolicies.h>
+-
+-#include "main.h"
++#include <version.h>
+ 
+ static int
+ filter_pathvec (vector pathvec, char * refwwid)
+@@ -73,7 +72,7 @@ static void
+ usage (char * progname)
+ {
+ 	fprintf (stderr, VERSION_STRING);
+-	fprintf (stderr, "Usage: %s\t[-v level] [-d] [-l|-ll|-f|-F]\n",
++	fprintf (stderr, "Usage: %s\t[-v level] [-d] [-h|-l|-ll|-f|-F]\n",
+ 		progname);
+ 	fprintf (stderr,
+ 		"\t\t\t[-p failover|multibus|group_by_serial|group_by_prio]\n" \
+@@ -84,6 +83,7 @@ usage (char * progname)
+ 		"\t   1\t\t\tprint created devmap names only\n" \
+ 		"\t   2\t\t\tdefault verbosity\n" \
+ 		"\t   3\t\t\tprint debug information\n" \
++		"\t-h\t\tprint this usage text\n" \
+ 		"\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" \
+@@ -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);
+ 
+-			if (!pp->priority)
++			if (pp->priority == PRIO_UNDEF)
+ 				pathinfo(pp, conf->hwtable, DI_PRIO);
+ 		}
+ 	}
+@@ -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);
+ 
+-	if (conf->list)
++	if (conf->list) {
++		r = 0;
+ 		goto out;
++	}
+ 
+ 	/*
+ 	 * core logic entry point
+@@ -305,24 +313,24 @@ main (int argc, char *argv[])
+ 	int arg;
+ 	extern char *optarg;
+ 	extern int optind;
+-	int i, r;
++	int i, r = 1;
+ 
+ 	if (getuid() != 0) {
+ 		fprintf(stderr, "need to be root\n");
+ 		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)) {
+-		condlog(0, "multipath tools need sysfs mounted");
+-		exit(1);
+-	}
+ 	if (load_config(DEFAULT_CONFIGFILE))
+ 		exit(1);
+ 
+-	while ((arg = getopt(argc, argv, ":qdl::Ffi:M:v:p:b:")) != EOF ) {
++	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:")) != EOF ) {
+ 		switch(arg) {
+ 		case 1: printf("optarg : %s\n",optarg);
+ 			break;
+@@ -365,6 +373,8 @@ main (int argc, char *argv[])
+ 				usage(argv[0]);
+ 			}                
+ 			break;
++		case 'h':
++			usage(argv[0]);
+ 		case ':':
+ 			fprintf(stderr, "Missing option arguement\n");
+ 			usage(argv[0]);        
+@@ -391,6 +401,7 @@ main (int argc, char *argv[])
+ 			conf->dev_type = DEV_DEVMAP;
+ 
+ 	}
++	dm_init();
+ 
+ 	if (conf->remove == FLUSH_ONE) {
+ 		if (conf->dev_type == DEV_DEVMAP)
+@@ -408,6 +419,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/main.h b/multipath/main.h
+deleted file mode 100644
+index 8d5b285..0000000
+--- a/multipath/main.h
++++ /dev/null
+@@ -1,39 +0,0 @@
+-/*
+- * Soft:        Description here...
+- *
+- * Version:     $Id: main.h,v 0.0.1 2003/09/18 15:13:38 cvaroqui Exp $
+- *
+- * Author:      Copyright (C) 2003 Christophe Varoqui
+- *
+- *              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.
+- *
+- *              This program is free software; you can redistribute it and/or
+- *              modify it under the terms of the GNU General Public License
+- *              as published by the Free Software Foundation; either version
+- *              2 of the License, or (at your option) any later version.
+- */
+-
+-#ifndef _MAIN_H
+-#define _MAIN_H
+-
+-/*
+- * Build version
+- */
+-#define PROG    "multipath"
+-
+-#define VERSION_CODE 0x000407
+-#define DATE_CODE    0x030c06
+-
+-#define MULTIPATH_VERSION(version)	\
+-	(version >> 16) & 0xFF,		\
+-	(version >> 8) & 0xFF,		\
+-	version & 0xFF
+-
+-#define VERSION_STRING PROG" v%d.%d.%d (%.2d/%.2d, 20%.2d)\n",	\
+-                MULTIPATH_VERSION(VERSION_CODE),		\
+-                MULTIPATH_VERSION(DATE_CODE)
+-
+-#endif
+diff --git a/multipath/multipath.8 b/multipath/multipath.8
+index 7133598..693872b 100644
+--- a/multipath/multipath.8
++++ b/multipath/multipath.8
+@@ -1,4 +1,4 @@
+-.TH MULTIPATH 8 "February 2004" "" "Linux Administrator's Manual"
++.TH MULTIPATH 8 "July 2006" "" "Linux Administrator's Manual"
+ .SH NAME
+ multipath \- Device mapper target autoconfig
+ .SH SYNOPSIS
+@@ -6,7 +6,7 @@ multipath \- Device mapper target autoconfig
+ .RB [\| \-v\ \c
+ .IR verbosity \|]
+ .RB [\| \-d \|]
+-.RB [\| \-l | \-ll | \-f | \-F \|]
++.RB [\| \-h | \-l | \-ll | \-f | \-F \|]
+ .RB [\| \-p\ \c
+ .BR failover | multibus | group_by_serial | group_by_prio | group_by_node_name \|]
+ .RB [\| device \|]
+@@ -29,6 +29,9 @@ print the created or updated multipath names only, for use to feed other tools l
+ print all info : detected paths, coalesced paths (ie multipaths) and device maps
+ .RE
+ .TP
++.B \-h
++print usage text
++.TP
+ .B \-d
+ dry run, do not create or update devmaps
+ .TP
+@@ -38,12 +41,6 @@ show the current multipath topology from information fetched in sysfs and the de
+ .B \-ll
+ show the current multipath topology from all available information (sysfs, the device mapper, path checkers ...)
+ .TP
+-.TP
+-.BI \-D " major:minor"
+-update only the devmap the path pointed by
+-.I major:minor
+-is in
+-.TP
+ .B \-f
+ flush a multipath device map specified as parameter, if unused
+ .TP
+@@ -64,7 +61,7 @@ all paths in 1 priority group
+ 1 priority group per serial
+ .TP
+ .B group_by_prio
+-1 priority group per priority value. Priorities are determined by callout programs specified as a global, per-controler or per-multipath option in the configuration file
++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.
+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
++<section> {
++.RS
++.ft B
++<attribute> <value>
++.I "..."
++.ft B
++<subsection> {
++.RS
++.ft B
++<attribute> <value>
++.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<n>.
++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, <christophe.varoqui at free.fr> and others.
+diff --git a/multipath/multipath.rules b/multipath/multipath.rules
+index 10751ce..9d3579f 100644
+--- a/multipath/multipath.rules
++++ b/multipath/multipath.rules
+@@ -1,18 +1,7 @@
+ #
+-# multipath and multipath partitions nodes are created in /dev/mapper/
+-# this file should be installed in /etc/udev/rules.d
++# udev rules for multipathing.
++# The persistent symlinks are created with the kpartx rules
+ #
+-# !! udev must not discard DM events !!
+-# !! check the other installed rules !!
+-#
+-
+-# lookup the devmap name
+-#ACTION=="add", SUBSYSTEM=="block", KERNEL=="dm-*", \
+-#	PROGRAM="/sbin/devmap_name %M %m"
+-ACTION=="add", SUBSYSTEM=="block", KERNEL=="dm-*", \
+-	PROGRAM="/sbin/dmsetup -j %M -m %m --noopencount --noheadings -c -o name info"
+-
+-# take care of devmap partitioning
+-ACTION=="add", SUBSYSTEM=="block", KERNEL=="dm-*", \
+-	RUN+="/sbin/kpartx -a /dev/mapper/%c"
+ 
++# socket for uevents
++RUN+="socket:/org/kernel/dm/multipath_event"
+diff --git a/multipathd/Makefile b/multipathd/Makefile
+index 8ad25ee..5ae9e7b 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
+diff --git a/multipathd/cli.c b/multipathd/cli.c
+index 475819b..d786eef 100644
+--- a/multipathd/cli.c
++++ b/multipathd/cli.c
+@@ -4,6 +4,8 @@
+ #include <memory.h>
+ #include <vector.h>
+ #include <util.h>
++#include <version.h>
++#include <readline/readline.h>
+ 
+ #include "cli.h"
+ 
+@@ -72,6 +74,30 @@ add_handler (int fp, int (*fn)(void *, char **, int *, void *))
+ 	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;
++	p += sprintf(p, VERSION_STRING);
++	p += sprintf(p, "CLI commands reference:\n");
+ 
+ 	vector_foreach_slot (handlers, h, i) {
+ 		fp = h->fingerprint;
+@@ -329,9 +355,13 @@ parse_cmd (char * cmd, char ** reply, int * len, void * data)
+ {
+ 	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..7bae02a 100644
+--- a/multipathd/cli_handlers.c
++++ b/multipathd/cli_handlers.c
+@@ -13,6 +13,7 @@
+ #include <blacklist.h>
+ #include <debug.h>
+ #include <print.h>
++#include <sysfs.h>
+ 
+ #include "main.h"
+ #include "cli.h"
+@@ -71,7 +72,7 @@ show_map_topology (char ** r, int * len, struct multipath * mpp)
+ 
+ 		c = reply;
+ 
+-		c += snprint_multipath_topology( c, reply + maxlen - c, mpp, 2);
++		c += snprint_multipath_topology(c, reply + maxlen - c, mpp, 2);
+ 		again = ((c - reply) == (maxlen - 1));
+ 
+ 		if (again)
+@@ -92,7 +93,8 @@ show_maps_topology (char ** r, int * len, struct vectors * vecs)
+ 	char * reply;
+ 	unsigned int maxlen = INITIAL_REPLY_LEN;
+ 	int again = 1;
+-
++ 
++	get_path_layout(vecs->pathvec);
+ 	reply = MALLOC(maxlen);
+ 
+ 	while (again) {
+@@ -142,6 +144,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 +191,7 @@ cli_list_map_topology (void * v, char ** reply, int * len, void * data)
+ 	struct vectors * vecs = (struct vectors *)data;
+ 	char * param = get_keyparam(v, MAP);
+ 	
++	get_path_layout(vecs->pathvec);
+ 	mpp = find_mp_by_str(vecs->mpvec, param);
+ 
+ 	if (!mpp)
+@@ -278,8 +287,8 @@ cli_add_path (void * v, char ** reply, int * len, void * data)
+ 
+ 	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);
+@@ -304,16 +313,30 @@ cli_add_map (void * v, char ** reply, int * len, void * data)
+ {
+ 	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);
+ 
+-	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);
+ 		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
+@@ -431,3 +454,79 @@ cli_fail(void * v, char ** reply, int * len, void * data)
+ 
+ 	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 ** reply, int * len, void * data);
+ 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..a173da3 100644
+--- a/multipathd/main.c
++++ b/multipathd/main.c
+@@ -14,12 +14,6 @@
+ #include <errno.h>
+ 
+ /*
+- * libsysfs
+- */
+-#include <sysfs/libsysfs.h>
+-#include <sysfs/dlist.h>
+-
+-/*
+  * libcheckers
+  */
+ #include <checkers.h>
+@@ -40,6 +34,7 @@
+ #include <structs_vec.h>
+ #include <dmparser.h>
+ #include <devmapper.h>
++#include <sysfs.h>
+ #include <dict.h>
+ #include <discovery.h>
+ #include <debug.h>
+@@ -55,136 +50,29 @@
+ #include "uxclnt.h"
+ #include "cli.h"
+ #include "cli_handlers.h"
++#include "lock.h"
++#include "waiter.h"
+ 
+ #define FILE_NAME_SIZE 256
+ #define CMDSIZE 160
+ 
+ #define LOG_MSG(a,b) \
+-	if (strlen(b)) condlog(a, "%s: %s", pp->dev_t, b);
+-
+-#ifdef LCKDBG
+-#define lock(a) \
+-	fprintf(stderr, "%s:%s(%i) lock %p\n", __FILE__, __FUNCTION__, __LINE__, a); \
+-	pthread_mutex_lock(a)
+-#define unlock(a) \
+-	fprintf(stderr, "%s:%s(%i) unlock %p\n", __FILE__, __FUNCTION__, __LINE__, a); \
+-	pthread_mutex_unlock(a)
+-#define lock_cleanup_pop(a) \
+-	fprintf(stderr, "%s:%s(%i) unlock %p\n", __FILE__, __FUNCTION__, __LINE__, a); \
+-	pthread_cleanup_pop(1);
+-#else
+-#define lock(a) pthread_mutex_lock(a)
+-#define unlock(a) pthread_mutex_unlock(a)
+-#define lock_cleanup_pop(a) pthread_cleanup_pop(1);
+-#endif
++	if (strlen(b)) condlog(a, "%s: %s", pp->dev, b);
+ 
+ pthread_cond_t exit_cond = PTHREAD_COND_INITIALIZER;
+ pthread_mutex_t exit_mutex = PTHREAD_MUTEX_INITIALIZER;
+ 
+ /*
+- * 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 *
+-alloc_waiter (void)
+-{
+-
+-	struct event_thread * wp;
+-
+-	wp = (struct event_thread *)MALLOC(sizeof(struct event_thread));
+-
+-	return wp;
+-}
+-
+-static void
+-free_waiter (void * data)
+-{
+-	struct event_thread * wp = (struct event_thread *)data;
+-
+-	/*
+-	 * indicate in mpp that the wp is already freed storage
+-	 */
+-	lock(wp->vecs->lock);
+-
+-	if (wp->mpp)
+-		/*
+-		 * be careful, mpp may already be freed -- null if so
+-		 */
+-		wp->mpp->waiter = NULL;
+-	else
+-		condlog(3, "free_waiter, mpp freed before wp=%p,", wp);
+-
+-	unlock(wp->vecs->lock);
+-
+-	if (wp->dmt)
+-		dm_task_destroy(wp->dmt);
+-
+-	FREE(wp);
+-}
+-
+-static void
+-stop_waiter_thread (struct multipath * mpp, struct vectors * vecs)
+-{
+-	struct event_thread * wp = (struct event_thread *)mpp->waiter;
+-	
+-	if (!wp) {
+-		condlog(3, "%s: no waiter thread", mpp->alias);
+-		return;
+-	}
+-	condlog(2, "%s: stop event checker thread", wp->mapname);
+-	pthread_kill((pthread_t)wp->thread, SIGUSR1);
+-}
+-
+-static void
+-cleanup_lock (void * data)
+-{
+-	unlock((pthread_mutex_t *)data);
+-}
+-
+-/*
+- * mpp->no_path_retry:
+- *   -2 (QUEUE) : queue_if_no_path enabled, never turned off
+- *   -1 (FAIL)  : fail_if_no_path
+- *    0 (UNDEF) : nothing
+- *   >0         : queue_if_no_path enabled, turned off after polling n times
+- */
+-static void
+-update_queue_mode_del_path(struct multipath *mpp)
+-{
+-	if (--mpp->nr_active == 0 && mpp->no_path_retry > 0) {
+-		/*
+-		 * Enter retry mode.
+-		 * meaning of +1: retry_tick may be decremented in
+-		 *                checkerloop before starting retry.
+-		 */
+-		mpp->stat_queueing_timeouts++;
+-		mpp->retry_tick = mpp->no_path_retry * conf->checkint + 1;
+-		condlog(1, "%s: Entering recovery mode: max_retries=%d",
+-			mpp->alias, mpp->no_path_retry);
+-	}
+-	condlog(2, "%s: remaining active paths: %d", mpp->alias, mpp->nr_active);
+-}
+-
+-static void
+-update_queue_mode_add_path(struct multipath *mpp)
+-{
+-	if (mpp->nr_active++ == 0 && mpp->no_path_retry > 0) {
+-		/* come back to normal mode from retry mode */
+-		mpp->retry_tick = 0;
+-		dm_queue_if_no_path(mpp->alias, 1);
+-		condlog(2, "%s: queue_if_no_path enabled", mpp->alias);
+-		condlog(1, "%s: Recovered to normal mode", mpp->alias);
+-	}
+-	condlog(2, "%s: remaining active paths: %d", mpp->alias, mpp->nr_active);
+-}
++struct vectors * gvecs;
+ 
+ static int
+ need_switch_pathgroup (struct multipath * mpp, int refresh)
+ {
+ 	struct pathgroup * pgp;
+ 	struct path * pp;
+-	int i, j;
++	unsigned int i, j;
+ 
+ 	if (!mpp || mpp->pgfailback == -FAILBACK_MANUAL)
+ 		return 0;
+@@ -219,7 +107,8 @@ coalesce_maps(struct vectors *vecs, vector nmpv)
+ {
+ 	struct multipath * ompp;
+ 	vector ompv = vecs->mpvec;
+-	int i, j;
++	unsigned int i;
++	int j;
+ 
+ 	vector_foreach_slot (ompv, ompp, i) {
+ 		if (!find_mp_by_wwid(nmpv, ompp->wwid)) {
+@@ -253,234 +142,12 @@ coalesce_maps(struct vectors *vecs, vector nmpv)
+ 	return 0;
+ }
+ 
+-static int
+-update_multipath (struct vectors *vecs, char *mapname)
+-{
+-	struct multipath *mpp;
+-	struct pathgroup  *pgp;
+-	struct path *pp;
+-	int i, j;
+-	int r = 1;
+-
+-	mpp = find_mp_by_alias(vecs->mpvec, mapname);
+-
+-	if (!mpp)
+-		goto out;
+-
+-	free_pgvec(mpp->pg, KEEP_PATHS);
+-	mpp->pg = NULL;
+-
+-	if (setup_multipath(vecs, mpp))
+-		goto out; /* mpp freed in setup_multipath */
+-
+-	/*
+-	 * compare checkers states with DM states
+-	 */
+-	vector_foreach_slot (mpp->pg, pgp, i) {
+-		vector_foreach_slot (pgp->paths, pp, j) {
+-			if (pp->dmstate != PSTATE_FAILED)
+-				continue;
+-
+-			if (pp->state != PATH_DOWN) {
+-				int oldstate = pp->state;
+-				condlog(2, "%s: mark as failed", pp->dev_t);
+-				mpp->stat_path_failures++;
+-				pp->state = PATH_DOWN;
+-				if (oldstate == PATH_UP ||
+-				    oldstate == PATH_GHOST)
+-					update_queue_mode_del_path(mpp);
+-
+-				/*
+-				 * if opportune,
+-				 * schedule the next check earlier
+-				 */
+-				if (pp->tick > conf->checkint)
+-					pp->tick = conf->checkint;
+-			}
+-		}
+-	}
+-	r = 0;
+-out:
+-	if (r)
+-		condlog(0, "failed to update multipath");
+-
+-	return r;
+-}
+-
+-static sigset_t unblock_signals(void)
+-{
+-	sigset_t set, old;
+-
+-	sigemptyset(&set);
+-	sigaddset(&set, SIGHUP);
+-	sigaddset(&set, SIGUSR1);
+-	pthread_sigmask(SIG_UNBLOCK, &set, &old);
+-	return old;
+-}
+-
+-/*
+- * returns the reschedule delay
+- * negative means *stop*
+- */
+-static int
+-waiteventloop (struct event_thread * waiter)
+-{
+-	sigset_t set;
+-	int event_nr;
+-	int r;
+-
+-	if (!waiter->event_nr)
+-		waiter->event_nr = dm_geteventnr(waiter->mapname);
+-
+-	if (!(waiter->dmt = dm_task_create(DM_DEVICE_WAITEVENT))) {
+-		condlog(0, "%s: devmap event #%i dm_task_create error",
+-				waiter->mapname, waiter->event_nr);
+-		return 1;
+-	}
+-
+-	if (!dm_task_set_name(waiter->dmt, waiter->mapname)) {
+-		condlog(0, "%s: devmap event #%i dm_task_set_name error",
+-				waiter->mapname, waiter->event_nr);
+-		dm_task_destroy(waiter->dmt);
+-		return 1;
+-	}
+-
+-	if (waiter->event_nr && !dm_task_set_event_nr(waiter->dmt,
+-						      waiter->event_nr)) {
+-		condlog(0, "%s: devmap event #%i dm_task_set_event_nr error",
+-				waiter->mapname, waiter->event_nr);
+-		dm_task_destroy(waiter->dmt);
+-		return 1;
+-	}
+-
+-	dm_task_no_open_count(waiter->dmt);
+-	
+-	/* 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;
+-
+-	dm_task_destroy(waiter->dmt);
+-	waiter->dmt = NULL;
+-	waiter->event_nr++;
+-
+-	/*
+-	 * upon event ...
+-	 */
+-	while (1) {
+-		condlog(3, "%s: devmap event #%i",
+-				waiter->mapname, waiter->event_nr);
+-
+-		/*
+-		 * event might be :
+-		 *
+-		 * 1) a table reload, which means our mpp structure is
+-		 *    obsolete : refresh it through update_multipath()
+-		 * 2) a path failed by DM : mark as such through
+-		 *    update_multipath()
+-		 * 3) map has gone away : stop the thread.
+-		 * 4) a path reinstate : nothing to do
+-		 * 5) a switch group : nothing to do
+-		 */
+-		pthread_cleanup_push(cleanup_lock, waiter->vecs->lock);
+-		lock(waiter->vecs->lock);
+-		r = update_multipath(waiter->vecs, waiter->mapname);
+-		lock_cleanup_pop(waiter->vecs->lock);
+-
+-		if (r)
+-			return -1; /* stop the thread */
+-
+-		event_nr = dm_geteventnr(waiter->mapname);
+-
+-		if (waiter->event_nr == event_nr)
+-			return 1; /* upon problem reschedule 1s later */
+-
+-		waiter->event_nr = event_nr;
+-	}
+-	return -1; /* never reach there */
+-}
+-
+-static void *
+-waitevent (void * et)
+-{
+-	int r;
+-	struct event_thread *waiter;
+-
+-	mlockall(MCL_CURRENT | MCL_FUTURE);
+-
+-	waiter = (struct event_thread *)et;
+-	pthread_cleanup_push(free_waiter, et);
+-
+-	while (1) {
+-		r = waiteventloop(waiter);
+-
+-		if (r < 0)
+-			break;
+-
+-		sleep(r);
+-	}
+-
+-	pthread_cleanup_pop(1);
+-	return NULL;
+-}
+-
+-static int
+-start_waiter_thread (struct multipath * mpp, struct vectors * vecs)
+-{
+-	pthread_attr_t attr;
+-	struct event_thread * wp;
+-
+-	if (!mpp)
+-		return 0;
+-
+-	if (pthread_attr_init(&attr))
+-		goto out;
+-
+-	pthread_attr_setstacksize(&attr, 32 * 1024);
+-	pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+-
+-	wp = alloc_waiter();
+-
+-	if (!wp)
+-		goto out;
+-
+-	mpp->waiter = (void *)wp;
+-	strncpy(wp->mapname, mpp->alias, WWID_SIZE);
+-	wp->vecs = vecs;
+-	wp->mpp = mpp;
+-
+-	if (pthread_create(&wp->thread, &attr, waitevent, wp)) {
+-		condlog(0, "%s: cannot create event checker", wp->mapname);
+-		goto out1;
+-	}
+-	condlog(2, "%s: event checker started", wp->mapname);
+-
+-	return 0;
+-out1:
+-	free_waiter(wp);
+-	mpp->waiter = NULL;
+-out:
+-	condlog(0, "failed to start waiter thread");
+-	return 1;
+-}
+-
+ static void
+ sync_map_state(struct multipath *mpp)
+ {
+-	int i, j;
+ 	struct pathgroup *pgp;
+         struct path *pp;
++	unsigned int i, j;
+ 
+ 	vector_foreach_slot (mpp->pg, pgp, i){
+ 		vector_foreach_slot (pgp->paths, pp, j){
+@@ -502,7 +169,7 @@ sync_map_state(struct multipath *mpp)
+ static void
+ sync_maps_state(vector mpvec)
+ {
+-	int i;
++	unsigned int i;
+ 	struct multipath *mpp;
+ 
+ 	vector_foreach_slot (mpvec, mpp, i) 
+@@ -536,29 +203,29 @@ flush_map(struct multipath * mpp, struct vectors * vecs)
+ }
+ 
+ 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;
+ 
+-	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;
+@@ -567,7 +234,6 @@ ev_add_map (char * devname, struct vectors * vecs)
+ 
+ 	if (map_present && dm_type(alias, DEFAULT_TARGET) <= 0) {
+ 		condlog(4, "%s: not a multipath map", alias);
+-		FREE(alias);
+ 		return 0;
+ 	}
+ 
+@@ -580,8 +246,7 @@ ev_add_map (char * devname, struct vectors * vecs)
+ 		 * of uev_add_path
+ 		 */
+ 		condlog(0, "%s: devmap already registered",
+-			devname);
+-		FREE(alias);
++			dev->kernel);
+ 		return 0;
+ 	}
+ 
+@@ -591,10 +256,10 @@ ev_add_map (char * devname, struct vectors * vecs)
+ 	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);
+@@ -602,20 +267,19 @@ ev_add_map (char * devname, struct vectors * vecs)
+ 	}
+ 	
+ 	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
+@@ -636,13 +300,13 @@ ev_remove_map (char * devname, struct vectors * vecs)
+ }
+ 
+ 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;
+@@ -657,10 +321,10 @@ uev_umount_map (char * devname, struct vectors * vecs)
+ }
+ 	
+ 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;
+ }
+ 
+ 
+@@ -704,7 +368,7 @@ ev_add_path (char * devname, struct vectors * vecs)
+ 		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);
+@@ -776,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
+@@ -863,7 +527,7 @@ ev_remove_path (char * devname, struct vectors * vecs)
+ 				}
+ 				sync_map_state(mpp);
+ 
+-				condlog(3, "%s path removed from devmap %s",
++				condlog(3, "%s: path removed from map %s",
+ 					devname, mpp->alias);
+ 			}
+ 			free_pathvec(rpvec, KEEP_PATHS);
+@@ -898,8 +562,8 @@ out:
+ static int
+ map_discovery (struct vectors * vecs)
+ {
+-	int i;
+ 	struct multipath * mpp;
++	unsigned int i;
+ 
+ 	if (dm_get_maps(vecs->mpvec, "multipath"))
+ 		return 1;
+@@ -962,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;
+@@ -970,23 +634,25 @@ uev_trigger (struct uevent * uev, void * trigger_data)
+ 	if (uev_discard(uev->devpath))
+ 		return 0;
+ 
+-	basename(uev->devpath, devname);
++	sysdev = sysfs_device_get(uev->devpath);
+ 	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)) {
+-			r = uev_add_map(devname, vecs);
++	if (!strncmp(sysdev->kernel, "dm-", 3)) {
++		if (!strncmp(uev->action, "change", 6)) {
++			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;
+@@ -995,15 +661,16 @@ uev_trigger (struct uevent * uev, void * trigger_data)
+ 	/*
+ 	 * path add/remove event
+ 	 */
+-	if (blacklist(conf->blist_devnode, devname))
++	if (filter_devnode(conf->blist_devnode, conf->elist_devnode,
++		 	   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;
+ 	}
+ 
+@@ -1024,30 +691,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 +795,7 @@ static void
+ mpvec_garbage_collector (struct vectors * vecs)
+ {
+ 	struct multipath * mpp;
+-	int i;
++	unsigned int i;
+ 
+ 	vector_foreach_slot (vecs->mpvec, mpp, i) {
+ 		if (mpp && mpp->alias && !dm_map_present(mpp->alias)) {
+@@ -1144,7 +810,7 @@ static void
+ defered_failback_tick (vector mpvec)
+ {
+ 	struct multipath * mpp;
+-	int i;
++	unsigned int i;
+ 
+ 	vector_foreach_slot (mpvec, mpp, i) {
+ 		/*
+@@ -1163,7 +829,7 @@ static void
+ retry_count_tick(vector mpvec)
+ {
+ 	struct multipath *mpp;
+-	int i;
++	unsigned int i;
+ 
+ 	vector_foreach_slot (mpvec, mpp, i) {
+ 		if (mpp->retry_tick) {
+@@ -1182,8 +848,9 @@ checkerloop (void *ap)
+ {
+ 	struct vectors *vecs;
+ 	struct path *pp;
+-	int i, count = 0;
++	int count = 0;
+ 	int newstate;
++	unsigned int i;
+ 
+ 	mlockall(MCL_CURRENT | MCL_FUTURE);
+ 	vecs = (struct vectors *)ap;
+@@ -1351,10 +1018,10 @@ configure (struct vectors * vecs, int start_waiters)
+ 	vector mpvec;
+ 	int i;
+ 
+-	if (!(vecs->pathvec = vector_alloc()))
++	if (!vecs->pathvec && !(vecs->pathvec = vector_alloc()))
+ 		return 1;
+ 	
+-	if (!(vecs->mpvec = vector_alloc()))
++	if (!vecs->mpvec && !(vecs->mpvec = vector_alloc()))
+ 		return 1;
+ 	
+ 	if (!(mpvec = vector_alloc()))
+@@ -1366,7 +1033,7 @@ configure (struct vectors * vecs, int start_waiters)
+ 	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 +1061,6 @@ configure (struct vectors * vecs, int start_waiters)
+ 
+ 	sync_maps_state(mpvec);
+ 
+-	if (conf->verbosity > 2)
+-		vector_foreach_slot(mpvec, mpp, i)
+-			print_map(mpp);
+-
+ 	/*
+ 	 * purge dm of old maps
+ 	 */
+@@ -1406,6 +1069,7 @@ configure (struct vectors * vecs, int start_waiters)
+ 	/*
+ 	 * save new set of maps formed by considering current path state
+ 	 */
++	vector_free(vecs->mpvec);
+ 	vecs->mpvec = mpvec;
+ 
+ 	/*
+@@ -1435,6 +1099,7 @@ reconfigure (struct vectors * vecs)
+ 	if (VECTOR_SIZE(vecs->pathvec))
+ 		free_pathvec(vecs->pathvec, FREE_PATHS);
+ 
++	vecs->pathvec = NULL;
+ 	conf = NULL;
+ 
+ 	if (load_config(DEFAULT_CONFIGFILE))
+@@ -1467,24 +1132,10 @@ init_vecs (void)
+ 	if (!vecs->lock)
+ 		goto out;
+ 
+-	vecs->pathvec = vector_alloc();
+-
+-	if (!vecs->pathvec)
+-		goto out1;
+-		
+-	vecs->mpvec = vector_alloc();
+-
+-	if (!vecs->mpvec)
+-		goto out2;
+-	
+ 	pthread_mutex_init(vecs->lock, NULL);
+ 
+ 	return vecs;
+ 
+-out2:
+-	vector_free(vecs->pathvec);
+-out1:
+-	FREE(vecs->lock);
+ out:
+ 	FREE(vecs);
+ 	condlog(0, "failed to init paths");
+@@ -1543,7 +1194,7 @@ signal_init(void)
+ 	signal_set(SIGUSR1, sigusr1);
+ 	signal_set(SIGINT, sigend);
+ 	signal_set(SIGTERM, sigend);
+-	signal_set(SIGKILL, sigend);
++	signal(SIGPIPE, SIG_IGN);
+ }
+ 
+ static void
+@@ -1551,7 +1202,7 @@ setscheduler (void)
+ {
+         int res;
+ 	static struct sched_param sched_param = {
+-		sched_priority: 99
++		.sched_priority = 99
+ 	};
+ 
+         res = sched_setscheduler (0, SCHED_RR, &sched_param);
+@@ -1617,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);
+ 	}
+@@ -1654,6 +1305,8 @@ child (void * param)
+ 	pthread_cancel(uevent_thr);
+ 	pthread_cancel(uxlsnr_thr);
+ 
++	sysfs_cleanup();
++
+ 	free_keys(keys);
+ 	keys = NULL;
+ 	free_handlers(handlers);
+@@ -1740,6 +1393,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/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.8 b/multipathd/multipathd.8
+index 48b1b04..480b8ed 100644
+--- a/multipathd/multipathd.8
++++ b/multipathd/multipathd.8
+@@ -1,22 +1,102 @@
+-.TH MULTIPATHD 8 "October 2004" "Linux Administrator's Manual"
++.TH MULTIPATHD 8 "November 2006" "Linux Administrator's Manual"
+ .SH NAME
+ multipathd \- multipath daemon
+-.SH SYNOPSYS
++
++.SH SYNOPSIS
+ .B multipathd
++.RB [\| options \|]
+ 
+-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.
+ 
+ This daemon executes the external multipath config tool when events occur. 
+-In turn, the multipath tool signals the multipathd daemon it is done with 
++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, <christophe.varoqui at free.fr> and others.
++.B multipathd
++was developed by Christophe Varoqui, <christophe.varoqui at free.fr> and others.
+diff --git a/multipathd/multipathd.init.debian b/multipathd/multipathd.init.debian
+diff --git a/multipathd/multipathd.init.redhat b/multipathd/multipathd.init.redhat
+diff --git a/multipathd/pidfile.c b/multipathd/pidfile.c
+diff --git a/multipathd/pidfile.h b/multipathd/pidfile.h
+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 <memory.h>
+ #include <defaults.h>
+ 
++#include <vector.h>
++#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/multipathd/uxclnt.h b/multipathd/uxclnt.h
+diff --git a/multipathd/uxlsnr.c b/multipathd/uxlsnr.c
+diff --git a/multipathd/uxlsnr.h b/multipathd/uxlsnr.h
+diff --git a/path_priority/pp_alua/LICENSE b/path_priority/pp_alua/LICENSE
+diff --git a/path_priority/pp_alua/Makefile b/path_priority/pp_alua/Makefile
+index 983ffe3..6f356a1 100644
+--- a/path_priority/pp_alua/Makefile
++++ b/path_priority/pp_alua/Makefile
+@@ -35,7 +35,7 @@ glibc:	$(OBJS)
+ klibc:	$(OBJS)
+ 	$(CC) -static -o $(EXEC) $(OBJS)
+ 
+-install: $(BUILD) $(EXEC).8.gz
++install: $(EXEC) $(EXEC).8.gz
+ 	$(INSTALL) -s -m 755 $(EXEC) $(DESTDIR)$(bindir)/$(EXEC)
+ 	$(INSTALL) -m 644 $(EXEC).8.gz $(DESTDIR)$(mandir)/$(EXEC).8.gz
+ 
+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
++++ b/path_priority/pp_alua/main.c
+@@ -12,8 +12,6 @@
+  * 
+  * This file is released under the GPL.
+  */
+-#include <linux/kdev_t.h>
+-
+ #include <sys/types.h>
+ #include <sys/stat.h>
+ 
+@@ -241,7 +239,7 @@ main (int argc, char **argv)
+ 			mknod(
+ 				devicepath,
+ 				S_IFBLK|S_IRUSR|S_IWUSR,
+-				MKDEV(major, minor)
++				makedev(major, minor)
+ 			);
+ 			
+ 		}
+diff --git a/path_priority/pp_alua/mpath_prio_alua.8 b/path_priority/pp_alua/mpath_prio_alua.8
+index 4843bcd..58568a5 100644
+--- a/path_priority/pp_alua/mpath_prio_alua.8
++++ b/path_priority/pp_alua/mpath_prio_alua.8
+@@ -1,4 +1,4 @@
+-.TH MPATH_PRIO_ALUA 8 "7. June 2005" "multipath-tools" \
++.TH MPATH_PRIO_ALUA 8 "July 2006" "multipath-tools" \
+ "Linux Administrator's Manual"
+ .SH NAME
+ mpath_prio_alua \- Path priority tool based on Asymmetric LUn Access
+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 <sys/stat.h>
+ #include <unistd.h>
+ #include <errno.h>
++#include <inttypes.h>
+ 
+ #define __user
+ #include <scsi/sg.h>
+@@ -28,7 +29,7 @@
+ #include "rtpg.h"
+ 
+ #define SENSE_BUFF_LEN  32
+-#define DEF_TIMEOUT     60000
++#define DEF_TIMEOUT     300000
+ 
+ /*
+  * Macro used to print debug messaged.
+@@ -251,14 +252,38 @@ do_rtpg(int fd, void* resp, long resplen)
+ int
+ get_asymmetric_access_state(int fd, unsigned int tpg)
+ {
+-	unsigned char		buf[128];
++	unsigned char		*buf;
+ 	struct rtpg_data *	tpgd;
+ 	struct rtpg_tpg_dscr *	dscr;
+ 	int			rc;
+-
+-	rc = do_rtpg(fd, buf, sizeof(buf));
++	int			buflen;
++	uint32_t		scsi_buflen;
++
++	buflen = 128; /* Initial value from old code */
++	buf = (unsigned char *)malloc(buflen);
++	if (!buf) {
++		PRINT_DEBUG ("malloc failed: could not allocate"
++			"%u bytes\n", buflen);
++		return -RTPG_RTPG_FAILED;
++	}
++	rc = do_rtpg(fd, buf, buflen);
+ 	if (rc < 0)
+ 		return rc;
++	scsi_buflen = buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3];
++	if (buflen < (scsi_buflen + 4)) {
++		free(buf);
++		buf = (unsigned char *)malloc(scsi_buflen);
++		if (!buf) {
++			PRINT_DEBUG ("malloc failed: could not allocate"
++				"%u bytes\n", scsi_buflen);
++			return -RTPG_RTPG_FAILED;
++		}
++		buflen = scsi_buflen;
++		rc = do_rtpg(fd, buf, buflen);
++		if (rc < 0)
++			goto out;
++	}
++		
+ 
+ 	tpgd = (struct rtpg_data *) buf;
+ 	rc   = -RTPG_TPG_NOT_FOUND;
+@@ -274,7 +299,8 @@ get_asymmetric_access_state(int fd, unsigned int tpg)
+ 			}
+ 		}
+ 	}
+-
++out:
++	free(buf);
+ 	return rc;
+ }
+ 
+diff --git a/path_priority/pp_alua/rtpg.h b/path_priority/pp_alua/rtpg.h
+diff --git a/path_priority/pp_alua/spc3.h b/path_priority/pp_alua/spc3.h
+index 11f5dbd..bddbbdd 100644
+--- a/path_priority/pp_alua/spc3.h
++++ b/path_priority/pp_alua/spc3.h
+@@ -148,10 +148,10 @@ struct inquiry_data {
+ 					/* ......x. = command queue support  */
+ 					/* .......x = vs2                    */
+ 	unsigned char	vendor_identification[8];
+-	unsigned char	product_identification[8];
++	unsigned char	product_identification[16];
+ 	unsigned char	product_revision[4];
+ 	unsigned char	vendor_specific[20];
+-	unsigned char	b48;		/* xxxx.... = reserved               */
++	unsigned char	b56;		/* xxxx.... = reserved               */
+ 					/* ....xx.. = clocking               */
+ 					/* ......x. = qas                    */
+ 					/* .......x = ius                    */
+diff --git a/path_priority/pp_balance_units/Makefile b/path_priority/pp_balance_units/Makefile
+diff --git a/path_priority/pp_balance_units/pp_balance_units.c b/path_priority/pp_balance_units/pp_balance_units.c
+index 307a959..ea70f13 100644
+--- a/path_priority/pp_balance_units/pp_balance_units.c
++++ b/path_priority/pp_balance_units/pp_balance_units.c
+@@ -3,15 +3,15 @@
+  * This code is GPLv2, see license file
+  *
+  * This path prioritizer aims to balance logical units over all
+- * controlers available. The logic is :
++ * controllers available. The logic is :
+  *
+  * - list all paths in all primary path groups
+- * - for each path, get the controler's serial
+- * - compute the number of active paths attached to each controler
+- * - compute the max number of paths attached to the same controler
++ * - for each path, get the controller's serial
++ * - compute the number of active paths attached to each controller
++ * - compute the max number of paths attached to the same controller
+  * - if sums are already balanced or if the path passed as parameter is
+- *   attached to controler with less active paths, then return 
+- *   (max_path_attached_to_one_controler - number_of_paths_on_this_controler)
++ *   attached to controller with less active paths, then return 
++ *   (max_path_attached_to_one_controller - number_of_paths_on_this_controller)
+  * - else, or if anything goes wrong, return 1 as a default prio
+  *
+  */
+@@ -38,7 +38,7 @@
+ #define INQUIRY_CMDLEN  6
+ #define INQUIRY_CMD     0x12
+ #define SENSE_BUFF_LEN  32
+-#define DEF_TIMEOUT     60000
++#define DEF_TIMEOUT     300000
+ #define RECOVERED_ERROR 0x01
+ #define MX_ALLOC_LEN    255
+ #define SCSI_CHECK_CONDITION    0x2
+@@ -61,7 +61,7 @@ struct path {
+ 	char serial[SERIAL_SIZE];
+ };
+ 
+-struct controler {
++struct controller {
+ 	char serial[SERIAL_SIZE];
+ 	int path_count;
+ };
+@@ -172,7 +172,7 @@ do_inq(int sg_fd, int cmddt, int evpd, unsigned int pg_op,
+ }
+ 
+ static int
+-get_serial (char * str, char * devt)
++get_serial (char * str, int maxlen, char * devt)
+ {
+ 	int fd;
+         int len;
+@@ -181,20 +181,22 @@ get_serial (char * str, char * devt)
+ 	fd = opennode(devt, O_RDONLY);
+ 
+ 	if (fd < 0)
+-                return 0;
++                return 1;
+ 
+ 	if (0 == do_inq(fd, 0, 1, 0x80, buff, MX_ALLOC_LEN, 0)) {
+ 		len = buff[3];
++		if (len >= maxlen)
++			return 1;
+ 		if (len > 0) {
+ 			memcpy(str, buff + 4, len);
+ 			buff[len] = '\0';
+ 		}
+ 		close(fd);
+-		return 1;
++		return 0;
+ 	}
+ 
+ 	closenode(devt, fd);
+-        return 0;
++        return 1;
+ }
+ 
+ static void *
+@@ -358,7 +360,7 @@ get_paths (vector pathvec)
+ 			if (pos == BEFOREPG)
+ 				pos = INPG;
+ 
+-			get_serial(pp->serial, pp->dev_t);
++			get_serial(pp->serial, SERIAL_SIZE, pp->dev_t);
+ 			vector_alloc_slot(pathvec);
+ 			vector_set_slot(pathvec, pp);
+ 			debug("store %s [%s]",
+@@ -370,40 +372,40 @@ get_paths (vector pathvec)
+ }
+ 
+ static void *
+-find_controler (vector controlers, char * serial)
++find_controller (vector controllers, char * serial)
+ {
+ 	int i;
+-	struct controler * cp;
++	struct controller * cp;
+ 
+-	if (!controlers)
++	if (!controllers)
+ 		return NULL;
+ 
+-	vector_foreach_slot (controlers, cp, i)
++	vector_foreach_slot (controllers, cp, i)
+ 		if (!strncmp(cp->serial, serial, SERIAL_SIZE))
+ 				return cp;
+ 	return NULL;
+ }
+ 
+ static void
+-get_controlers (vector controlers, vector pathvec)
++get_controllers (vector controllers, vector pathvec)
+ {
+ 	int i;
+ 	struct path * pp;
+-	struct controler * cp;
++	struct controller * cp;
+ 	
+-	if (!controlers)
++	if (!controllers)
+ 		return;
+ 
+ 	vector_foreach_slot (pathvec, pp, i) {
+ 		if (!pp || !strlen(pp->serial))
+ 			continue;
+ 		
+-		cp = find_controler(controlers, pp->serial);
++		cp = find_controller(controllers, pp->serial);
+ 
+ 		if (!cp) {
+-			cp = zalloc(sizeof(struct controler));
+-			vector_alloc_slot(controlers);
+-			vector_set_slot(controlers, cp);
++			cp = zalloc(sizeof(struct controller));
++			vector_alloc_slot(controllers);
++			vector_set_slot(controllers, cp);
+ 			strncpy(cp->serial, pp->serial, SERIAL_SIZE);
+ 		}
+ 		cp->path_count++;	
+@@ -411,17 +413,17 @@ get_controlers (vector controlers, vector pathvec)
+ }
+ 
+ static int
+-get_max_path_count (vector controlers)
++get_max_path_count (vector controllers)
+ {
+ 	int i;
+ 	int max = 0;
+-	struct controler * cp;
++	struct controller * cp;
+ 
+-	if (!controlers)
++	if (!controllers)
+ 		return 0;
+ 
+-	vector_foreach_slot (controlers, cp, i) {
+-		debug("controler %s : %i paths", cp->serial, cp->path_count);
++	vector_foreach_slot (controllers, cp, i) {
++		debug("controller %s : %i paths", cp->serial, cp->path_count);
+ 		if(cp->path_count > max)
+ 			max = cp->path_count;
+ 	}
+@@ -433,9 +435,9 @@ int
+ main (int argc, char **argv)
+ {
+ 	vector pathvec = NULL;
+-	vector controlers = NULL;
++	vector controllers = NULL;
+ 	struct path * ref_path = NULL;
+-	struct controler * cp = NULL;
++	struct controller * cp = NULL;
+ 	int max_path_count = 0;
+ 
+ 	ref_path = zalloc(sizeof(struct path));
+@@ -449,18 +451,18 @@ main (int argc, char **argv)
+ 	if (optind<argc)
+ 		strncpy(ref_path->dev_t, argv[optind], WORD_SIZE);
+ 
+-	get_serial(ref_path->serial, ref_path->dev_t);
++	get_serial(ref_path->serial, SERIAL_SIZE, ref_path->dev_t);
+ 
+ 	if (!ref_path->serial || !strlen(ref_path->serial))
+ 		exit_tool(0);
+ 
+ 	pathvec = vector_alloc();
+-	controlers = vector_alloc();
++	controllers = vector_alloc();
+ 
+ 	get_paths(pathvec);
+-	get_controlers(controlers, pathvec);
+-	max_path_count = get_max_path_count(controlers);
+-	cp = find_controler(controlers, ref_path->serial);
++	get_controllers(controllers, pathvec);
++	max_path_count = get_max_path_count(controllers);
++	cp = find_controller(controllers, ref_path->serial);
+ 
+ 	if (!cp) {
+ 		debug("no other active path on serial %s\n",
+diff --git a/path_priority/pp_emc/Makefile b/path_priority/pp_emc/Makefile
+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_hds_modular/Makefile b/path_priority/pp_hds_modular/Makefile
+new file mode 100644
+index 0000000..a0249a5
+--- /dev/null
++++ b/path_priority/pp_hds_modular/Makefile
+@@ -0,0 +1,22 @@
++EXEC		= mpath_prio_hds_modular
++BUILD		= glibc
++OBJS		= pp_hds_modular.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 -s -m 755 $(EXEC) $(DESTDIR)$(bindir)/$(EXEC)
++
++uninstall:
++	rm $(DESTDIR)$(bindir)/$(EXEC)
++clean:	
++	rm -f *.o $(EXEC)
+diff --git a/path_priority/pp_hds_modular/pp_hds_modular.c b/path_priority/pp_hds_modular/pp_hds_modular.c
+new file mode 100644
+index 0000000..f38ebcf
+--- /dev/null
++++ b/path_priority/pp_hds_modular/pp_hds_modular.c
+@@ -0,0 +1,252 @@
++/*
++ * (C) Copyright HDS GmbH 2006. All Rights Reserved.
++ *
++ * pp_hds_modular.c
++ * Version 1.12
++ *
++ * Prioritizer for multipath tools device mapper and HDS Storage
++ *
++ * Hitachis Modular Storage contains two controllers for redundancy. The
++ * Storage internal LUN (LDEV) will normally allocated via two pathes to the
++ * server (one path per controller). For performance reasons should the server
++ * access to a LDEV only via one controller. The other path to the other
++ * controller is stand-by. It is also possible to allocate more as one path
++ * for a LDEV per controller. Here is active/active access allowed. The other
++ * pathes via the other controller are stand-by.
++ * 
++ * This prioritizer checks with inquiry commands the represented LDEV and
++ * Controller number and gives back a priority followed by this scheme :
++ *
++ * CONTROLLER ODD  and LDEV  ODD: PRIORITY 1
++ * CONTROLLER ODD  and LDEV EVEN: PRIORITY 0
++ * CONTROLLER EVEN and LDEV  ODD: PRIORITY 0
++ * CONTROLLER EVEN and LDEV EVEN: PRIORITY 1
++ *
++ * In the storage you can define for each LDEV a owner controller. If the
++ * server makes IOs via the other controller the storage will switch the
++ * ownership automatically. In this case you can see in the storage that the
++ * current controller is different from the default controller, but this is
++ * absolutely no problem.
++ *
++ * With this prioritizer it is possible to establish a static load balancing.
++ * Half of the LUNs are accessed via one HBA/storage controller and the other
++ * half via the other HBA/storage controller.
++ *
++ * In cluster environmemnts (RAC) it also guarantees that all cluster nodes
++ * have access to the LDEVs via the same controller.
++ * 
++ * You can run the prioritizer manually in verbose mode :
++ * # pp_hds_modular -v 8:224
++ * VENDOR:  HITACHI
++ * PRODUCT: DF600F-CM
++ * SERIAL:  0x0105
++ * LDEV:    0x00C6
++ * CTRL:    1
++ * PORT:    B
++ * CTRL ODD, LDEV EVEN, PRIO 0
++ *
++ * The items VENDOR and PRODUCT helps you to make the correct entries in file
++ * /etc/multipath.conf :
++ * # cat /etc/multipath.conf
++ * ...
++ * devices {
++ *        device {
++ *                vendor                  "HITACHI"
++ *                product                 "DF600F"
++ *                path_grouping_policy    group_by_prio
++ *                prio_callout            "/sbin/pp_hds_modular %d"
++ *                path_checker            readsector0
++ *                getuid_callout          "/sbin/scsi_id -g -u -s /block/%n"
++ *                failback                immediate
++ *        }
++ *        device {
++ *                vendor                  "HITACHI"
++ *                product                 "DF600F-CM"
++ *                path_grouping_policy    group_by_prio
++ *                prio_callout            "/sbin/pp_hds_modular %d"
++ *                path_checker            readsector0
++ *                getuid_callout          "/sbin/scsi_id -g -u -s /block/%n"
++ *                failback                immediate
++ *
++ *
++ * Author: Matthias Rudolph <matthias.rudolph at hds.com>
++ *
++ * This file is released under the GPL.
++ *
++ */
++
++#include <unistd.h>
++#include <fcntl.h>
++#include <stdio.h>
++#include <string.h>
++#include <errno.h>
++#include <sys/ioctl.h>
++#include <stdlib.h>
++#include <libdevmapper.h>
++#include <memory.h>
++#include <sys/types.h>
++#include <sys/stat.h>
++#include <scsi/sg.h> /* take care: fetches glibc's /usr/include/scsi/sg.h */
++
++#define INQ_REPLY_LEN 255
++#define INQ_CMD_CODE 0x12
++#define INQ_CMD_LEN 6
++#define FILE_NAME_SIZE 255
++#define safe_sprintf(var, format, args...)	\
++	snprintf(var, sizeof(var), format, ##args) >= sizeof(var)
++#define safe_snprintf(var, size, format, args...)      \
++	snprintf(var, size, format, ##args) >= size
++
++int verbose;
++
++int hds_modular_prio(char * major_minor)
++{
++	int sg_fd, k, i;
++	char vendor[32];
++	char product[32];
++	char serial[32];
++	char ldev[32];
++	char ctrl[32];
++	char port[32];
++	char devpath[FILE_NAME_SIZE];
++	unsigned int major;
++	unsigned int minor;
++	unsigned char inqCmdBlk[INQ_CMD_LEN] = 
++		{INQ_CMD_CODE, 0, 0, 0, INQ_REPLY_LEN, 0};
++	unsigned char inqBuff[INQ_REPLY_LEN];
++	unsigned char sense_buffer[32];
++	sg_io_hdr_t io_hdr;
++
++	sscanf(major_minor, "%u:%u", &major, &minor);
++	memset(devpath, 0, FILE_NAME_SIZE);
++
++	if (safe_sprintf(devpath, "/tmp/.pp_balance.%u.%u.devnode",
++			 major, minor))
++		exit(1);
++
++	unlink (devpath);
++	mknod(devpath, S_IFBLK|S_IRUSR|S_IWUSR, makedev(major, minor));
++
++	if ((sg_fd = open(devpath, O_RDONLY)) < 0) exit(1);
++	if ((ioctl(sg_fd, SG_GET_VERSION_NUM, &k) < 0) || (k < 30000))
++		exit(1);
++
++	memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
++	io_hdr.interface_id = 'S';
++	io_hdr.cmd_len = sizeof(inqCmdBlk);
++	io_hdr.mx_sb_len = sizeof(sense_buffer);
++	io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
++	io_hdr.dxfer_len = INQ_REPLY_LEN;
++	io_hdr.dxferp = inqBuff;
++	io_hdr.cmdp = inqCmdBlk;
++	io_hdr.sbp = sense_buffer;
++	io_hdr.timeout = 2000;     /* TimeOut = 2 seconds */
++
++        if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) exit(1);
++	if ((io_hdr.info & SG_INFO_OK_MASK) != SG_INFO_OK) exit(1);
++
++	for (i = 0; i <  8 ; i++) vendor[i]  = inqBuff[i+8];
++	vendor[8] = 0;
++	for (i = 0; i < 16 ; i++) product[i] = inqBuff[i+16];
++	product[16] = 0;
++	for (i = 0; i <  4 ; i++) serial[i]  = inqBuff[i+40];
++	serial[4] = 0;
++	for (i = 0; i <  4 ; i++) ldev[i]    = inqBuff[i+44];
++	ldev[4] = 0;
++	ctrl[0] = inqBuff[49];
++	ctrl[1] = 0;
++	port[0] = inqBuff[50];
++	port[1] = 0;
++
++	close(sg_fd);
++
++	if (1 == verbose) {
++		printf("VENDOR:  %s\n", vendor);
++		printf("PRODUCT: %s\n", product);
++		printf("SERIAL:  0x%s\n", serial);
++		printf("LDEV:    0x%s\n", ldev);
++		printf("CTRL:    %s\n", ctrl);
++		printf("PORT:    %s\n", port);
++	}
++	switch( ctrl[0] ) {
++		case '0': case '2': case '4': case '6': case '8':
++			switch( ldev[3] ) {
++				case '0': case '2': case '4': case '6':
++				case '8': case 'A': case 'C': case 'E':
++					if (1 == verbose)
++						printf("CTRL EVEN, LDEV EVEN, "
++						       "PRIO 1\n");
++					return 1;
++					break;
++				case '1': case '3': case '5': case '7':
++				case '9': case 'B': case 'D': case 'F':
++					if (1 == verbose)
++						printf("CTRL EVEN, LDEV ODD, "
++						       "PRIO 0\n");
++					return 0;
++					break;
++
++			}
++		case '1': case '3': case '5': case '7': case '9':
++			switch( ldev[3] ) {
++				case '0': case '2': case '4': case '6':
++				case '8': case 'A': case 'C': case 'E':
++					if (1 == verbose)
++						printf("CTRL ODD, LDEV EVEN, "
++						       "PRIO 0\n");
++					return 0;
++					break;
++				case '1': case '3': case '5': case '7':
++				case '9': case 'B': case 'D': case 'F':
++					if (1 == verbose)
++						printf("CTRL ODD, LDEV ODD, "
++						       "PRIO 1\n");
++					return 1;
++					break;
++			}
++	}
++	exit(1);
++}
++
++void print_help(void)
++{
++	printf("Usage:       "
++			"pp_hds_modular [-v] <device_major:device_minor>\n");
++	printf("Option:      "
++			"-v verbose mode\n");
++	printf("Description: "
++			"Prioritizer for Multipath Tools and HDS Storage\n");
++	printf("Version:     "
++			"1.12\n");
++	printf("Author:      "
++			"Matthias Rudolph <matthias.rudolph at hds.com>\n");
++	return;
++}
++
++int main(int argc, char * argv[])
++{
++	int prio;
++
++	if (2 == argc) {
++		if (0 == strcmp(argv[1], "-h")) {
++			print_help();
++			exit(0);
++		}
++		else {
++			verbose = 0;
++			prio = hds_modular_prio(argv[1]);
++			printf("%d\n", prio);
++			exit(0);
++		}
++	}
++
++	if ((3 == argc) && (0 == strcmp(argv[1], "-v"))) {
++		verbose = 1;
++		prio = hds_modular_prio(argv[2]);
++		printf("%d\n", prio);
++		exit(0);
++	}
++	print_help();
++	exit(1);
++}
++
+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 <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++#include <sys/types.h>
++#include <sys/stat.h>
++#include <unistd.h>
++#include <fcntl.h>
++#include <sys/ioctl.h>
++#include <errno.h>
++
++#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/path_priority/pp_netapp/Makefile b/path_priority/pp_netapp/Makefile
+diff --git a/path_priority/pp_netapp/pp_netapp.c b/path_priority/pp_netapp/pp_netapp.c
+diff --git a/path_priority/pp_random/Makefile b/path_priority/pp_random/Makefile
+diff --git a/path_priority/pp_random/pp_random.c b/path_priority/pp_random/pp_random.c
+diff --git a/path_priority/pp_tpc/Makefile b/path_priority/pp_tpc/Makefile
+diff --git a/path_priority/pp_tpc/pp_tpc.c b/path_priority/pp_tpc/pp_tpc.c
+index 76e7c47..a7ed7ad 100644
+--- a/path_priority/pp_tpc/pp_tpc.c
++++ b/path_priority/pp_tpc/pp_tpc.c
+@@ -62,18 +62,13 @@ int sgi_tpc_prio(const char *dev)
+ 		goto out;
+ 	}
+ 	
+-	if ( /* Auto-volume Transfer Enabled */
+-	    	(sense_buffer[8] & 0x80) != 0x80 ) {
+-		fprintf(stderr, "Auto-volume Transfer not enabled");
+-	}
+-
+ 	if ( /* Current Volume Path Bit */
+ 		( sense_buffer[8] & 0x01) == 0x01 ) {
+ 		/* 
+ 		 * This volume was owned by the controller receiving
+ 		 * the inquiry command.
+ 		 */
+-		ret |= 0x02;
++		ret |= 0x01;
+ 	}
+ 
+ 	/* Volume Preferred Path Priority */
+@@ -83,7 +78,7 @@ int sgi_tpc_prio(const char *dev)
+ 		 * Access to this volume is most preferred through
+ 		 * this path and other paths with this value.
+ 		 */
+-		ret |= 0x04;
++		ret |= 0x02;
+ 		break;
+ 	case 0x02:
+ 		/*
+@@ -91,8 +86,7 @@ int sgi_tpc_prio(const char *dev)
+ 		 * as a secondary path. Typically this path would be used
+ 		 * for fail-over situations.
+ 		 */
+-		ret |= 0x01;
+-		break;
++		/* Fallthrough */
+ 	default:
+ 		/* Reserved values */
+ 		break;

Modified: multipath-tools/trunk/debian/patches/scsi_id.diff
==============================================================================
--- multipath-tools/trunk/debian/patches/scsi_id.diff	(original)
+++ multipath-tools/trunk/debian/patches/scsi_id.diff	Sat Jun  9 15:59:27 2007
@@ -1,16 +1,20 @@
 * scsi_id moved from /sbin/scsi_id to /lib/udev/
 
---- multipath-tools-0.4.7.orig/libmultipath/defaults.h
-+++ multipath-tools-0.4.7/libmultipath/defaults.h
+Index: multipath-tools.debian-git/libmultipath/defaults.h
+===================================================================
+--- multipath-tools.debian-git.orig/libmultipath/defaults.h	2007-06-07 23:11:03.000000000 +0200
++++ multipath-tools.debian-git/libmultipath/defaults.h	2007-06-07 23:11:48.000000000 +0200
 @@ -1,4 +1,4 @@
 -#define DEFAULT_GETUID		"/sbin/scsi_id -g -u -s /block/%n"
 +#define DEFAULT_GETUID		"/lib/udev/scsi_id -g -u -s /block/%n"
  #define DEFAULT_UDEVDIR		"/dev"
  #define DEFAULT_SELECTOR	"round-robin 0"
  #define DEFAULT_FEATURES	"0"
---- multipath-tools-0.4.7.orig/libmultipath/hwtable.c
-+++ multipath-tools-0.4.7/libmultipath/hwtable.c
-@@ -128,7 +128,7 @@
+Index: multipath-tools.debian-git/libmultipath/hwtable.c
+===================================================================
+--- multipath-tools.debian-git.orig/libmultipath/hwtable.c	2007-06-07 23:11:03.000000000 +0200
++++ multipath-tools.debian-git/libmultipath/hwtable.c	2007-06-07 23:12:11.000000000 +0200
+@@ -183,7 +183,7 @@
  	{
  		.vendor        = "EMC",
  		.product       = "SYMMETRIX",
@@ -19,8 +23,10 @@
  		.getprio       = NULL,
  		.features      = DEFAULT_FEATURES,
  		.hwhandler     = DEFAULT_HWHANDLER,
---- multipath-tools-0.4.7.orig/multipath.conf.synthetic
-+++ multipath-tools-0.4.7/multipath.conf.synthetic
+Index: multipath-tools.debian-git/multipath.conf.synthetic
+===================================================================
+--- multipath-tools.debian-git.orig/multipath.conf.synthetic	2007-06-07 23:11:03.000000000 +0200
++++ multipath-tools.debian-git/multipath.conf.synthetic	2007-06-07 23:11:48.000000000 +0200
 @@ -7,7 +7,7 @@
  #	polling_interval 	10
  #	selector		"round-robin 0"
@@ -30,7 +36,7 @@
  #	prio_callout		/bin/true
  #	path_checker		readsector0
  #	rr_min_io		100
-@@ -48,7 +48,7 @@
+@@ -52,7 +52,7 @@
  #		vendor			"COMPAQ  "
  #		product			"HSV110 (C)COMPAQ"
  #		path_grouping_policy	multibus
@@ -39,8 +45,10 @@
  #		path_checker		readsector0
  #		path_selector		"round-robin 0"
  #		hardware_handler	"0"
---- multipath-tools-0.4.7.orig/multipath.conf.annotated
-+++ multipath-tools-0.4.7/multipath.conf.annotated
+Index: multipath-tools.debian-git/multipath.conf.annotated
+===================================================================
+--- multipath-tools.debian-git.orig/multipath.conf.annotated	2007-06-07 23:11:03.000000000 +0200
++++ multipath-tools.debian-git/multipath.conf.annotated	2007-06-07 23:11:48.000000000 +0200
 @@ -11,7 +11,7 @@
  #	#
  #	# name    : udev_dir
@@ -62,7 +70,7 @@
  #
  #	#
  #	# name    : prio_callout
-@@ -275,9 +275,9 @@
+@@ -291,9 +291,9 @@
  #		# scope   : multipath
  #		# desc    : the program and args to callout to obtain a unique 
  #		#           path identifier. Absolute path required

Modified: multipath-tools/trunk/debian/patches/series
==============================================================================
--- multipath-tools/trunk/debian/patches/series	(original)
+++ multipath-tools/trunk/debian/patches/series	Sat Jun  9 15:59:27 2007
@@ -1,6 +1,5 @@
-git-40b575955cc189aa993e6a030b66b5fef6bcf288.diff
+git-178b93111d54828a89ad280c0aaaea0812343a6a.diff
 exclude-quilt.diff
 scsi_id.diff
 Makefile-cleanups.diff
 remove-arch-ifdefs.diff
-udev.diff



More information about the pkg-lvm-commits mailing list