forked from pool/multipath-tools
11669 lines
303 KiB
Diff
11669 lines
303 KiB
Diff
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);
|
|
-}
|