File multipath-tools-git-update.patch of Package multipath-tools

diff --git a/Makefile b/Makefile
index 83ae2fe..ee554e7 100644
--- a/Makefile
+++ b/Makefile
@@ -20,9 +20,13 @@ endif
 export KRNLSRC
 export KRNLOBJ
 
-BUILDDIRS = $(shell find . -mindepth 2 -name Makefile -exec dirname {} \; | grep -v ^lib)
+BUILDDIRS = $(shell find . -mindepth 2 -name Makefile -exec dirname {} \; | grep -vE '^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..7e2d4e6 100644
--- a/Makefile.inc
+++ b/Makefile.inc
@@ -17,15 +17,16 @@ ifeq ($(strip $(BUILD)),klibc)
 	CC = klcc
 	klibcdir = /usr/lib/klibc
 	libdm    = $(klibcdir)/lib/libdevmapper.a
-	libsysfs = $(klibcdir)/lib/libsysfs.a
 endif
 
 prefix      = 
 exec_prefix = $(prefix)
 bindir      = $(exec_prefix)/sbin
+libudevdir  = ${prefix}/lib/udev
 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
@@ -33,6 +34,8 @@ GZIP        = /bin/gzip -9 -c
 CHECKERSLIB = $(checkersdir)/libcheckers
 MULTIPATHLIB = $(multipathdir)/libmultipath
 
+INSTALL_PROGRAM = install -s
+
 OPTFLAGS     = -pipe -g -Wall -Wunused -Wstrict-prototypes
 CFLAGS	     = $(OPTFLAGS)
 
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/kpartx/Makefile b/kpartx/Makefile
index bf6e6c1..b4cca6c 100644
--- a/kpartx/Makefile
+++ b/kpartx/Makefile
@@ -6,15 +6,15 @@ BUILD=glibc
 
 include ../Makefile.inc
 
-CFLAGS += -I. -D_LARGEFILE64_SOURCE
+CFLAGS += -I. -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64
 
 ifeq ($(strip $(BUILD)),klibc)
 	OBJS = bsd.o dos.o kpartx.o solaris.o unixware.o gpt.o crc32.o \
-	       lopart.o xstrncpy.o devmapper.o dasd.o mac.o \
+	       lopart.o xstrncpy.o devmapper.o dasd.o mac.o sun.o \
 	       $(MULTIPATHLIB)-$(BUILD).a $(libdm)
 else
 	LDFLAGS = -ldevmapper
-	OBJS = bsd.o dos.o kpartx.o solaris.o unixware.o dasd.o \
+	OBJS = bsd.o dos.o kpartx.o solaris.o unixware.o dasd.o sun.o \
 	       gpt.o mac.o crc32.o lopart.o xstrncpy.o devmapper.o
 endif
 
@@ -36,9 +36,13 @@ 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_PROGRAM) -m 755 $(EXEC) $(DESTDIR)$(bindir)
+	install -d $(DESTDIR)$(libudevdir)
+	install -m 755 kpartx_id $(DESTDIR)$(libudevdir)
+	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/bsd.c b/kpartx/bsd.c
index 3ae2dc4..f87175e 100644
--- a/kpartx/bsd.c
+++ b/kpartx/bsd.c
@@ -10,7 +10,7 @@ struct bsd_disklabel {
 	short int	d_type;		/* drive type */
 	short int	d_subtype;	/* controller/d_type specific */
 	char	d_typename[16];		/* type name, e.g. "eagle" */
-	char	d_packname[16];		/* pack identifier */ 
+	char	d_packname[16];		/* pack identifier */
 	unsigned int	d_secsize;	/* # of bytes per sector */
 	unsigned int	d_nsectors;	/* # of data sectors per track */
 	unsigned int	d_ntracks;	/* # of tracks per cylinder */
@@ -50,12 +50,12 @@ int
 read_bsd_pt(int fd, struct slice all, struct slice *sp, int ns) {
 	struct bsd_disklabel *l;
 	struct bsd_partition *p;
-	unsigned int offset = all.start;
+	unsigned int offset = all.start, end;
 	int max_partitions;
 	char *bp;
-	int n = 0;
+	int n = 0, i, j;
 
-	bp = getblock(fd, offset+1); 	/* 1 sector suffices */
+	bp = getblock(fd, offset+1);    /* 1 sector suffices */
 	if (bp == NULL)
 		return -1;
 
@@ -79,5 +79,36 @@ read_bsd_pt(int fd, struct slice all, struct slice *sp, int ns) {
 			break;
 		}
 	}
+	/*
+	 * Convention has it that the bsd disklabel will always have
+	 * the 'c' partition spanning the entire disk.
+	 * So we have to check for contained slices.
+	 */
+	for(i = 0; i < n; i++) {
+		if (sp[i].size == 0)
+			continue;
+
+		end = sp[i].start + sp[i].size;
+		for(j = 0; j < n; j ++) {
+			if ( i == j )
+				continue;
+			if (sp[j].size == 0)
+				continue;
+
+			if (sp[i].start < sp[j].start) {
+				if (end > sp[j].start &&
+				    end < sp[j].start + sp[j].size) {
+					/* Invalid slice */
+					fprintf(stderr,
+						"bsd_disklabel: slice %d overlaps with %d\n", i , j);
+					sp[i].size = 0;
+				}
+			} else {
+				if (end <= sp[j].start + sp[j].size) {
+					sp[i].container = j + 1;
+				}
+			}
+		}
+	}
 	return n;
 }
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/devmapper.c b/kpartx/devmapper.c
index 1253941..6e3e198 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,56 @@ out:
 	return ret;
 }
 
+char *
+dm_mapuuid(int major, int minor)
+{
+	struct dm_task *dmt;
+	const char *tmp;
+	char *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;
+}
+
+int
+dm_devn (char * mapname, int *major, int *minor)
+{
+	int r = 1;
+	struct dm_task *dmt;
+	struct dm_info info;
+
+	if (!(dmt = dm_task_create(DM_DEVICE_INFO)))
+		return 0;
+
+	if (!dm_task_set_name(dmt, mapname))
+		goto out;
+
+	if (!dm_task_run(dmt))
+		goto out;
+
+	if (!dm_task_get_info(dmt, &info))
+		goto out;
+
+	*major = info.major;
+	*minor = info.minor;
+
+	r = 0;
+out:
+	dm_task_destroy(dmt);
+	return r;
+}
+
diff --git a/kpartx/devmapper.h b/kpartx/devmapper.h
index 9607476..ccdbead 100644
--- a/kpartx/devmapper.h
+++ b/kpartx/devmapper.h
@@ -1,6 +1,9 @@
 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);
+int dm_devn (char * mapname, int *major, int *minor);
diff --git a/kpartx/dos.c b/kpartx/dos.c
index a707423..1691105 100644
--- a/kpartx/dos.c
+++ b/kpartx/dos.c
@@ -16,7 +16,7 @@ is_extended(int type) {
 }
 
 static int
-read_extended_partition(int fd, struct partition *ep,
+read_extended_partition(int fd, struct partition *ep, int en,
 			struct slice *sp, int ns)
 {
 	struct partition p;
@@ -53,6 +53,7 @@ read_extended_partition(int fd, struct partition *ep,
 			if (n < ns) {
 				sp[n].start = here + le32_to_cpu(p.start_sect);
 				sp[n].size = le32_to_cpu(p.nr_sects);
+				sp[n].container = en + 1;
 				n++;
 			} else {
 				fprintf(stderr,
@@ -97,9 +98,7 @@ read_dos_pt(int fd, struct slice all, struct slice *sp, int ns) {
 			break;
 		}
 		if (is_extended(p.sys_type)) {
-			n += read_extended_partition(fd, &p, sp+n, ns-n);
-			/* hide the extended partition itself */
-			sp[i].size = 0;
+			n += read_extended_partition(fd, &p, i, sp+n, ns-n);
 		}
 	}
 	return n;
diff --git a/kpartx/kpartx.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..dbe2ee2 100644
--- a/kpartx/kpartx.c
+++ b/kpartx/kpartx.c
@@ -79,6 +79,7 @@ initpts(void)
 	addpts("unixware", read_unixware_pt);
 	addpts("dasd", read_dasd_pt);
 	addpts("mac", read_mac_pt);
+	addpts("sun", read_sun_pt);
 }
 
 static char short_opts[] = "ladgvnp:t:";
@@ -115,7 +116,7 @@ strip_slash (char * device)
 	char * p = device;
 
 	while (*(p++) != 0x0) {
-		
+
 		if (*p == '/')
 			*p = '!';
 	}
@@ -125,9 +126,9 @@ static int
 find_devname_offset (char * device)
 {
 	char *p, *q = NULL;
-	
+
 	p = device;
-	
+
 	while (*p++)
 		if (*p == '/')
 			q = p;
@@ -182,7 +183,7 @@ get_hotplug_device(void)
 
 int
 main(int argc, char **argv){
-        int fd, i, j, k, n, op, off, arg;
+	int fd, i, j, m, n, op, off, arg, c, d;
 	struct slice all;
 	struct pt *ptp;
 	enum action what = LIST;
@@ -192,6 +193,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;
@@ -203,7 +206,7 @@ main(int argc, char **argv){
 	type = device = diskdevice = NULL;
 	memset(&all, 0, sizeof(all));
 	memset(&partname, 0, sizeof(partname));
-	
+
 	/* Check whether hotplug mode. */
 	progname = strrchr(argv[0], '/');
 
@@ -284,17 +287,12 @@ 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);
 
 		if (!loopdev && what == DELETE)
 			exit (0);
-				
+
 		if (!loopdev) {
 			loopdev = find_unused_loop_device();
 
@@ -311,8 +309,22 @@ main(int argc, char **argv){
 		memset(delim, 0, DELIM_SIZE);
 		set_delimiter(device, delim);
 	}
-	
+
 	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) {
@@ -328,7 +340,7 @@ main(int argc, char **argv){
 
 		if (type && strcmp(type, ptp->type))
 			continue;
-		
+
 		/* here we get partitions */
 		n = ptp->fn(fd, all, slices, SIZE(slices));
 
@@ -342,36 +354,56 @@ main(int argc, char **argv){
 		else
 			continue;
 
-		/*
-		 * test for overlap, as in the case of an extended partition
-		 * zero their size to avoid mapping
-		 */
-		for (j=0; j<n; j++) {
-			for (k=j+1; k<n; k++) {
-				if (slices[k].start > slices[j].start &&
-				    slices[k].start < slices[j].start +
-				    slices[j].size)
-					slices[j].size = 0;
-			}
-		}
-
 		switch(what) {
 		case LIST:
-			for (j = 0; j < n; j++) {
+			for (j = 0, c = 0, m = 0; j < n; j++) {
 				if (slices[j].size == 0)
 					continue;
+				if (slices[j].container > 0) {
+					c++;
+					continue;
+				}
+
+				slices[j].minor = m++;
 
 				printf("%s%s%d : 0 %lu %s %lu\n",
-					device + off, delim, j+1,
-					(unsigned long) slices[j].size, device,
-				        (unsigned long) slices[j].start);
+				       mapname, delim, j+1,
+				       (unsigned long) slices[j].size, device,
+				       (unsigned long) slices[j].start);
 			}
+			/* Loop to resolve contained slices */
+			d = c;
+			while (c) {
+				for (j = 0; j < n; j++) {
+					unsigned long start;
+					int k = slices[j].container - 1;
+
+					if (slices[j].size == 0)
+						continue;
+					if (slices[j].minor > 0)
+						continue;
+					if (slices[j].container == 0)
+						continue;
+					slices[j].minor = m++;
+
+					start = slices[j].start - slices[k].start;
+					printf("%s%s%d : 0 %lu /dev/dm-%d %lu\n",
+					       mapname, delim, j+1,
+					       (unsigned long) slices[j].size,
+					       slices[k].minor, start);
+					c--;
+				}
+				/* Terminate loop if nothing more to resolve */
+				if (d == c)
+					break;
+			}
+
 			break;
 
 		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);
 				}
@@ -390,7 +422,7 @@ main(int argc, char **argv){
 			if (S_ISREG (buf.st_mode)) {
 				if (del_loop(device)) {
 					if (verbose)
-				    		printf("can't del loop : %s\n",
+						printf("can't del loop : %s\n",
 							device);
 					exit(1);
 				}
@@ -399,17 +431,23 @@ main(int argc, char **argv){
 			break;
 
 		case ADD:
-			for (j=0; j<n; j++) {
+			for (j=0, c = 0; j<n; j++) {
 				if (slices[j].size == 0)
 					continue;
 
+				/* Skip all contained slices */
+				if (slices[j].container > 0) {
+					c++;
+					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);
 				}
 				strip_slash(partname);
-				
+
 				if (safe_sprintf(params, "%s %lu", device,
 					     (unsigned long)slices[j].start)) {
 					fprintf(stderr, "params too small\n");
@@ -420,16 +458,80 @@ 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,
 							partname);
 
+				dm_devn(partname, &slices[j].major,
+					&slices[j].minor);
+
 				if (verbose)
-					printf("add map %s : 0 %lu %s %s\n",
-						partname, slices[j].size,
-						DM_TARGET, params);
+					printf("add map %s (%d:%d): 0 %lu %s %s\n",
+					       partname, slices[j].major,
+					       slices[j].minor, slices[j].size,
+					       DM_TARGET, params);
+			}
+			/* Loop to resolve contained slices */
+			d = c;
+			while (c) {
+				for (j = 0; j < n; j++) {
+					int k = slices[j].container - 1;
+
+					if (slices[j].size == 0)
+						continue;
+
+					/* Skip all existing slices */
+					if (slices[j].minor > 0)
+						continue;
+
+					/* Skip all simple slices */
+					if (k < 0)
+						continue;
+
+					/* Check container slice */
+					if (slices[k].size == 0)
+						fprintf(stderr, "Invalid slice %d\n",
+							k);
+
+					if (safe_sprintf(partname, "%s%s%d",
+							 mapname, delim, j+1)) {
+						fprintf(stderr, "partname too small\n");
+						exit(1);
+					}
+					strip_slash(partname);
+
+					if (safe_sprintf(params, "%d:%d %lu",
+							 slices[k].major,
+							 slices[k].minor,
+							 (unsigned long)slices[j].start)) {
+						fprintf(stderr, "params too small\n");
+						exit(1);
+					}
+
+					op = (dm_map_present(partname) ?
+					      DM_DEVICE_RELOAD : DM_DEVICE_CREATE);
+
+					dm_addmap(op, partname, DM_TARGET, params,
+						  slices[j].size, uuid, j+1);
+
+					if (op == DM_DEVICE_RELOAD)
+						dm_simplecmd(DM_DEVICE_RESUME,
+							     partname);
+
+					dm_devn(partname, &slices[j].major,
+						&slices[j].minor);
+
+					if (verbose)
+						printf("add map %s : 0 %lu %s %s\n",
+						       partname, slices[j].size,
+						       DM_TARGET, params);
+					c--;
+				}
+				/* Terminate loop */
+				if (d == c)
+					break;
 			}
 			break;
 
@@ -505,7 +607,7 @@ getblock (int fd, unsigned int secnr) {
 	bp->next = blockhead;
 	blockhead = bp;
 	bp->block = (char *) xmalloc(READ_SIZE);
-	
+
 	if (read(fd, bp->block, READ_SIZE) != READ_SIZE) {
 		fprintf(stderr, "read error, sector %d\n", secnr);
 		bp->block = NULL;
diff --git a/kpartx/kpartx.h b/kpartx/kpartx.h
index 6a715de..9b3aeca 100644
--- a/kpartx/kpartx.h
+++ b/kpartx/kpartx.h
@@ -22,6 +22,9 @@
 struct slice {
 	unsigned long start;
 	unsigned long size;
+	int container;
+	int major;
+	int minor;
 };
 
 typedef int (ptreader)(int fd, struct slice all, struct slice *sp, int ns);
@@ -33,6 +36,7 @@ extern ptreader read_unixware_pt;
 extern ptreader read_gpt_pt;
 extern ptreader read_dasd_pt;
 extern ptreader read_mac_pt;
+extern ptreader read_sun_pt;
 
 char *getblock(int fd, unsigned int secnr);
 
diff --git a/kpartx/kpartx.rules b/kpartx/kpartx.rules
new file mode 100644
index 0000000..f32c718
--- /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}=="/lib/udev/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..81f32bf
--- /dev/null
+++ b/kpartx/kpartx_id
@@ -0,0 +1,93 @@
+#!/bin/sh
+#
+# kpartx_id
+#
+# Generates ID information for device-mapper tables.
+#
+# Copyright (C) 2006 SUSE Linux Products GmbH
+# Author:
+#       Hannes Reinecke <hare@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/sun.c b/kpartx/sun.c
new file mode 100644
index 0000000..3d88b21
--- /dev/null
+++ b/kpartx/sun.c
@@ -0,0 +1,131 @@
+/*
+ * Lifted from util-linux' partx sun.c
+ *
+ * Copyrights of the original file apply
+ * Copyright (c) 2007 Hannes Reinecke
+ */
+#include "kpartx.h"
+#include "byteorder.h"
+#include <stdio.h>
+#include <sys/types.h>
+#include <time.h>		/* time_t */
+
+#define SUN_DISK_MAGIC		0xDABE	/* Disk magic number */
+#define SUN_DISK_MAXPARTITIONS	8
+
+struct __attribute__ ((packed)) sun_raw_part {
+	u_int32_t	start_cylinder; /* where the part starts... */
+	u_int32_t	num_sectors;	/* ...and it's length */
+};
+
+struct __attribute__ ((packed)) sun_part_info {
+	u_int8_t	spare1;
+	u_int8_t	id;		/* Partition type */
+	u_int8_t	spare2;
+	u_int8_t	flags;		/* Partition flags */
+};
+
+struct __attribute__ ((packed)) sun_disk_label {
+	char		info[128];	/* Informative text string */
+	u_int8_t	spare0[14];
+	struct sun_part_info infos[SUN_DISK_MAXPARTITIONS];
+	u_int8_t	spare1[246];	/* Boot information etc. */
+	u_int16_t	rspeed;		/* Disk rotational speed */
+	u_int16_t	pcylcount;	/* Physical cylinder count */
+	u_int16_t	sparecyl;	/* extra sects per cylinder */
+	u_int8_t	spare2[4];	/* More magic... */
+	u_int16_t	ilfact;		/* Interleave factor */
+	u_int16_t	ncyl;		/* Data cylinder count */
+	u_int16_t	nacyl;		/* Alt. cylinder count */
+	u_int16_t	ntrks;		/* Tracks per cylinder */
+	u_int16_t	nsect;		/* Sectors per track */
+	u_int8_t	spare3[4];	/* Even more magic... */
+	struct sun_raw_part partitions[SUN_DISK_MAXPARTITIONS];
+	u_int16_t	magic;		/* Magic number */
+	u_int16_t	csum;		/* Label xor'd checksum */
+};
+
+/* Checksum Verification */
+static int
+sun_verify_checksum (struct sun_disk_label *label)
+{
+	u_int16_t *ush = ((u_int16_t *)(label + 1)) - 1;
+	u_int16_t csum = 0;
+
+	while (ush >= (u_int16_t *)label)
+		csum ^= *ush--;
+
+	return !csum;
+}
+
+int
+read_sun_pt(int fd, struct slice all, struct slice *sp, int ns) {
+	struct sun_disk_label *l;
+	struct sun_raw_part *s;
+	unsigned int offset = all.start, end;
+	int i, j, n;
+	char *bp;
+
+	bp = getblock(fd, offset);
+	if (bp == NULL)
+		return -1;
+
+	l = (struct sun_disk_label *) bp;
+	if(be16_to_cpu(l->magic) != SUN_DISK_MAGIC)
+		return -1;
+
+	if (!sun_verify_checksum(l)) {
+		fprintf(stderr, "Corrupted Sun disk label\n");
+		return -1;
+	}
+
+	for(i=0, n=0; i<SUN_DISK_MAXPARTITIONS; i++) {
+		s = &l->partitions[i];
+
+		if (s->num_sectors == 0)
+			continue;
+		if (n < ns) {
+			sp[n].start = offset +
+				be32_to_cpu(s->start_cylinder) * be16_to_cpu(l->nsect) * be16_to_cpu(l->ntrks);
+			sp[n].size = be32_to_cpu(s->num_sectors);
+			n++;
+		} else {
+			fprintf(stderr,
+				"sun_disklabel: too many slices\n");
+			break;
+		}
+	}
+	/*
+	 * Convention has it that the SUN disklabel will always have
+	 * the 'c' partition spanning the entire disk.
+	 * So we have to check for contained slices.
+	 */
+	for(i = 0; i < SUN_DISK_MAXPARTITIONS; i++) {
+		if (sp[i].size == 0)
+			continue;
+
+		end = sp[i].start + sp[i].size;
+		for(j = 0; j < SUN_DISK_MAXPARTITIONS; j ++) {
+			if ( i == j )
+				continue;
+			if (sp[j].size == 0)
+				continue;
+
+			if (sp[i].start < sp[j].start) {
+				if (end > sp[j].start &&
+				    end < sp[j].start + sp[j].size) {
+					/* Invalid slice */
+					fprintf(stderr,
+						"sun_disklabel: slice %d overlaps with %d\n", i , j);
+					sp[i].size = 0;
+				}
+			} else {
+				if (end <= sp[j].start + sp[j].size) {
+					sp[i].container = j + 1;
+				}
+			}
+		}
+	}
+	return n;
+}
+
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..d7728a5 100644
--- a/libcheckers/checkers.c
+++ b/libcheckers/checkers.c
@@ -7,11 +7,13 @@
 #include "tur.h"
 #include "hp_sw.h"
 #include "emc_clariion.h"
+#include "rdac.h"
 #include "readsector0.h"
 
 static struct checker checkers[] = {
 	{
 		.fd         = 0,
+		.sync       = 1,
 		.name       = DIRECTIO,
 		.message    = "",
 		.context    = NULL,
@@ -21,6 +23,7 @@ static struct checker checkers[] = {
 	},
 	{
 		.fd         = 0,
+		.sync       = 1,
 		.name       = TUR,
 		.message    = "",
 		.context    = NULL,
@@ -30,6 +33,7 @@ static struct checker checkers[] = {
 	},
 	{
 		.fd         = 0,
+		.sync       = 1,
 		.name       = HP_SW,
 		.message    = "",
 		.context    = NULL,
@@ -39,6 +43,7 @@ static struct checker checkers[] = {
 	},
 	{
 		.fd         = 0,
+		.sync       = 1,
 		.name       = EMC_CLARIION,
 		.message    = "",
 		.context    = NULL,
@@ -48,6 +53,17 @@ static struct checker checkers[] = {
 	},
 	{
 		.fd         = 0,
+		.sync       = 1,
+		.name       = RDAC,
+		.message    = "",
+		.context    = NULL,
+		.check      = rdac,
+		.init       = rdac_init,
+		.free       = rdac_free
+	},
+	{
+		.fd         = 0,
+		.sync       = 1,
 		.name       = READSECTOR0,
 		.message    = "",
 		.context    = NULL,
@@ -55,7 +71,7 @@ static struct checker checkers[] = {
 		.init       = readsector0_init,
 		.free       = readsector0_free
 	},
-	{0, "", "", NULL, NULL, NULL, NULL},
+	{0, 1, "", "", NULL, NULL, NULL, NULL},
 };
 
 void checker_set_fd (struct checker * c, int fd)
@@ -63,6 +79,16 @@ void checker_set_fd (struct checker * c, int fd)
 	c->fd = fd;
 }
 
+void checker_set_sync (struct checker * c)
+{
+	c->sync = 1;
+}
+
+void checker_set_async (struct checker * c)
+{
+	c->sync = 0;
+}
+
 struct checker * checker_lookup (char * name)
 {
 	struct checker * c = &checkers[0];
@@ -75,8 +101,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);
 }
 
@@ -123,6 +150,7 @@ struct checker * checker_default (void)
 void checker_get (struct checker * dst, struct checker * src)
 {
 	dst->fd = src->fd;
+	dst->sync = src->sync;
 	strncpy(dst->name, src->name, CHECKER_NAME_LEN);
 	strncpy(dst->message, src->message, CHECKER_MSG_LEN);
 	dst->check = src->check;
diff --git a/libcheckers/checkers.h b/libcheckers/checkers.h
index ac795ed..9b270eb 100644
--- a/libcheckers/checkers.h
+++ b/libcheckers/checkers.h
@@ -2,7 +2,47 @@
 #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.
+ *
+ * PATH_PENDING:
+ * - Use: All async checkers
+ * - Description: Indicates a check IO is in flight.
  */
 #define PATH_WILD	-1
 #define PATH_UNCHECKED	0
@@ -10,14 +50,32 @@
 #define PATH_UP		2
 #define PATH_SHAKY	3
 #define PATH_GHOST	4
+#define PATH_PENDING	5
 
 #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
+#define DEFAULT_CHECKER DIRECTIO
+
+/*
+ * 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
+#define ASYNC_TIMEOUT_SEC	30
 
 /*
  * strings lengths
@@ -28,19 +86,24 @@
 
 struct checker {
 	int fd;
+	int sync;
 	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_sync (struct checker * c);
+void checker_set_async (struct checker * c);
 void checker_set_fd (struct checker *, int);
 struct checker * checker_lookup (char *);
 int checker_check (struct checker *);
diff --git a/libcheckers/directio.c b/libcheckers/directio.c
index b53c1c3..ee09af7 100644
--- a/libcheckers/directio.c
+++ b/libcheckers/directio.c
@@ -12,28 +12,47 @@
 #include <sys/ioctl.h>
 #include <linux/fs.h>
 #include <errno.h>
+#include <linux/kdev_t.h>
+#include <asm/unistd.h>
+#include <libaio.h>
 
 #include "checkers.h"
+#include "../libmultipath/debug.h"
 
 #define MSG_DIRECTIO_UNKNOWN	"directio checker is not available"
 #define MSG_DIRECTIO_UP		"directio checker reports path is up"
 #define MSG_DIRECTIO_DOWN	"directio checker reports path is down"
+#define MSG_DIRECTIO_PENDING	"directio checker is waiting on aio"
+
+#define LOG(prio, fmt, args...) condlog(prio, "directio: " fmt, ##args)
 
 struct directio_context {
-	int blksize; 
-	unsigned char *buf;
-	unsigned char *ptr;
+	int		running;
+	int		reset_flags;
+	int		blksize;
+	unsigned char *	buf;
+	unsigned char * ptr;
+	io_context_t	ioctx;
+	struct iocb	io;
 };
 
+
 int directio_init (struct checker * c)
 {
 	unsigned long pgsize = getpagesize();
 	struct directio_context * ct;
+	long flags;
 
 	ct = malloc(sizeof(struct directio_context));
 	if (!ct)
 		return 1;
-	c->context = (void *)ct;
+	memset(ct, 0, sizeof(struct directio_context));
+
+	if (io_setup(1, &ct->ioctx) != 0) {
+		condlog(1, "io_setup failed");
+		free(ct);
+		return 1;
+	}
 
 	if (ioctl(c->fd, BLKBSZGET, &ct->blksize) < 0) {
 		MSG(c, "cannot get blocksize, set default");
@@ -50,11 +69,28 @@ int directio_init (struct checker * c)
 	ct->buf = (unsigned char *)malloc(ct->blksize + pgsize);
 	if (!ct->buf)
 		goto out;
-	ct->ptr = (unsigned char *)(((unsigned long)ct->buf + pgsize - 1) &
-		  (~(pgsize - 1))); 
 
+	flags = fcntl(c->fd, F_GETFL);
+	if (flags < 0)
+		goto out;
+	if (!(flags & O_DIRECT)) {
+		flags |= O_DIRECT;
+		if (fcntl(c->fd, F_SETFL, flags) < 0)
+			goto out;
+		ct->reset_flags = 1;
+	}
+
+	ct->ptr = (unsigned char *) (((unsigned long)ct->buf + pgsize - 1) &
+		  (~(pgsize - 1)));
+
+	/* Sucessfully initialized, return the context. */
+	c->context = (void *) ct;
 	return 0;
+
 out:
+	if (ct->buf)
+		free(ct->buf);
+	io_destroy(ct->ioctx);
 	free(ct);
 	return 1;
 }
@@ -62,56 +98,72 @@ out:
 void directio_free (struct checker * c)
 {
 	struct directio_context * ct = (struct directio_context *)c->context;
+	long flags;
 
 	if (!ct)
 		return;
+
+	if (ct->reset_flags) {
+		if ((flags = fcntl(c->fd, F_GETFL)) >= 0) {
+			flags &= ~O_DIRECT;
+			/* No point in checking for errors */
+			fcntl(c->fd, F_SETFL, flags);
+		}
+	}
+
 	if (ct->buf)
 		free(ct->buf);
+	io_destroy(ct->ioctx);
 	free(ct);
 }
 
 static int
-direct_read (int fd, unsigned char * buff, int size)
+check_state(int fd, struct directio_context *ct, int sync)
 {
-	long flags;
-	int reset_flags = 0;
-	int res, retval;
-
-	flags = fcntl(fd,F_GETFL);
-
-	if (flags < 0) {
-		return PATH_UNCHECKED;
+	struct timespec	timeout = { .tv_nsec = 5 };
+	struct io_event event;
+	struct stat	sb;
+	int		rc = PATH_UNCHECKED;
+	long		r;
+
+	if (fstat(fd, &sb) == 0) {
+		LOG(4, "called for %x", (unsigned) sb.st_rdev);
+	}
+	if (sync) {
+		LOG(4, "called in synchronous mode");
+		timeout.tv_sec  = ASYNC_TIMEOUT_SEC;
+		timeout.tv_nsec = 0;
 	}
 
-	if (!(flags & O_DIRECT)) {
-		flags |= O_DIRECT;
-		if (fcntl(fd,F_SETFL,flags) < 0) {
+	if (!ct->running) {
+		struct iocb *ios[1] = { &ct->io };
+
+		LOG(3, "starting new request");
+		memset(&ct->io, 0, sizeof(struct iocb));
+		io_prep_pread(&ct->io, fd, ct->ptr, ct->blksize, 0);
+		if (io_submit(ct->ioctx, 1, ios) != 1) {
+			LOG(3, "io_submit error %i", errno);
 			return PATH_UNCHECKED;
 		}
-		reset_flags = 1;
 	}
+	ct->running++;
 
-	while ( (res = read(fd,buff,size)) < 0 && errno == EINTR );
-	if (res < 0) {
-		if (errno == EINVAL) {
-			/* O_DIRECT is not available */
-			retval = PATH_UNCHECKED;
-		} else if (errno == ENOMEM) {
-			retval = PATH_UP;
-		} else {
-			retval = PATH_DOWN;
-		}
+	r = io_getevents(ct->ioctx, 1L, 1L, &event, &timeout);
+	LOG(3, "async io getevents returns %li (errno=%s)", r, strerror(errno));
+
+	if (r < 1L) {
+		if (ct->running > ASYNC_TIMEOUT_SEC || sync) {
+			LOG(3, "abort check on timeout");
+			rc = PATH_DOWN;
+		} else
+			rc = PATH_PENDING;
 	} else {
-		retval = PATH_UP;
-	}
-	
-	if (reset_flags) {
-		flags &= ~O_DIRECT;
-		/* No point in checking for errors */
-		fcntl(fd,F_SETFL,flags);
+		LOG(3, "io finished %lu/%lu", event.res, event.res2);
+		ct->running = 0;
+		rc = (event.res == ct->blksize) ? PATH_UP : PATH_DOWN;
 	}
 
-	return retval;
+	return rc;
 }
 
 int directio (struct checker * c)
@@ -119,7 +171,10 @@ int directio (struct checker * c)
 	int ret;
 	struct directio_context * ct = (struct directio_context *)c->context;
 
-	ret = direct_read(c->fd, ct->ptr, ct->blksize);
+	if (!ct)
+		return PATH_UNCHECKED;
+
+	ret = check_state(c->fd, ct, c->sync);
 
 	switch (ret)
 	{
@@ -132,6 +187,9 @@ int directio (struct checker * c)
 	case PATH_UP:
 		MSG(c, MSG_DIRECTIO_UP);
 		break;
+	case PATH_PENDING:
+		MSG(c, MSG_DIRECTIO_PENDING);
+		break;
 	default:
 		break;
 	}
diff --git a/libcheckers/emc_clariion.c b/libcheckers/emc_clariion.c
index 1d7b684..6c7167e 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[4096];
+
+		ret = sg_read(c->fd, &buf[0], sbb = &sb[0]);
+		if (ret == PATH_DOWN) {
+			hexadecimal_to_ascii(ct->wwn, wwnstr);
+
+			/*
+		 	 * Check for inactive snapshot LU this way.  Must
+			 * fail these.
+	 	 	 */
+			if (((sbb[2]&0xf) == 5) && (sbb[12] == 0x25) &&
+			    (sbb[13]==1)) {
+				/*
+			 	 * Do this so that we can fail even the
+			 	 * passive paths which will return
+				 * 02/04/03 not 05/25/01 on read.
+			 	 */
+				SET_INACTIVE_SNAP(c);
+				MSG(c, "emc_clariion_checker: Active "
+					"path to inactive snapshot WWN %s.",
+					wwnstr);
+			} else
+				MSG(c, "emc_clariion_checker: Read "
+					"error for WWN %s.  Sense data are "
+					"0x%x/0x%x/0x%x.", wwnstr,
+					sbb[2]&0xf, sbb[12], sbb[13]);
+		} else {
+			MSG(c, "emc_clariion_checker: Active path is "
+			    "healthy.");
+			/*
+		 	 * Remove the path from the set of paths to inactive
+		 	 * snapshot LUs if it was in this list since the
+		 	 * snapshot is no longer inactive.
+		 	 */
+			CLR_INACTIVE_SNAP(c);
+		}
+	} else {
+		if (IS_INACTIVE_SNAP(c)) {
+			hexadecimal_to_ascii(ct->wwn, wwnstr);
+			MSG(c, "emc_clariion_checker: Passive "
+				"path to inactive snapshot WWN %s.",
+				wwnstr);
+			ret = PATH_DOWN;
+		} else {
+			MSG(c,
+		    	    "emc_clariion_checker: Passive path is healthy.");
+			ret = PATH_UP;	/* not ghost */
+		}
+	}
+
+	return ret;
 }
diff --git a/libcheckers/hp_sw.c b/libcheckers/hp_sw.c
index 509e9c4..b9731ff 100644
--- a/libcheckers/hp_sw.c
+++ b/libcheckers/hp_sw.c
@@ -19,7 +19,6 @@
 #define 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/libsg.c b/libcheckers/libsg.c
new file mode 100644
index 0000000..9171b10
--- /dev/null
+++ b/libcheckers/libsg.c
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2004, 2005 Christophe Varoqui
+ */
+#include <string.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <sys/stat.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;
+	struct stat filestatus;
+	int retry_count = 3;
+
+	if (fstat(sg_fd, &filestatus) != 0)
+		return PATH_DOWN;
+	bs = (filestatus.st_blksize > 4096)? 4096: filestatus.st_blksize;
+	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..bef0eb6 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 buf[4096];
+	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/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/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..ca434fe 100644
--- a/libmultipath/alias.c
+++ b/libmultipath/alias.c
@@ -120,21 +120,34 @@ lock_bindings_file(int fd)
 
 
 static int
-open_bindings_file(char *file)
+open_bindings_file(char *file, int *can_write)
 {
 	int fd;
 	struct stat s;
 
 	if (ensure_directories_exist(file, 0700))
 		return -1;
+	*can_write = 1;
 	fd = open(file, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
 	if (fd < 0) {
-		condlog(0, "Cannot open bindings file [%s] : %s", file,
-			strerror(errno));
-		return -1;
+		if (errno == EROFS) {
+			*can_write = 0;
+			condlog(3, "Cannot open bindings file [%s] read/write. "
+				" trying readonly", file);
+			fd = open(file, O_RDONLY);
+			if (fd < 0) {
+				condlog(0, "Cannot open bindings file [%s] "
+					"readonly : %s", file, strerror(errno));
+				return -1;
+			}
+		}
+		else {
+			condlog(0, "Cannot open bindings file [%s] : %s", file,
+				strerror(errno));
+			return -1;
+		}
 	}
-
-	if (lock_bindings_file(fd) < 0)
+	if (*can_write && lock_bindings_file(fd) < 0)
 		goto fail;
 	
 	memset(&s, 0, sizeof(s));
@@ -143,6 +156,8 @@ open_bindings_file(char *file)
 		goto fail;
 	}
 	if (s.st_size == 0) {
+		if (*can_write == 0)
+			goto fail;
 		/* If bindings file is empty, write the header */
 		size_t len = strlen(BINDINGS_FILE_HEADER);
 		if (write_all(fd, BINDINGS_FILE_HEADER, len) != len) {
@@ -166,28 +181,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 +211,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 +259,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,24 +310,49 @@ char *
 get_user_friendly_alias(char *wwid, char *file)
 {
 	char *alias;
-	int fd, id;
+	int fd, scan_fd, id;
+	FILE *f;
+	int can_write;
 
 	if (!wwid || *wwid == '\0') {
 		condlog(3, "Cannot find binding for empty WWID");
 		return NULL;
 	}
 
-	fd = open_bindings_file(file);
+	fd = open_bindings_file(file, &can_write);
 	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)
+
+	if (!alias && can_write)
 		alias = allocate_binding(fd, wwid, id);
 
+	fclose(f);
+	close(scan_fd);
 	close(fd);
 	return alias;
 }
@@ -353,22 +361,45 @@ char *
 get_user_friendly_wwid(char *alias, char *file)
 {
 	char *wwid;
-	int fd, id;
+	int fd, scan_fd, id, unused;
+	FILE *f;
 
 	if (!alias || *alias == '\0') {
 		condlog(3, "Cannot find binding for empty alias");
 		return NULL;
 	}
 
-	fd = open_bindings_file(file);
+	fd = open_bindings_file(file, &unused);
 	if (fd < 0)
 		return NULL;
-	id = rlookup_binding(fd, &wwid, alias);
+
+	scan_fd = dup(fd);
+	if (scan_fd < 0) {
+		condlog(0, "Cannot dup bindings file descriptor : %s",
+			strerror(errno));
+		close(fd);
+		return NULL;
+	}
+
+	f = fdopen(scan_fd, "r");
+	if (!f) {
+		condlog(0, "cannot fdopen on bindings file descriptor : %s",
+			strerror(errno));
+		close(scan_fd);
+		close(fd);
+		return NULL;
+	}
+
+	id = rlookup_binding(f, &wwid, alias);
 	if (id < 0) {
+		fclose(f);
+		close(scan_fd);
 		close(fd);
 		return NULL;
 	}
 
+	fclose(f);
+	close(scan_fd);
 	close(fd);
 	return wwid;
 }
diff --git a/libmultipath/blacklist.c b/libmultipath/blacklist.c
index 6a8a681..0c277cb 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]*");
+
+	str = STRDUP("^dcssblk[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,63 +129,194 @@ 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;
 			}
 		}
 	}
-	
 	return 0;
 }
 
 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_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)
+_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_wwid, 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/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.h b/libmultipath/defaults.h
index ab65492..df7d971 100644
--- a/libmultipath/defaults.h
+++ b/libmultipath/defaults.h
@@ -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"
@@ -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/discovery.c b/libmultipath/discovery.c
index cf8289c..c842eb0 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,
@@ -70,8 +71,10 @@ path_discover (vector pathvec, struct config * conf, char * devname, int flag)
 		return 1;
 	}
 			
-	if (!filepresent(path))
+	if (strncmp(devname,"cciss",5) && !filepresent(path)) {
+		condlog(4, "path %s not present", path);
 		return 0;
+	}
 
 	pp = find_path_by_dev(pathvec, devname);
 
@@ -86,127 +89,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]; \
+	char *attr; \
 \
-	if (safe_sprintf(attr_path, fmt, sysfs_path, dev)) \
+	attr = sysfs_attr_get_value(dev->devpath, #fname); \
+	if (!attr) \
 		return 1; \
 \
-	if (wait_for_file(attr_path)) \
-		return 1; \
-\
-	if (!(attr = sysfs_open_attribute(attr_path))) \
-		return 1; \
-\
-	if (0 > sysfs_read_attribute(attr)) \
-		goto out; \
-\
-	if (attr->len < 2 || attr->len - 1 > len) \
-		goto out; \
-\
-	strncpy(buff, attr->value, attr->len - 1); \
-	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;
 }
 	
@@ -216,78 +209,74 @@ out:
 static int
 opennode (char * dev, int mode)
 {
-	char devpath[FILE_NAME_SIZE];
+	char devpath[FILE_NAME_SIZE], *ptr;
 
 	if (safe_sprintf(devpath, "%s/%s", conf->udev_dir, dev)) {
 		condlog(0, "devpath too small");
 		return -1;
 	}
-
-	if (wait_for_file(devpath)) {
-		condlog(3, "failed to open %s", devpath);
-		return -1;
+	/*
+	 * Translate '!' into '/'
+	 */
+	ptr = devpath;
+	while ((ptr = strchr(ptr, '!'))) {
+		*ptr = '/';
+		ptr++;
 	}
-
 	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 +284,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 +302,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 +328,64 @@ 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;
 	}
-        return 0;
+        return 1;
 }
 
 static int
-sysfs_get_bus (char * sysfs_path, struct path * pp)
+get_inq (char * vendor, char * product, char * rev, int fd)
 {
-	struct sysfs_device *sdev;
-	char attr_path[FILE_NAME_SIZE];
-	char attr_buff[FILE_NAME_SIZE];
-
-	pp->bus = SYSFS_BUS_UNDEF;
+	char buff[MX_ALLOC_LEN + 1] = {0};
 
-	/*
-	 * 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)))
+	if (fd < 0)
 		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);
+	if (0 == do_inq(fd, 0, 0, 0, buff, MX_ALLOC_LEN, 0)) {
+		memcpy(vendor, buff + 8, 8);
+		vendor[8] = '\0';
+		memcpy(product, buff + 16, 16);
+		product[16] = '\0';
+		memcpy(rev, buff + 32, 4);
+		rev[4] = '\0';
+		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 +393,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 +415,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 +436,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 +452,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 +474,43 @@ ccw_sysfs_pathinfo (struct path * pp)
 }
 
 static int
-common_sysfs_pathinfo (struct path * pp)
+cciss_sysfs_pathinfo (struct path * pp, struct sysfs_device * dev)
 {
-	if (sysfs_get_bus(sysfs_path, pp))
-		return 1;
+	char attr_path[FILE_NAME_SIZE];
 
-	condlog(3, "%s: bus = %i", pp->dev, pp->bus);
+	/*
+	 * host / bus / target / lun
+	 */
+	basename(dev->devpath, attr_path);
+	pp->sg_id.lun = 0;
+	pp->sg_id.channel = 0;
+	sscanf(attr_path, "cciss!c%id%i",
+			&pp->sg_id.host_no,
+			&pp->sg_id.scsi_id);
+	condlog(3, "%s: h:b:t:l = %i:%i:%i:%i",
+			pp->dev,
+			pp->sg_id.host_no,
+			pp->sg_id.channel,
+			pp->sg_id.scsi_id,
+			pp->sg_id.lun);
+	return 0;
+}
+
+static int
+common_sysfs_pathinfo (struct path * pp, struct sysfs_device *dev)
+{
+	char *attr;
 
-	if (sysfs_get_dev(sysfs_path, pp->dev,
-			  pp->dev_t, BLK_DEV_SIZE))
+	attr = sysfs_attr_get_value(dev->devpath, "dev");
+	if (!attr) {
+		condlog(3, "%s: no 'dev' attribute in sysfs", pp->dev);
 		return 1;
+	}
+	strlcpy(pp->dev_t, attr, BLK_DEV_SIZE);
 
 	condlog(3, "%s: dev_t = %s", pp->dev, pp->dev_t);
 
-	if (sysfs_get_size(sysfs_path, pp->dev, &pp->size))
+	if (sysfs_get_size(dev, &pp->size))
 		return 1;
 
 	condlog(3, "%s: size = %llu", pp->dev, pp->size);
@@ -575,19 +518,53 @@ 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;
+	}
+
+	if (common_sysfs_pathinfo(pp, pp->sysdev))
 		return 1;
 
+	parent = sysfs_device_get_parent(pp->sysdev);
+	if (!parent)
+		parent = pp->sysdev;
+
+	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 (!strncmp(pp->dev,"cciss",5))
+		pp->bus = SYSFS_BUS_CCISS;
+
 	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;
+	} else if (pp->bus == SYSFS_BUS_CCISS) {
+		if (cciss_sysfs_pathinfo(pp, pp->sysdev))
 			return 1;
 	}
 	return 0;
@@ -597,7 +574,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);
 	}
 
@@ -605,16 +582,37 @@ scsi_ioctl_pathinfo (struct path * pp, int mask)
 }
 
 static int
+cciss_ioctl_pathinfo (struct path * pp, int mask)
+{
+	if (mask & DI_SYSFS) {
+		get_inq(pp->vendor_id, pp->product_id, pp->rev, pp->fd);
+		condlog(3, "%s: vendor = %s", pp->dev, pp->vendor_id);
+		condlog(3, "%s: product = %s", pp->dev, pp->product_id);
+		condlog(3, "%s: revision = %s", pp->dev, pp->rev);
+		/*
+		 * set the hwe configlet pointer
+		 */
+		pp->hwe = find_hwe(conf->hwtable, pp->vendor_id,
+				   pp->product_id, pp->rev);
+
+	}
+	return 0;
+}
+
+static int
 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 +634,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);
@@ -689,17 +687,29 @@ pathinfo (struct path *pp, vector hwtable, int mask)
 	if (pp->fd < 0)
 		pp->fd = opennode(pp->dev, O_RDONLY);
 
-	if (pp->fd < 0)
+	if (pp->fd < 0) {
+		condlog(4, "Couldn't open node for %s: %s",
+			pp->dev, strerror(errno));
 		goto blank;
+	}
 
 	if (pp->bus == SYSFS_BUS_SCSI &&
 	    scsi_ioctl_pathinfo(pp, mask))
 		goto blank;
 
+	if (pp->bus == SYSFS_BUS_CCISS &&
+	    cciss_ioctl_pathinfo(pp, mask))
+		goto blank;
+
 	if (mask & DI_CHECKER && get_state(pp))
 		goto blank;
-	
-	if (mask & DI_PRIO && pp->state != PATH_DOWN)
+
+	 /*
+	  * Retrieve path priority, even for PATH_DOWN paths if it has never
+	  * been successfully obtained before.
+	  */
+	if (mask & DI_PRIO &&
+	    (pp->state != PATH_DOWN || 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/hwtable.c b/libmultipath/hwtable.c
index 5c7d625..ef761d7 100644
--- a/libmultipath/hwtable.c
+++ b/libmultipath/hwtable.c
@@ -12,13 +12,34 @@
  * Tuning suggestions on these parameters should go to
  * dm-devel@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@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@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,24 +106,58 @@ 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       = "/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,
+	},
+	{
+		/* HP Smart Array */
+		.vendor        = "HP",
+		.product       = "LOGICAL VOLUME.*",
+		.getuid        = "/lib/udev/scsi_id -n -g -u -s /block/%n",
 		.getprio       = NULL,
 		.features      = DEFAULT_FEATURES,
 		.hwhandler     = DEFAULT_HWHANDLER,
@@ -96,10 +167,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  = TUR,
 	},
 	/*
-	 * DDN controler family
+	 * DDN controller family
 	 *
 	 * Maintainer : Christophe Varoqui
 	 * Mail : christophe.varoqui@free.fr
@@ -117,10 +188,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@emc.com
@@ -128,7 +199,7 @@ static struct hwentry default_hw[] = {
 	{
 		.vendor        = "EMC",
 		.product       = "SYMMETRIX",
-		.getuid        = "/sbin/scsi_id -g -u -ppre-spc3-83 -s /block/%n",
+		.getuid        = "/lib/udev/scsi_id -g -u -ppre-spc3-83 -s /block/%n",
 		.getprio       = NULL,
 		.features      = DEFAULT_FEATURES,
 		.hwhandler     = DEFAULT_HWHANDLER,
@@ -142,7 +213,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 +228,7 @@ static struct hwentry default_hw[] = {
 		.checker_name  = EMC_CLARIION,
 	},
 	/*
-	 * Fujitsu controler family
+	 * Fujitsu controller family
 	 *
 	 * Maintainer : Christophe Varoqui
 	 * Mail : christophe.varoqui@free.fr
@@ -178,17 +249,17 @@ static struct hwentry default_hw[] = {
 		.checker_name  = READSECTOR0,
 	},
 	/*
-	 * Hitachi controler family
+	 * Hitachi controller family
 	 *
-	 * Maintainer : Christophe Varoqui
-	 * Mail : christophe.varoqui@free.fr
+	 * Maintainer : Matthias Rudolph
+	 * Mail : matthias.rudolph@hds.com
 	 */
 	{
-		.vendor        = "HITACHI",
-		.product       = "{A6189A,OPEN-}",
+		.vendor        = "(HITACHI|HP)",
+		.product       = "OPEN-.*",
 		.getuid        = DEFAULT_GETUID,
 		.getprio       = NULL,
-		.features      = DEFAULT_FEATURES,
+		.features      = "1 queue_if_no_path",
 		.hwhandler     = DEFAULT_HWHANDLER,
 		.selector      = DEFAULT_SELECTOR,
 		.pgpolicy      = MULTIBUS,
@@ -196,12 +267,27 @@ 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  = TUR,
+	},
+	{
+		.vendor        = "HITACHI",
+		.product       = "DF.*",
+		.getuid        = DEFAULT_GETUID,
+		.getprio       = "/sbin/mpath_prio_hds_modular /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 controler family
+	 * IBM controller family
 	 *
-	 * Maintainer : Hannes Reinecke, Suse
+	 * Maintainer : Hannes Reinecke, SuSE
 	 * Mail : hare@suse.de
 	 */
 	{
@@ -224,7 +310,23 @@ static struct hwentry default_hw[] = {
 		.vendor        = "IBM",
 		.product       = "1742",
 		.getuid        = DEFAULT_GETUID,
-		.getprio       = "/sbin/mpath_prio_tpc /dev/%n",
+		.getprio       = "/sbin/mpath_prio_rdac /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 Netfinity Fibre Channel RAID Controller Unit */
+		.vendor        = "IBM",
+		.product       = "3526",
+		.getuid        = DEFAULT_GETUID,
+		.getprio       = "/sbin/mpath_prio_rdac /dev/%n",
 		.features      = DEFAULT_FEATURES,
 		.hwhandler     = DEFAULT_HWHANDLER,
 		.selector      = DEFAULT_SELECTOR,
@@ -254,7 +356,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 +370,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 +394,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 +402,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 +434,59 @@ static struct hwentry default_hw[] = {
 		.minio         = DEFAULT_MINIO,
 		.checker_name  = DIRECTIO,
 	},
-	/*
-	 * NETAPP controler family
+ 	/*
+	 * NETAPP controller family
 	 *
-	 * Maintainer : Igor Feoktistov
-	 * Mail : igorf@netapp.com
+	 * Maintainer : Dave Wysochanski
+	 * Mail : davidw@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@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@free.fr
+	 * Maintainer : Srinivasan Ramani
+	 * Mail : sramani@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,
@@ -382,16 +522,31 @@ static struct hwentry default_hw[] = {
 		.vendor        = "SGI",
 		.product       = "TP9[45]00",
 		.getuid        = DEFAULT_GETUID,
-		.getprio       = "/sbin/mpath_prio_tpc /dev/%n",
+		.getprio       = "/sbin/mpath_prio_rdac /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,
+		.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_rdac /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
@@ -403,7 +558,7 @@ static struct hwentry default_hw[] = {
 		.vendor        = "STK",
 		.product       = "OPENstorage D280",
 		.getuid        = DEFAULT_GETUID,
-		.getprio       = "/sbin/mpath_prio_tpc /dev/%n",
+		.getprio       = "/sbin/mpath_prio_rdac /dev/%n",
 		.features      = DEFAULT_FEATURES,
 		.hwhandler     = DEFAULT_HWHANDLER,
 		.selector      = DEFAULT_SELECTOR,
@@ -422,7 +577,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/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_pthread.c b/libmultipath/log_pthread.c
index f98cfa4..5a82b6a 100644
--- a/libmultipath/log_pthread.c
+++ b/libmultipath/log_pthread.c
@@ -12,7 +12,7 @@
 #include "log_pthread.h"
 #include "log.h"
 
-void log_safe (int prio, char * fmt, va_list ap)
+void log_safe (int prio, const char * fmt, va_list ap)
 {
 	pthread_mutex_lock(logq_lock);
 	//va_start(ap, fmt);
diff --git a/libmultipath/log_pthread.h b/libmultipath/log_pthread.h
index 7c902c7..2b18f59 100644
--- a/libmultipath/log_pthread.h
+++ b/libmultipath/log_pthread.h
@@ -7,7 +7,7 @@ pthread_mutex_t *logq_lock;
 pthread_mutex_t *logev_lock;
 pthread_cond_t *logev_cond;
 
-void log_safe(int prio, char * fmt, va_list ap);
+void log_safe(int prio, const char * fmt, va_list ap);
 void log_thread_start(void);
 void log_thread_stop(void);
 
diff --git a/libmultipath/parser.c b/libmultipath/parser.c
index 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.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..01a157a 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)
 {
@@ -211,6 +224,10 @@ static int
 snprint_action (char * buff, size_t len, struct multipath * mpp)
 {
 	switch (mpp->action) {
+	case ACT_REJECT:
+		return snprint_str(buff, len, ACT_REJECT_STR);
+	case ACT_RENAME:
+		return snprint_str(buff, len, ACT_RENAME_STR);
 	case ACT_RELOAD:
 		return snprint_str(buff, len, ACT_RELOAD_STR);
 	case ACT_CREATE:
@@ -222,6 +239,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 +312,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 +516,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 +535,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 +652,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 +683,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 +857,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 +1023,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/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..f821f87 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
@@ -40,6 +46,7 @@ enum sysfs_buses {
 	SYSFS_BUS_SCSI,
 	SYSFS_BUS_IDE,
 	SYSFS_BUS_CCW,
+	SYSFS_BUS_CCISS,
 };
 
 enum pathstates {
@@ -55,6 +62,11 @@ enum pgstates {
 	PGSTATE_ACTIVE
 };
 
+enum pgtimeouts {
+	PGTIMEOUT_UNDEF,
+	PGTIMEOUT_NONE
+};
+
 struct scsi_idlun {
 	int dev_id;
 	int host_unique_id;
@@ -78,9 +90,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 +149,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 +165,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 +174,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 +209,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/sysfs.c b/libmultipath/sysfs.c
new file mode 100644
index 0000000..b9621ac
--- /dev/null
+++ b/libmultipath/sysfs.c
@@ -0,0 +1,460 @@
+/*
+ * Copyright (C) 2005-2006 Kay Sievers <kay.sievers@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];
+};
+
+/* list of sysfs devices */
+static LIST_HEAD(sysfs_dev_list);
+struct sysfs_dev {
+	struct list_head node;
+	struct sysfs_device dev;
+};
+
+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);
+	INIT_LIST_HEAD(&sysfs_dev_list);
+	return 0;
+}
+
+void sysfs_cleanup(void)
+{
+	struct sysfs_attr *attr_loop;
+	struct sysfs_attr *attr_temp;
+
+	struct sysfs_dev *sysdev_loop;
+	struct sysfs_dev *sysdev_temp;
+
+	list_for_each_entry_safe(attr_loop, attr_temp, &attr_list, node) {
+		list_del(&attr_loop->node);
+		free(attr_loop);
+	}
+
+	list_for_each_entry_safe(sysdev_loop, sysdev_temp, &sysfs_dev_list, node) {
+		free(sysdev_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 = NULL;
+	struct sysfs_dev *sysdev_loop, *sysdev;
+	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) {
+		/* if stat fails look in the cache */
+		dbg("stat '%s' failed: %s", path, strerror(errno));
+		list_for_each_entry(sysdev_loop, &sysfs_dev_list, node) {
+			if (strcmp(sysdev_loop->dev.devpath, devpath_real) == 0) {
+				dbg("found vanished dev in cache '%s'", sysdev_loop->dev.devpath);
+				return &sysdev_loop->dev;
+			}
+		}
+		return NULL;
+	}
+	if (S_ISLNK(statbuf.st_mode)) {
+		if (sysfs_resolve_link(devpath_real, sizeof(devpath_real)) != 0)
+			return NULL;
+
+	}
+
+	list_for_each_entry(sysdev_loop, &sysfs_dev_list, node) {
+		if (strcmp(sysdev_loop->dev.devpath, devpath_real) == 0) {
+			dbg("found dev in cache '%s'", sysdev_loop->dev.devpath);
+				dev = &sysdev_loop->dev;
+		}
+	}
+	if(!dev) {
+		/* it is a new device */
+		dbg("new device '%s'", devpath_real);
+		sysdev = malloc(sizeof(struct sysfs_dev));
+		if (sysdev == NULL)
+			return NULL;
+		memset(sysdev, 0x00, sizeof(struct sysfs_dev));
+		list_add(&sysdev->node, &sysfs_dev_list);
+		dev = &sysdev->dev;
+	}
+
+	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;
+}
+
+void sysfs_device_put(struct sysfs_device *dev)
+{
+	struct sysfs_dev *sysdev_loop;
+
+	list_for_each_entry(sysdev_loop, &sysfs_dev_list, node) {
+		if (&sysdev_loop->dev == dev) {
+			dbg("removed dev '%s' from cache",
+			    sysdev_loop->dev.devpath);
+			list_del(&sysdev_loop->node);
+			free(sysdev_loop);
+			return;
+		}
+	}
+	dbg("dev '%s' not found in cache",
+	    sysdev_loop->dev.devpath);
+
+	return;
+}
+
+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..e7fa3e7
--- /dev/null
+++ b/libmultipath/sysfs.h
@@ -0,0 +1,25 @@
+/*
+ * sysfs.h
+ */
+
+#ifndef _LIBMULTIPATH_SYSFS_H
+#define _LIBMULTIPATH_SYSFS_H
+
+#ifdef DEBUG
+# define dbg(format, args...) printf(format "\n", ##args)
+#else
+# define dbg(format, args...) 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);
+void sysfs_device_put(struct sysfs_device *dev);
+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/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/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..3caede6 100644
--- a/multipath-tools.spec.in
+++ b/multipath-tools.spec.in
@@ -49,7 +49,8 @@ rm -rf $RPM_BUILD_ROOT
 %{prefix}/sbin/mpath_prio_random
 %{prefix}/sbin/mpath_prio_balance_units
 %{prefix}/sbin/mpath_prio_netapp
-%{prefix}/sbin/mpath_prio_tpc
+%{prefix}/sbin/mpath_prio_rdac
+%{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..e6cfe9a 100644
--- a/multipath.conf.annotated
+++ b/multipath.conf.annotated
@@ -11,7 +11,7 @@
 #	#
 #	# name    : udev_dir
 #	# desc    : directory where udev creates its device nodes
-#	# default : /udev
+#	# default : /dev
 #	#
 #	udev_dir	/dev
 #
@@ -47,9 +47,9 @@
 #	# scope   : multipath
 #	# desc    : the default program and args to callout to obtain a unique 
 #	#           path identifier. Absolute path required
-#	# default : /sbin/scsi_id -g -u -s
+#	# default : /lib/udev/scsi_id -g -u -s
 #	#
-#	getuid_callout	"/sbin/scsi_id -g -u -s /block/%n"
+#	getuid_callout	"/lib/udev/scsi_id -g -u -s /block/%n"
 #
 #	#
 #	# name    : prio_callout
@@ -66,9 +66,9 @@
 #	# scope   : multipath & multipathd
 #	# desc    : the default method used to determine the paths' state
 #	# values  : readsector0|tur|emc_clariion|hp_sw|directio
-#	# default : readsector0
+#	# default : directio
 #	#
-#	#path_checker	readsector0
+#	#path_checker	directio
 #
 #	#
 #	# name    : rr_min_io
@@ -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
@@ -182,10 +198,10 @@
 #		# name    : path_checker
 #		# scope   : multipathd
 #		# desc    : path checking alorithm to use to check path state
-#		# values  : readsector0, tur
-#		# default : readsector0
+#		# values  : readsector0|tur|emc_clariion|hp_sw|directio
+#		# default : directio
 #		#
-#		# path_checker		readsector0
+#		# path_checker		directio
 #		
 #		#
 #		# name    : path_selector
@@ -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
@@ -275,9 +291,9 @@
 #		# scope   : multipath
 #		# desc    : the program and args to callout to obtain a unique 
 #		#           path identifier. Absolute path required
-#		# default : /sbin/scsi_id -g -u -s
+#		# default : /lib/udev/scsi_id -g -u -s
 #		#
-#		getuid_callout          "/sbin/scsi_id -g -u -s /block/%n"
+#		getuid_callout          "/lib/udev/scsi_id -g -u -s /block/%n"
 #
 #		#
 #		# name    : prio_callout
@@ -294,10 +310,10 @@
 #		# name    : path_checker
 #		# scope   : multipathd
 #		# desc    : path checking alorithm to use to check path state
-#		# values  : readsector0, tur
-#		# default : readsector0
+#		# values  : readsector0|tur|emc_clariion|hp_sw|directio
+#		# default : directio
 #		#
-#		path_checker		readsector0
+#		path_checker		directio
 #
 #		#
 #		# name    : path_selector
diff --git a/multipath.conf.synthetic b/multipath.conf.synthetic
index 4a1f5a4..633d625 100644
--- a/multipath.conf.synthetic
+++ b/multipath.conf.synthetic
@@ -7,16 +7,16 @@
 #	polling_interval 	10
 #	selector		"round-robin 0"
 #	path_grouping_policy	multibus
-#	getuid_callout		"/sbin/scsi_id -g -u -s /block/%n"
+#	getuid_callout		"/lib/udev/scsi_id -g -u -s /block/%n"
 #	prio_callout		/bin/true
-#	path_checker		readsector0
+#	path_checker		directio
 #	rr_min_io		100
 #	rr_weight		priorities
 #	failback		immediate
 #	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,12 +26,16 @@
 #		product MSA[15]00
 #	}
 #}
+#blacklist_exceptions {
+#       devnode "^dasd[c-d]+[0-9]*"
+#       wwid    "IBM.75000000092461.4d00.34"
+#}
 #multipaths {
 #	multipath {
 #		wwid			3600508b4000156d700012000000b0000
 #		alias			yellow
 #		path_grouping_policy	multibus
-#		path_checker		readsector0
+#		path_checker		directio
 #		path_selector		"round-robin 0"
 #		failback		manual
 #		rr_weight		priorities
@@ -48,8 +52,8 @@
 #		vendor			"COMPAQ  "
 #		product			"HSV110 (C)COMPAQ"
 #		path_grouping_policy	multibus
-#		getuid_callout          "/sbin/scsi_id -g -u -s /block/%n"
-#		path_checker		readsector0
+#		getuid_callout          "/lib/udev/scsi_id -g -u -s /block/%n"
+#		path_checker		directio
 #		path_selector		"round-robin 0"
 #		hardware_handler	"0"
 #		failback		15
diff --git a/multipath/02_multipath b/multipath/02_multipath
index 1a5d5a1..067c582 100755
--- a/multipath/02_multipath
+++ b/multipath/02_multipath
@@ -12,10 +12,10 @@ cp /sbin/kpartx $INITRDDIR/sbin
 # feed the dependencies too
 # scsi_id is dynamicaly linked, so store the libs too
 #
-cp /sbin/scsi_id $INITRDDIR/sbin
+cp /lib/udev/scsi_id $INITRDDIR/lib/udev/
 cp /bin/mountpoint $INITRDDIR/bin
 
-PROGS="/sbin/scsi_id /bin/mountpoint"
+PROGS="/lib/udev/scsi_id /bin/mountpoint"
 LIBS=`ldd $PROGS | grep -v linux-gate.so | sort -u | \
 awk '{print $3}'` 
 for i in $LIBS
diff --git a/multipath/Makefile b/multipath/Makefile
index 646dfc2..4923b2f 100644
--- a/multipath/Makefile
+++ b/multipath/Makefile
@@ -8,11 +8,12 @@ include ../Makefile.inc
 OBJS = main.o $(MULTIPATHLIB)-$(BUILD).a $(CHECKERSLIB)-$(BUILD).a
 
 CFLAGS += -I$(multipathdir) -I$(checkersdir)
+LDFLAGS += -laio
 
 ifeq ($(strip $(BUILD)),klibc)
-	OBJS += $(libdm) $(libsysfs)
+	OBJS += $(libdm)
 else
-	LDFLAGS += -ldevmapper -lsysfs
+	LDFLAGS += -ldevmapper
 endif
 
 EXEC = multipath
@@ -22,14 +23,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)
@@ -39,16 +40,19 @@ $(MULTIPATHLIB)-$(BUILD).a:
 
 install:
 	install -d $(DESTDIR)$(bindir)
-	install -s -m 755 $(EXEC) $(DESTDIR)$(bindir)/
+	$(INSTALL_PROGRAM) -m 755 $(EXEC) $(DESTDIR)$(bindir)/
 	install -d $(DESTDIR)/etc/udev/rules.d
 	install -m 644 multipath.rules $(DESTDIR)/etc/udev/rules.d/
 	install -d $(DESTDIR)$(mandir)
 	install -m 644 $(EXEC).8.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/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..c8ab6b0
--- /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 /lib/udev/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_rdac /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@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..b430b94 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 -laio
 
 #
 # debuging stuff
@@ -45,7 +45,7 @@ $(MULTIPATHLIB)-glibc.a:
 
 install:
 	install -d $(DESTDIR)$(bindir)
-	install -s -m 755 $(EXEC) $(DESTDIR)$(bindir)
+	$(INSTALL_PROGRAM) -m 755 $(EXEC) $(DESTDIR)$(bindir)
 	install -d $(DESTDIR)$(rcdir)
 	install -d $(DESTDIR)$(mandir)
 	install -m 644 $(EXEC).8.gz $(DESTDIR)$(mandir)
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..da5fd8f 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
- */
-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
+ * global copy of vecs for use in sig handlers
  */
-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;
+	struct path *pp;
+	unsigned int i, j;
 
 	vector_foreach_slot (mpp->pg, pgp, i){
 		vector_foreach_slot (pgp->paths, pp, j){
@@ -502,10 +169,10 @@ 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) 
+	vector_foreach_slot (mpvec, mpp, i)
 		sync_map_state(mpp);
 }
 
@@ -536,52 +203,50 @@ 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;
-	
+
 	map_present = dm_map_present(alias);
 
 	if (map_present && dm_type(alias, DEFAULT_TARGET) <= 0) {
 		condlog(4, "%s: not a multipath map", alias);
-		FREE(alias);
 		return 0;
 	}
 
 	mpp = find_mp_by_alias(vecs->mpvec, alias);
 
 	if (mpp) {
-                /*
+		/*
 		 * Not really an error -- we generate our own uevent
 		 * if we create a multipath mapped device as a result
 		 * of uev_add_path
 		 */
 		condlog(0, "%s: devmap already registered",
-			devname);
-		FREE(alias);
+			dev->kernel);
 		return 0;
 	}
 
@@ -591,31 +256,30 @@ 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);
 		dm_lib_release();
 	}
-	
+
 	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;
@@ -655,12 +319,12 @@ uev_umount_map (char * devname, struct vectors * vecs)
 
 	return 0;
 }
-	
+
 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,13 +368,13 @@ 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);
 		free_path(pp);
 		return 2;
-	}	
+	}
 	mpp = pp->mpp = find_mp_by_wwid(vecs->mpvec, pp->wwid);
 rescan:
 	if (mpp) {
@@ -742,8 +406,8 @@ rescan:
 		condlog(0, "%s: failed in domap for addition of new "
 			"path %s", mpp->alias, devname);
 		/*
- 		 * deal with asynchronous uevents :((
- 		 */
+		 * deal with asynchronous uevents :((
+		 */
 		if (mpp->action == ACT_RELOAD) {
 			condlog(0, "%s: uev_add_path sleep", mpp->alias);
 			sleep(1);
@@ -776,10 +440,16 @@ 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);
+	int retval;
+
+	condlog(2, "%s: remove path (uevent)", dev->kernel);
+	retval = ev_remove_path(dev->kernel, vecs);
+	if (!retval)
+		sysfs_device_put(dev);
+
+	return retval;
 }
 
 int
@@ -809,9 +479,9 @@ ev_remove_path (char * devname, struct vectors * vecs)
 			vector rpvec = vector_alloc();
 
 			/*
-	 	 	 * transform the mp->pg vector of vectors of paths
-	 	 	 * into a mp->params string to feed the device-mapper
-	 	 	 */
+			 * transform the mp->pg vector of vectors of paths
+			 * into a mp->params string to feed the device-mapper
+			 */
 			update_mpp_paths(mpp, vecs->pathvec);
 			if ((i = find_slot(mpp->paths, (void *)pp)) != -1)
 				vector_del_slot(mpp->paths, i);
@@ -837,8 +507,8 @@ ev_remove_path (char * devname, struct vectors * vecs)
 					goto out;
 				}
 				/*
-	 	 	 	 * reload the map
-	 	 	 	 */
+				 * reload the map
+				 */
 				mpp->action = ACT_RELOAD;
 				if (domap(mpp) <= 0) {
 					condlog(0, "%s: failed in domap for "
@@ -863,7 +533,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 +568,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;
@@ -916,7 +586,7 @@ uxsock_trigger (char * str, char ** reply, int * len, void * trigger_data)
 {
 	struct vectors * vecs;
 	int r;
-	
+
 	*reply = NULL;
 	*len = 0;
 	vecs = (struct vectors *)trigger_data;
@@ -958,11 +628,11 @@ uev_discard(char * devpath)
 	return 0;
 }
 
-int 
+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,40 +640,46 @@ 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);
+	if(!sysdev)
+		return 0;
+
 	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;
 	}
-	
+
 	/*
 	 * 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;
 	}
 
@@ -1017,37 +693,36 @@ ueventloop (void * ap)
 {
 	if (uevent_listen(&uev_trigger, ap))
 		fprintf(stderr, "error starting uevent listener");
-		
+
 	return NULL;
 }
 
 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);
 
@@ -1113,12 +788,12 @@ enable_group(struct path * pp)
 	 *
 	 * we can safely return here, because upon map reload, all
 	 * PG will be enabled.
- 	 */
+	 */
 	if (!pp->mpp->pg || !pp->pgindex)
 		return;
 
 	pgp = VECTOR_SLOT(pp->mpp->pg, pp->pgindex - 1);
-	
+
 	if (pgp->status == PGSTATE_DISABLED) {
 		condlog(2, "%s: enable group #%i", pp->mpp->alias, pp->pgindex);
 		dm_enablegroup(pp->mpp->alias, pp->pgindex);
@@ -1129,7 +804,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 +819,7 @@ static void
 defered_failback_tick (vector mpvec)
 {
 	struct multipath * mpp;
-	int i;
+	unsigned int i;
 
 	vector_foreach_slot (mpvec, mpp, i) {
 		/*
@@ -1163,7 +838,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 +857,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;
@@ -1213,24 +889,36 @@ checkerloop (void *ap)
 			 * in case we exit abnormaly from here
 			 */
 			pp->tick = conf->checkint;
-			
+
 			if (!checker_selected(&pp->checker)) {
 				pathinfo(pp, conf->hwtable, DI_SYSFS);
 				select_checker(pp);
 			}
-
 			if (!checker_selected(&pp->checker)) {
 				condlog(0, "%s: checker is not set", pp->dev);
 				continue;
 			}
+			/*
+			 * Set checker in async mode.
+			 * Honored only by checker implementing the said mode.
+			 */
+			checker_set_async(&pp->checker);
+
 			newstate = checker_check(&pp->checker);
-			
+
 			if (newstate < 0) {
 				condlog(2, "%s: unusable path", pp->dev);
 				pathinfo(pp, conf->hwtable, 0);
 				continue;
 			}
-
+			/*
+			 * Async IO in flight. Keep the previous path state
+			 * and reschedule as soon as possible
+			 */
+			if (newstate == PATH_PENDING) {
+				pp->tick = 1;
+				continue;
+			}
 			if (newstate != pp->state) {
 				int oldstate = pp->state;
 				pp->state = newstate;
@@ -1245,7 +933,7 @@ checkerloop (void *ap)
 				if (newstate == PATH_DOWN ||
 				    newstate == PATH_SHAKY ||
 				    update_multipath_strings(pp->mpp,
-					    		     vecs->pathvec)) {
+							     vecs->pathvec)) {
 					/*
 					 * proactively fail path in the DM
 					 */
@@ -1336,7 +1024,7 @@ checkerloop (void *ap)
 			mpvec_garbage_collector(vecs);
 			count = MAPGCINT;
 		}
-		
+
 		lock_cleanup_pop(vecs->lock);
 		sleep(1);
 	}
@@ -1351,12 +1039,12 @@ 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()))
 		return 1;
 
@@ -1366,11 +1054,11 @@ 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--;
-		}	
+		}
 		else
 			pp->checkint = conf->checkint;
 	}
@@ -1394,10 +1082,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 +1090,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 +1120,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))
@@ -1461,30 +1147,16 @@ init_vecs (void)
 	if (!vecs)
 		return NULL;
 
-	vecs->lock = 
+	vecs->lock =
 		(pthread_mutex_t *)MALLOC(sizeof(pthread_mutex_t));
 
 	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,21 +1215,21 @@ 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
 setscheduler (void)
 {
-        int res;
+	int res;
 	static struct sched_param sched_param = {
-		sched_priority: 99
+		.sched_priority = 99
 	};
 
-        res = sched_setscheduler (0, SCHED_RR, &sched_param);
+	res = sched_setscheduler (0, SCHED_RR, &sched_param);
 
-        if (res == -1)
-                condlog(LOG_WARNING, "Could not set SCHED_RR at priority 99");
+	if (res == -1)
+		condlog(LOG_WARNING, "Could not set SCHED_RR at priority 99");
 	return;
 }
 
@@ -1574,7 +1246,7 @@ set_oom_adj (int val)
 	fprintf(fp, "%i", val);
 	fclose(fp);
 }
-	
+
 static int
 child (void * param)
 {
@@ -1617,7 +1289,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);
 	}
@@ -1636,7 +1308,7 @@ child (void * param)
 	pthread_attr_init(&attr);
 	pthread_attr_setstacksize(&attr, 64 * 1024);
 	pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
-	
+
 	pthread_create(&check_thr, &attr, checkerloop, vecs);
 	pthread_create(&uevent_thr, &attr, ueventloop, vecs);
 	pthread_create(&uxlsnr_thr, &attr, uxlsnrloop, vecs);
@@ -1654,6 +1326,8 @@ child (void * param)
 	pthread_cancel(uevent_thr);
 	pthread_cancel(uxlsnr_thr);
 
+	sysfs_cleanup();
+
 	free_keys(keys);
 	keys = NULL;
 	free_handlers(handlers);
@@ -1670,7 +1344,7 @@ child (void * param)
 	conf = NULL;
 
 	condlog(2, "--------shut down-------");
-	
+
 	if (logsink)
 		log_thread_stop();
 
@@ -1738,8 +1412,9 @@ main (int argc, char *argv[])
 	extern int optind;
 	int arg;
 	int err;
-	
+
 	logsink = 1;
+	dm_init();
 
 	if (getuid() != 0) {
 		fprintf(stderr, "need to be root\n");
@@ -1780,7 +1455,7 @@ main (int argc, char *argv[])
 		err = 0;
 	else
 		err = daemonize();
-	
+
 	if (err < 0)
 		/* error */
 		exit(1);
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@free.fr> and others.
+.B multipathd
+was developed by Christophe Varoqui, <christophe.varoqui@free.fr> and others.
diff --git a/multipathd/uxclnt.c b/multipathd/uxclnt.c
index ff7b578..009e5cb 100644
--- a/multipathd/uxclnt.c
+++ b/multipathd/uxclnt.c
@@ -21,6 +21,9 @@
 #include <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/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/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
index bed7fb0..cb1e6c6 100644
--- a/path_priority/pp_balance_units/Makefile
+++ b/path_priority/pp_balance_units/Makefile
@@ -35,7 +35,7 @@ $(MULTIPATHLIB)-$(BUILD).a:
 
 install:
 	install -d $(DESTDIR)$(bindir)
-	install -s -m 755 $(EXEC) $(DESTDIR)$(bindir)/
+	$(INSTALL_PROGRAM) -m 755 $(EXEC) $(DESTDIR)$(bindir)/
 
 uninstall:
 	rm $(DESTDIR)$(bindir)/$(EXEC)
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
index 651bdcd..93e6075 100644
--- a/path_priority/pp_emc/Makefile
+++ b/path_priority/pp_emc/Makefile
@@ -14,7 +14,7 @@ klibc:	$(OBJS)
 	$(CC) -static -o $(EXEC) $(OBJS)
 
 install: $(EXEC)
-	install -s -m 755 $(EXEC) $(DESTDIR)$(bindir)/$(EXEC)
+	$(INSTALL_PROGRAM) -m 755 $(EXEC) $(DESTDIR)$(bindir)/$(EXEC)
 
 uninstall:
 	rm $(DESTDIR)$(bindir)/$(EXEC)
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..ca00ca7
--- /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_PROGRAM) -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..7411508
--- /dev/null
+++ b/path_priority/pp_hds_modular/pp_hds_modular.c
@@ -0,0 +1,211 @@
+/*
+ * (C) Copyright HDS GmbH 2006. All Rights Reserved.
+ *
+ * pp_hds_modular.c
+ * Version 2.00
+ *
+ * Prioritizer for Device Mapper Multipath 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 command 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
+ *
+ * To compile this source please execute # cc pp_hds_modular.c -o /sbin/mpath_prio_hds_modular
+ *
+ * Changes 2006-07-16:
+ *         - Changed to forward declaration of functions
+ *         - The switch-statement was changed to a logical expression
+ *         - unlinking of the devpath now also occurs at the end of 
+ *           hds_modular_prio to avoid old /tmp/.pp_balance.%u.%u.devnode
+ *           entries in /tmp-Directory
+ *         - The for-statements for passing variables where changed to
+ *           snprintf-commands in verbose mode
+ * Changes 2006-08-10:
+ *         - Back to the old switch statements because the regular expression does
+ *           not work under RHEL4 U3 i386
+ * Changes 2007-06-27:
+ *	- switched from major:minor argument to device node argument
+ *
+ * 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>
+
+#define INQ_REPLY_LEN 255
+#define INQ_CMD_CODE 0x12
+#define INQ_CMD_LEN 6
+#define FILE_NAME_SIZE 255
+
+int verbose=0;
+
+void print_help (void);
+int hds_modular_prio (const char *);
+
+int main (int argc, char **argv)
+{
+	int prio;
+	if (argc == 2)
+	{
+		if (strcmp (argv[1], "-h") == 0)
+		{
+			print_help ();
+			exit (0);
+		}
+		else
+		{
+			verbose = 0;
+			prio = hds_modular_prio (argv[1]);
+			printf ("%d\n", prio);
+			exit (0);
+		}
+	}
+	if ((argc == 3) && (strcmp (argv[1], "-v")) == 0)
+	{
+		verbose = 1;
+		prio = hds_modular_prio (argv[2]);
+		printf ("%d\n", prio);
+		exit (0);
+	}
+	print_help ();
+	exit (1);
+}
+
+int hds_modular_prio (const char *dev)
+{
+	int sg_fd, k;
+	char vendor[8];
+	char product[32];
+	char serial[32];
+	char ldev[32];
+	char ctrl[32];
+	char port[32];
+	unsigned char inqCmdBlk[INQ_CMD_LEN] = { INQ_CMD_CODE, 0, 0, 0, INQ_REPLY_LEN, 0 };
+	unsigned char inqBuff[INQ_REPLY_LEN];
+	unsigned char *inqBuffp = inqBuff;
+	unsigned char sense_buffer[32];
+	sg_io_hdr_t io_hdr;
+
+	if ((sg_fd = open (dev, 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);
+
+	snprintf (vendor, 9, "%.8s\n", inqBuffp + 8);
+	snprintf (product, 17, "%.16s", inqBuffp + 16);
+	snprintf (serial, 5, "%.4s", inqBuffp + 40);
+	snprintf (ldev, 5, "%.4s", inqBuffp + 44);
+	snprintf (ctrl, 2, "%.1s", inqBuffp + 49);
+	snprintf (port, 2, "%.1s", inqBuffp + 50);
+
+	close (sg_fd);
+
+	if (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;
+		}
+	}
+	return 0;
+}
+
+void print_help (void)
+{
+	printf ("\n");
+	printf ("Usage:       pp_hds_modular [-v] <device>\n");
+	printf ("Option:      -v verbose mode\n");
+	printf ("Description: Prioritizer for Device Mapper Multipath and HDS Storage\n");
+	printf ("Version:     2.00\n");
+	printf ("Author:      Matthias Rudolph <matthias.rudolph@hds.com>\n");
+	printf ("Remarks:     Prioritizer CTRL#1 corresponds to hardware CTRL#0\n");
+	printf ("             Prioritizer CTRL#2 corresponds to hardware CTRL#1\n");
+	printf ("\n");
+	return;
+}
+
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
index 9e7d3a3..b29d002 100644
--- a/path_priority/pp_netapp/Makefile
+++ b/path_priority/pp_netapp/Makefile
@@ -14,7 +14,7 @@ klibc:	$(OBJS)
 	$(CC) -static -o $(EXEC) $(OBJS)
 
 install: $(EXEC)
-	install -s -m 755 $(EXEC) $(DESTDIR)$(bindir)/$(EXEC)
+	$(INSTALL_PROGRAM) -m 755 $(EXEC) $(DESTDIR)$(bindir)/$(EXEC)
 
 uninstall:
 	rm $(DESTDIR)$(bindir)/$(EXEC)
diff --git a/path_priority/pp_random/Makefile b/path_priority/pp_random/Makefile
index 85f42a2..85d7c2f 100644
--- a/path_priority/pp_random/Makefile
+++ b/path_priority/pp_random/Makefile
@@ -14,7 +14,7 @@ klibc:	$(OBJS)
 	$(CC) -static -o $(EXEC) $(OBJS)
 
 install: $(EXEC)
-	install -s -m 755 $(EXEC) $(DESTDIR)$(bindir)/$(EXEC)
+	$(INSTALL_PROGRAM) -m 755 $(EXEC) $(DESTDIR)$(bindir)/$(EXEC)
 
 uninstall:
 	rm $(DESTDIR)$(bindir)/$(EXEC)
diff --git a/path_priority/pp_rdac/Makefile b/path_priority/pp_rdac/Makefile
new file mode 100644
index 0000000..64ed4c3
--- /dev/null
+++ b/path_priority/pp_rdac/Makefile
@@ -0,0 +1,22 @@
+EXEC		= mpath_prio_rdac
+BUILD		= glibc
+OBJS		= pp_rdac.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_PROGRAM) -m 755 $(EXEC) $(DESTDIR)$(bindir)/$(EXEC)
+
+uninstall:
+	rm $(DESTDIR)$(bindir)/$(EXEC)
+clean:	
+	rm -f *.o $(EXEC)
diff --git a/path_priority/pp_rdac/pp_rdac.c b/path_priority/pp_rdac/pp_rdac.c
new file mode 100644
index 0000000..49a13cf
--- /dev/null
+++ b/path_priority/pp_rdac/pp_rdac.c
@@ -0,0 +1,112 @@
+#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 "../../libmultipath/sg_include.h"
+
+#define INQUIRY_CMD     0x12
+#define INQUIRY_CMDLEN  6
+
+int rdac_prio(const char *dev)
+{
+	unsigned char sense_buffer[256];
+	unsigned char sb[128];
+	unsigned char inqCmdBlk[INQUIRY_CMDLEN] = {INQUIRY_CMD, 1, 0xC9, 0,
+						sizeof(sb), 0};
+	struct sg_io_hdr io_hdr;
+	int ret = 0;
+	int fd;
+
+	fd = open(dev, O_RDWR|O_NONBLOCK);
+
+	if (fd <= 0) {
+		fprintf(stderr, "opening of 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 (inqCmdBlk);
+	io_hdr.mx_sb_len = sizeof (sb);
+	io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
+	io_hdr.dxfer_len = sizeof (sense_buffer);
+	io_hdr.dxferp = sense_buffer;
+	io_hdr.cmdp = inqCmdBlk;
+	io_hdr.sbp = sb;
+	io_hdr.timeout = 60000;
+	io_hdr.pack_id = 0;
+	if (ioctl(fd, SG_IO, &io_hdr) < 0) {
+		fprintf(stderr, "sending inquiry command failed\n");
+		goto out;
+	}
+	if (io_hdr.info & SG_INFO_OK_MASK) {
+		fprintf(stderr, "inquiry command indicates error");
+		goto out;
+	}
+
+	close(fd);
+	
+	if (/* Verify the code page - right page & page identifier */
+	    sense_buffer[1] != 0xc9 || 
+	    sense_buffer[3] != 0x2c ||
+	    sense_buffer[4] != 'v' ||
+	    sense_buffer[5] != 'a' ||
+	    sense_buffer[6] != 'c' ) {
+		fprintf(stderr, "Volume access control page in unknown format");
+		goto out;
+	}
+	
+	if ( /* Current Volume Path Bit */
+		( sense_buffer[8] & 0x01) == 0x01 ) {
+		/* 
+		 * This volume was owned by the controller receiving
+		 * the inquiry command.
+		 */
+		ret |= 0x01;
+	}
+
+	/* Volume Preferred Path Priority */
+	switch ( sense_buffer[9] & 0x0F ) {
+	case 0x01:
+		/* 
+		 * Access to this volume is most preferred through
+		 * this path and other paths with this value.
+		 */
+		ret |= 0x02;
+		break;
+	case 0x02:
+		/*
+		 * Access to this volume through this path is to be used
+		 * as a secondary path. Typically this path would be used
+		 * for fail-over situations.
+		 */
+		/* Fallthrough */
+	default:
+		/* Reserved values */
+		break;
+	}
+	
+out:
+	return(ret);
+}
+
+int
+main (int argc, char **argv)
+{
+	int prio;
+	if (argc != 2) {
+		fprintf(stderr, "Wrong number of arguments.\n");
+		fprintf(stderr, "Usage: %s device\n", argv[0]);
+		prio = 0;
+	} else
+		prio = rdac_prio(argv[1]);
+
+	printf("%d\n", prio);
+	exit(0);
+}
diff --git a/path_priority/pp_tpc/Makefile b/path_priority/pp_tpc/Makefile
deleted file mode 100644
index 86841dd..0000000
--- a/path_priority/pp_tpc/Makefile
+++ /dev/null
@@ -1,22 +0,0 @@
-EXEC		= mpath_prio_tpc
-BUILD		= glibc
-OBJS		= pp_tpc.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_tpc/pp_tpc.c b/path_priority/pp_tpc/pp_tpc.c
deleted file mode 100644
index 76e7c47..0000000
--- a/path_priority/pp_tpc/pp_tpc.c
+++ /dev/null
@@ -1,118 +0,0 @@
-#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 "../../libmultipath/sg_include.h"
-
-#define INQUIRY_CMD     0x12
-#define INQUIRY_CMDLEN  6
-
-int sgi_tpc_prio(const char *dev)
-{
-	unsigned char sense_buffer[256];
-	unsigned char sb[128];
-	unsigned char inqCmdBlk[INQUIRY_CMDLEN] = {INQUIRY_CMD, 1, 0xC9, 0,
-						sizeof(sb), 0};
-	struct sg_io_hdr io_hdr;
-	int ret = 0;
-	int fd;
-
-	fd = open(dev, O_RDWR|O_NONBLOCK);
-
-	if (fd <= 0) {
-		fprintf(stderr, "opening of 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 (inqCmdBlk);
-	io_hdr.mx_sb_len = sizeof (sb);
-	io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
-	io_hdr.dxfer_len = sizeof (sense_buffer);
-	io_hdr.dxferp = sense_buffer;
-	io_hdr.cmdp = inqCmdBlk;
-	io_hdr.sbp = sb;
-	io_hdr.timeout = 60000;
-	io_hdr.pack_id = 0;
-	if (ioctl(fd, SG_IO, &io_hdr) < 0) {
-		fprintf(stderr, "sending inquiry command failed\n");
-		goto out;
-	}
-	if (io_hdr.info & SG_INFO_OK_MASK) {
-		fprintf(stderr, "inquiry command indicates error");
-		goto out;
-	}
-
-	close(fd);
-	
-	if (/* Verify the code page - right page & page identifier */
-	    sense_buffer[1] != 0xc9 || 
-	    sense_buffer[3] != 0x2c ||
-	    sense_buffer[4] != 'v' ||
-	    sense_buffer[5] != 'a' ||
-	    sense_buffer[6] != 'c' ) {
-		fprintf(stderr, "Volume access control page in unknown format");
-		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;
-	}
-
-	/* Volume Preferred Path Priority */
-	switch ( sense_buffer[9] & 0x0F ) {
-	case 0x01:
-		/* 
-		 * Access to this volume is most preferred through
-		 * this path and other paths with this value.
-		 */
-		ret |= 0x04;
-		break;
-	case 0x02:
-		/*
-		 * Access to this volume through this path is to be used
-		 * as a secondary path. Typically this path would be used
-		 * for fail-over situations.
-		 */
-		ret |= 0x01;
-		break;
-	default:
-		/* Reserved values */
-		break;
-	}
-	
-out:
-	return(ret);
-}
-
-int
-main (int argc, char **argv)
-{
-	int prio;
-	if (argc != 2) {
-		fprintf(stderr, "Wrong number of arguments.\n");
-		fprintf(stderr, "Usage: %s device\n", argv[0]);
-		prio = 0;
-	} else
-		prio = sgi_tpc_prio(argv[1]);
-
-	printf("%d\n", prio);
-	exit(0);
-}
openSUSE Build Service is sponsored by