diff --git a/Makefile b/Makefile index 83ae2fe..a099daf 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,7 @@ # Makefile # # Copyright (C) 2003 Christophe Varoqui, - -BUILD = glibc +# # # Try to supply the linux kernel headers. @@ -20,9 +19,20 @@ endif export KRNLSRC export KRNLOBJ -BUILDDIRS = $(shell find . -mindepth 2 -name Makefile -exec dirname {} \; | grep -v ^lib) +BUILDDIRS = \ + libmultipath \ + libmultipath/prioritizers \ + libmultipath/checkers \ + multipath \ + multipathd \ + devmap_name \ + kpartx +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..622f557 100644 --- a/Makefile.inc +++ b/Makefile.inc @@ -13,28 +13,22 @@ ifeq ($(TOPDIR),) TOPDIR = .. endif -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 -checkersdir = $(TOPDIR)/libcheckers +libudevdir = ${prefix}/lib/udev multipathdir = $(TOPDIR)/libmultipath mandir = $(prefix)/usr/share/man/man8 +man5dir = $(prefix)/usr/share/man/man5 rcdir = $(prefix)/etc/init.d +libdir = $(prefix)/lib/multipath GZIP = /bin/gzip -9 -c +INSTALL_PROGRAM = install -CHECKERSLIB = $(checkersdir)/libcheckers -MULTIPATHLIB = $(multipathdir)/libmultipath - -OPTFLAGS = -pipe -g -Wall -Wunused -Wstrict-prototypes +OPTFLAGS = -pipe -g -Wall -Wunused -Wstrict-prototypes -fPIC CFLAGS = $(OPTFLAGS) +SHARED_FLAGS = -shared %.o: %.c $(CC) $(CFLAGS) -c -o $@ $< diff --git a/devmap_name/Makefile b/devmap_name/Makefile index 380c85b..a167537 100644 --- a/devmap_name/Makefile +++ b/devmap_name/Makefile @@ -1,38 +1,25 @@ # Makefile # # Copyright (C) 2003 Christophe Varoqui, -BUILD = glibc - +# include ../Makefile.inc -OBJS = devmap_name.o - -ifeq ($(strip $(BUILD)),klibc) - OBJS += $(libdm) -else - LDFLAGS = -ldevmapper -endif - EXEC = devmap_name +OBJS = devmap_name.o -all: $(BUILD) +LDFLAGS = -ldevmapper -prepare: - rm -f core *.o *.gz +all: $(EXEC) -glibc: prepare $(OBJS) +$(EXEC): $(OBJS) $(CC) $(OBJS) -o $(EXEC) $(LDFLAGS) $(GZIP) $(EXEC).8 > $(EXEC).8.gz -klibc: prepare $(OBJS) - $(CC) -static -o $(EXEC) $(OBJS) - $(GZIP) $(EXEC).8 > $(EXEC).8.gz - -install: - install -d $(DESTDIR)$(bindir) - install -s -m 755 $(EXEC) $(DESTDIR)$(bindir)/ - install -d $(DESTDIR)$(mandir) - install -m 644 $(EXEC).8.gz $(DESTDIR)$(mandir) +install: $(EXEC) $(EXEC).8 + $(INSTALL_PROGRAM) -d $(DESTDIR)$(bindir) + $(INSTALL_PROGRAM) -m 755 $(EXEC) $(DESTDIR)$(bindir)/ + $(INSTALL_PROGRAM) -d $(DESTDIR)$(mandir) + $(INSTALL_PROGRAM) -m 644 $(EXEC).8.gz $(DESTDIR)$(mandir) uninstall: rm $(DESTDIR)$(bindir)/$(EXEC) 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..21e4ad4 100644 --- a/kpartx/Makefile +++ b/kpartx/Makefile @@ -2,45 +2,30 @@ # # Copyright (C) 2003 Christophe Varoqui, # -BUILD=glibc - include ../Makefile.inc -CFLAGS += -I. -D_LARGEFILE64_SOURCE - -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 \ - $(MULTIPATHLIB)-$(BUILD).a $(libdm) -else - LDFLAGS = -ldevmapper - OBJS = bsd.o dos.o kpartx.o solaris.o unixware.o dasd.o \ - gpt.o mac.o crc32.o lopart.o xstrncpy.o devmapper.o -endif +CFLAGS += -I. -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 +LDFLAGS = -ldevmapper +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 EXEC = kpartx -all: $(BUILD) - -prepare: - rm -f core *.o *.gz +all: $(EXEC) -glibc: prepare $(OBJS) +$(EXEC): $(OBJS) $(CC) $(OBJS) -o $(EXEC) $(LDFLAGS) $(GZIP) $(EXEC).8 > $(EXEC).8.gz -klibc: prepare $(OBJS) - $(CC) -static -o $(EXEC) $(CRT0) $(OBJS) $(KLIBC) $(LIBGCC) - $(GZIP) $(EXEC).8 > $(EXEC).8.gz - -$(MULTIPATHLIB)-$(BUILD).a: - make -C $(multipathdir) BUILD=$(BUILD) - -install: - install -d $(DESTDIR)$(bindir) - install -s -m 755 $(EXEC) $(DESTDIR)$(bindir) - install -d $(DESTDIR)$(mandir) - install -m 644 $(EXEC).8.gz $(DESTDIR)$(mandir) +install: $(EXEC) $(EXEC).8 + $(INSTALL_PROGRAM) -d $(DESTDIR)$(bindir) + $(INSTALL_PROGRAM) -m 755 $(EXEC) $(DESTDIR)$(bindir) + $(INSTALL_PROGRAM) -d $(DESTDIR)$(libudevdir) + $(INSTALL_PROGRAM) -m 755 kpartx_id $(DESTDIR)$(libudevdir) + $(INSTALL_PROGRAM) -d $(DESTDIR)/etc/udev/rules.d + $(INSTALL_PROGRAM) -m 644 kpartx.rules $(DESTDIR)/etc/udev/rules.d/ + $(INSTALL_PROGRAM) -d $(DESTDIR)$(mandir) + $(INSTALL_PROGRAM) -m 644 $(EXEC).8.gz $(DESTDIR)$(mandir) uninstall: rm -f $(DESTDIR)$(bindir)/$(EXEC) 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..893d6dd 100644 --- a/kpartx/devmapper.c +++ b/kpartx/devmapper.c @@ -4,9 +4,15 @@ #include #include #include +#include #include #include #include +#include +#include "devmapper.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 +74,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, uint64_t size, const char *uuid, int part) { int r = 0; struct dm_task *dmt; + char *prefixed_uuid = NULL; if (!(dmt = dm_task_create (task))) return 0; @@ -81,12 +88,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 +198,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..2bd27d2 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 *, uint64_t, + const 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/gpt.c b/kpartx/gpt.c index dc846ca..047a829 100644 --- a/kpartx/gpt.c +++ b/kpartx/gpt.c @@ -36,6 +36,7 @@ #include #include #include +#include #include "crc32.h" #if BYTE_ORDER == LITTLE_ENDIAN @@ -50,10 +51,18 @@ # define __cpu_to_le32(x) bswap_32(x) #endif +#ifndef BLKGETLASTSECT #define BLKGETLASTSECT _IO(0x12,108) /* get last sector of block device */ +#endif +#ifndef BLKGETSIZE #define BLKGETSIZE _IO(0x12,96) /* return device size */ +#endif +#ifndef BLKSSZGET #define BLKSSZGET _IO(0x12,104) /* get block device sector size */ +#endif +#ifndef BLKGETSIZE64 #define BLKGETSIZE64 _IOR(0x12,114,sizeof(uint64_t)) /* return device size in bytes (u64 *arg) */ +#endif struct blkdev_ioctl_param { unsigned int block; @@ -143,20 +152,14 @@ get_sector_size(int filedes) static uint64_t _get_num_sectors(int filedes) { - unsigned long sectors=0; int rc; -#if 0 - uint64_t bytes=0; + uint64_t bytes=0; - rc = ioctl(filedes, BLKGETSIZE64, &bytes); + rc = ioctl(filedes, BLKGETSIZE64, &bytes); if (!rc) return bytes / get_sector_size(filedes); -#endif - rc = ioctl(filedes, BLKGETSIZE, §ors); - if (rc) - return 0; - - return sectors; + + return 0; } /************************************************************ @@ -193,7 +196,7 @@ last_lba(int filedes) sectors = 1; } - return sectors - 1; + return sectors ? sectors - 1 : 0; } @@ -220,17 +223,22 @@ read_lba(int fd, uint64_t lba, void *buffer, size_t bytes) { int sector_size = get_sector_size(fd); off_t offset = lba * sector_size; + uint64_t lastlba; ssize_t bytesread; lseek(fd, offset, SEEK_SET); bytesread = read(fd, buffer, bytes); + lastlba = last_lba(fd); + if (!lastlba) + return bytesread; + /* Kludge. This is necessary to read/write the last block of an odd-sized disk, until Linux 2.5.x kernel fixes. This is only used by gpt.c, and only to read one sector, so we don't have to be fancy. */ - if (!bytesread && !(last_lba(fd) & 1) && lba == last_lba(fd)) { + if (!bytesread && !(lastlba & 1) && lba == lastlba) { bytesread = read_lastoddsector(fd, lba, buffer, bytes); } return bytesread; @@ -505,7 +513,8 @@ find_valid_gpt(int fd, gpt_header ** gpt, gpt_entry ** ptes) if (!gpt || !ptes) return 0; - lastlba = last_lba(fd); + if (!(lastlba = last_lba(fd))) + return 0; good_pgpt = is_gpt_valid(fd, GPT_PRIMARY_PARTITION_TABLE_LBA, &pgpt, &pptes); if (good_pgpt) { diff --git a/kpartx/kpartx.8 b/kpartx/kpartx.8 index 259ce3f..c61f312 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 @@ -26,6 +26,9 @@ List partition mappings that would be added -a .B \-p set device name-partition number delimiter .TP +.B \-g +force GUID partition table (GPT) +.TP .B \-v Operate verbosely .SH "SEE ALSO" diff --git a/kpartx/kpartx.c b/kpartx/kpartx.c index 2198302..13d039d 100644 --- a/kpartx/kpartx.c +++ b/kpartx/kpartx.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -79,9 +80,10 @@ 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:"; +static char short_opts[] = "ladgvp:t:"; /* Used in gpt.c */ int force_gpt=0; @@ -93,6 +95,7 @@ usage(void) { printf("\t-d del partition devmappings\n"); printf("\t-l list partitions devmappings that would be added by -a\n"); printf("\t-p set device name-partition number delimiter\n"); + printf("\t-g force GUID partition table (GPT)\n"); printf("\t-v verbose\n"); return 1; } @@ -115,7 +118,7 @@ strip_slash (char * device) char * p = device; while (*(p++) != 0x0) { - + if (*p == '/') *p = '!'; } @@ -125,9 +128,9 @@ static int find_devname_offset (char * device) { char *p, *q = NULL; - + p = device; - + while (*p++) if (*p == '/') q = p; @@ -182,16 +185,17 @@ 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; - char *p, *type, *diskdevice, *device, *progname; - int lower, upper; + char *type, *diskdevice, *device, *progname; int verbose = 0; 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; @@ -199,11 +203,10 @@ main(int argc, char **argv){ initpts(); init_crc32(); - lower = upper = 0; type = device = diskdevice = NULL; memset(&all, 0, sizeof(all)); memset(&partname, 0, sizeof(partname)); - + /* Check whether hotplug mode. */ progname = strrchr(argv[0], '/'); @@ -236,14 +239,6 @@ main(int argc, char **argv){ case 'v': verbose = 1; break; - case 'n': - p = optarg; - lower = atoi(p); - if ((p[1] == '-') && p[2]) - upper = atoi(p+2); - else - upper = lower; - break; case 'p': delim = optarg; break; @@ -284,17 +279,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,16 +301,28 @@ 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) { perror(device); exit(1); } - if (!lower) - lower = 1; /* add/remove partitions to the kernel devmapper tables */ for (i = 0; i < ptct; i++) { @@ -328,7 +330,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 +344,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 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; + } - 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); + slices[j].minor = m++; + + printf("%s%s%d : 0 %" PRIu64 " %s %" PRIu64"\n", + mapname, delim, j+1, + slices[j].size, device, + slices[j].start); } + /* Loop to resolve contained slices */ + d = c; + while (c) { + for (j = 0; j < n; j++) { + uint64_t 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 %" PRIu64 " /dev/dm-%d %" PRIu64 "\n", + mapname, delim, j+1, + 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++) { + for (j = n-1; j >= 0; 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 +412,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,19 +421,25 @@ main(int argc, char **argv){ break; case ADD: - for (j=0; j 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)) { + + if (safe_sprintf(params, "%s %" PRIu64 , + device, slices[j].start)) { fprintf(stderr, "params too small\n"); exit(1); } @@ -420,16 +448,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 %" PRIu64 " %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 %" PRIu64, + slices[k].major, + slices[k].minor, + 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 %" PRIu64 " %s %s\n", + partname, slices[j].size, + DM_TARGET, params); + c--; + } + /* Terminate loop */ + if (d == c) + break; } break; @@ -505,7 +597,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..43ae3f8 100644 --- a/kpartx/kpartx.h +++ b/kpartx/kpartx.h @@ -1,6 +1,8 @@ #ifndef _KPARTX_H #define _KPARTX_H +#include + /* * For each partition type there is a routine that takes * a block device and a range, and returns the list of @@ -20,8 +22,11 @@ * units: 512 byte sectors */ struct slice { - unsigned long start; - unsigned long size; + uint64_t start; + uint64_t size; + int container; + int major; + int minor; }; typedef int (ptreader)(int fd, struct slice all, struct slice *sp, int ns); @@ -33,6 +38,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 +# +# +# 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 '-'. +dmuuid=${UUID#*-} +dmtbl=${UUID%%-*} +dmpart=${dmtbl#part} +# kpartx types are 'part' +if [ "$dmpart" = "$dmtbl" ] ; then + dmpart= +else + dmtbl=part +fi + +# Set the name of the table. We're only interested in dmraid, +# multipath, and kpartx tables; everything else is ignored. +if [ "$dmtbl" = "part" ] ; then + # The name of the kpartx table is the name of the parent table + dmname=$($DMSETUP info -c --noheadings -o name -u $dmuuid) + 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 +#include +#include /* 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; ipartitions[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 deleted file mode 100644 index ec8c10d..0000000 --- a/libcheckers/Makefile +++ /dev/null @@ -1,27 +0,0 @@ -# Makefile -# -# Copyright (C) 2003 Christophe Varoqui, -# -BUILD = glibc - -include ../Makefile.inc - -OBJS = checkers.o readsector0.o tur.o directio.o emc_clariion.o hp_sw.o - -all: $(BUILD) - -prepare: - @file *-$(BUILD).a >/dev/null 2>&1 || rm -f core *.o *.gz - -klibc: prepare $(OBJS) - ar rs libcheckers-klibc.a *.o - -glibc: prepare $(OBJS) - ar rs libcheckers-glibc.a *.o - -install: - -uninstall: - -clean: - rm -f core *.a *.o *.gz diff --git a/libcheckers/checkers.c b/libcheckers/checkers.c deleted file mode 100644 index 53770a6..0000000 --- a/libcheckers/checkers.c +++ /dev/null @@ -1,131 +0,0 @@ -#include -#include - -#include "checkers.h" - -#include "directio.h" -#include "tur.h" -#include "hp_sw.h" -#include "emc_clariion.h" -#include "readsector0.h" - -static struct checker checkers[] = { - { - .fd = 0, - .name = DIRECTIO, - .message = "", - .context = NULL, - .check = directio, - .init = directio_init, - .free = directio_free - }, - { - .fd = 0, - .name = TUR, - .message = "", - .context = NULL, - .check = tur, - .init = tur_init, - .free = tur_free - }, - { - .fd = 0, - .name = HP_SW, - .message = "", - .context = NULL, - .check = hp_sw, - .init = hp_sw_init, - .free = hp_sw_free - }, - { - .fd = 0, - .name = EMC_CLARIION, - .message = "", - .context = NULL, - .check = emc_clariion, - .init = emc_clariion_init, - .free = emc_clariion_free - }, - { - .fd = 0, - .name = READSECTOR0, - .message = "", - .context = NULL, - .check = readsector0, - .init = readsector0_init, - .free = readsector0_free - }, - {0, "", "", NULL, NULL, NULL, NULL}, -}; - -void checker_set_fd (struct checker * c, int fd) -{ - c->fd = fd; -} - -struct checker * checker_lookup (char * name) -{ - struct checker * c = &checkers[0]; - - while (c->check) { - if (!strncmp(name, c->name, CHECKER_NAME_LEN)) - return c; - c++; - } - return NULL; -} - -int checker_init (struct checker * c) -{ - return c->init(c); -} - -void checker_put (struct checker * c) -{ - if (c->free) - c->free(c); - memset(c, 0x0, sizeof(struct checker)); -} - -int checker_check (struct checker * c) -{ - int r; - - if (c->fd <= 0) { - MSG(c, "no usable fd"); - return PATH_WILD; - } - r = c->check(c); - - return r; -} - -int checker_selected (struct checker * c) -{ - return (c->check) ? 1 : 0; -} - -char * checker_name (struct checker * c) -{ - return c->name; -} - -char * checker_message (struct checker * c) -{ - return c->message; -} - -struct checker * checker_default (void) -{ - return checker_lookup(DEFAULT_CHECKER); -} - -void checker_get (struct checker * dst, struct checker * src) -{ - dst->fd = src->fd; - strncpy(dst->name, src->name, CHECKER_NAME_LEN); - strncpy(dst->message, src->message, CHECKER_MSG_LEN); - dst->check = src->check; - dst->init = src->init; - dst->free = src->free; -} diff --git a/libcheckers/checkers.h b/libcheckers/checkers.h deleted file mode 100644 index ac795ed..0000000 --- a/libcheckers/checkers.h +++ /dev/null @@ -1,53 +0,0 @@ -#ifndef _CHECKERS_H -#define _CHECKERS_H - -/* - * path states - */ -#define PATH_WILD -1 -#define PATH_UNCHECKED 0 -#define PATH_DOWN 1 -#define PATH_UP 2 -#define PATH_SHAKY 3 -#define PATH_GHOST 4 - -#define DIRECTIO "directio" -#define TUR "tur" -#define HP_SW "hp_sw" -#define EMC_CLARIION "emc_clariion" -#define READSECTOR0 "readsector0" - -#define DEFAULT_CHECKER READSECTOR0 - -/* - * strings lengths - */ -#define CHECKER_NAME_LEN 16 -#define CHECKER_MSG_LEN 256 -#define CHECKER_DEV_LEN 256 - -struct checker { - int fd; - char name[CHECKER_NAME_LEN]; - char message[CHECKER_MSG_LEN]; /* comm with callers */ - void * context; /* store for persistent data */ - 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); - -int checker_init (struct checker *); -void checker_put (struct checker *); -void checker_reset (struct checker * c); -void checker_set_fd (struct checker *, int); -struct checker * checker_lookup (char *); -int checker_check (struct checker *); -int checker_selected (struct checker *); -char * checker_name (struct checker *); -char * checker_message (struct checker *); -struct checker * checker_default (void); -void checker_get (struct checker *, struct checker *); - -#endif /* _CHECKERS_H */ diff --git a/libcheckers/directio.c b/libcheckers/directio.c deleted file mode 100644 index b53c1c3..0000000 --- a/libcheckers/directio.c +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright (c) 2005 Hannes Reinecke, Suse - */ -#define _GNU_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "checkers.h" - -#define MSG_DIRECTIO_UNKNOWN "directio checker is not available" -#define MSG_DIRECTIO_UP "directio checker reports path is up" -#define MSG_DIRECTIO_DOWN "directio checker reports path is down" - -struct directio_context { - int blksize; - unsigned char *buf; - unsigned char *ptr; -}; - -int directio_init (struct checker * c) -{ - unsigned long pgsize = getpagesize(); - struct directio_context * ct; - - ct = malloc(sizeof(struct directio_context)); - if (!ct) - return 1; - c->context = (void *)ct; - - if (ioctl(c->fd, BLKBSZGET, &ct->blksize) < 0) { - MSG(c, "cannot get blocksize, set default"); - ct->blksize = 512; - } - if (ct->blksize > 4096) { - /* - * Sanity check for DASD; BSZGET is broken - */ - ct->blksize = 4096; - } - if (!ct->blksize) - goto out; - 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))); - - return 0; -out: - free(ct); - return 1; -} - -void directio_free (struct checker * c) -{ - struct directio_context * ct = (struct directio_context *)c->context; - - if (!ct) - return; - if (ct->buf) - free(ct->buf); - free(ct); -} - -static int -direct_read (int fd, unsigned char * buff, int size) -{ - long flags; - int reset_flags = 0; - int res, retval; - - flags = fcntl(fd,F_GETFL); - - if (flags < 0) { - return PATH_UNCHECKED; - } - - if (!(flags & O_DIRECT)) { - flags |= O_DIRECT; - if (fcntl(fd,F_SETFL,flags) < 0) { - return PATH_UNCHECKED; - } - reset_flags = 1; - } - - while ( (res = read(fd,buff,size)) < 0 && errno == EINTR ); - if (res < 0) { - if (errno == EINVAL) { - /* O_DIRECT is not available */ - retval = PATH_UNCHECKED; - } else if (errno == ENOMEM) { - retval = PATH_UP; - } else { - retval = PATH_DOWN; - } - } else { - retval = PATH_UP; - } - - if (reset_flags) { - flags &= ~O_DIRECT; - /* No point in checking for errors */ - fcntl(fd,F_SETFL,flags); - } - - return retval; -} - -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); - - switch (ret) - { - case PATH_UNCHECKED: - MSG(c, MSG_DIRECTIO_UNKNOWN); - break; - case PATH_DOWN: - MSG(c, MSG_DIRECTIO_DOWN); - break; - case PATH_UP: - MSG(c, MSG_DIRECTIO_UP); - break; - default: - break; - } - return ret; -} diff --git a/libcheckers/directio.h b/libcheckers/directio.h deleted file mode 100644 index 1865b1f..0000000 --- a/libcheckers/directio.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef _DIRECTIO_H -#define _DIRECTIO_H - -int directio (struct checker *); -int directio_init (struct checker *); -void directio_free (struct checker *); - -#endif /* _DIRECTIO_H */ diff --git a/libcheckers/emc_clariion.c b/libcheckers/emc_clariion.c deleted file mode 100644 index 1d7b684..0000000 --- a/libcheckers/emc_clariion.c +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright (c) 2004, 2005 Lars Marowsky-Bree - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "checkers.h" - -#include "../libmultipath/sg_include.h" - -#define INQUIRY_CMD 0x12 -#define INQUIRY_CMDLEN 6 -#define HEAVY_CHECK_COUNT 10 - -struct emc_clariion_checker_context { - char wwn[16]; - unsigned wwn_set; -}; - -int emc_clariion_init (struct checker * c) -{ - c->context = malloc(sizeof(struct emc_clariion_checker_context)); - if (!c->context) - return 1; - ((struct emc_clariion_checker_context *)c->context)->wwn_set = 0; - return 0; -} - -void emc_clariion_free (struct checker * c) -{ - free(c->context); -} - -int emc_clariion(struct checker * c) -{ - unsigned char sense_buffer[256] = { 0, }; - unsigned char sb[128] = { 0, }; - unsigned char inqCmdBlk[INQUIRY_CMDLEN] = {INQUIRY_CMD, 1, 0xC0, 0, - sizeof(sb), 0}; - struct sg_io_hdr io_hdr; - struct emc_clariion_checker_context * ct = - (struct emc_clariion_checker_context *)c->context; - - 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(c->fd, SG_IO, &io_hdr) < 0) { - MSG(c, "emc_clariion_checker: sending query command failed"); - return PATH_DOWN; - } - if (io_hdr.info & SG_INFO_OK_MASK) { - MSG(c, "emc_clariion_checker: query command indicates error"); - return PATH_DOWN; - } - 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"); - return PATH_DOWN; - } - - if ( /* Effective initiator type */ - sense_buffer[27] != 0x03 - /* Failover mode should be set to 1 */ - || (sense_buffer[28] & 0x07) != 0x04 - /* Arraycommpath should be set to 1 */ - || (sense_buffer[30] & 0x04) != 0x04) { - MSG(c, "emc_clariion_checker: Path not correctly configured for failover"); - 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"); - 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; - } -#endif - - /* - * store the LUN WWN there and compare that it indeed did not - * change in between, to protect against the path suddenly - * pointing somewhere else. - */ - if (ct->wwn_set) { - if (memcmp(ct->wwn, &sense_buffer[10], 16) != 0) { - MSG(c, "emc_clariion_checker: Logical Unit WWN has changed!"); - return PATH_DOWN; - } - } else { - memcpy(ct->wwn, &sense_buffer[10], 16); - ct->wwn_set = 1; - } - - - MSG(c, "emc_clariion_checker: Path healthy"); - return PATH_UP; -} diff --git a/libcheckers/emc_clariion.h b/libcheckers/emc_clariion.h deleted file mode 100644 index a1018a6..0000000 --- a/libcheckers/emc_clariion.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef _EMC_CLARIION_H -#define _EMC_CLARIION_H - -int emc_clariion (struct checker *); -int emc_clariion_init (struct checker *); -void emc_clariion_free (struct checker *); - -#endif /* _EMC_CLARIION_H */ diff --git a/libcheckers/hp_sw.c b/libcheckers/hp_sw.c deleted file mode 100644 index 509e9c4..0000000 --- a/libcheckers/hp_sw.c +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright (c) 2005 Christophe Varoqui - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "checkers.h" - -#include "../libmultipath/sg_include.h" - -#define TUR_CMD_LEN 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 -#define RECOVERED_ERROR 0x01 -#define MX_ALLOC_LEN 255 -#define HEAVY_CHECK_COUNT 10 - -#define MSG_HP_SW_UP "hp_sw checker reports path is up" -#define MSG_HP_SW_DOWN "hp_sw checker reports path is down" -#define MSG_HP_SW_GHOST "hp_sw checker reports path is ghost" - -struct sw_checker_context { - void * dummy; -}; - -int hp_sw_init (struct checker * c) -{ - return 0; -} - -void hp_sw_free (struct checker * c) -{ - return; -} - -static int -do_inq(int sg_fd, int cmddt, int evpd, unsigned int pg_op, - void *resp, int mx_resp_len, int noisy) -{ - unsigned char inqCmdBlk[INQUIRY_CMDLEN] = - { 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) - inqCmdBlk[1] |= 1; - inqCmdBlk[2] = (unsigned char) pg_op; - inqCmdBlk[3] = (unsigned char)((mx_resp_len >> 8) & 0xff); - 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 = 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; -} - -static int -do_tur (int fd) -{ - unsigned char turCmdBlk[TUR_CMD_LEN] = { 0x00, 0, 0, 0, 0, 0 }; - struct sg_io_hdr io_hdr; - unsigned char sense_buffer[32]; - - 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 (sense_buffer); - io_hdr.dxfer_direction = SG_DXFER_NONE; - io_hdr.cmdp = turCmdBlk; - io_hdr.sbp = sense_buffer; - io_hdr.timeout = 20000; - io_hdr.pack_id = 0; - - if (ioctl(fd, SG_IO, &io_hdr) < 0) - return 1; - - if (io_hdr.info & SG_INFO_OK_MASK) - return 1; - - return 0; -} - -extern int -hp_sw (struct checker * c) -{ - char buff[MX_ALLOC_LEN]; - - if (0 != do_inq(c->fd, 0, 1, 0x80, buff, MX_ALLOC_LEN, 0)) { - MSG(c, MSG_HP_SW_DOWN); - return PATH_DOWN; - } - - if (do_tur(c->fd)) { - MSG(c, MSG_HP_SW_GHOST); - return PATH_GHOST; - } - MSG(c, MSG_HP_SW_UP); - return PATH_UP; -} diff --git a/libcheckers/hp_sw.h b/libcheckers/hp_sw.h deleted file mode 100644 index 3be0d8e..0000000 --- a/libcheckers/hp_sw.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef _HP_SW_H -#define _HP_SW_H - -int hp_sw (struct checker *); -int hp_sw_init (struct checker *); -void hp_sw_free (struct checker *); - -#endif /* _HP_SW_H */ diff --git a/libcheckers/readsector0.c b/libcheckers/readsector0.c deleted file mode 100644 index e368fb4..0000000 --- a/libcheckers/readsector0.c +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (c) 2004, 2005 Christophe Varoqui - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "checkers.h" - -#include "../libmultipath/sg_include.h" - -#define SENSE_BUFF_LEN 32 -#define DEF_TIMEOUT 60000 - -#define MSG_READSECTOR0_UP "readsector0 checker reports path is up" -#define MSG_READSECTOR0_DOWN "readsector0 checker reports path is down" - -struct readsector0_checker_context { - void * dummy; -}; - -int readsector0_init (struct checker * c) -{ - return 0; -} - -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]; - int ret; - - ret = sg_read(c->fd, &buf[0]); - - switch (ret) - { - case PATH_DOWN: - MSG(c, MSG_READSECTOR0_DOWN); - break; - case PATH_UP: - MSG(c, MSG_READSECTOR0_UP); - break; - default: - break; - } - return ret; -} diff --git a/libcheckers/readsector0.h b/libcheckers/readsector0.h deleted file mode 100644 index 0f5d654..0000000 --- a/libcheckers/readsector0.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef _READSECTOR0_H -#define _READSECTOR0_H - -int readsector0 (struct checker *); -int readsector0_init (struct checker *); -void readsector0_free (struct checker *); - -#endif /* _READSECTOR0_H */ diff --git a/libcheckers/tur.c b/libcheckers/tur.c deleted file mode 100644 index d40a273..0000000 --- a/libcheckers/tur.c +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Some code borrowed from sg-utils. - * - * Copyright (c) 2004 Christophe Varoqui - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "checkers.h" - -#include "../libmultipath/sg_include.h" - -#define TUR_CMD_LEN 6 -#define HEAVY_CHECK_COUNT 10 - -#define MSG_TUR_UP "tur checker reports path is up" -#define MSG_TUR_DOWN "tur checker reports path is down" - -struct tur_checker_context { - void * dummy; -}; - -int tur_init (struct checker * c) -{ - return 0; -} - -void tur_free (struct checker * c) -{ - return; -} - -extern int -tur (struct checker * c) -{ - struct sg_io_hdr io_hdr; - unsigned char turCmdBlk[TUR_CMD_LEN] = { 0x00, 0, 0, 0, 0, 0 }; - unsigned char sense_buffer[32]; - - 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 (sense_buffer); - io_hdr.dxfer_direction = SG_DXFER_NONE; - io_hdr.cmdp = turCmdBlk; - io_hdr.sbp = sense_buffer; - io_hdr.timeout = 20000; - io_hdr.pack_id = 0; - if (ioctl(c->fd, SG_IO, &io_hdr) < 0) { - MSG(c, MSG_TUR_DOWN); - return PATH_DOWN; - } - if (io_hdr.info & SG_INFO_OK_MASK) { - MSG(c, MSG_TUR_DOWN); - return PATH_DOWN; - } - MSG(c, MSG_TUR_UP); - return PATH_UP; -} diff --git a/libcheckers/tur.h b/libcheckers/tur.h deleted file mode 100644 index a2e8c88..0000000 --- a/libcheckers/tur.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef _TUR_H -#define _TUR_H - -int tur (struct checker *); -int tur_init (struct checker *); -void tur_free (struct checker *); - -#endif /* _TUR_H */ diff --git a/libmultipath/Makefile b/libmultipath/Makefile index 8a14b04..16bd978 100644 --- a/libmultipath/Makefile +++ b/libmultipath/Makefile @@ -2,43 +2,39 @@ # # Copyright (C) 2003 Christophe Varoqui, # -BUILD = glibc - include ../Makefile.inc -CFLAGS = -I$(checkersdir) +LIBS = libmultipath.so 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 - -PREVBUILD = $(shell nm debug.o 2> /dev/null|grep log_safe) + log.o configure.o structs_vec.o sysfs.o prio.o checkers.o -ifeq ($(strip $(DAEMON)),1) +#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 +#endif -all: $(BUILD) +LIBDM_API_FLUSH = $(shell objdump -T /lib/libdevmapper.so.* | grep -c dm_task_no_flush) -prepare: $(CLEAN) - @file *-$(BUILD).a >/dev/null 2>&1 || rm -f core *.o *.gz - @rm -f *-$(BUILD).a +ifeq ($(strip $(LIBDM_API_FLUSH)),1) + CFLAGS += -DLIBDM_API_FLUSH +endif -klibc: $(OBJS) - ar rs libmultipath-klibc.a *.o +all: $(LIBS) -glibc: $(OBJS) - ar rs libmultipath-glibc.a *.o +$(LIBS): $(OBJS) + $(CC) $(SHARED_FLAGS) $(CFLAGS) -o $@ $(OBJS) install: + $(INSTALL_PROGRAM) -o root -g root -m 755 -d $(libdir) + $(INSTALL_PROGRAM) -o root -g root -m 755 $(LIBS) $(libdir)/$(LIBS) uninstall: + rm -f $(libdir)/$(LIBS) clean: - rm -f core *.a *.o *.gz + rm -f core *.a *.o *.gz *.so 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..d85b385 100644 --- a/libmultipath/blacklist.c +++ b/libmultipath/blacklist.c @@ -3,8 +3,7 @@ */ #include -#include - +#include "checkers.h" #include "memory.h" #include "vector.h" #include "util.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,192 @@ 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 > 0) + return r; + r = _filter_device(conf->blist_device, conf->elist_device, + pp->vendor_id, pp->product_id); + if (r > 0) + return r; + r = _filter_wwid(conf->blist_wwid, conf->elist_wwid, pp->wwid); + return r; +} + +int +filter_path (struct config * conf, struct path * pp) +{ + int r=_filter_path(conf, pp); + log_filter(pp->dev, pp->vendor_id, pp->product_id, pp->wwid, r); + return r; +} + void free_blacklist (vector blist) { diff --git a/libmultipath/blacklist.h b/libmultipath/blacklist.h index 9e2b63c..cdbebef 100644 --- a/libmultipath/blacklist.h +++ b/libmultipath/blacklist.h @@ -3,9 +3,18 @@ #include "regex.h" +#define MATCH_NOTHING 0 +#define MATCH_WWID_BLIST 1 +#define MATCH_DEVICE_BLIST 2 +#define MATCH_DEVNODE_BLIST 3 +#define MATCH_WWID_BLIST_EXCEPT -MATCH_WWID_BLIST +#define MATCH_DEVICE_BLIST_EXCEPT -MATCH_DEVICE_BLIST +#define MATCH_DEVNODE_BLIST_EXCEPT -MATCH_DEVNODE_BLIST + struct blentry { char * str; regex_t regex; + int origin; }; struct blentry_device { @@ -13,15 +22,17 @@ struct blentry_device { char * product; regex_t vendor_reg; regex_t product_reg; + int origin; }; int setup_default_blist (struct config *); int alloc_ble_device (vector); -int blacklist (vector, char *); -int blacklist_device (vector, char *, char *); -int blacklist_path (struct config *, struct path *); -int store_ble (vector, char *); -int set_ble_device (vector, char *, char *); +int filter_devnode (vector, vector, char *); +int filter_wwid (vector, vector, char *); +int filter_device (vector, vector, char *, char *); +int filter_path (struct config *, struct path *); +int store_ble (vector, char *, int); +int set_ble_device (vector, char *, char *, int); void free_blacklist (vector); void free_blacklist_device (vector); diff --git a/libmultipath/callout.c b/libmultipath/callout.c index 46b89e6..d54f3ca 100644 --- a/libmultipath/callout.c +++ b/libmultipath/callout.c @@ -13,8 +13,7 @@ #include #include -#include - +#include "checkers.h" #include "vector.h" #include "structs.h" #include "debug.h" diff --git a/libmultipath/checkers.c b/libmultipath/checkers.c new file mode 100644 index 0000000..ff606d0 --- /dev/null +++ b/libmultipath/checkers.c @@ -0,0 +1,186 @@ +#include +#include +#include +#include + +#include "debug.h" +#include "checkers.h" + +static LIST_HEAD(checkers); + +int init_checkers (void) +{ + INIT_LIST_HEAD(&checkers); + if (!add_checker(DEFAULT_CHECKER)) + return 1; + return 0; +} + +struct checker * alloc_checker (void) +{ + return zalloc(sizeof(struct checker)); +} + +void free_checker (struct checker * c) +{ + free(c); +} + +void cleanup_checkers (void) +{ + struct checker * checker_loop; + struct checker * checker_temp; + + list_for_each_entry_safe(checker_loop, checker_temp, &checkers, node) { + list_del(&checker_loop->node); + free(checker_loop); + } +} + +struct checker * checker_lookup (char * name) +{ + struct checker * c; + + list_for_each_entry(c, &checkers, node) { + if (!strncmp(name, c->name, CHECKER_NAME_LEN)) + return c; + } + c = add_checker(name); + if (c) + return c; + return checker_default(); +} + +struct checker * add_checker (char * name) +{ + char libname[LIB_CHECKER_NAMELEN]; + void * handle; + struct checker * c; + char *errstr; + + c = alloc_checker(); + if (!c) + return NULL; + snprintf(libname, LIB_CHECKER_NAMELEN, "libcheck%s.so", name); + condlog(3, "loading %s checker", libname); + handle = dlopen(libname, RTLD_NOW); + errstr = dlerror(); + if (errstr != NULL) + condlog(0, "A dynamic linking error occurred: (%s)", errstr); + if (!handle) + goto out; + + c->check = (int (*)(struct checker *)) dlsym(handle, "libcheck_check"); + errstr = dlerror(); + if (errstr != NULL) + condlog(0, "A dynamic linking error occurred: (%s)", errstr); + if (!c->check) + goto out; + + c->init = (int (*)(struct checker *)) dlsym(handle, "libcheck_init"); + errstr = dlerror(); + if (errstr != NULL) + condlog(0, "A dynamic linking error occurred: (%s)", errstr); + if (!c->init) + goto out; + + c->free = (void (*)(struct checker *)) dlsym(handle, "libcheck_free"); + errstr = dlerror(); + if (errstr != NULL) + condlog(0, "A dynamic linking error occurred: (%s)", errstr); + if (!c->free) + goto out; + + snprintf(c->name, CHECKER_NAME_LEN, "%s", name); + c->fd = 0; + c->sync = 1; + list_add(&c->node, &checkers); + return c; +out: + free_checker(c); + return NULL; +} + +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; +} + +void checker_enable (struct checker * c) +{ + c->disable = 0; +} + +void checker_disable (struct checker * c) +{ + c->disable = 1; +} + +int checker_init (struct checker * c, void ** mpctxt_addr) +{ + c->mpcontext = mpctxt_addr; + return c->init(c); +} + +void checker_put (struct checker * c) +{ + if (c->free) + c->free(c); + memset(c, 0x0, sizeof(struct checker)); +} + +int checker_check (struct checker * c) +{ + int r; + + if (c->disable) + return PATH_UNCHECKED; + if (c->fd <= 0) { + MSG(c, "no usable fd"); + return PATH_WILD; + } + r = c->check(c); + + return r; +} + +int checker_selected (struct checker * c) +{ + return (c->check) ? 1 : 0; +} + +char * checker_name (struct checker * c) +{ + return c->name; +} + +char * checker_message (struct checker * c) +{ + return c->message; +} + +struct checker * checker_default (void) +{ + return checker_lookup(DEFAULT_CHECKER); +} + +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; + dst->init = src->init; + dst->free = src->free; +} diff --git a/libmultipath/checkers.h b/libmultipath/checkers.h new file mode 100644 index 0000000..93337f1 --- /dev/null +++ b/libmultipath/checkers.h @@ -0,0 +1,127 @@ +#ifndef _CHECKERS_H +#define _CHECKERS_H + +#include "list.h" +#include "memory.h" + +/* + * + * 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 +#define PATH_DOWN 1 +#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 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 + */ +#define CHECKER_NAME_LEN 16 +#define CHECKER_MSG_LEN 256 +#define CHECKER_DEV_LEN 256 +#define LIB_CHECKER_NAMELEN 256 + +struct checker { + struct list_head node; + int fd; + int sync; + int disable; + 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, fmt, args...) snprintf((c)->message, CHECKER_MSG_LEN, fmt, ##args); + +int init_checkers (void); +struct checker * add_checker (char *); +struct checker * checker_lookup (char *); +int checker_init (struct checker *, void **); +void checker_put (struct checker *); +void checker_reset (struct checker *); +void checker_set_sync (struct checker *); +void checker_set_async (struct checker *); +void checker_set_fd (struct checker *, int); +void checker_enable (struct checker *); +void checker_disable (struct checker *); +struct checker * checker_lookup (char *); +int checker_check (struct checker *); +int checker_selected (struct checker *); +char * checker_name (struct checker *); +char * checker_message (struct checker *); +struct checker * checker_default (void); +void checker_get (struct checker *, struct checker *); + +#endif /* _CHECKERS_H */ diff --git a/libmultipath/checkers/Makefile b/libmultipath/checkers/Makefile new file mode 100644 index 0000000..f37a0f9 --- /dev/null +++ b/libmultipath/checkers/Makefile @@ -0,0 +1,29 @@ +# Makefile +# +# Copyright (C) 2003 Christophe Varoqui, +# +include ../../Makefile.inc + +LIBS= \ + libcheckreadsector0.so \ + libchecktur.so \ + libcheckdirectio.so \ + libcheckemc_clariion.so \ + libcheckhp_sw.so \ + libcheckrdac.so + +CFLAGS += -I.. + +all: $(LIBS) + +libcheck%.so: libsg.o %.o + $(CC) $(SHARED_FLAGS) -o $@ $^ + +install: + $(INSTALL_PROGRAM) -o root -g root -m 755 $(LIBS) $(libdir) + +uninstall: + rm -f $(libdir)/$(LIBS) + +clean: + rm -f core *.a *.o *.gz *.so diff --git a/libmultipath/checkers/directio.c b/libmultipath/checkers/directio.c new file mode 100644 index 0000000..5c92dac --- /dev/null +++ b/libmultipath/checkers/directio.c @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2005 Hannes Reinecke, Suse + */ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 running; + int reset_flags; + int blksize; + unsigned char * buf; + unsigned char * ptr; + io_context_t ioctx; + struct iocb io; +}; + + +int libcheck_init (struct checker * c) +{ + unsigned long pgsize = getpagesize(); + struct directio_context * ct; + long flags; + + ct = malloc(sizeof(struct directio_context)); + if (!ct) + return 1; + 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"); + ct->blksize = 512; + } + if (ct->blksize > 4096) { + /* + * Sanity check for DASD; BSZGET is broken + */ + ct->blksize = 4096; + } + if (!ct->blksize) + goto out; + ct->buf = (unsigned char *)malloc(ct->blksize + pgsize); + if (!ct->buf) + goto out; + + 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; +} + +void libcheck_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 +check_state(int fd, struct directio_context *ct, int sync) +{ + 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 (!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; + } + } + ct->running++; + + 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 { + LOG(3, "io finished %lu/%lu", event.res, event.res2); + ct->running = 0; + rc = (event.res == ct->blksize) ? PATH_UP : PATH_DOWN; + } + + return rc; +} + +int libcheck_check (struct checker * c) +{ + int ret; + struct directio_context * ct = (struct directio_context *)c->context; + + if (!ct) + return PATH_UNCHECKED; + + ret = check_state(c->fd, ct, c->sync); + + switch (ret) + { + case PATH_UNCHECKED: + MSG(c, MSG_DIRECTIO_UNKNOWN); + break; + case PATH_DOWN: + MSG(c, MSG_DIRECTIO_DOWN); + break; + case PATH_UP: + MSG(c, MSG_DIRECTIO_UP); + break; + case PATH_PENDING: + MSG(c, MSG_DIRECTIO_PENDING); + break; + default: + break; + } + return ret; +} diff --git a/libmultipath/checkers/directio.h b/libmultipath/checkers/directio.h new file mode 100644 index 0000000..1865b1f --- /dev/null +++ b/libmultipath/checkers/directio.h @@ -0,0 +1,8 @@ +#ifndef _DIRECTIO_H +#define _DIRECTIO_H + +int directio (struct checker *); +int directio_init (struct checker *); +void directio_free (struct checker *); + +#endif /* _DIRECTIO_H */ diff --git a/libmultipath/checkers/emc_clariion.c b/libmultipath/checkers/emc_clariion.c new file mode 100644 index 0000000..9eac31d --- /dev/null +++ b/libmultipath/checkers/emc_clariion.c @@ -0,0 +1,230 @@ +/* + * Copyright (c) 2004, 2005 Lars Marowsky-Bree + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../libmultipath/sg_include.h" +#include "libsg.h" +#include "checkers.h" + +#define INQUIRY_CMD 0x12 +#define INQUIRY_CMDLEN 6 +#define HEAVY_CHECK_COUNT 10 + +/* + * 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 libcheck_init (struct checker * c) +{ + /* + * 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_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; +} + +void libcheck_free (struct checker * c) +{ + free(c->context); +} + +int libcheck_check (struct checker * c) +{ + 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(sense_buffer), 0}; + struct sg_io_hdr io_hdr; + 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'; + 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 = 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"); + return PATH_DOWN; + } + if (io_hdr.info & SG_INFO_OK_MASK) { + MSG(c, "emc_clariion_checker: query command indicates error"); + return PATH_DOWN; + } + 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"); + return PATH_DOWN; + } + + if ( /* Effective initiator type */ + sense_buffer[27] != 0x03 + /* + * 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"); + 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"); + return PATH_SHAKY; + } + + 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; + } + + /* + * store the LUN WWN there and compare that it indeed did not + * change in between, to protect against the path suddenly + * pointing somewhere else. + */ + if (ct->wwn_set) { + if (memcmp(ct->wwn, &sense_buffer[10], 16) != 0) { + MSG(c, "emc_clariion_checker: Logical Unit WWN " + "has changed!"); + return PATH_DOWN; + } + } else { + memcpy(ct->wwn, &sense_buffer[10], 16); + ct->wwn_set = 1; + } + + /* + * 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/libmultipath/checkers/emc_clariion.h b/libmultipath/checkers/emc_clariion.h new file mode 100644 index 0000000..a1018a6 --- /dev/null +++ b/libmultipath/checkers/emc_clariion.h @@ -0,0 +1,8 @@ +#ifndef _EMC_CLARIION_H +#define _EMC_CLARIION_H + +int emc_clariion (struct checker *); +int emc_clariion_init (struct checker *); +void emc_clariion_free (struct checker *); + +#endif /* _EMC_CLARIION_H */ diff --git a/libmultipath/checkers/hp_sw.c b/libmultipath/checkers/hp_sw.c new file mode 100644 index 0000000..7509307 --- /dev/null +++ b/libmultipath/checkers/hp_sw.c @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2005 Christophe Varoqui + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "checkers.h" + +#include "../libmultipath/sg_include.h" + +#define TUR_CMD_LEN 6 +#define INQUIRY_CMDLEN 6 +#define INQUIRY_CMD 0x12 +#define SENSE_BUFF_LEN 32 +#define SCSI_CHECK_CONDITION 0x2 +#define SCSI_COMMAND_TERMINATED 0x22 +#define SG_ERR_DRIVER_SENSE 0x08 +#define RECOVERED_ERROR 0x01 +#define MX_ALLOC_LEN 255 +#define HEAVY_CHECK_COUNT 10 + +#define MSG_HP_SW_UP "hp_sw checker reports path is up" +#define MSG_HP_SW_DOWN "hp_sw checker reports path is down" +#define MSG_HP_SW_GHOST "hp_sw checker reports path is ghost" + +struct sw_checker_context { + void * dummy; +}; + +int libcheck_init (struct checker * c) +{ + return 0; +} + +void libcheck_free (struct checker * c) +{ + return; +} + +static int +do_inq(int sg_fd, int cmddt, int evpd, unsigned int pg_op, + void *resp, int mx_resp_len, int noisy) +{ + unsigned char inqCmdBlk[INQUIRY_CMDLEN] = + { 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) + inqCmdBlk[1] |= 1; + inqCmdBlk[2] = (unsigned char) pg_op; + inqCmdBlk[3] = (unsigned char)((mx_resp_len >> 8) & 0xff); + 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 = 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; +} + +static int +do_tur (int fd) +{ + unsigned char turCmdBlk[TUR_CMD_LEN] = { 0x00, 0, 0, 0, 0, 0 }; + struct sg_io_hdr io_hdr; + unsigned char sense_buffer[32]; + + 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 (sense_buffer); + io_hdr.dxfer_direction = SG_DXFER_NONE; + io_hdr.cmdp = turCmdBlk; + io_hdr.sbp = sense_buffer; + io_hdr.timeout = DEF_TIMEOUT; + io_hdr.pack_id = 0; + + if (ioctl(fd, SG_IO, &io_hdr) < 0) + return 1; + + if (io_hdr.info & SG_INFO_OK_MASK) + return 1; + + return 0; +} + +extern int +libcheck_check (struct checker * c) +{ + char buff[MX_ALLOC_LEN]; + + if (0 != do_inq(c->fd, 0, 1, 0x80, buff, MX_ALLOC_LEN, 0)) { + MSG(c, MSG_HP_SW_DOWN); + return PATH_DOWN; + } + + if (do_tur(c->fd)) { + MSG(c, MSG_HP_SW_GHOST); + return PATH_GHOST; + } + MSG(c, MSG_HP_SW_UP); + return PATH_UP; +} diff --git a/libmultipath/checkers/hp_sw.h b/libmultipath/checkers/hp_sw.h new file mode 100644 index 0000000..3be0d8e --- /dev/null +++ b/libmultipath/checkers/hp_sw.h @@ -0,0 +1,8 @@ +#ifndef _HP_SW_H +#define _HP_SW_H + +int hp_sw (struct checker *); +int hp_sw_init (struct checker *); +void hp_sw_free (struct checker *); + +#endif /* _HP_SW_H */ diff --git a/libmultipath/checkers/libsg.c b/libmultipath/checkers/libsg.c new file mode 100644 index 0000000..9171b10 --- /dev/null +++ b/libmultipath/checkers/libsg.c @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2004, 2005 Christophe Varoqui + */ +#include +#include +#include +#include + +#include "checkers.h" +#include "libsg.h" +#include "../libmultipath/sg_include.h" + +int +sg_read (int sg_fd, unsigned char * buff, unsigned char * senseBuff) +{ + /* defaults */ + int blocks = 1; + long long start_block = 0; + int bs = 512; + int cdbsz = 10; + int * diop = NULL; + + unsigned char rdCmd[cdbsz]; + unsigned char *sbb = senseBuff; + struct sg_io_hdr io_hdr; + int res; + int rd_opcode[] = {0x8, 0x28, 0xa8, 0x88}; + int sz_ind; + 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/libmultipath/checkers/libsg.h b/libmultipath/checkers/libsg.h new file mode 100644 index 0000000..97c4491 --- /dev/null +++ b/libmultipath/checkers/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/libmultipath/checkers/rdac.c b/libmultipath/checkers/rdac.c new file mode 100644 index 0000000..0086125 --- /dev/null +++ b/libmultipath/checkers/rdac.c @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2005 Christophe Varoqui + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "checkers.h" + +#include "../libmultipath/sg_include.h" + +#define INQUIRY_CMDLEN 6 +#define INQUIRY_CMD 0x12 +#define SENSE_BUFF_LEN 32 +#define RDAC_DEF_TIMEOUT 60000 +#define SCSI_CHECK_CONDITION 0x2 +#define SCSI_COMMAND_TERMINATED 0x22 +#define SG_ERR_DRIVER_SENSE 0x08 +#define RECOVERED_ERROR 0x01 + +#define MSG_RDAC_UP "rdac checker reports path is up" +#define MSG_RDAC_DOWN "rdac checker reports path is down" +#define MSG_RDAC_GHOST "rdac checker reports path is ghost" + +struct rdac_checker_context { + void * dummy; +}; + +int libcheck_init (struct checker * c) +{ + return 0; +} + +void libcheck_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 +libcheck_check (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/libmultipath/checkers/rdac.h b/libmultipath/checkers/rdac.h new file mode 100644 index 0000000..d7bf812 --- /dev/null +++ b/libmultipath/checkers/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/libmultipath/checkers/readsector0.c b/libmultipath/checkers/readsector0.c new file mode 100644 index 0000000..24182e6 --- /dev/null +++ b/libmultipath/checkers/readsector0.c @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2004, 2005 Christophe Varoqui + */ +#include + +#include "checkers.h" +#include "libsg.h" + +#define MSG_READSECTOR0_UP "readsector0 checker reports path is up" +#define MSG_READSECTOR0_DOWN "readsector0 checker reports path is down" + +struct readsector0_checker_context { + void * dummy; +}; + +int libcheck_init (struct checker * c) +{ + return 0; +} + +void libcheck_free (struct checker * c) +{ + return; +} + +int libcheck_check (struct checker * c) +{ + unsigned char buf[4096]; + unsigned char sbuf[SENSE_BUFF_LEN]; + int ret; + + ret = sg_read(c->fd, &buf[0], &sbuf[0]); + + switch (ret) + { + case PATH_DOWN: + MSG(c, MSG_READSECTOR0_DOWN); + break; + case PATH_UP: + MSG(c, MSG_READSECTOR0_UP); + break; + default: + break; + } + return ret; +} diff --git a/libmultipath/checkers/readsector0.h b/libmultipath/checkers/readsector0.h new file mode 100644 index 0000000..0f5d654 --- /dev/null +++ b/libmultipath/checkers/readsector0.h @@ -0,0 +1,8 @@ +#ifndef _READSECTOR0_H +#define _READSECTOR0_H + +int readsector0 (struct checker *); +int readsector0_init (struct checker *); +void readsector0_free (struct checker *); + +#endif /* _READSECTOR0_H */ diff --git a/libmultipath/checkers/tur.c b/libmultipath/checkers/tur.c new file mode 100644 index 0000000..e84435e --- /dev/null +++ b/libmultipath/checkers/tur.c @@ -0,0 +1,66 @@ +/* + * Some code borrowed from sg-utils. + * + * Copyright (c) 2004 Christophe Varoqui + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "checkers.h" + +#include "../libmultipath/sg_include.h" + +#define TUR_CMD_LEN 6 +#define HEAVY_CHECK_COUNT 10 + +#define MSG_TUR_UP "tur checker reports path is up" +#define MSG_TUR_DOWN "tur checker reports path is down" + +struct tur_checker_context { + void * dummy; +}; + +int libcheck_init (struct checker * c) +{ + return 0; +} + +void libcheck_free (struct checker * c) +{ + return; +} + +extern int +libcheck_check (struct checker * c) +{ + struct sg_io_hdr io_hdr; + unsigned char turCmdBlk[TUR_CMD_LEN] = { 0x00, 0, 0, 0, 0, 0 }; + unsigned char sense_buffer[32]; + + 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 (sense_buffer); + io_hdr.dxfer_direction = SG_DXFER_NONE; + io_hdr.cmdp = turCmdBlk; + io_hdr.sbp = sense_buffer; + io_hdr.timeout = DEF_TIMEOUT; + io_hdr.pack_id = 0; + if (ioctl(c->fd, SG_IO, &io_hdr) < 0) { + MSG(c, MSG_TUR_DOWN); + return PATH_DOWN; + } + if (io_hdr.info & SG_INFO_OK_MASK) { + MSG(c, MSG_TUR_DOWN); + return PATH_DOWN; + } + MSG(c, MSG_TUR_UP); + return PATH_UP; +} diff --git a/libmultipath/checkers/tur.h b/libmultipath/checkers/tur.h new file mode 100644 index 0000000..a2e8c88 --- /dev/null +++ b/libmultipath/checkers/tur.h @@ -0,0 +1,8 @@ +#ifndef _TUR_H +#define _TUR_H + +int tur (struct checker *); +int tur_init (struct checker *); +void tur_free (struct checker *); + +#endif /* _TUR_H */ diff --git a/libmultipath/config.c b/libmultipath/config.c index 1068755..af862b7 100644 --- a/libmultipath/config.c +++ b/libmultipath/config.c @@ -6,8 +6,7 @@ #include #include -#include - +#include "checkers.h" #include "memory.h" #include "util.h" #include "debug.h" @@ -19,27 +18,63 @@ #include "config.h" #include "blacklist.h" #include "defaults.h" +#include "prio.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,15 +126,15 @@ free_hwe (struct hwentry * hwe) if (hwe->product) FREE(hwe->product); + if (hwe->revision) + FREE(hwe->revision); + if (hwe->selector) FREE(hwe->selector); if (hwe->getuid) FREE(hwe->getuid); - if (hwe->getprio) - FREE(hwe->getprio); - if (hwe->features) FREE(hwe->features); @@ -204,23 +239,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,12 +256,12 @@ 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; - if (dhwe->getprio && !(hwe->getprio = set_param_str(dhwe->getprio))) - goto out; - if (dhwe->features && !(hwe->features = set_param_str(dhwe->features))) goto out; @@ -253,6 +277,7 @@ store_hwe (vector hwtable, struct hwentry * dhwe) hwe->no_path_retry = dhwe->no_path_retry; hwe->minio = dhwe->minio; hwe->checker = dhwe->checker; + hwe->prio = dhwe->prio; if (dhwe->bl_product && !(hwe->bl_product = set_param_str(dhwe->bl_product))) goto out; @@ -291,9 +316,6 @@ free_config (struct config * conf) if (conf->getuid) FREE(conf->getuid); - if (conf->getprio) - FREE(conf->getprio); - if (conf->features) FREE(conf->features); @@ -303,9 +325,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); } @@ -326,12 +353,14 @@ load_config (char * file) conf->dev_type = DEV_NONE; conf->minio = 1000; + conf->max_fds = 0; conf->bindings_file = DEFAULT_BINDINGS_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 +401,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(); @@ -398,6 +447,9 @@ load_config (char * file) !conf->hwhandler) goto out; + if (!conf->prio) + conf->prio = prio_default(); + if (!conf->checker) conf->checker = checker_lookup(DEFAULT_CHECKER); diff --git a/libmultipath/config.h b/libmultipath/config.h index 6e5633a..7568d7b 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,18 +14,21 @@ enum devtypes { struct hwentry { char * vendor; char * product; + char * revision; char * getuid; - char * getprio; char * features; char * hwhandler; char * selector; char * checker_name; + char * prio_name; int pgpolicy; int pgfailback; int rr_weight; int no_path_retry; int minio; + int pg_timeout; + struct prio * prio; struct checker * checker; char * bl_product; }; @@ -38,6 +44,7 @@ struct mpentry { int rr_weight; int no_path_retry; int minio; + int pg_timeout; }; struct config { @@ -47,8 +54,9 @@ struct config { int pgpolicy_flag; int with_sysfs; int pgpolicy; + struct prio * prio; struct checker * checker; - int dev_type; + enum devtypes dev_type; int minio; int checkint; int max_checkint; @@ -57,27 +65,33 @@ struct config { int rr_weight; int no_path_retry; int user_friendly_names; + int pg_timeout; + int max_fds; char * dev; + char * sysfs_dir; char * udev_dir; char * selector; char * getuid; - char * getprio; char * features; 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..df31cdd 100644 --- a/libmultipath/configure.c +++ b/libmultipath/configure.c @@ -14,8 +14,7 @@ #include #include -#include - +#include "checkers.h" #include "vector.h" #include "memory.h" #include "devmapper.h" @@ -34,6 +33,7 @@ #include "pgpolicies.h" #include "dict.h" #include "alias.h" +#include "prio.h" extern int setup_map (struct multipath * mpp) @@ -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: @@ -365,7 +364,7 @@ domap (struct multipath * mpp) if (r) { /* - * DM_DEVICE_CREATE, DM_DEIVCE_RENAME, or DM_DEVICE_RELOAD + * DM_DEVICE_CREATE, DM_DEVICE_RENAME, or DM_DEVICE_RELOAD * succeeded */ #ifndef DAEMON @@ -376,10 +375,14 @@ domap (struct multipath * mpp) mpp->stat_map_loads++; condlog(2, "%s: load table [0 %llu %s %s]", mpp->alias, mpp->size, DEFAULT_TARGET, mpp->params); + /* + * Required action is over, reset for the stateful daemon + */ + mpp->action = ACT_NOTHING; #endif + return DOMAP_OK; } - - return r; + return DOMAP_FAIL; } static int @@ -423,7 +426,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,12 +447,12 @@ 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) { condlog(0, "%s: skip coalesce (no paths)", mpp->alias); - remove_map(mpp, vecs, NULL, 0); + remove_map(mpp, vecs, 0); continue; } @@ -471,13 +474,13 @@ 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); if (setup_map(mpp)) { - remove_map(mpp, vecs, NULL, 0); + remove_map(mpp, vecs, 0); continue; } @@ -486,19 +489,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, 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 +508,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) { @@ -514,7 +522,7 @@ coalesce_paths (struct vectors * vecs, vector newmp, char * refwwid) vector_set_slot(newmp, mpp); } else - remove_map(mpp, vecs, NULL, 0); + remove_map(mpp, vecs, 0); } } /* @@ -534,7 +542,7 @@ coalesce_paths (struct vectors * vecs, vector newmp, char * refwwid) if ((j = find_slot(newmp, (void *)mpp)) != -1) vector_del_slot(newmp, j); - remove_map(mpp, vecs, NULL, 0); + remove_map(mpp, vecs, 0); if (dm_flush_map(mpp->alias, DEFAULT_TARGET)) condlog(2, "%s: remove failed (dead)", @@ -547,11 +555,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 +614,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..97a3dd7 100644 --- a/libmultipath/defaults.h +++ b/libmultipath/defaults.h @@ -1,14 +1,14 @@ -#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" #define DEFAULT_HWHANDLER "0" #define DEFAULT_MINIO 1000 -#define DEFAULT_GETPRIO NULL #define DEFAULT_PGPOLICY FAILOVER #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..f21d3ab 100644 --- a/libmultipath/devmapper.c +++ b/libmultipath/devmapper.c @@ -6,48 +6,110 @@ */ #include #include +#include #include #include #include #include #include +#include -#include - +#include "checkers.h" #include "vector.h" #include "structs.h" #include "debug.h" #include "memory.h" #include "devmapper.h" +#include "config.h" + +#if DAEMON +#include "log_pthread.h" +#include +#include +#endif #define MAX_WAIT 5 #define LOOPS_PER_SEC 5 +#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 +120,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 +169,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 +186,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 +197,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 +290,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 +303,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 +601,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 +693,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 +800,7 @@ dm_mapname(int major, int minor) * daemon uev_trigger -> uev_add_map */ while (--loop) { - dm_shut_log(); r = dm_task_run(dmt); - dm_restore_log(); if (r) break; diff --git a/libmultipath/devmapper.h b/libmultipath/devmapper.h index 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..76571d7 100644 --- a/libmultipath/dict.c +++ b/libmultipath/dict.c @@ -4,8 +4,7 @@ * Copyright (c) 2005 Benjamin Marzinski, Redhat * Copyright (c) 2005 Kiyoshi Ueda, NEC */ -#include - +#include "checkers.h" #include "vector.h" #include "hwtable.h" #include "structs.h" @@ -16,6 +15,7 @@ #include "pgpolicies.h" #include "blacklist.h" #include "defaults.h" +#include "prio.h" /* * default block handlers @@ -82,19 +82,16 @@ def_getuid_callout_handler(vector strvec) } static int -def_prio_callout_handler(vector strvec) +def_prio_handler(vector strvec) { - conf->getprio = set_value(strvec); + char * buff; - if (!conf->getprio) + buff = set_value(strvec); + if (!buff) return 1; - - if (strlen(conf->getprio) == 4 && - !strcmp(conf->getprio, "none")) { - FREE(conf->getprio); - conf->getprio = NULL; - } - + + conf->prio = prio_lookup(buff); + FREE(buff); return 0; } @@ -142,6 +139,26 @@ def_minio_handler(vector strvec) } static int +max_fds_handler(vector strvec) +{ + char * buff; + + buff = set_value(strvec); + + if (!buff) + return 1; + + if (strlen(buff) == 9 && + !strcmp(buff, "unlimited")) + conf->max_fds = MAX_FDS_UNLIMITED; + else + conf->max_fds = atoi(buff); + FREE(buff); + + return 0; +} + +static int def_weight_handler(vector strvec) { char * buff; @@ -201,6 +218,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 +281,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 +303,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 +329,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 +352,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 +367,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 +393,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); } /* @@ -474,23 +588,20 @@ hw_handler_handler(vector strvec) } static int -prio_callout_handler(vector strvec) +hw_prio_handler(vector strvec) { struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable); + char * buff; if (!hwe) return 1; - hwe->getprio = set_value(strvec); - - if (!hwe->getprio) + buff = set_value(strvec); + if (!buff) return 1; - - if (strlen(hwe->getprio) == 4 && !strcmp(hwe->getprio, "none")) { - FREE(hwe->getprio); - hwe->getprio = NULL; - } - + + hwe->prio = prio_lookup(buff); + FREE(buff); return 0; } @@ -585,6 +696,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 +918,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 +1066,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; @@ -943,23 +1129,16 @@ snprint_hw_getuid_callout (char * buff, int len, void * data) } static int -snprint_hw_prio_callout (char * buff, int len, void * data) +snprint_hw_prio (char * buff, int len, void * data) { struct hwentry * hwe = (struct hwentry *)data; - if (!conf->getprio && !hwe->getprio) + if (!hwe->prio) return 0; - if (!conf->getprio && hwe->getprio) - return snprintf(buff, len, "%s", hwe->getprio); - if (conf->getprio && !hwe->getprio) - return snprintf(buff, len, "none"); - - /* conf->getprio && hwe->getprio */ - if (strlen(hwe->getprio) == strlen(conf->getprio) && - !strcmp(hwe->getprio, conf->getprio)) + if (hwe->prio == conf->prio) return 0; - - return snprintf(buff, len, "%s", hwe->getprio); + + return snprintf(buff, len, "%s", prio_name(hwe->prio)); } static int @@ -1097,6 +1276,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; @@ -1171,12 +1371,12 @@ snprint_def_getuid_callout (char * buff, int len, void * data) } static int -snprint_def_getprio_callout (char * buff, int len, void * data) +snprint_def_prio (char * buff, int len, void * data) { - if (!conf->getprio) + if (!conf->prio) return 0; - return snprintf(buff, len, "%s", conf->getprio); + return snprintf(buff, len, "%s", prio_name(conf->prio)); } static int @@ -1235,6 +1435,17 @@ snprint_def_rr_min_io (char * buff, int len, void * data) } static int +snprint_max_fds (char * buff, int len, void * data) +{ + if (!conf->max_fds) + return 0; + + if (conf->max_fds < 0) + return snprintf(buff, len, "unlimited"); + return snprintf(buff, len, "%d", conf->max_fds); +} + +static int snprint_def_rr_weight (char * buff, int len, void * data) { if (!conf->rr_weight) @@ -1268,6 +1479,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) @@ -1313,18 +1541,20 @@ init_keywords(void) install_keyword("selector", &def_selector_handler, &snprint_def_selector); install_keyword("path_grouping_policy", &def_pgpolicy_handler, &snprint_def_path_grouping_policy); install_keyword("getuid_callout", &def_getuid_callout_handler, &snprint_def_getuid_callout); - install_keyword("prio_callout", &def_prio_callout_handler, &snprint_def_getprio_callout); + install_keyword("prio", &def_prio_handler, &snprint_def_prio); install_keyword("features", &def_features_handler, &snprint_def_features); install_keyword("path_checker", &def_path_checker_handler, &snprint_def_path_checker); + install_keyword("checker", &def_path_checker_handler, &snprint_def_path_checker); install_keyword("failback", &default_failback_handler, &snprint_def_failback); install_keyword("rr_min_io", &def_minio_handler, &snprint_def_rr_min_io); + install_keyword("max_fds", &max_fds_handler, &snprint_max_fds); 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); __deprecated install_keyword("default_getuid_callout", &def_getuid_callout_handler, NULL); - __deprecated install_keyword("default_prio_callout", &def_prio_callout_handler, NULL); __deprecated install_keyword("default_features", &def_features_handler, NULL); __deprecated install_keyword("default_path_checker", &def_path_checker_handler, NULL); @@ -1336,6 +1566,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); @@ -1358,13 +1596,15 @@ init_keywords(void) install_keyword("getuid_callout", &hw_getuid_callout_handler, &snprint_hw_getuid_callout); install_keyword("path_selector", &hw_selector_handler, &snprint_hw_selector); install_keyword("path_checker", &hw_path_checker_handler, &snprint_hw_path_checker); + install_keyword("checker", &hw_path_checker_handler, &snprint_hw_path_checker); install_keyword("features", &hw_features_handler, &snprint_hw_features); install_keyword("hardware_handler", &hw_handler_handler, &snprint_hw_hardware_handler); - install_keyword("prio_callout", &prio_callout_handler, &snprint_hw_prio_callout); + install_keyword("prio", &hw_prio_handler, &snprint_hw_prio); install_keyword("failback", &hw_failback_handler, &snprint_hw_failback); 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 +1618,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..83e1865 100644 --- a/libmultipath/discovery.c +++ b/libmultipath/discovery.c @@ -8,12 +8,10 @@ #include #include #include +#include #include -#include -#include - -#include +#include "checkers.h" #include "vector.h" #include "memory.h" #include "util.h" @@ -24,7 +22,9 @@ #include "debug.h" #include "propsel.h" #include "sg_include.h" +#include "sysfs.h" #include "discovery.h" +#include "prio.h" struct path * store_pathinfo (vector pathvec, vector hwtable, char * devname, int flag) @@ -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; + strcpy(devpath,"/sys/block"); + while ((blkdev = readdir(blkdir)) != NULL) { + if ((strcmp(blkdev->d_name,".") == 0) || + (strcmp(blkdev->d_name,"..") == 0)) + continue; - r = 0; + devptr = devpath + 10; + *devptr = '\0'; + strcat(devptr,"/"); + strcat(devptr,blkdev->d_name); + if (stat(devpath, &statbuf) < 0) + continue; - dlist_for_each_data(ls, dev, struct sysfs_class_device) - r += path_discover(pathvec, conf, dev->name, flag); + if (S_ISDIR(statbuf.st_mode) == 0) + continue; -out: - sysfs_close_class(class); - return r; -} + condlog(4, "Discover device %s", devpath); -/* - * the daemon can race udev upon path add, - * not multipath(8), ran by udev - */ -#if DAEMON -#define WAIT_MAX_SECONDS 5 -#define WAIT_LOOP_PER_SECOND 5 - -static int -wait_for_file (char * filename) -{ - int loop; - struct stat stats; - - loop = WAIT_MAX_SECONDS * WAIT_LOOP_PER_SECOND; - - while (--loop) { - if (stat(filename, &stats) == 0) - return 0; - - if (errno != ENOENT) - return 1; - - usleep(1000 * 1000 / WAIT_LOOP_PER_SECOND); + r += path_discover(pathvec, conf, blkdev->d_name, flag); } - return 1; -} -#else -static int -wait_for_file (char * filename) -{ - return 0; + closedir(blkdir); + condlog(4, "Discovery status %d", r); + return r; } -#endif -#define declare_sysfs_get_str(fname, fmt) \ +#define declare_sysfs_get_str(fname) \ extern int \ -sysfs_get_##fname (char * sysfs_path, char * dev, char * buff, int len) \ +sysfs_get_##fname (struct sysfs_device * dev, char * buff, size_t len) \ { \ - struct sysfs_attribute * attr; \ - char attr_path[SYSFS_PATH_SIZE]; \ + char *attr; \ \ - if (safe_sprintf(attr_path, fmt, sysfs_path, dev)) \ + attr = sysfs_attr_get_value(dev->devpath, #fname); \ + if (!attr) \ return 1; \ -\ - if (wait_for_file(attr_path)) \ - return 1; \ -\ - if (!(attr = sysfs_open_attribute(attr_path))) \ - return 1; \ -\ - if (0 > sysfs_read_attribute(attr)) \ - goto out; \ -\ - if (attr->len < 2 || attr->len - 1 > len) \ - goto out; \ -\ - strncpy(buff, attr->value, attr->len - 1); \ - buff[attr->len - 1] = '\0'; \ - sysfs_close_attribute(attr); \ + if (strlcpy(buff, attr, len) != strlen(attr)) \ + return 2; \ + strchop(buff); \ 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,67 @@ 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; - - /* - * 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; - } + char buff[MX_ALLOC_LEN + 1] = {0}; - 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'; + strchop(vendor); + memcpy(product, buff + 16, 16); + product[16] = '\0'; + strchop(product); + memcpy(rev, buff + 32, 4); + rev[4] = '\0'; + strchop(rev); + 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 +396,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 +418,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 +439,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 +455,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 +477,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 +521,56 @@ 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; + + if (!strncmp(parent->kernel, "block",5)) + parent = sysfs_device_get_parent(parent); + + 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 +580,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 +588,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); @@ -628,27 +632,22 @@ get_state (struct path * pp) static int get_prio (struct path * pp) { - char buff[CALLOUT_MAX_SIZE]; - char prio[16]; + if (!pp) + return 0; - if (!pp->getprio_selected) { - select_getprio(pp); - pp->getprio_selected = 1; + if (!pp->prio) { + select_prio(pp); + if (!pp->prio) + return 1; } - if (!pp->getprio) { - pp->priority = 1; - } else if (apply_format(pp->getprio, &buff[0], pp)) { - condlog(0, "error formatting prio callout command"); - pp->priority = -1; - return 1; - } else if (execute_program(buff, prio, 16)) { - condlog(0, "error calling out %s", buff); - pp->priority = -1; + pp->priority = prio_getprio(pp->prio, pp); + if (pp->priority < 0) { + condlog(0, "%s: %s prio error", pp->dev, prio_name(pp->prio)); + pp->priority = PRIO_UNDEF; return 1; - } else - pp->priority = atoi(prio); - - condlog(3, "%s: prio = %u", pp->dev, pp->priority); + } + condlog(3, "%s: %s prio = %u", + pp->dev, prio_name(pp->prio), pp->priority); return 0; } @@ -689,22 +688,38 @@ 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)) get_uid(pp); +#ifndef DAEMON + close(pp->fd); + pp->fd = -1; +#endif return 0; blank: @@ -713,5 +728,11 @@ blank: */ memset(pp->wwid, 0, WWID_SIZE); pp->state = PATH_DOWN; +#ifndef DAEMON + if (pp->fd > 0){ + close(pp->fd); + pp->fd = -1; + } +#endif return 0; } 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..fe7d986 100644 --- a/libmultipath/dmparser.c +++ b/libmultipath/dmparser.c @@ -7,8 +7,7 @@ #include #include -#include - +#include "checkers.h" #include "vector.h" #include "memory.h" #include "structs.h" @@ -84,11 +83,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 +119,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 +308,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..d938379 100644 --- a/libmultipath/hwtable.c +++ b/libmultipath/hwtable.c @@ -1,24 +1,45 @@ #include -#include - +#include "checkers.h" #include "vector.h" #include "defaults.h" #include "structs.h" #include "config.h" #include "pgpolicies.h" +#include "prio.h" /* * 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, + .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, + .prio_name = DEFAULT_PRIO, + }, + /* + * StorageWorks controller family * * Maintainer : Christophe Varoqui * Mail : christophe.varoqui@free.fr @@ -27,7 +48,6 @@ static struct hwentry default_hw[] = { .vendor = "3PARdata", .product = "VV", .getuid = DEFAULT_GETUID, - .getprio = NULL, .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, @@ -37,57 +57,122 @@ static struct hwentry default_hw[] = { .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = DEFAULT_MINIO, .checker_name = DEFAULT_CHECKER, + .prio_name = DEFAULT_PRIO, }, { .vendor = "DEC", .product = "HSG80", .getuid = DEFAULT_GETUID, - .getprio = NULL, - .features = DEFAULT_FEATURES, - .hwhandler = "1 hp_sw", + .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, .minio = DEFAULT_MINIO, .checker_name = HP_SW, + .prio_name = PRIO_HP_SW, }, { - .vendor = "{COMPAQ,HP}", - .product = "{MSA,HSV}1*", + .vendor = "HP", + .product = "A6189A", .getuid = DEFAULT_GETUID, - .getprio = NULL, .features = DEFAULT_FEATURES, - .hwhandler = "1 hp_sw", + .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, - .pgpolicy = GROUP_BY_SERIAL, + .pgpolicy = MULTIBUS, + .pgfailback = FAILBACK_UNDEF, + .rr_weight = RR_WEIGHT_NONE, + .no_path_retry = NO_PATH_RETRY_UNDEF, + .minio = DEFAULT_MINIO, + .checker_name = DIRECTIO, + .prio_name = DEFAULT_PRIO, + }, + { + /* MSA 1000/MSA1500 EVA 3000/5000 with old firmware */ + .vendor = "(COMPAQ|HP)", + .product = "(MSA|HSV)1.0.*", + .getuid = DEFAULT_GETUID, + .features = "1 queue_if_no_path", + .hwhandler = "1 hp-sw", + .selector = DEFAULT_SELECTOR, + .pgpolicy = GROUP_BY_PRIO, .pgfailback = FAILBACK_UNDEF, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = DEFAULT_MINIO, .checker_name = HP_SW, + .prio_name = PRIO_HP_SW, }, { + /* MSA 1000/1500 with new firmware */ .vendor = "HP", - .product = "HSV2*", + .product = "MSA VOLUME", .getuid = DEFAULT_GETUID, - .getprio = NULL, + .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, + .prio_name = PRIO_ALUA, + }, + { + .vendor = "HP", + .product = "MSA2000s*", + .getuid = "/sbin/cciss_id %n", .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, .pgpolicy = MULTIBUS, .pgfailback = FAILBACK_UNDEF, .rr_weight = RR_WEIGHT_NONE, + .no_path_retry = 12, + .minio = DEFAULT_MINIO, + .checker_name = TUR, + .prio_name = DEFAULT_PRIO, + }, + { + /* EVA 3000/5000 with new firmware */ + .vendor = "(COMPAQ|HP)", + .product = "(MSA|HSV)1.1.*", + .getuid = DEFAULT_GETUID, + .features = DEFAULT_FEATURES, + .hwhandler = DEFAULT_HWHANDLER, + .selector = DEFAULT_SELECTOR, + .pgpolicy = GROUP_BY_PRIO, + .pgfailback = -FAILBACK_IMMEDIATE, + .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = DEFAULT_MINIO, - .checker_name = READSECTOR0, + .checker_name = TUR, + .prio_name = PRIO_ALUA, }, { + /* EVA 4000/6000/8000 */ .vendor = "HP", - .product = "DF[456]00", + .product = "HSV2.*", .getuid = DEFAULT_GETUID, - .getprio = NULL, + .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, + .prio_name = PRIO_ALUA, + }, + { + /* HP Smart Array */ + .vendor = "HP", + .product = "LOGICAL VOLUME.*", + .getuid = "/lib/udev/scsi_id -n -g -u -s /block/%n", .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, @@ -96,10 +181,11 @@ 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, + .prio_name = DEFAULT_PRIO, }, /* - * DDN controler family + * DDN controller family * * Maintainer : Christophe Varoqui * Mail : christophe.varoqui@free.fr @@ -108,7 +194,6 @@ static struct hwentry default_hw[] = { .vendor = "DDN", .product = "SAN DataDirector", .getuid = DEFAULT_GETUID, - .getprio = NULL, .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, @@ -117,10 +202,11 @@ 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, + .prio_name = DEFAULT_PRIO, }, /* - * EMC / Clariion controler family + * EMC / Clariion controller family * * Maintainer : Edward Goggin, EMC * Mail : egoggin@emc.com @@ -128,8 +214,7 @@ static struct hwentry default_hw[] = { { .vendor = "EMC", .product = "SYMMETRIX", - .getuid = "/sbin/scsi_id -g -u -ppre-spc3-83 -s /block/%n", - .getprio = NULL, + .getuid = "/lib/udev/scsi_id -g -u -ppre-spc3-83 -s /block/%n", .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, @@ -138,14 +223,14 @@ 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, + .prio_name = DEFAULT_PRIO, }, { .vendor = "DGC", - .product = "*", + .product = ".*", .bl_product = "LUNZ", .getuid = DEFAULT_GETUID, - .getprio = "/sbin/mpath_prio_emc /dev/%n", .features = "1 queue_if_no_path", .hwhandler = "1 emc", .selector = DEFAULT_SELECTOR, @@ -155,9 +240,10 @@ static struct hwentry default_hw[] = { .no_path_retry = (300 / DEFAULT_CHECKINT), .minio = DEFAULT_MINIO, .checker_name = EMC_CLARIION, + .prio_name = PRIO_EMC, }, /* - * Fujitsu controler family + * Fujitsu controller family * * Maintainer : Christophe Varoqui * Mail : christophe.varoqui@free.fr @@ -166,7 +252,6 @@ static struct hwentry default_hw[] = { .vendor = "FSC", .product = "CentricStor", .getuid = DEFAULT_GETUID, - .getprio = NULL, .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, @@ -175,20 +260,20 @@ 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, + .prio_name = DEFAULT_PRIO, }, /* - * 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,19 +281,34 @@ 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, + .prio_name = DEFAULT_PRIO, + }, + { + .vendor = "HITACHI", + .product = "DF.*", + .getuid = DEFAULT_GETUID, + .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, + .prio_name = PRIO_HDS, }, /* - * IBM controler family + * IBM controller family * - * Maintainer : Hannes Reinecke, Suse + * Maintainer : Hannes Reinecke, SuSE * Mail : hare@suse.de */ { .vendor = "IBM", .product = "ProFibre 4000R", .getuid = DEFAULT_GETUID, - .getprio = NULL, .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, @@ -217,14 +317,14 @@ 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, + .prio_name = DEFAULT_PRIO, }, { /* IBM DS4100 / FAStT100 */ .vendor = "IBM", .product = "1742", .getuid = DEFAULT_GETUID, - .getprio = "/sbin/mpath_prio_tpc /dev/%n", .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, @@ -234,13 +334,29 @@ static struct hwentry default_hw[] = { .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = DEFAULT_MINIO, .checker_name = TUR, + .prio_name = PRIO_RDAC, + }, + { + /* IBM Netfinity Fibre Channel RAID Controller Unit */ + .vendor = "IBM", + .product = "3526", + .getuid = DEFAULT_GETUID, + .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, + .prio_name = PRIO_RDAC, }, { /* IBM DS4200 / FAStT200 */ .vendor = "IBM", .product = "3542", .getuid = DEFAULT_GETUID, - .getprio = NULL, .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, @@ -250,13 +366,13 @@ static struct hwentry default_hw[] = { .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = DEFAULT_MINIO, .checker_name = TUR, + .prio_name = DEFAULT_PRIO, }, { /* IBM ESS F20 aka Shark */ .vendor = "IBM", - .product = "2105F20", + .product = "2105(800|F20)", .getuid = DEFAULT_GETUID, - .getprio = NULL, .features = "1 queue_if_no_path", .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, @@ -266,13 +382,13 @@ static struct hwentry default_hw[] = { .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = DEFAULT_MINIO, .checker_name = TUR, + .prio_name = DEFAULT_PRIO, }, { - /* 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", .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, @@ -282,30 +398,47 @@ static struct hwentry default_hw[] = { .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = DEFAULT_MINIO, .checker_name = TUR, + .prio_name = PRIO_ALUA, }, { /* IBM DS8000 */ .vendor = "IBM", .product = "2107900", .getuid = DEFAULT_GETUID, - .getprio = NULL, .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, .minio = DEFAULT_MINIO, .checker_name = TUR, + .prio_name = DEFAULT_PRIO, + }, + { + /* IBM SAN Volume Controller */ + .vendor = "IBM", + .product = "2145", + .getuid = DEFAULT_GETUID, + .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, + .prio_name = PRIO_ALUA, }, { /* IBM S/390 ECKD DASD */ .vendor = "IBM", .product = "S/390 DASD ECKD", - .getuid = "/sbin/dasdview -j /dev/%n", - .getprio = NULL, - .features = DEFAULT_FEATURES, + .bl_product = "S/390.*", + .getuid = "/sbin/dasdinfo -u -b %n", + .features = "1 queue_if_no_path", .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, .pgpolicy = MULTIBUS, @@ -314,39 +447,60 @@ static struct hwentry default_hw[] = { .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = DEFAULT_MINIO, .checker_name = DIRECTIO, + .prio_name = DEFAULT_PRIO, }, - /* - * 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, - .checker_name = READSECTOR0, + .minio = 128, + .checker_name = DIRECTIO, + .prio_name = PRIO_NETAPP, + }, + /* + * IBM NSeries (NETAPP) controller family + * + * Maintainer : Dave Wysochanski + * Mail : davidw@netapp.com + */ + { + .vendor = "IBM", + .product = "Nseries.*", + .getuid = DEFAULT_GETUID, + .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 = DIRECTIO, + .prio_name = PRIO_NETAPP, }, /* - * 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", .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, @@ -356,6 +510,7 @@ static struct hwentry default_hw[] = { .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = DEFAULT_MINIO, .checker_name = TUR, + .prio_name = PRIO_ALUA, }, /* * SGI arrays @@ -367,7 +522,6 @@ static struct hwentry default_hw[] = { .vendor = "SGI", .product = "TP9[13]00", .getuid = DEFAULT_GETUID, - .getprio = NULL, .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, @@ -376,22 +530,38 @@ 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, + .prio_name = DEFAULT_PRIO, }, { .vendor = "SGI", .product = "TP9[45]00", .getuid = DEFAULT_GETUID, - .getprio = "/sbin/mpath_prio_tpc /dev/%n", .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, - .no_path_retry = NO_PATH_RETRY_UNDEF, + .no_path_retry = NO_PATH_RETRY_QUEUE, .minio = DEFAULT_MINIO, - .checker_name = TUR, + .checker_name = RDAC, + .prio_name = PRIO_RDAC, + }, + { + .vendor = "SGI", + .product = "IS.*", + .getuid = DEFAULT_GETUID, + .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, + .prio_name = PRIO_RDAC, }, /* * STK arrays @@ -403,7 +573,6 @@ static struct hwentry default_hw[] = { .vendor = "STK", .product = "OPENstorage D280", .getuid = DEFAULT_GETUID, - .getprio = "/sbin/mpath_prio_tpc /dev/%n", .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, @@ -413,6 +582,7 @@ static struct hwentry default_hw[] = { .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = DEFAULT_MINIO, .checker_name = TUR, + .prio_name = PRIO_RDAC, }, /* * SUN arrays @@ -422,9 +592,8 @@ static struct hwentry default_hw[] = { */ { .vendor = "SUN", - .product = "{StorEdge 3510,T4}", + .product = "(StorEdge 3510|T4)", .getuid = DEFAULT_GETUID, - .getprio = NULL, .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, @@ -433,7 +602,29 @@ 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, + .prio_name = DEFAULT_PRIO, + }, + /* + * Pivot3 RAIGE + * + * Maintainer : Bart Brooks, Pivot3 + * Mail : bartb@pivot3.com + */ + { + .vendor = "PIVOT3", + .product = "RAIGE VOLUME", + .getuid = "/sbin/scsi_id -p 0x80 -g -u -s /block/%n", + .features = "1 queue_if_no_path", + .hwhandler = DEFAULT_HWHANDLER, + .selector = DEFAULT_SELECTOR, + .pgpolicy = MULTIBUS, + .pgfailback = FAILBACK_UNDEF, + .rr_weight = RR_WEIGHT_NONE, + .no_path_retry = NO_PATH_RETRY_UNDEF, + .minio = 100, + .checker_name = TUR, + .prio_name = DEFAULT_PRIO, }, /* * EOL @@ -442,7 +633,6 @@ static struct hwentry default_hw[] = { .vendor = NULL, .product = NULL, .getuid = NULL, - .getprio = NULL, .features = NULL, .hwhandler = NULL, .selector = NULL, @@ -452,6 +642,7 @@ static struct hwentry default_hw[] = { .no_path_retry = 0, .minio = 0, .checker_name = NULL, + .prio_name = NULL, }, }; @@ -463,6 +654,7 @@ setup_default_hwtable (vector hw) while (hwe->vendor) { hwe->checker = checker_lookup(hwe->checker_name); + hwe->prio = prio_lookup(hwe->prio_name); r += store_hwe(hw, hwe); hwe++; } 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 +#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..ad2bb4f 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) @@ -190,7 +203,7 @@ alloc_strvec(char *string) cp = string; /* Skip white spaces */ - while (isspace((int) *cp) && *cp != '\0') + while ((isspace((int) *cp) || !isascii((int) *cp)) && *cp != '\0') cp++; /* Return if there is only white spaces */ @@ -228,8 +241,10 @@ alloc_strvec(char *string) in_string = 1; } else { - while ((in_string || !isspace((int) *cp)) && *cp - != '\0' && *cp != '"') + while ((in_string || + (!isspace((int) *cp) && isascii((int) *cp) && + *cp != '!' && *cp != '#')) && + *cp != '\0' && *cp != '"') cp++; strlen = cp - start; token = MALLOC(strlen + 1); @@ -242,7 +257,8 @@ alloc_strvec(char *string) } vector_set_slot(strvec, token); - while (isspace((int) *cp) && *cp != '\0') + while ((isspace((int) *cp) || !isascii((int) *cp)) + && *cp != '\0') cp++; if (*cp == '\0' || *cp == '!' || *cp == '#') return strvec; diff --git a/libmultipath/parser.h b/libmultipath/parser.h index f0fdd94..95d4e6f 100644 --- a/libmultipath/parser.h +++ b/libmultipath/parser.h @@ -76,6 +76,7 @@ extern void *set_value(vector strvec); extern int process_stream(vector keywords); extern int init_data(char *conf_file, void (*init_keywords) (void)); extern struct keyword * find_keyword(vector v, char * name); +void set_current_keywords (vector *k); int snprint_keyword(char *buff, int len, char *fmt, struct keyword *kw, void *data); diff --git a/libmultipath/pgpolicies.c b/libmultipath/pgpolicies.c index 0ac7448..1a355af 100644 --- a/libmultipath/pgpolicies.c +++ b/libmultipath/pgpolicies.c @@ -5,8 +5,7 @@ #include #include -#include - +#include "checkers.h" #include "util.h" #include "memory.h" #include "vector.h" 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..641ade6 100644 --- a/libmultipath/print.c +++ b/libmultipath/print.c @@ -5,19 +5,22 @@ #include #include #include +#include +#include -#include - +#include "checkers.h" #include "vector.h" #include "structs.h" #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" +#include "switchgroup.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) { @@ -76,8 +89,8 @@ snprint_sysfs (char * buff, size_t len, struct multipath * mpp) { if (mpp->dmi) return snprintf(buff, len, "dm-%i", mpp->dmi->minor); - - return 0; + else + return snprintf(buff, len, "n/a"); } static int @@ -208,9 +221,23 @@ snprint_multipath_uuid (char * buff, size_t len, struct multipath * mpp) } static int +snprint_multipath_vpr (char * buff, size_t len, struct multipath * mpp) +{ + struct path * pp = first_path(mpp); + if (!pp) + return 0; + return snprintf(buff, len, "%s,%s", + pp->vendor_id, pp->product_id); +} + +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 +249,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 +322,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 @@ -320,6 +350,13 @@ snprint_pg_selector (char * buff, size_t len, struct pathgroup * pgp) static int snprint_pg_pri (char * buff, size_t len, struct pathgroup * pgp) { + /* + * path group priority is not updated for every path prio change, + * but only on switch group code path. + * + * Printing is another reason to update. + */ + path_group_prio_update(pgp); return snprint_int(buff, len, pgp->priority); } @@ -361,6 +398,7 @@ struct multipath_data mpd[] = { {'2', "map_loads", 0, snprint_map_loads}, {'3', "total_q_time", 0, snprint_total_q_time}, {'4', "q_timeouts", 0, snprint_q_timeouts}, + {'s', "vend/prod/rev", 0, snprint_multipath_vpr}, {0, NULL, 0 , NULL} }; @@ -496,7 +534,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 +553,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 +670,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); @@ -664,6 +703,8 @@ snprint_multipath_topology (char * buff, int len, struct multipath * mpp, if (strncmp(mpp->alias, mpp->wwid, WWID_SIZE)) c += sprintf(c, " (%%w)"); + c += sprintf(c, " %%d %%s"); + fwd += snprint_multipath(buff + fwd, len - fwd, style, mpp); if (fwd > len) return len; @@ -833,6 +874,110 @@ snprint_defaults (char * buff, int len) } +static int +snprint_blacklist_group (char *buff, int len, int *fwd, vector *vec) +{ + int threshold = MAX_LINE_LEN; + struct blentry * ble; + int pos; + int i; + + pos = *fwd; + if (!VECTOR_SIZE(*vec)) { + if ((len - pos - threshold) <= 0) + return 0; + pos += snprintf(buff + pos, len - pos, " \n"); + } else vector_foreach_slot (*vec, ble, i) { + if ((len - pos - threshold) <= 0) + return 0; + if (ble->origin == ORIGIN_CONFIG) + pos += snprintf(buff + pos, len - pos, " (config file rule) "); + else if (ble->origin == ORIGIN_DEFAULT) + pos += snprintf(buff + pos, len - pos, " (default rule) "); + pos += snprintf(buff + pos, len - pos, "%s\n", ble->str); + } + + *fwd = pos; + return pos; +} + +static int +snprint_blacklist_devgroup (char *buff, int len, int *fwd, vector *vec) +{ + int threshold = MAX_LINE_LEN; + struct blentry_device * bled; + int pos; + int i; + + pos = *fwd; + if (!VECTOR_SIZE(*vec)) { + if ((len - pos - threshold) <= 0) + return 0; + pos += snprintf(buff + pos, len - pos, " \n"); + } else vector_foreach_slot (*vec, bled, i) { + if ((len - pos - threshold) <= 0) + return 0; + if (bled->origin == ORIGIN_CONFIG) + pos += snprintf(buff + pos, len - pos, " (config file rule) "); + else if (bled->origin == ORIGIN_DEFAULT) + pos += snprintf(buff + pos, len - pos, " (default rule) "); + pos += snprintf(buff + pos, len - pos, "%s:%s\n", bled->vendor, bled->product); + } + + *fwd = pos; + return pos; +} + +extern int +snprint_blacklist_report (char * buff, int len) +{ + int threshold = MAX_LINE_LEN; + int fwd = 0; + + if ((len - fwd - threshold) <= 0) + return len; + fwd += snprintf(buff + fwd, len - fwd, "device node rules:\n" + "- blacklist:\n"); + if (!snprint_blacklist_group(buff, len, &fwd, &conf->blist_devnode)) + return len; + + if ((len - fwd - threshold) <= 0) + return len; + fwd += snprintf(buff + fwd, len - fwd, "- exceptions:\n"); + if (snprint_blacklist_group(buff, len, &fwd, &conf->elist_devnode) == 0) + return len; + + if ((len - fwd - threshold) <= 0) + return len; + fwd += snprintf(buff + fwd, len - fwd, "wwid rules:\n" + "- blacklist:\n"); + if (snprint_blacklist_group(buff, len, &fwd, &conf->blist_wwid) == 0) + return len; + + if ((len - fwd - threshold) <= 0) + return len; + fwd += snprintf(buff + fwd, len - fwd, "- exceptions:\n"); + if (snprint_blacklist_group(buff, len, &fwd, &conf->elist_wwid) == 0) + return len; + + if ((len - fwd - threshold) <= 0) + return len; + fwd += snprintf(buff + fwd, len - fwd, "device rules:\n" + "- blacklist:\n"); + if (snprint_blacklist_devgroup(buff, len, &fwd, &conf->blist_device) == 0) + return len; + + if ((len - fwd - threshold) <= 0) + return len; + fwd += snprintf(buff + fwd, len - fwd, "- exceptions:\n"); + if (snprint_blacklist_devgroup(buff, len, &fwd, &conf->elist_device) == 0) + return len; + + if (fwd > len) + return len; + return fwd; +} + extern int snprint_blacklist (char * buff, int len) { @@ -895,12 +1040,140 @@ 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/"); + while ((blkdev = readdir(blkdir)) != NULL) { + if ((strcmp(blkdev->d_name,".") == 0) || + (strcmp(blkdev->d_name,"..") == 0)) + continue; + + devptr = devpath + 11; + *devptr = '\0'; + strncat(devptr, blkdev->d_name, PATH_MAX-12); + if (stat(devpath, &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", devptr); + pp = find_path_by_dev(vecs->pathvec, devptr); + if (!pp) { + r = filter_devnode(conf->blist_devnode, + conf->elist_devnode, devptr); + if (r > 0) + fwd += snprintf(buff + fwd, len - fwd, + " devnode blacklisted, unmonitored"); + else if (r < 0) + fwd += snprintf(buff + fwd, len - fwd, + " devnode whitelisted, unmonitored"); + } else + fwd += snprintf(buff + fwd, len - fwd, + " devnode whitelisted, monitored"); + 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/prio.c b/libmultipath/prio.c new file mode 100644 index 0000000..617fc19 --- /dev/null +++ b/libmultipath/prio.c @@ -0,0 +1,99 @@ +#include +#include +#include +#include + +#include "debug.h" +#include "prio.h" + +static LIST_HEAD(prioritizers); + +int init_prio (void) +{ + INIT_LIST_HEAD(&prioritizers); + if (!add_prio(DEFAULT_PRIO)) + return 1; + return 0; +} + +struct prio * alloc_prio (void) +{ + return zalloc(sizeof(struct prio)); +} + +void free_prio (struct prio * p) +{ + free(p); +} + +void cleanup_prio(void) +{ + struct prio * prio_loop; + struct prio * prio_temp; + + list_for_each_entry_safe(prio_loop, prio_temp, &prioritizers, node) { + list_del(&prio_loop->node); + free(prio_loop); + } +} + +struct prio * prio_lookup (char * name) +{ + struct prio * p; + + list_for_each_entry(p, &prioritizers, node) { + if (!strncmp(name, p->name, PRIO_NAME_LEN)) + return p; + } + p = add_prio(name); + if (p) + return p; + return prio_default(); +} + +struct prio * add_prio (char * name) +{ + char libname[LIB_PRIO_NAMELEN]; + void * handle; + struct prio * p; + char *errstr; + + p = alloc_prio(); + if (!p) + return NULL; + snprintf(libname, LIB_PRIO_NAMELEN, "libprio%s.so", name); + condlog(3, "loading %s prioritizer", libname); + handle = dlopen(libname, RTLD_NOW); + errstr = dlerror(); + if (errstr != NULL) + condlog(0, "A dynamic linking error occurred: (%s)", errstr); + if (!handle) + goto out; + p->getprio = (int (*)(struct path *)) dlsym(handle, "getprio"); + errstr = dlerror(); + if (errstr != NULL) + condlog(0, "A dynamic linking error occurred: (%s)", errstr); + if (!p->getprio) + goto out; + snprintf(p->name, PRIO_NAME_LEN, "%s", name); + list_add(&p->node, &prioritizers); + return p; +out: + free_prio(p); + return NULL; +} + +int prio_getprio (struct prio * p, struct path * pp) +{ + return p->getprio(pp); +} + +char * prio_name (struct prio * p) +{ + return p->name; +} + +struct prio * prio_default (void) +{ + return prio_lookup(DEFAULT_PRIO); +} diff --git a/libmultipath/prio.h b/libmultipath/prio.h new file mode 100644 index 0000000..6bf6d4b --- /dev/null +++ b/libmultipath/prio.h @@ -0,0 +1,51 @@ +#ifndef _PRIO_H +#define _PRIO_H + +/* + * knowing about path struct gives flexibility to prioritizers + */ +#include "checkers.h" +#include "vector.h" +#include "structs.h" +#include "list.h" +#include "memory.h" + +#define DEFAULT_PRIO "const" + +/* + * Known prioritizers for use in hwtable.c + */ +#define PRIO_ALUA "alua" +#define PRIO_CONST "const" +#define PRIO_EMC "emc" +#define PRIO_HDS "hds" +#define PRIO_HP_SW "hp_sw" +#define PRIO_NETAPP "netapp" +#define PRIO_RANDOM "random" +#define PRIO_RDAC "rdac" + +/* + * Value used to mark the fact prio was not defined + */ +#define PRIO_UNDEF -1 + +/* + * strings lengths + */ +#define LIB_PRIO_NAMELEN 255 +#define PRIO_NAME_LEN 16 + +struct prio { + struct list_head node; + char name[PRIO_NAME_LEN]; + int (*getprio)(struct path *); +}; + +int init_prio (void); +struct prio * add_prio (char *); +struct prio * prio_lookup (char *); +int prio_getprio (struct prio *, struct path *); +char * prio_name (struct prio *); +struct prio * prio_default (void); + +#endif /* _PRIO_H */ diff --git a/libmultipath/prioritizers/Makefile b/libmultipath/prioritizers/Makefile new file mode 100644 index 0000000..c05d457 --- /dev/null +++ b/libmultipath/prioritizers/Makefile @@ -0,0 +1,34 @@ +# Makefile +# +# Copyright (C) 2007 Christophe Varoqui, +# +include ../../Makefile.inc + +LIBS = \ + libpriorandom.so \ + libprioconst.so \ + libpriohp_sw.so \ + libprioemc.so \ + libpriordac.so \ + libprioalua.so \ + libprionetapp.so \ + libpriohds.so + +CFLAGS += -I.. + +all: $(LIBS) + +libprioalua.so: alua.o alua_rtpg.o + $(CC) $(SHARED_FLAGS) -o $@ $^ + +libprio%.so: %.o + $(CC) $(SHARED_FLAGS) -o $@ $^ + +install: $(LIBS) + install -m 755 libprio*.so $(libdir) + +uninstall: + rm -f $(libdir)/libprio*.so + +clean: + rm -f core *.a *.o *.gz *.so diff --git a/libmultipath/prioritizers/alua.c b/libmultipath/prioritizers/alua.c new file mode 100644 index 0000000..67e4adb --- /dev/null +++ b/libmultipath/prioritizers/alua.c @@ -0,0 +1,91 @@ +/* + * (C) Copyright IBM Corp. 2004, 2005 All Rights Reserved. + * + * main.c + * + * Tool to make use of a SCSI-feature called Asymmetric Logical Unit Access. + * It determines the ALUA state of a device and prints a priority value to + * stdout. + * + * Author(s): Jan Kunigk + * S. Bader + * + * This file is released under the GPL. + */ +#include + +#include +#include + +#include "alua.h" + +#define ALUA_PRIO_NOT_SUPPORTED 1 +#define ALUA_PRIO_RTPG_FAILED 2 +#define ALUA_PRIO_GETAAS_FAILED 3 + +int +get_alua_info(int fd) +{ + char * aas_string[] = { + [AAS_OPTIMIZED] = "active/optimized", + [AAS_NON_OPTIMIZED] = "active/non-optimized", + [AAS_STANDBY] = "standby", + [AAS_UNAVAILABLE] = "unavailable", + [AAS_TRANSITIONING] = "transitioning between states", + }; + int rc; + int tpg; + + rc = get_target_port_group_support(fd); + if (rc < 0) + return rc; + + if (rc == TPGS_NONE) + return -ALUA_PRIO_NOT_SUPPORTED; + + tpg = get_target_port_group(fd); + if (tpg < 0) + return -ALUA_PRIO_RTPG_FAILED; + + condlog(3, "reported target port group is %i", tpg); + rc = get_asymmetric_access_state(fd, tpg); + if (rc < 0) + return -ALUA_PRIO_GETAAS_FAILED; + + condlog(3, "aas = [%s]", + (aas_string[rc]) ? aas_string[rc] : "invalid/reserved"); + return rc; +} + +int getprio (struct path * pp) +{ + int rc = get_alua_info(pp->fd); + if (rc >= 0) { + switch(rc) { + case AAS_OPTIMIZED: + rc = 50; + break; + case AAS_NON_OPTIMIZED: + rc = 10; + break; + case AAS_STANDBY: + rc = 1; + break; + default: + rc = 0; + } + } else { + switch(-rc) { + case ALUA_PRIO_NOT_SUPPORTED: + condlog(0, "%s: alua not supported", pp->dev); + break; + case ALUA_PRIO_RTPG_FAILED: + condlog(0, "%s: couldn't get target port group", pp->dev); + break; + case ALUA_PRIO_GETAAS_FAILED: + condlog(0, "%s: couln't get asymmetric access state", pp->dev); + break; + } + } + return rc; +} diff --git a/libmultipath/prioritizers/alua.h b/libmultipath/prioritizers/alua.h new file mode 100644 index 0000000..78a3d15 --- /dev/null +++ b/libmultipath/prioritizers/alua.h @@ -0,0 +1,9 @@ +#ifndef _ALUA_H +#define _ALUA_H + +#include "alua_rtpg.h" + +#define PRIO_ALUA "alua" +int prio_alua(struct path * pp); + +#endif diff --git a/libmultipath/prioritizers/alua_rtpg.c b/libmultipath/prioritizers/alua_rtpg.c new file mode 100644 index 0000000..c5528c5 --- /dev/null +++ b/libmultipath/prioritizers/alua_rtpg.c @@ -0,0 +1,301 @@ +/* + * (C) Copyright IBM Corp. 2004, 2005 All Rights Reserved. + * + * rtpg.c + * + * Tool to make use of a SCSI-feature called Asymmetric Logical Unit Access. + * It determines the ALUA state of a device and prints a priority value to + * stdout. + * + * Author(s): Jan Kunigk + * S. Bader + * + * This file is released under the GPL. + */ +#include +#include +#include +#include +#include + +#define __user +#include + +#include "alua_rtpg.h" + +#define SENSE_BUFF_LEN 32 +#define DEF_TIMEOUT 300000 + +/* + * Macro used to print debug messaged. + */ +#if DEBUG > 0 +#define PRINT_DEBUG(f, a...) \ + fprintf(stderr, "DEBUG: " f, ##a) +#else +#define PRINT_DEBUG(f, a...) +#endif + +/* + * Optionally print the commands sent and the data received a hex dump. + */ +#if DEBUG > 0 +#if DEBUG_DUMPHEX > 0 +#define PRINT_HEX(p, l) print_hex(p, l) +void +print_hex(unsigned char *p, unsigned long len) +{ + int i; + + for(i = 0; i < len; i++) { + if (i % 16 == 0) + printf("%04x: ", i); + printf("%02x%s", p[i], (((i + 1) % 16) == 0) ? "\n" : " "); + } + printf("\n"); +} +#else +#define PRINT_HEX(p, l) +#endif +#else +#define PRINT_HEX(p, l) +#endif + +/* + * Returns 0 if the SCSI command either was successful or if the an error was + * recovered, otherwise 1. (definitions taken from sg_err.h) + */ +#define SCSI_CHECK_CONDITION 0x2 +#define SCSI_COMMAND_TERMINATED 0x22 +#define SG_ERR_DRIVER_SENSE 0x08 +#define RECOVERED_ERROR 0x01 + +static int +scsi_error(struct sg_io_hdr *hdr) +{ + /* Treat SG_ERR here to get rid of sg_err.[ch] */ + hdr->status &= 0x7e; + + if ( + (hdr->status == 0) && + (hdr->host_status == 0) && + (hdr->driver_status == 0) + ) { + return 0; + } + + if ( + (hdr->status == SCSI_CHECK_CONDITION) || + (hdr->status == SCSI_COMMAND_TERMINATED) || + ((hdr->driver_status & 0xf) == SG_ERR_DRIVER_SENSE) + ) { + if (hdr->sbp && (hdr->sb_len_wr > 2)) { + int sense_key; + unsigned char * sense_buffer = hdr->sbp; + + if (sense_buffer[0] & 0x2) + sense_key = sense_buffer[1] & 0xf; + else + sense_key = sense_buffer[2] & 0xf; + + if (sense_key == RECOVERED_ERROR) + return 0; + } + } + + return 1; +} + +/* + * Helper function to setup and run a SCSI inquiry command. + */ +int +do_inquiry(int fd, int evpd, unsigned int codepage, void *resp, int resplen) +{ + struct inquiry_command cmd; + struct sg_io_hdr hdr; + unsigned char sense[SENSE_BUFF_LEN]; + + memset(&cmd, 0, sizeof(cmd)); + cmd.op = OPERATION_CODE_INQUIRY; + if (evpd) { + inquiry_command_set_evpd(&cmd); + cmd.page = codepage; + } + set_uint16(cmd.length, resplen); + PRINT_HEX((unsigned char *) &cmd, sizeof(cmd)); + + memset(&hdr, 0, sizeof(hdr)); + hdr.interface_id = 'S'; + hdr.cmdp = (unsigned char *) &cmd; + hdr.cmd_len = sizeof(cmd); + hdr.dxfer_direction = SG_DXFER_FROM_DEV; + hdr.dxferp = resp; + hdr.dxfer_len = resplen; + hdr.sbp = sense; + hdr.mx_sb_len = sizeof(sense); + hdr.timeout = DEF_TIMEOUT; + + if (ioctl(fd, SG_IO, &hdr) < 0) { + PRINT_DEBUG("do_inquiry: IOCTL failed!\n"); + return -RTPG_INQUIRY_FAILED; + } + + if (scsi_error(&hdr)) { + PRINT_DEBUG("do_inquiry: SCSI error!\n"); + return -RTPG_INQUIRY_FAILED; + } + PRINT_HEX((unsigned char *) resp, resplen); + + return 0; +} + +/* + * This function returns the support for target port groups by evaluating the + * data returned by the standard inquiry command. + */ +int +get_target_port_group_support(int fd) +{ + struct inquiry_data inq; + int rc; + + rc = do_inquiry(fd, 0, 0x00, &inq, sizeof(inq)); + if (!rc) { + rc = inquiry_data_get_tpgs(&inq); + } + + return rc; +} + +int +get_target_port_group(int fd) +{ + unsigned char buf[128]; + struct vpd83_data * vpd83; + struct vpd83_dscr * dscr; + int rc; + + rc = do_inquiry(fd, 1, 0x83, buf, sizeof(buf)); + if (!rc) { + vpd83 = (struct vpd83_data *) buf; + + rc = -RTPG_NO_TPG_IDENTIFIER; + FOR_EACH_VPD83_DSCR(vpd83, dscr) { + if ((((char *) dscr) - ((char *) vpd83)) > sizeof(buf)) + break; + + if (vpd83_dscr_istype(dscr, IDTYPE_TARGET_PORT_GROUP)) { + struct vpd83_tpg_dscr * p; + + if (rc != -RTPG_NO_TPG_IDENTIFIER) { + PRINT_DEBUG("get_target_port_group: " + "more than one TPG identifier " + "found!\n"); + continue; + } + + p = (struct vpd83_tpg_dscr *) dscr->data; + rc = get_uint16(p->tpg); + } + } + if (rc == -RTPG_NO_TPG_IDENTIFIER) { + PRINT_DEBUG("get_target_port_group: " + "no TPG identifier found!\n"); + } + } + + return rc; +} + +int +do_rtpg(int fd, void* resp, long resplen) +{ + struct rtpg_command cmd; + struct sg_io_hdr hdr; + unsigned char sense[SENSE_BUFF_LEN]; + + memset(&cmd, 0, sizeof(cmd)); + cmd.op = OPERATION_CODE_RTPG; + rtpg_command_set_service_action(&cmd); + set_uint32(cmd.length, resplen); + PRINT_HEX((unsigned char *) &cmd, sizeof(cmd)); + + memset(&hdr, 0, sizeof(hdr)); + hdr.interface_id = 'S'; + hdr.cmdp = (unsigned char *) &cmd; + hdr.cmd_len = sizeof(cmd); + hdr.dxfer_direction = SG_DXFER_FROM_DEV; + hdr.dxferp = resp; + hdr.dxfer_len = resplen; + hdr.mx_sb_len = sizeof(sense); + hdr.sbp = sense; + hdr.timeout = DEF_TIMEOUT; + + if (ioctl(fd, SG_IO, &hdr) < 0) + return -RTPG_RTPG_FAILED; + + if (scsi_error(&hdr)) { + PRINT_DEBUG("do_rtpg: SCSI error!\n"); + return -RTPG_RTPG_FAILED; + } + PRINT_HEX(resp, resplen); + + return 0; +} + +int +get_asymmetric_access_state(int fd, unsigned int tpg) +{ + unsigned char *buf; + struct rtpg_data * tpgd; + struct rtpg_tpg_dscr * dscr; + int rc; + 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; + RTPG_FOR_EACH_PORT_GROUP(tpgd, dscr) { + if (get_uint16(dscr->tpg) == tpg) { + if (rc != -RTPG_TPG_NOT_FOUND) { + PRINT_DEBUG("get_asymmetric_access_state: " + "more than one entry with same port " + "group.\n"); + } else { + PRINT_DEBUG("pref=%i\n", dscr->pref); + rc = rtpg_tpg_dscr_get_aas(dscr); + } + } + } +out: + free(buf); + return rc; +} + diff --git a/libmultipath/prioritizers/alua_rtpg.h b/libmultipath/prioritizers/alua_rtpg.h new file mode 100644 index 0000000..c43e0a9 --- /dev/null +++ b/libmultipath/prioritizers/alua_rtpg.h @@ -0,0 +1,30 @@ +/* + * (C) Copyright IBM Corp. 2004, 2005 All Rights Reserved. + * + * rtpg.h + * + * Tool to make use of a SCSI-feature called Asymmetric Logical Unit Access. + * It determines the ALUA state of a device and prints a priority value to + * stdout. + * + * Author(s): Jan Kunigk + * S. Bader + * + * This file is released under the GPL. + */ +#ifndef __RTPG_H__ +#define __RTPG_H__ +#include "alua_spc3.h" + +#define RTPG_SUCCESS 0 +#define RTPG_INQUIRY_FAILED 1 +#define RTPG_NO_TPG_IDENTIFIER 2 +#define RTPG_RTPG_FAILED 3 +#define RTPG_TPG_NOT_FOUND 4 + +int get_target_port_group_support(int fd); +int get_target_port_group(int fd); +int get_asymmetric_access_state(int fd, unsigned int tpg); + +#endif /* __RTPG_H__ */ + diff --git a/libmultipath/prioritizers/alua_spc3.h b/libmultipath/prioritizers/alua_spc3.h new file mode 100644 index 0000000..bddbbdd --- /dev/null +++ b/libmultipath/prioritizers/alua_spc3.h @@ -0,0 +1,322 @@ +/* + * (C) Copyright IBM Corp. 2004, 2005 All Rights Reserved. + * + * spc3.h + * + * Tool to make use of a SCSI-feature called Asymmetric Logical Unit Access. + * It determines the ALUA state of a device and prints a priority value to + * stdout. + * + * Author(s): Jan Kunigk + * S. Bader + * + * This file is released under the GPL. + */ +#ifndef __SPC3_H__ +#define __SPC3_H__ +/*============================================================================= + * Some helper functions for getting and setting 16 and 32 bit values. + *============================================================================= + */ +static inline unsigned short +get_uint16(unsigned char *p) +{ + return (p[0] << 8) + p[1]; +} + +static inline void +set_uint16(unsigned char *p, unsigned short v) +{ + p[0] = (v >> 8) & 0xff; + p[1] = v & 0xff; +} + +static inline unsigned int +get_uint32(unsigned char *p) +{ + return (p[0] << 24) + (p[1] << 16) + (p[2] << 8) + p[3]; +} + +static inline void +set_uint32(unsigned char *p, unsigned int v) +{ + p[0] = (v >> 24) & 0xff; + p[1] = (v >> 16) & 0xff; + p[2] = (v >> 8) & 0xff; + p[3] = v & 0xff; +} + +/*============================================================================= + * Definitions to support the standard inquiry command as defined in SPC-3. + * If the evpd (enable vital product data) bit is set the data that will be + * returned is selected by the page field. This field must be 0 if the evpd + * bit is not set. + *============================================================================= + */ +#define OPERATION_CODE_INQUIRY 0x12 + +struct inquiry_command { + unsigned char op; + unsigned char b1; /* xxxxxx.. = reserved */ + /* ......x. = obsolete */ + /* .......x = evpd */ + unsigned char page; + unsigned char length[2]; + unsigned char control; +} __attribute__((packed)); + +static inline void +inquiry_command_set_evpd(struct inquiry_command *ic) +{ + ic->b1 |= 1; +} + +/*----------------------------------------------------------------------------- + * Data returned by the standard inquiry command. + *----------------------------------------------------------------------------- + * + * Peripheral qualifier codes. + */ +#define PQ_CONNECTED 0x0 +#define PQ_DISCONNECTED 0x1 +#define PQ_UNSUPPORTED 0x3 + +/* Defined peripheral device types. */ +#define PDT_DIRECT_ACCESS 0x00 +#define PDT_SEQUENTIAL_ACCESS 0x01 +#define PDT_PRINTER 0x02 +#define PDT_PROCESSOR 0x03 +#define PDT_WRITE_ONCE 0x04 +#define PDT_CD_DVD 0x05 +#define PDT_SCANNER 0x06 +#define PDT_OPTICAL_MEMORY 0x07 +#define PDT_MEDIUM_CHANGER 0x08 +#define PDT_COMMUNICATIONS 0x09 +#define PDT_STORAGE_ARRAY_CONTROLLER 0x0c +#define PDT_ENCLOSURE_SERVICES 0x0d +#define PDT_SIMPLIFIED_DIRECT_ACCESS 0x0e +#define PDT_OPTICAL_CARD_READER_WRITER 0x0f +#define PDT_BRIDGE_CONTROLLER 0x10 +#define PDT_OBJECT_BASED 0x11 +#define PDT_AUTOMATION_INTERFACE 0x12 +#define PDT_LUN 0x1e +#define PDT_UNKNOWN 0x1f + +/* Defined version codes. */ +#define VERSION_NONE 0x00 +#define VERSION_SPC 0x03 +#define VERSION_SPC2 0x04 +#define VERSION_SPC3 0x05 + +/* Defined TPGS field values. */ +#define TPGS_NONE 0x0 +#define TPGS_IMPLICIT 0x1 +#define TPGS_EXPLICIT 0x2 +#define TPGS_BOTH 0x3 + +struct inquiry_data { + unsigned char b0; /* xxx..... = peripheral_qualifier */ + /* ...xxxxx = peripheral_device_type */ + unsigned char b1; /* x....... = removable medium */ + /* .xxxxxxx = reserverd */ + unsigned char version; + unsigned char b3; /* xx...... = obsolete */ + /* ..x..... = normal aca supported */ + /* ...x.... = hirarchichal lun supp. */ + /* ....xxxx = response format */ + /* 2 is spc-3 format */ + unsigned char length; + unsigned char b5; /* x....... = storage controller */ + /* component supported */ + /* .x...... = access controls coord. */ + /* ..xx.... = target port group supp.*/ + /* ....x... = third party copy supp. */ + /* .....xx. = reserved */ + /* .......x = protection info supp. */ + unsigned char b6; /* x....... = bque */ + /* .x...... = enclosure services sup.*/ + /* ..x..... = vs1 */ + /* ...x.... = multiport support */ + /* ....x... = medium changer */ + /* .....xx. = obsolete */ + /* .......x = add16 */ + unsigned char b7; /* xx...... = obsolete */ + /* ..x..... = wbus16 */ + /* ...x.... = sync */ + /* ....x... = linked commands supp. */ + /* .....x.. = obsolete */ + /* ......x. = command queue support */ + /* .......x = vs2 */ + unsigned char vendor_identification[8]; + unsigned char product_identification[16]; + unsigned char product_revision[4]; + unsigned char vendor_specific[20]; + unsigned char b56; /* xxxx.... = reserved */ + /* ....xx.. = clocking */ + /* ......x. = qas */ + /* .......x = ius */ + unsigned char reserved4; + unsigned char version_descriptor[8][2]; + unsigned char reserved5[22]; + unsigned char vendor_parameters[0]; +} __attribute__((packed)); + +static inline int +inquiry_data_get_tpgs(struct inquiry_data *id) +{ + return (id->b5 >> 4) & 3; +} + +/*----------------------------------------------------------------------------- + * Inquiry data returned when requesting vital product data page 0x83. + *----------------------------------------------------------------------------- + */ +#define CODESET_BINARY 0x1 +#define CODESET_ACSII 0x2 +#define CODESET_UTF8 0x3 + +#define ASSOCIATION_UNIT 0x0 +#define ASSOCIATION_PORT 0x1 +#define ASSOCIATION_DEVICE 0x2 + +#define IDTYPE_VENDOR_SPECIFIC 0x0 +#define IDTYPE_T10_VENDOR_ID 0x1 +#define IDTYPE_EUI64 0x2 +#define IDTYPE_NAA 0x3 +#define IDTYPE_RELATIVE_TPG_ID 0x4 +#define IDTYPE_TARGET_PORT_GROUP 0x5 +#define IDTYPE_LUN_GROUP 0x6 +#define IDTYPE_MD5_LUN_ID 0x7 +#define IDTYPE_SCSI_NAME_STRING 0x8 + +struct vpd83_tpg_dscr { + unsigned char reserved1[2]; + unsigned char tpg[2]; +} __attribute__((packed)); + +struct vpd83_dscr { + unsigned char b0; /* xxxx.... = protocol id */ + /* ....xxxx = codeset */ + unsigned char b1; /* x....... = protocol id valid */ + /* .x...... = reserved */ + /* ..xx.... = association */ + /* ....xxxx = id type */ + unsigned char reserved2; + unsigned char length; /* size-4 */ + unsigned char data[0]; +} __attribute__((packed)); + +static inline int +vpd83_dscr_istype(struct vpd83_dscr *d, unsigned char type) +{ + return ((d->b1 & 7) == type); +} + +struct vpd83_data { + unsigned char b0; /* xxx..... = peripheral_qualifier */ + /* ...xxxxx = peripheral_device_type */ + unsigned char page_code; /* 0x83 */ + unsigned char length[2]; /* size-4 */ + struct vpd83_dscr data[0]; +} __attribute__((packed)); + +/*----------------------------------------------------------------------------- + * This macro should be used to walk through all identification descriptors + * defined in the code page 0x83. + * The argument p is a pointer to the code page 0x83 data and d is used to + * point to the current descriptor. + *----------------------------------------------------------------------------- + */ +#define FOR_EACH_VPD83_DSCR(p, d) \ + for( \ + d = p->data; \ + (((char *) d) - ((char *) p)) < \ + get_uint16(p->length); \ + d = (struct vpd83_dscr *) \ + ((char *) d + d->length + 4) \ + ) + +/*============================================================================= + * The following stuctures and macros are used to call the report target port + * groups command defined in SPC-3. + * This command is used to get information about the target port groups (which + * states are supported, which ports belong to this group, and so on) and the + * current state of each target port group. + *============================================================================= + */ +#define OPERATION_CODE_RTPG 0xa3 +#define SERVICE_ACTION_RTPG 0x0a + +struct rtpg_command { + unsigned char op; /* 0xa3 */ + unsigned char b1; /* xxx..... = reserved */ + /* ...xxxxx = service action (0x0a) */ + unsigned char reserved2[4]; + unsigned char length[4]; + unsigned char reserved3; + unsigned char control; +} __attribute__((packed)); + +static inline void +rtpg_command_set_service_action(struct rtpg_command *cmd) +{ + cmd->b1 = (cmd->b1 & 0xe0) | SERVICE_ACTION_RTPG; +} + +struct rtpg_tp_dscr { + unsigned char obsolete1[2]; + /* The Relative Target Port Identifier of a target port. */ + unsigned char rtpi[2]; +} __attribute__((packed)); + +#define AAS_OPTIMIZED 0x0 +#define AAS_NON_OPTIMIZED 0x1 +#define AAS_STANDBY 0x2 +#define AAS_UNAVAILABLE 0x3 +#define AAS_TRANSITIONING 0xf + +#define TPG_STATUS_NONE 0x0 +#define TPG_STATUS_SET 0x1 +#define TPG_STATUS_IMPLICIT_CHANGE 0x2 + +struct rtpg_tpg_dscr { + unsigned char b0; /* x....... = pref(ered) port */ + /* .xxx.... = reserved */ + /* ....xxxx = asymetric access state */ + unsigned char b1; /* xxxx.... = reserved */ + /* ....x... = unavailable support */ + /* .....x.. = standby support */ + /* ......x. = non-optimized support */ + /* .......x = optimized support */ + unsigned char tpg[2]; + unsigned char reserved3; + unsigned char status; + unsigned char vendor_unique; + unsigned char port_count; + struct rtpg_tp_dscr data[0]; +} __attribute__((packed)); + +static inline int +rtpg_tpg_dscr_get_aas(struct rtpg_tpg_dscr *d) +{ + return (d->b0 & 0x0f); +} + +struct rtpg_data { + unsigned char length[4]; /* size-4 */ + struct rtpg_tpg_dscr data[0]; +} __attribute__((packed)); + +#define RTPG_FOR_EACH_PORT_GROUP(p, g) \ + for( \ + g = &(p->data[0]); \ + (((char *) g) - ((char *) p)) < get_uint32(p->length); \ + g = (struct rtpg_tpg_dscr *) ( \ + ((char *) g) + \ + sizeof(struct rtpg_tpg_dscr) + \ + g->port_count * sizeof(struct rtpg_tp_dscr) \ + ) \ + ) + +#endif /* __SPC3_H__ */ + diff --git a/libmultipath/prioritizers/const.c b/libmultipath/prioritizers/const.c new file mode 100644 index 0000000..529cf82 --- /dev/null +++ b/libmultipath/prioritizers/const.c @@ -0,0 +1,8 @@ +#include + +#include + +int getprio (struct path * pp) +{ + return 1; +} diff --git a/libmultipath/prioritizers/const.h b/libmultipath/prioritizers/const.h new file mode 100644 index 0000000..220db54 --- /dev/null +++ b/libmultipath/prioritizers/const.h @@ -0,0 +1,7 @@ +#ifndef _CONST_H +#define _CONST_H + +#define PRIO_CONST "const" +int prio_const(struct path * pp); + +#endif diff --git a/libmultipath/prioritizers/emc.c b/libmultipath/prioritizers/emc.c new file mode 100644 index 0000000..61d7a77 --- /dev/null +++ b/libmultipath/prioritizers/emc.c @@ -0,0 +1,79 @@ +#include +#include +#include + +#include +#include +#include + +#define INQUIRY_CMD 0x12 +#define INQUIRY_CMDLEN 6 + +#define pp_emc_log(prio, msg) condlog(prio, "%s: emc prio: " msg, dev) + +int emc_clariion_prio(const char *dev, int fd) +{ + unsigned char sense_buffer[256]; + unsigned char sb[128]; + unsigned char inqCmdBlk[INQUIRY_CMDLEN] = {INQUIRY_CMD, 1, 0xC0, 0, + sizeof(sb), 0}; + struct sg_io_hdr io_hdr; + int ret = 0; + + 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) { + pp_emc_log(0, "sending query command failed"); + goto out; + } + if (io_hdr.info & SG_INFO_OK_MASK) { + pp_emc_log(0, "query command indicates error"); + goto out; + } + + if (/* Verify the code page - right page & revision */ + sense_buffer[1] != 0xc0 || sense_buffer[9] != 0x00) { + pp_emc_log(0, "path unit report page in unknown format"); + goto out; + } + + if ( /* Effective initiator type */ + sense_buffer[27] != 0x03 + /* + * 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) { + pp_emc_log(0, "path not correctly configured for failover"); + } + + if ( /* LUN operations should indicate normal operations */ + sense_buffer[48] != 0x00) { + pp_emc_log(0, "path not available for normal operations"); + } + + /* Is the default owner equal to this path? */ + /* Note this will switch to the default priority group, even if + * it is not the currently active one. */ + ret = (sense_buffer[5] == sense_buffer[8]) ? 1 : 0; + +out: + return(ret); +} + +int getprio (struct path * pp) +{ + return emc_clariion_prio(pp->dev, pp->fd); +} diff --git a/libmultipath/prioritizers/emc.h b/libmultipath/prioritizers/emc.h new file mode 100644 index 0000000..0ab0bc5 --- /dev/null +++ b/libmultipath/prioritizers/emc.h @@ -0,0 +1,7 @@ +#ifndef _EMC_H +#define _EMC_H + +#define PRIO_EMC "emc" +int prio_emc(struct path * pp); + +#endif diff --git a/libmultipath/prioritizers/hds.c b/libmultipath/prioritizers/hds.c new file mode 100644 index 0000000..6ebe4d8 --- /dev/null +++ b/libmultipath/prioritizers/hds.c @@ -0,0 +1,170 @@ +/* + * (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 +#include +#include +#include +#include +#include + +#include +#include +#include + +#define INQ_REPLY_LEN 255 +#define INQ_CMD_CODE 0x12 +#define INQ_CMD_LEN 6 + +#define pp_hds_log(prio, fmt, args...) \ + condlog(prio, "%s: hds prio: " fmt, dev, ##args) + +int hds_modular_prio (const char *dev, int fd) +{ + int 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 ((ioctl (fd, SG_GET_VERSION_NUM, &k) < 0) || (k < 30000)) { + pp_hds_log(0, "can't use SG ioctl interface"); + return -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 (fd, SG_IO, &io_hdr) < 0) { + pp_hds_log(0, "SG_IO error"); + return -1; + } + if ((io_hdr.info & SG_INFO_OK_MASK) != SG_INFO_OK) { + pp_hds_log(0, "SCSI error"); + return -1; + } + + snprintf (vendor, 9, "%.8s", 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); + + pp_hds_log(4, "VENDOR: %s", vendor); + pp_hds_log(4, "PRODUCT: %s", product); + pp_hds_log(4, "SERIAL: 0x%s", serial); + pp_hds_log(4, "LDEV: 0x%s", ldev); + pp_hds_log(4, "CTRL: %s", ctrl); + pp_hds_log(4, "PORT: %s", 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': + pp_hds_log(4, "CTRL EVEN, LDEV EVEN, PRIO 1"); + return 1; + break; + case '1': case '3': case '5': case '7': case '9': case 'B': case 'D': case 'F': + pp_hds_log(4, "CTRL EVEN, LDEV ODD, PRIO 0"); + 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': + pp_hds_log(4, "CTRL ODD, LDEV EVEN, PRIO 0"); + return 0; + break; + case '1': case '3': case '5': case '7': case '9': case 'B': case 'D': case 'F': + pp_hds_log(4, "CTRL ODD, LDEV ODD, PRIO 1"); + return 1; + break; + } + } + return -1; +} + +int getprio (struct path * pp) +{ + return hds_modular_prio(pp->dev, pp->fd); +} diff --git a/libmultipath/prioritizers/hds.h b/libmultipath/prioritizers/hds.h new file mode 100644 index 0000000..084d77c --- /dev/null +++ b/libmultipath/prioritizers/hds.h @@ -0,0 +1,7 @@ +#ifndef _HDS_H +#define _HDS_H + +#define PRIO_HDS "hds" +int prio_hds(struct path * pp); + +#endif diff --git a/libmultipath/prioritizers/hp_sw.c b/libmultipath/prioritizers/hp_sw.c new file mode 100644 index 0000000..ae0975f --- /dev/null +++ b/libmultipath/prioritizers/hp_sw.c @@ -0,0 +1,100 @@ +/* + * Path priority checker for HP active/standby controller + * + * Check the path state and sort them into groups. + * There is actually a preferred path in the controller; + * we should ask HP on how to retrieve that information. + */ +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define TUR_CMD_LEN 6 +#define SCSI_CHECK_CONDITION 0x2 +#define SCSI_COMMAND_TERMINATED 0x22 +#define SG_ERR_DRIVER_SENSE 0x08 +#define RECOVERED_ERROR 0x01 +#define NOT_READY 0x02 +#define UNIT_ATTENTION 0x06 + +#define HP_PATH_ACTIVE 0x04 +#define HP_PATH_STANDBY 0x02 +#define HP_PATH_FAILED 0x00 + +#define pp_hp_sw_log(prio, fmt, args...) \ + condlog(prio, "%s: hp_sw prio: " fmt, dev, ##args) + +int hp_sw_prio(const char *dev, int fd) +{ + 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; + + 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) { + pp_hp_sw_log(0, "sending tur command failed"); + 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; + } + } + } + } +out: + return(ret); +} + +int getprio (struct path * pp) +{ + return hp_sw_prio(pp->dev, pp->fd); +} diff --git a/libmultipath/prioritizers/hp_sw.h b/libmultipath/prioritizers/hp_sw.h new file mode 100644 index 0000000..2fea486 --- /dev/null +++ b/libmultipath/prioritizers/hp_sw.h @@ -0,0 +1,7 @@ +#ifndef _HP_SW_H +#define _HP_SW_H + +#define PRIO_HP_SW "hp_sw" +int prio_hp_sw(struct path * pp); + +#endif diff --git a/libmultipath/prioritizers/netapp.c b/libmultipath/prioritizers/netapp.c new file mode 100644 index 0000000..812ecf6 --- /dev/null +++ b/libmultipath/prioritizers/netapp.c @@ -0,0 +1,243 @@ +/* + * Copyright 2005 Network Appliance, Inc., All Rights Reserved + * Author: David Wysochanski available at davidw@netapp.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 v2 for more details. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define INQUIRY_CMD 0x12 +#define INQUIRY_CMDLEN 6 +#define DEFAULT_PRIOVAL 10 +#define RESULTS_MAX 256 +#define SG_TIMEOUT 30000 + +#define pp_netapp_log(prio, fmt, args...) \ + condlog(prio, "%s: netapp prio: " fmt, dev, ##args) + +static void dump_cdb(unsigned char *cdb, int size) +{ + int i; + char buf[10*5+1]; + char * p = &buf[0]; + + condlog(0, "- SCSI CDB: "); + for (i=0; imasked_status, + io_hdr->host_status, io_hdr->driver_status); + if (io_hdr->sb_len_wr > 0) { + condlog(0, "- SCSI sense data: "); + for (i=0; isb_len_wr; i++) { + p += snprintf(p, 128*(io_hdr->sb_len_wr-i), "0x%02x ", + io_hdr->sbp[i]); + } + condlog(0, "%s", buf); + } +} + +/* + * Returns: + * -1: error, errno set + * 0: success + */ +static int send_gva(const char *dev, int fd, unsigned char pg, + unsigned char *results, int *results_size) +{ + unsigned char sb[128]; + unsigned char cdb[10] = {0xc0, 0, 0x1, 0xa, 0x98, 0xa, + pg, sizeof(sb), 0, 0}; + struct sg_io_hdr io_hdr; + int ret = -1; + + memset(&io_hdr, 0, sizeof (struct sg_io_hdr)); + io_hdr.interface_id = 'S'; + io_hdr.cmd_len = sizeof (cdb); + io_hdr.mx_sb_len = sizeof (sb); + io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; + io_hdr.dxfer_len = *results_size; + io_hdr.dxferp = results; + io_hdr.cmdp = cdb; + io_hdr.sbp = sb; + io_hdr.timeout = SG_TIMEOUT; + io_hdr.pack_id = 0; + if (ioctl(fd, SG_IO, &io_hdr) < 0) { + pp_netapp_log(0, "SG_IO ioctl failed, errno=%d", errno); + dump_cdb(cdb, sizeof(cdb)); + goto out; + } + if (io_hdr.info & SG_INFO_OK_MASK) { + pp_netapp_log(0, "SCSI error"); + dump_cdb(cdb, sizeof(cdb)); + process_sg_error(&io_hdr); + goto out; + } + + if (results[4] != 0x0a || results[5] != 0x98 || + results[6] != 0x0a ||results[7] != 0x01) { + dump_cdb(cdb, sizeof(cdb)); + pp_netapp_log(0, "GVA return wrong format "); + pp_netapp_log(0, "results[4-7] = 0x%02x 0x%02x 0x%02x 0x%02x", + results[4], results[5], results[6], results[7]); + goto out; + } + ret = 0; + out: + return(ret); +} + +/* + * Retuns: + * -1: Unable to obtain proxy info + * 0: Device _not_ proxy path + * 1: Device _is_ proxy path + */ +static int get_proxy(const char *dev, int fd) +{ + unsigned char results[256]; + unsigned char sb[128]; + unsigned char cdb[INQUIRY_CMDLEN] = {INQUIRY_CMD, 1, 0xc1, 0, + sizeof(sb), 0}; + struct sg_io_hdr io_hdr; + int ret = -1; + + memset(&results, 0, sizeof (results)); + memset(&io_hdr, 0, sizeof (struct sg_io_hdr)); + io_hdr.interface_id = 'S'; + io_hdr.cmd_len = sizeof (cdb); + io_hdr.mx_sb_len = sizeof (sb); + io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; + io_hdr.dxfer_len = sizeof (results); + io_hdr.dxferp = results; + io_hdr.cmdp = cdb; + io_hdr.sbp = sb; + io_hdr.timeout = SG_TIMEOUT; + io_hdr.pack_id = 0; + if (ioctl(fd, SG_IO, &io_hdr) < 0) { + pp_netapp_log(0, "ioctl sending inquiry command failed, " + "errno=%d", errno); + dump_cdb(cdb, sizeof(cdb)); + goto out; + } + if (io_hdr.info & SG_INFO_OK_MASK) { + pp_netapp_log(0, "SCSI error"); + dump_cdb(cdb, sizeof(cdb)); + process_sg_error(&io_hdr); + goto out; + } + + if (results[1] != 0xc1 || results[8] != 0x0a || + results[9] != 0x98 || results[10] != 0x0a || + results[11] != 0x0 || results[12] != 0xc1 || + results[13] != 0x0) { + pp_netapp_log(0,"proxy info page in unknown format - "); + pp_netapp_log(0,"results[8-13]=0x%02x 0x%02x 0x%02x 0x%02x " + "0x%02x 0x%02x", + results[8], results[9], results[10], + results[11], results[12], results[13]); + dump_cdb(cdb, sizeof(cdb)); + goto out; + } + ret = (results[19] & 0x02) >> 1; + + out: + return(ret); +} + +/* + * Returns priority of device based on device info. + * + * 4: FCP non-proxy, FCP proxy unknown, or unable to determine protocol + * 3: iSCSI HBA + * 2: iSCSI software + * 1: FCP proxy + */ +static int netapp_prio(const char *dev, int fd) +{ + unsigned char results[RESULTS_MAX]; + int results_size=RESULTS_MAX; + int rc; + int is_proxy; + int is_iscsi_software; + int is_iscsi_hardware; + int tot_len; + + is_iscsi_software = is_iscsi_hardware = is_proxy = 0; + + memset(&results, 0, sizeof (results)); + rc = send_gva(dev, fd, 0x41, results, &results_size); + if (rc == 0) { + tot_len = results[0] << 24 | results[1] << 16 | + results[2] << 8 | results[3]; + if (tot_len <= 8) { + goto try_fcp_proxy; + } + if (results[8] != 0x41) { + pp_netapp_log(0, "GVA page 0x41 error - " + "results[8] = 0x%x", results[8]); + goto try_fcp_proxy; + } + if ((strncmp((char *)&results[12], "ism_sw", 6) == 0) || + (strncmp((char *)&results[12], "iswt", 4) == 0)) { + is_iscsi_software = 1; + goto prio_select; + } + else if (strncmp((char *)&results[12], "ism_sn", 6) == 0) { + is_iscsi_hardware = 1; + goto prio_select; + } + } + + try_fcp_proxy: + rc = get_proxy(dev, fd); + if (rc >= 0) { + is_proxy = rc; + } + + prio_select: + if (is_iscsi_hardware) { + return 3; + } else if (is_iscsi_software) { + return 2; + } else { + if (is_proxy) { + return 1; + } else { + /* Either non-proxy, or couldn't get proxy info */ + return 4; + } + } +} + +int getprio (struct path * pp) +{ + return netapp_prio(pp->dev, pp->fd); +} diff --git a/libmultipath/prioritizers/netapp.h b/libmultipath/prioritizers/netapp.h new file mode 100644 index 0000000..ec38821 --- /dev/null +++ b/libmultipath/prioritizers/netapp.h @@ -0,0 +1,7 @@ +#ifndef _NETAPP_H +#define _NETAPP_H + +#define PRIO_NETAPP "netapp" +int prio_netapp(struct path * pp); + +#endif diff --git a/libmultipath/prioritizers/random.c b/libmultipath/prioritizers/random.c new file mode 100644 index 0000000..e3852a7 --- /dev/null +++ b/libmultipath/prioritizers/random.c @@ -0,0 +1,15 @@ +#include +#include +#include +#include + +#include + +int getprio (struct path * pp) +{ + struct timeval tv; + + gettimeofday(&tv, NULL); + srand((unsigned int)tv.tv_usec); + return 1+(int) (10.0*rand()/(RAND_MAX+1.0)); +} diff --git a/libmultipath/prioritizers/random.h b/libmultipath/prioritizers/random.h new file mode 100644 index 0000000..b9dae69 --- /dev/null +++ b/libmultipath/prioritizers/random.h @@ -0,0 +1,7 @@ +#ifndef _RANDOM_H +#define _RANDOM_H + +#define PRIO_RANDOM "random" +int prio_random(struct path * pp); + +#endif diff --git a/libmultipath/prioritizers/rdac.c b/libmultipath/prioritizers/rdac.c new file mode 100644 index 0000000..4dd8f44 --- /dev/null +++ b/libmultipath/prioritizers/rdac.c @@ -0,0 +1,90 @@ +#include +#include +#include + +#include +#include +#include + +#define INQUIRY_CMD 0x12 +#define INQUIRY_CMDLEN 6 + +#define pp_rdac_log(prio, msg) condlog(prio, "%s: rdac prio: " msg, dev) + +int rdac_prio(const char *dev, int fd) +{ + 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; + + 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) { + pp_rdac_log(0, "sending inquiry command failed"); + goto out; + } + if (io_hdr.info & SG_INFO_OK_MASK) { + pp_rdac_log(0, "inquiry command indicates error"); + goto out; + } + + 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' ) { + pp_rdac_log(0, "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 getprio (struct path * pp) +{ + return rdac_prio(pp->dev, pp->fd); +} diff --git a/libmultipath/prioritizers/rdac.h b/libmultipath/prioritizers/rdac.h new file mode 100644 index 0000000..a5b7f8e --- /dev/null +++ b/libmultipath/prioritizers/rdac.h @@ -0,0 +1,7 @@ +#ifndef _RDAC_H +#define _RDAC_H + +#define PRIO_RDAC "rdac" +int prio_rdac(struct path * pp); + +#endif diff --git a/libmultipath/propsel.c b/libmultipath/propsel.c index 79cee8b..0c7fb99 100644 --- a/libmultipath/propsel.c +++ b/libmultipath/propsel.c @@ -5,8 +5,7 @@ */ #include -#include - +#include "checkers.h" #include "memory.h" #include "vector.h" #include "structs.h" @@ -15,6 +14,8 @@ #include "pgpolicies.h" #include "alias.h" #include "defaults.h" +#include "devmapper.h" +#include "prio.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; } @@ -246,22 +257,23 @@ select_getuid (struct path * pp) } extern int -select_getprio (struct path * pp) +select_prio (struct path * pp) { - if (pp->hwe && pp->hwe->getprio) { - pp->getprio = pp->hwe->getprio; - condlog(3, "%s: getprio = %s (controler setting)", - pp->dev, pp->getprio); + if (pp->hwe && pp->hwe->prio) { + pp->prio = pp->hwe->prio; + condlog(3, "%s: prio = %s (controller setting)", + pp->dev, prio_name(pp->prio)); return 0; } - if (conf->getprio) { - pp->getprio = conf->getprio; - condlog(3, "%s: getprio = %s (config file default)", - pp->dev, pp->getprio); + if (conf->prio) { + pp->prio = conf->prio; + condlog(3, "%s: prio = %s (config file default)", + pp->dev, prio_name(pp->prio)); return 0; } - pp->getprio = DEFAULT_GETPRIO; - condlog(3, "%s: getprio = NULL (internal default)", pp->dev); + pp->prio = prio_default(); + condlog(3, "%s: prio = %s (internal default)", + pp->dev, prio_name(pp->prio)); return 0; } @@ -276,7 +288,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 +315,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 +331,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..62802f8 100644 --- a/libmultipath/propsel.h +++ b/libmultipath/propsel.h @@ -7,6 +7,7 @@ int select_features (struct multipath * mp); int select_hwhandler (struct multipath * mp); int select_checker(struct path *pp); int select_getuid (struct path * pp); -int select_getprio (struct path * pp); +int select_prio (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..11f3061 100644 --- a/libmultipath/structs.c +++ b/libmultipath/structs.c @@ -6,8 +6,7 @@ #include #include -#include - +#include "checkers.h" #include "memory.h" #include "vector.h" #include "util.h" @@ -16,6 +15,8 @@ #include "debug.h" #include "structs_vec.h" #include "blacklist.h" +#include "waiter.h" +#include "prio.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..2517782 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,8 @@ #define NO_PATH_RETRY_FAIL -1 #define NO_PATH_RETRY_QUEUE -2 +#define MAX_FDS_UNLIMITED -1 + enum free_path_switch { KEEP_PATHS, FREE_PATHS @@ -40,6 +45,7 @@ enum sysfs_buses { SYSFS_BUS_SCSI, SYSFS_BUS_IDE, SYSFS_BUS_CCW, + SYSFS_BUS_CCISS, }; enum pathstates { @@ -55,6 +61,11 @@ enum pgstates { PGSTATE_ACTIVE }; +enum pgtimeouts { + PGTIMEOUT_UNDEF, + PGTIMEOUT_NONE +}; + struct scsi_idlun { int dev_id; int host_unique_id; @@ -78,9 +89,19 @@ struct scsi_dev { int host_no; }; +struct sysfs_device { + struct sysfs_device *parent; /* parent device */ + char devpath[PATH_SIZE]; + char subsystem[NAME_SIZE]; /* $class, $bus, drivers, module */ + char kernel[NAME_SIZE]; /* device instance name */ + char kernel_number[NAME_SIZE]; + char driver[NAME_SIZE]; /* device driver name */ +}; + struct path { char dev[FILE_NAME_SIZE]; char dev_t[BLK_DEV_SIZE]; + struct sysfs_device *sysdev; struct scsi_idlun scsi_id; struct sg_id sg_id; char wwid[WWID_SIZE]; @@ -99,8 +120,7 @@ struct path { int priority; int pgindex; char * getuid; - char * getprio; - int getprio_selected; + struct prio * prio; struct checker checker; struct multipath * mpp; int fd; @@ -127,6 +147,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 +163,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 +172,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 +207,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..f4d69bb 100644 --- a/libmultipath/structs_vec.c +++ b/libmultipath/structs_vec.c @@ -2,19 +2,20 @@ #include #include -#include - +#include "checkers.h" #include "vector.h" #include "defaults.h" #include "debug.h" #include "structs.h" #include "structs_vec.h" +#include "waiter.h" #include "devmapper.h" #include "dmparser.h" #include "config.h" #include "propsel.h" +#include "sysfs.h" #include "discovery.h" - +#include "prio.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())) @@ -80,8 +81,7 @@ orphan_path (struct path * pp) pp->mpp = NULL; pp->dmstate = PSTATE_UNDEF; pp->getuid = NULL; - pp->getprio = NULL; - pp->getprio_selected = 0; + pp->prio = NULL; checker_put(&pp->checker); if (pp->fd >= 0) close(pp->fd); @@ -96,7 +96,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); } } @@ -111,17 +111,23 @@ set_multipath_wwid (struct multipath * mpp) dm_get_uuid(mpp->alias, mpp->wwid); } -extern void -remove_map (struct multipath * mpp, struct vectors * vecs, - stop_waiter_thread_func *stop_waiter, int purge_vec) +#define KEEP_WAITER 0 +#define STOP_WAITER 1 +#define PURGE_VEC 1 + +static void +_remove_map (struct multipath * mpp, struct vectors * vecs, + int stop_waiter, int purge_vec) { int i; + condlog(4, "%s: remove multipath map", mpp->alias); + /* * stop the DM event waiter thread */ if (stop_waiter) - stop_waiter(mpp, vecs); + stop_waiter_thread(mpp, vecs); /* * clear references to this map @@ -139,14 +145,26 @@ remove_map (struct multipath * mpp, struct vectors * vecs, } extern void -remove_maps (struct vectors * vecs, - stop_waiter_thread_func *stop_waiter) +remove_map (struct multipath * mpp, struct vectors * vecs, int purge_vec) +{ + _remove_map(mpp, vecs, KEEP_WAITER, purge_vec); +} + +extern void +remove_map_and_stop_waiter (struct multipath * mpp, struct vectors * vecs, + int purge_vec) +{ + _remove_map(mpp, vecs, STOP_WAITER, purge_vec); +} + +static void +_remove_maps (struct vectors * vecs, int stop_waiter) { int i; struct multipath * mpp; vector_foreach_slot (vecs->mpvec, mpp, i) { - remove_map(mpp, vecs, stop_waiter, 1); + _remove_map(mpp, vecs, stop_waiter, 1); i--; } @@ -154,6 +172,18 @@ remove_maps (struct vectors * vecs, vecs->mpvec = NULL; } +extern void +remove_maps (struct vectors * vecs) +{ + _remove_maps(vecs, KEEP_WAITER); +} + +extern void +remove_maps_and_stop_waiters (struct vectors * vecs) +{ + _remove_maps(vecs, STOP_WAITER); +} + static struct hwentry * extract_hwe_from_path(struct multipath * mpp) { @@ -244,8 +274,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 +308,7 @@ retry: #endif goto retry; } + condlog(0, "%s: failed to setup multipath", mpp->alias); goto out; } @@ -277,18 +317,17 @@ 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); + remove_map(mpp, vecs, PURGE_VEC); return 1; } extern struct multipath * add_map_without_path (struct vectors * vecs, - int minor, char * alias, - start_waiter_thread_func *start_waiter) + int minor, char * alias) { struct multipath * mpp = alloc_multipath(); @@ -308,12 +347,12 @@ add_map_without_path (struct vectors * vecs, vector_set_slot(vecs->mpvec, mpp); - if (start_waiter(mpp, vecs)) + if (start_waiter_thread(mpp, vecs)) goto out; return mpp; out: - remove_map(mpp, vecs, NULL, 1); + remove_map(mpp, vecs, PURGE_VEC); return NULL; } @@ -346,7 +385,7 @@ add_map_with_path (struct vectors * vecs, return mpp; out: - remove_map(mpp, vecs, NULL, add_vec); + remove_map(mpp, vecs, PURGE_VEC); return NULL; } @@ -361,10 +400,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 +421,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..b8a416f 100644 --- a/libmultipath/structs_vec.h +++ b/libmultipath/structs_vec.h @@ -9,20 +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 *); - void set_no_path_retry(struct multipath *mpp); int adopt_paths (vector pathvec, struct multipath * mpp); @@ -34,15 +20,17 @@ int update_mpp_paths(struct multipath * mpp, vector pathvec); int setup_multipath (struct vectors * vecs, struct multipath * mpp); int update_multipath_strings (struct multipath *mpp, vector pathvec); -void remove_map (struct multipath * mpp, struct vectors * vecs, - stop_waiter_thread_func *stop_waiter, int purge_vec); -void remove_maps (struct vectors * vecs, - stop_waiter_thread_func *stop_waiter); +void remove_map (struct multipath * mpp, struct vectors * vecs, int purge_vec); +void remove_map_and_stop_waiter (struct multipath * mpp, struct vectors * vecs, int purge_vec); +void remove_maps (struct vectors * vecs); +void remove_maps_and_stop_waiters (struct vectors * vecs); struct multipath * add_map_without_path (struct vectors * vecs, - int minor, char * alias, - start_waiter_thread_func *start_waiter); + int minor, char * alias); struct multipath * add_map_with_path (struct vectors * vecs, struct path * pp, int add_vec); +int update_multipath (struct vectors *vecs, char *mapname); +void update_queue_mode_del_path(struct multipath *mpp); +void update_queue_mode_add_path(struct multipath *mpp); #endif /* _STRUCTS_VEC_H */ diff --git a/libmultipath/switchgroup.c b/libmultipath/switchgroup.c index 757543f..58b08f6 100644 --- a/libmultipath/switchgroup.c +++ b/libmultipath/switchgroup.c @@ -2,21 +2,36 @@ * Copyright (c) 2005 Christophe Varoqui * Copyright (c) 2005 Edward Goggin, EMC */ -#include - +#include "checkers.h" #include "vector.h" #include "structs.h" #include "switchgroup.h" +extern void +path_group_prio_update (struct pathgroup * pgp) +{ + int i; + int priority = 0; + struct path * pp; + + if (!pgp->paths) { + pgp->priority = 0; + return; + } + vector_foreach_slot (pgp->paths, pp, i) { + if (pp->state != PATH_DOWN) + priority += pp->priority; + } + pgp->priority = priority; +} + extern int select_path_group (struct multipath * mpp) { - int i, j; + int i; int highest = 0; int bestpg = 1; struct pathgroup * pgp; - struct path * pp; - int priority; if (!mpp->pg) return 1; @@ -25,14 +40,7 @@ select_path_group (struct multipath * mpp) if (!pgp->paths) continue; - priority = 0; - - vector_foreach_slot (pgp->paths, pp, j) { - if (pp->state != PATH_DOWN) - priority += pp->priority; - } - pgp->priority = priority; - + path_group_prio_update(pgp); if (pgp->priority > highest) { highest = pgp->priority; bestpg = i + 1; diff --git a/libmultipath/switchgroup.h b/libmultipath/switchgroup.h index edf6f24..9365e2e 100644 --- a/libmultipath/switchgroup.h +++ b/libmultipath/switchgroup.h @@ -1 +1,2 @@ +void path_group_prio_update (struct pathgroup * pgp); int select_path_group (struct multipath * mpp); diff --git a/libmultipath/sysfs.c b/libmultipath/sysfs.c new file mode 100644 index 0000000..9f11b95 --- /dev/null +++ b/libmultipath/sysfs.c @@ -0,0 +1,551 @@ +/* + * Copyright (C) 2005-2006 Kay Sievers + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "checkers.h" +#include "vector.h" +#include "structs.h" +#include "sysfs.h" +#include "list.h" +#include "util.h" + +char sysfs_path[PATH_SIZE]; + +/* attribute value cache */ +static LIST_HEAD(attr_list); +struct sysfs_attr { + struct list_head node; + char path[PATH_SIZE]; + char *value; /* points to value_local if value is cached */ + char value_local[NAME_SIZE]; +}; + +/* 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", sizeof(sysfs_path)); + 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) { + list_del(&sysdev_loop->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; + + /* we handle only these devpathes */ + if (devpath != NULL && + strncmp(devpath, "/devices/", 9) != 0 && + strncmp(devpath, "/subsystem/", 11) != 0 && + strncmp(devpath, "/module/", 8) != 0 && + strncmp(devpath, "/bus/", 5) != 0 && + strncmp(devpath, "/class/", 7) != 0 && + strncmp(devpath, "/block/", 7) != 0) { + dbg("invalid devpath '%s'", devpath); + return NULL; + } + + dbg("open '%s'", devpath); + strlcpy(devpath_real, devpath, sizeof(devpath_real)); + remove_trailing_chars(devpath_real, '/'); + if (devpath[0] == '\0' ) + return NULL; + + /* 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 (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)); + } else if (strncmp(dev->devpath, "/subsystem/", 11) == 0) { + pos = strrchr(dev->devpath, '/'); + if (pos == &dev->devpath[10]) + strlcpy(dev->subsystem, "subsystem", + sizeof(dev->subsystem)); + } else if (strncmp(dev->devpath, "/class/", 7) == 0) { + pos = strrchr(dev->devpath, '/'); + if (pos == &dev->devpath[6]) + strlcpy(dev->subsystem, "subsystem", + sizeof(dev->subsystem)); + } else if (strncmp(dev->devpath, "/bus/", 5) == 0) { + pos = strrchr(dev->devpath, '/'); + if (pos == &dev->devpath[4]) + strlcpy(dev->subsystem, "subsystem", + 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; + + 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'; + + 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; + } + + /* are we at the top level? */ + pos = strrchr(parent_devpath, '/'); + if (pos == NULL || pos == parent_devpath) + return NULL; + + /* 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)); + if(sysfs_len >= sizeof(path_full)) + sysfs_len = sizeof(path_full) - 1; + 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' can not be opened: %s", + path_full, strerror(errno)); + 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; +} + +int sysfs_lookup_devpath_by_subsys_id(char *devpath_full, size_t len, + const char *subsystem, const char *id) +{ + size_t sysfs_len; + char path_full[PATH_SIZE]; + char *path; + struct stat statbuf; + + sysfs_len = strlcpy(path_full, sysfs_path, sizeof(path_full)); + path = &path_full[sysfs_len]; + + if (strcmp(subsystem, "subsystem") == 0) { + strlcpy(path, "/subsystem/", sizeof(path_full) - sysfs_len); + strlcat(path, id, sizeof(path_full) - sysfs_len); + if (stat(path_full, &statbuf) == 0) + goto found; + + strlcpy(path, "/bus/", sizeof(path_full) - sysfs_len); + strlcat(path, id, sizeof(path_full) - sysfs_len); + if (stat(path_full, &statbuf) == 0) + goto found; + goto out; + + strlcpy(path, "/class/", sizeof(path_full) - sysfs_len); + strlcat(path, id, sizeof(path_full) - sysfs_len); + if (stat(path_full, &statbuf) == 0) + goto found; + } + + if (strcmp(subsystem, "module") == 0) { + strlcpy(path, "/module/", sizeof(path_full) - sysfs_len); + strlcat(path, id, sizeof(path_full) - sysfs_len); + if (stat(path_full, &statbuf) == 0) + goto found; + goto out; + } + + if (strcmp(subsystem, "drivers") == 0) { + char subsys[NAME_SIZE]; + char *driver; + + strlcpy(subsys, id, sizeof(subsys)); + driver = strchr(subsys, ':'); + if (driver != NULL) { + driver[0] = '\0'; + driver = &driver[1]; + strlcpy(path, "/subsystem/", sizeof(path_full) - sysfs_len); + strlcat(path, subsys, sizeof(path_full) - sysfs_len); + strlcat(path, "/drivers/", sizeof(path_full) - sysfs_len); + strlcat(path, driver, sizeof(path_full) - sysfs_len); + if (stat(path_full, &statbuf) == 0) + goto found; + + strlcpy(path, "/bus/", sizeof(path_full) - sysfs_len); + strlcat(path, subsys, sizeof(path_full) - sysfs_len); + strlcat(path, "/drivers/", sizeof(path_full) - sysfs_len); + strlcat(path, driver, sizeof(path_full) - sysfs_len); + if (stat(path_full, &statbuf) == 0) + goto found; + } + goto out; + } + + strlcpy(path, "/subsystem/", sizeof(path_full) - sysfs_len); + strlcat(path, subsystem, sizeof(path_full) - sysfs_len); + strlcat(path, "/devices/", sizeof(path_full) - sysfs_len); + strlcat(path, id, sizeof(path_full) - sysfs_len); + if (stat(path_full, &statbuf) == 0) + goto found; + + strlcpy(path, "/bus/", sizeof(path_full) - sysfs_len); + strlcat(path, subsystem, sizeof(path_full) - sysfs_len); + strlcat(path, "/devices/", sizeof(path_full) - sysfs_len); + strlcat(path, id, sizeof(path_full) - sysfs_len); + if (stat(path_full, &statbuf) == 0) + goto found; + + strlcpy(path, "/class/", sizeof(path_full) - sysfs_len); + strlcat(path, subsystem, sizeof(path_full) - sysfs_len); + strlcat(path, "/", sizeof(path_full) - sysfs_len); + strlcat(path, id, sizeof(path_full) - sysfs_len); + if (stat(path_full, &statbuf) == 0) + goto found; +out: + return 0; +found: + if (S_ISLNK(statbuf.st_mode)) + sysfs_resolve_link(path, sizeof(path_full) - sysfs_len); + strlcpy(devpath_full, path, len); + return 1; +} 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 #include #include +#include #include #include #include #include #include -#include +#include +#include #include #include #include @@ -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..c4d3266 --- /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 0x000408 +#define DATE_CODE 0x080207 + +#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 +#include +#include +#include +#include + +#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..49c9c5c 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 # @@ -38,7 +38,7 @@ # # scope : multipath # # desc : the default path grouping policy to apply to unspecified # # multipaths -# # default : multibus +# # default : failover # # # path_grouping_policy multibus # @@ -47,28 +47,28 @@ # # 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 +# # name : prio # # scope : multipath -# # desc : the default program and args to callout to obtain a path +# # desc : the default function to call to obtain a path # # priority value. The ALUA bits in SPC-3 provide an -# # exploitable prio value for example. "none" is a valid value +# # exploitable prio value for example. # # default : (null) # # -# #prio_callout "/bin/true" +# #prio "alua" # # # # # name : path_checker # # 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 @@ -80,6 +80,16 @@ # rr_min_io 100 # # # +# # name : max_fds +# # scope : multipathd +# # desc : Sets the maximum number of open file descriptors for the +# # multipathd process. +# # values : unlimited|n > 0 +# # default : None +# # +# max_fds 8192 +# +# # # # name : rr_weight # # scope : multipath # # desc : if set to priorities the multipath configurator will assign @@ -96,9 +106,9 @@ # # 0 means immediate failback, values >0 means deffered failback # # expressed in seconds. # # values : manual|immediate|n > 0 -# # default : immediate +# # default : manual # # -# failback manual +# failback immediate # # # # # name : no_path_retry @@ -142,6 +152,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 +208,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 @@ -203,9 +229,9 @@ # # 0 means immediate failback, values >0 means deffered failback # # expressed in seconds. # # values : manual|immediate|n > 0 -# # default : immediate +# # default : manual # # -# failback manual +# failback immediate # # # # # name : no_path_retry @@ -237,7 +263,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 +271,7 @@ # # # # name : device # # scope : multipath & multipathd -# # desc : settings for this specific storage controler +# # desc : settings for this specific storage controller # # # device { # # @@ -260,7 +286,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,29 +301,28 @@ # # 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 +# # name : prio # # scope : multipath -# # desc : the program and args to callout to obtain a path +# # desc : the function to call to obtain a path # # weight. Weights are summed for each path group to # # determine the next PG to use case of failure. -# # "none" is a valid value. # # default : no callout, all paths equals # # -# prio_callout "/sbin/mpath_prio_balance_units %d" +# prio "hp_sw" # # # # # 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 @@ -315,7 +340,7 @@ # # 0 means immediate failback, values >0 means deffered failback # # expressed in seconds. # # values : manual|immediate|n > 0 -# # default : immediate +# # default : manual # # # failback 30 # diff --git a/multipath.conf.synthetic b/multipath.conf.synthetic index 4a1f5a4..33f820b 100644 --- a/multipath.conf.synthetic +++ b/multipath.conf.synthetic @@ -7,16 +7,17 @@ # polling_interval 10 # selector "round-robin 0" # path_grouping_policy multibus -# getuid_callout "/sbin/scsi_id -g -u -s /block/%n" -# prio_callout /bin/true -# path_checker readsector0 +# getuid_callout "/lib/udev/scsi_id -g -u -s /block/%n" +# prio const +# path_checker directio # rr_min_io 100 +# max_fds 8192 # 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 +27,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 +53,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..71df431 100644 --- a/multipath/Makefile +++ b/multipath/Makefile @@ -1,54 +1,39 @@ # Makefile # # Copyright (C) 2003 Christophe Varoqui, -BUILD = glibc - +# include ../Makefile.inc -OBJS = main.o $(MULTIPATHLIB)-$(BUILD).a $(CHECKERSLIB)-$(BUILD).a +OBJS = main.o -CFLAGS += -I$(multipathdir) -I$(checkersdir) - -ifeq ($(strip $(BUILD)),klibc) - OBJS += $(libdm) $(libsysfs) -else - LDFLAGS += -ldevmapper -lsysfs -endif +CFLAGS += -I$(multipathdir) -Wl,-rpath,$(libdir) +LDFLAGS += -laio -ldevmapper -lpthread \ + -lmultipath -L$(multipathdir) EXEC = multipath -all: $(BUILD) - -prepare: - make -C $(multipathdir) prepare - rm -f core *.o *.gz +all: $(EXEC) -glibc: prepare $(OBJS) - $(CC) $(OBJS) -o $(EXEC) $(LDFLAGS) +$(EXEC): $(OBJS) + $(CC) $(CFLAGS) $(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) - -$(MULTIPATHLIB)-$(BUILD).a: - make -C $(multipathdir) BUILD=$(BUILD) $(BUILD) + $(GZIP) $(EXEC).conf.5 > $(EXEC).conf.5.gz install: - install -d $(DESTDIR)$(bindir) - install -s -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_PROGRAM) -d $(DESTDIR)$(bindir) + $(INSTALL_PROGRAM) -m 755 $(EXEC) $(DESTDIR)$(bindir)/ + $(INSTALL_PROGRAM) -d $(DESTDIR)/etc/udev/rules.d + $(INSTALL_PROGRAM) -m 644 multipath.rules $(DESTDIR)/etc/udev/rules.d/ + $(INSTALL_PROGRAM) -d $(DESTDIR)$(mandir) + $(INSTALL_PROGRAM) -m 644 $(EXEC).8.gz $(DESTDIR)$(mandir) + $(INSTALL_PROGRAM) -d $(DESTDIR)$(man5dir) + $(INSTALL_PROGRAM) -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..0f4b2d2 100644 --- a/multipath/main.c +++ b/multipath/main.c @@ -25,9 +25,9 @@ #include #include #include -#include #include +#include #include #include #include @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -46,8 +47,7 @@ #include #include #include - -#include "main.h" +#include static int filter_pathvec (vector pathvec, char * refwwid) @@ -73,7 +73,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 +84,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 +129,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 +225,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 +235,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 +254,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 +288,10 @@ configure (void) filter_pathvec(pathvec, refwwid); - if (conf->list) + if (conf->list) { + r = 0; goto out; + } /* * core logic entry point @@ -305,24 +314,32 @@ 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"); + if (init_checkers()) { + condlog(0, "failed to initialize checkers"); + exit(1); + } + if (init_prio()) { + condlog(0, "failed to initialize prioritizers"); 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 +382,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 +410,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 +428,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..c66d7fc --- /dev/null +++ b/multipath/multipath.conf.5 @@ -0,0 +1,393 @@ +.TH MULTIPATH.CONF 5 "30 November 2006" +.SH NAME +multipath.conf \- multipath daemon configuration file +.SH DESCRIPTION +.B "multipath.conf" +is the configuration file for the multipath daemon. It is used to +overwrite the built-in configuration table of \fBmultipathd\fP. +Any line whose first non-white-space character is a '#' is considered +a comment line. Empty lines are ignored. +.SH SYNTAX +The configuration file contains entries of the form: +.RS +.nf +.ft B +.sp +
{ +.RS +.ft B + +.I "..." +.ft B + { +.RS +.ft B + +.I "..." +.RE +} +.RE +} +.ft R +.fi +.RE +.LP +Each \fIsection\fP contains one or more attributes or subsections. The +recognized keywords for attributes or subsections depend on the +section in which they occor. +.LP +The following \fIsection\fP keywords are recognized: +.TP 17 +.B defaults +This section defines default values for attributes which are used +whenever no specific setting is given. +.TP +.B blacklist +This section defines which devices should be excluded from the +multipath topology discovery. +.TP +.B blacklist_exceptions +This section defines which devices should be included in the +multipath topology discovery, despite being listed in the +.I blacklist +section. +.TP +.B multipaths +This section defines the multipath topologies. They are indexed by a +\fIWorld Wide Identifier\fR(wwid), which is the result of the +\fIgetuid_callout\fR program. +.TP +.B devices +This section defines the device-specific settings. +.RE +.LP +.SH "defaults section" +The +.B defaults +section recognizes the following keywords: +.TP 17 +.B polling_interval +interval between two path checks in seconds; default is +.I 5 +.TP +.B udev_dir +directory where udev creates its device nodes; default is +.I /dev +.TP +.B selector +The default path selector algorithm to use; they are offered by the +kernel multipath target. The only currently implemented is +.I "round-robin 0" +.TP +.B path_grouping_policy +The default path grouping policy to apply to unspecified +multipaths. Possible values are +.RS +.TP 12 +.B failover +1 path per priority group +.TP +.B multibus +all paths in 1 priority group +.TP +.B group_by_serial +1 priority group per serial number +.TP +.B group_by_prio +1 priority group per priority value. Priorities are determined by +callout programs specified as a global, per-controller or +per-multipath option in the configuration file. +.TP +.B group_by_node_name +1 priority group per target node name. Target node names are fetched +in /sys/class/fc_transport/target*/node_name. +.TP +Default value is \fImultibus\fR. +.RE +.TP +.B getuid_callout +The default program and args to callout to obtain a unique path +identifier. Should be specified with an absolute path. Default value +is +.I /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. +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 "KNOWN ISSUES" +The usage of +.B queue_if_no_path +option can lead to +.B D state +processes being hung and not killable in situations where all the paths to the LUN go offline. +It is advisable to use the +.B no_path_retry +option instead. +.SH "SEE ALSO" +.BR udev (8), +.BR dmsetup (8) +.BR multipath (8) +.BR multipathd (8) +.SH AUTHORS +.B multipath +was developed by Christophe Varoqui, and others. diff --git a/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..6beb993 100644 --- a/multipathd/Makefile +++ b/multipathd/Makefile @@ -1,4 +1,3 @@ -BUILD = glibc EXEC = multipathd include ../Makefile.inc @@ -6,8 +5,9 @@ include ../Makefile.inc # # basic flags setting # -CFLAGS += -DDAEMON -I$(multipathdir) -I$(checkersdir) -LDFLAGS = -lpthread -ldevmapper -lsysfs -lreadline -lncurses +CFLAGS += -DDAEMON -I$(multipathdir) -Wl,-rpath,$(libdir) +LDFLAGS += -lpthread -ldevmapper -lreadline -lncurses -laio \ + -lmultipath -L$(multipathdir) # # debuging stuff @@ -19,36 +19,24 @@ LDFLAGS = -lpthread -ldevmapper -lsysfs -lreadline -lncurses # # object files # -OBJS = main.o pidfile.o uxlsnr.o uxclnt.o cli.o cli_handlers.o \ - $(MULTIPATHLIB)-glibc.a $(CHECKERSLIB)-glibc.a \ +OBJS = main.o pidfile.o uxlsnr.o uxclnt.o cli.o cli_handlers.o # # directives # -all : $(BUILD) +all : $(EXEC) -glibc: $(EXEC) - -klibc: - $(MAKE) BUILD=glibc glibc - -$(EXEC): clean $(OBJS) - $(CC) $(OBJS) -o $(EXEC) $(LDFLAGS) +$(EXEC): $(OBJS) + $(CC) $(CFLAGS) $(LDFLAGS) -o $(EXEC) $(OBJS) $(GZIP) $(EXEC).8 > $(EXEC).8.gz -$(CHECKERSLIB)-glibc.a: - $(MAKE) -C $(checkersdir) BUILD=glibc glibc - -$(MULTIPATHLIB)-glibc.a: - $(MAKE) -C $(multipathdir) DAEMON=1 BUILD=glibc glibc - install: - install -d $(DESTDIR)$(bindir) - install -s -m 755 $(EXEC) $(DESTDIR)$(bindir) - install -d $(DESTDIR)$(rcdir) - install -d $(DESTDIR)$(mandir) - install -m 644 $(EXEC).8.gz $(DESTDIR)$(mandir) + $(INSTALL_PROGRAM) -d $(DESTDIR)$(bindir) + $(INSTALL_PROGRAM) -m 755 $(EXEC) $(DESTDIR)$(bindir) + $(INSTALL_PROGRAM) -d $(DESTDIR)$(rcdir) + $(INSTALL_PROGRAM) -d $(DESTDIR)$(mandir) + $(INSTALL_PROGRAM) -m 644 $(EXEC).8.gz $(DESTDIR)$(mandir) uninstall: rm -f $(DESTDIR)$(bindir)/$(EXEC) @@ -56,6 +44,5 @@ uninstall: rm -f $(DESTDIR)$(mandir)/$(EXEC).8.gz clean: - $(MAKE) -C $(multipathdir) prepare DAEMON=1 rm -f core *.o $(EXEC) *.gz 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 #include #include +#include +#include #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..f71dca0 100644 --- a/multipathd/cli_handlers.c +++ b/multipathd/cli_handlers.c @@ -13,6 +13,7 @@ #include #include #include +#include #include "main.h" #include "cli.h" @@ -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 @@ -408,6 +431,7 @@ cli_reinstate(void * v, char ** reply, int * len, void * data) condlog(2, "%s: reinstate path %s (operator)", pp->mpp->alias, pp->dev_t); + checker_enable(&pp->checker); return dm_reinstate_path(pp->mpp->alias, pp->dev_t); } @@ -417,6 +441,7 @@ cli_fail(void * v, char ** reply, int * len, void * data) struct vectors * vecs = (struct vectors *)data; char * param = get_keyparam(v, PATH); struct path * pp; + int r; pp = find_path_by_dev(vecs->pathvec, param); @@ -429,5 +454,87 @@ cli_fail(void * v, char ** reply, int * len, void * data) condlog(2, "%s: fail path %s (operator)", pp->mpp->alias, pp->dev_t); - return dm_fail_path(pp->mpp->alias, pp->dev_t); + r = dm_fail_path(pp->mpp->alias, pp->dev_t); + /* + * Suspend path checking to avoid auto-reinstating the path + */ + if (!r) + checker_disable(&pp->checker); + return r; +} + +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..50e7441 100644 --- a/multipathd/main.c +++ b/multipathd/main.c @@ -12,12 +12,8 @@ #include #include #include - -/* - * libsysfs - */ -#include -#include +#include +#include /* * libcheckers @@ -40,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -48,6 +45,7 @@ #include #include #include +#include #include "main.h" #include "pidfile.h" @@ -55,136 +53,29 @@ #include "uxclnt.h" #include "cli.h" #include "cli_handlers.h" +#include "lock.h" +#include "waiter.h" #define FILE_NAME_SIZE 256 #define CMDSIZE 160 #define LOG_MSG(a,b) \ - if (strlen(b)) condlog(a, "%s: %s", pp->dev_t, b); - -#ifdef LCKDBG -#define lock(a) \ - fprintf(stderr, "%s:%s(%i) lock %p\n", __FILE__, __FUNCTION__, __LINE__, a); \ - pthread_mutex_lock(a) -#define unlock(a) \ - fprintf(stderr, "%s:%s(%i) unlock %p\n", __FILE__, __FUNCTION__, __LINE__, a); \ - pthread_mutex_unlock(a) -#define lock_cleanup_pop(a) \ - fprintf(stderr, "%s:%s(%i) unlock %p\n", __FILE__, __FUNCTION__, __LINE__, a); \ - pthread_cleanup_pop(1); -#else -#define lock(a) pthread_mutex_lock(a) -#define unlock(a) pthread_mutex_unlock(a) -#define lock_cleanup_pop(a) pthread_cleanup_pop(1); -#endif + if (strlen(b)) condlog(a, "%s: %s", pp->dev, b); pthread_cond_t exit_cond = PTHREAD_COND_INITIALIZER; pthread_mutex_t exit_mutex = PTHREAD_MUTEX_INITIALIZER; /* - * structs + * global copy of vecs for use in sig handlers */ -struct vectors * gvecs; /* global copy of vecs for use in sig handlers */ - -static struct event_thread * -alloc_waiter (void) -{ - - struct event_thread * wp; - - wp = (struct event_thread *)MALLOC(sizeof(struct event_thread)); - - return wp; -} - -static void -free_waiter (void * data) -{ - struct event_thread * wp = (struct event_thread *)data; - - /* - * indicate in mpp that the wp is already freed storage - */ - lock(wp->vecs->lock); - - if (wp->mpp) - /* - * be careful, mpp may already be freed -- null if so - */ - wp->mpp->waiter = NULL; - else - condlog(3, "free_waiter, mpp freed before wp=%p,", wp); - - unlock(wp->vecs->lock); - - if (wp->dmt) - dm_task_destroy(wp->dmt); - - FREE(wp); -} - -static void -stop_waiter_thread (struct multipath * mpp, struct vectors * vecs) -{ - struct event_thread * wp = (struct event_thread *)mpp->waiter; - - if (!wp) { - condlog(3, "%s: no waiter thread", mpp->alias); - return; - } - condlog(2, "%s: stop event checker thread", wp->mapname); - pthread_kill((pthread_t)wp->thread, SIGUSR1); -} - -static void -cleanup_lock (void * data) -{ - unlock((pthread_mutex_t *)data); -} - -/* - * mpp->no_path_retry: - * -2 (QUEUE) : queue_if_no_path enabled, never turned off - * -1 (FAIL) : fail_if_no_path - * 0 (UNDEF) : nothing - * >0 : queue_if_no_path enabled, turned off after polling n times - */ -static void -update_queue_mode_del_path(struct multipath *mpp) -{ - if (--mpp->nr_active == 0 && mpp->no_path_retry > 0) { - /* - * Enter retry mode. - * meaning of +1: retry_tick may be decremented in - * checkerloop before starting retry. - */ - mpp->stat_queueing_timeouts++; - mpp->retry_tick = mpp->no_path_retry * conf->checkint + 1; - condlog(1, "%s: Entering recovery mode: max_retries=%d", - mpp->alias, mpp->no_path_retry); - } - condlog(2, "%s: remaining active paths: %d", mpp->alias, mpp->nr_active); -} - -static void -update_queue_mode_add_path(struct multipath *mpp) -{ - if (mpp->nr_active++ == 0 && mpp->no_path_retry > 0) { - /* come back to normal mode from retry mode */ - mpp->retry_tick = 0; - dm_queue_if_no_path(mpp->alias, 1); - condlog(2, "%s: queue_if_no_path enabled", mpp->alias); - condlog(1, "%s: Recovered to normal mode", mpp->alias); - } - condlog(2, "%s: remaining active paths: %d", mpp->alias, mpp->nr_active); -} +struct vectors * gvecs; static int need_switch_pathgroup (struct multipath * mpp, int refresh) { struct pathgroup * pgp; struct path * pp; - int i, j; + unsigned int i, j; if (!mpp || mpp->pgfailback == -FAILBACK_MANUAL) return 0; @@ -219,7 +110,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 +145,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 +172,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); } @@ -530,92 +200,88 @@ flush_map(struct multipath * mpp, struct vectors * vecs) } orphan_paths(vecs->pathvec, mpp); - remove_map(mpp, vecs, stop_waiter_thread, 1); + remove_map_and_stop_waiter(mpp, vecs, 1); return 0; } 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; } /* * now we can register the map */ - if (map_present && (mpp = add_map_without_path(vecs, minor, alias, - start_waiter_thread))) { + if (map_present && (mpp = add_map_without_path(vecs, minor, alias))) { 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 +302,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 +321,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 +370,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) > 0){ 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 +408,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); @@ -771,15 +437,21 @@ rescan: return 0; out: - remove_map(mpp, vecs, NULL, 1); + remove_map(mpp, vecs, 1); return 1; } 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 +481,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 +509,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 +535,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); @@ -891,15 +563,15 @@ ev_remove_path (char * devname, struct vectors * vecs) return 0; out: - remove_map(mpp, vecs, stop_waiter_thread, 1); + remove_map_and_stop_waiter(mpp, vecs, 1); return 1; } 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 +588,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 +630,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 +642,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 +695,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 +790,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,12 +806,12 @@ 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)) { condlog(2, "%s: remove dead map", mpp->alias); - remove_map(mpp, vecs, stop_waiter_thread, 1); + remove_map_and_stop_waiter(mpp, vecs, 1); i--; } } @@ -1144,7 +821,7 @@ static void defered_failback_tick (vector mpvec) { struct multipath * mpp; - int i; + unsigned int i; vector_foreach_slot (mpvec, mpp, i) { /* @@ -1163,7 +840,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) { @@ -1177,154 +854,175 @@ retry_count_tick(vector mpvec) } } -static void * -checkerloop (void *ap) +void +check_path (struct vectors * vecs, struct path * pp) { - struct vectors *vecs; - struct path *pp; - int i, count = 0; int newstate; - mlockall(MCL_CURRENT | MCL_FUTURE); - vecs = (struct vectors *)ap; - condlog(2, "path checkers start up"); + if (!pp->mpp) + return; + + if (pp->tick && --pp->tick) + return; /* don't check this path yet */ /* - * init the path check interval + * provision a next check soonest, + * in case we exit abnormaly from here */ - vector_foreach_slot (vecs->pathvec, pp, i) { - pp->checkint = conf->checkint; + 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); + return; + } + /* + * Set checker in async mode. + * Honored only by checker implementing the said mode. + */ + checker_set_async(&pp->checker); - while (1) { - pthread_cleanup_push(cleanup_lock, vecs->lock); - lock(vecs->lock); - condlog(4, "tick"); + newstate = checker_check(&pp->checker); - vector_foreach_slot (vecs->pathvec, pp, i) { - if (!pp->mpp) - continue; + if (newstate < 0) { + condlog(2, "%s: unusable path", pp->dev); + pathinfo(pp, conf->hwtable, 0); + return; + } + /* + * Async IO in flight. Keep the previous path state + * and reschedule as soon as possible + */ + if (newstate == PATH_PENDING) { + pp->tick = 1; + return; + } + if (newstate != pp->state) { + int oldstate = pp->state; + pp->state = newstate; + LOG_MSG(1, checker_message(&pp->checker)); - if (pp->tick && --pp->tick) - continue; /* don't check this path yet */ + /* + * upon state change, reset the checkint + * to the shortest delay + */ + pp->checkint = conf->checkint; + if (newstate == PATH_DOWN || newstate == PATH_SHAKY || + update_multipath_strings(pp->mpp, vecs->pathvec)) { /* - * provision a next check soonest, - * in case we exit abnormaly from here + * proactively fail path in the DM */ - pp->tick = conf->checkint; - - if (!checker_selected(&pp->checker)) { - pathinfo(pp, conf->hwtable, DI_SYSFS); - select_checker(pp); - } + if (oldstate == PATH_UP || + oldstate == PATH_GHOST) + fail_path(pp, 1); + else + fail_path(pp, 0); - if (!checker_selected(&pp->checker)) { - condlog(0, "%s: checker is not set", pp->dev); - continue; - } - newstate = checker_check(&pp->checker); - - if (newstate < 0) { - condlog(2, "%s: unusable path", pp->dev); - pathinfo(pp, conf->hwtable, 0); - continue; - } + /* + * cancel scheduled failback + */ + pp->mpp->failback_tick = 0; - if (newstate != pp->state) { - int oldstate = pp->state; - pp->state = newstate; - LOG_MSG(1, checker_message(&pp->checker)); + pp->mpp->stat_path_failures++; + return; + } - /* - * upon state change, reset the checkint - * to the shortest delay - */ - pp->checkint = conf->checkint; + /* + * reinstate this path + */ + if (oldstate != PATH_UP && + oldstate != PATH_GHOST) + reinstate_path(pp, 1); + else + reinstate_path(pp, 0); - if (newstate == PATH_DOWN || - newstate == PATH_SHAKY || - update_multipath_strings(pp->mpp, - vecs->pathvec)) { - /* - * proactively fail path in the DM - */ - if (oldstate == PATH_UP || - oldstate == PATH_GHOST) - fail_path(pp, 1); - else - fail_path(pp, 0); + /* + * schedule [defered] failback + */ + if (pp->mpp->pgfailback > 0) + pp->mpp->failback_tick = + pp->mpp->pgfailback + 1; + else if (pp->mpp->pgfailback == -FAILBACK_IMMEDIATE && + need_switch_pathgroup(pp->mpp, 1)) + switch_pathgroup(pp->mpp); - /* - * cancel scheduled failback - */ - pp->mpp->failback_tick = 0; + /* + * if at least one path is up in a group, and + * the group is disabled, re-enable it + */ + if (newstate == PATH_UP) + enable_group(pp); + } + else if (newstate == PATH_UP || newstate == PATH_GHOST) { + LOG_MSG(4, checker_message(&pp->checker)); + /* + * double the next check delay. + * max at conf->max_checkint + */ + if (pp->checkint < (conf->max_checkint / 2)) + pp->checkint = 2 * pp->checkint; + else + pp->checkint = conf->max_checkint; - pp->mpp->stat_path_failures++; - continue; - } + pp->tick = pp->checkint; + condlog(4, "%s: delay next check %is", + pp->dev_t, pp->tick); + } + else if (newstate == PATH_DOWN) + LOG_MSG(2, checker_message(&pp->checker)); - /* - * reinstate this path - */ - if (oldstate != PATH_UP && - oldstate != PATH_GHOST) - reinstate_path(pp, 1); - else - reinstate_path(pp, 0); + pp->state = newstate; - /* - * schedule [defered] failback - */ - if (pp->mpp->pgfailback > 0) - pp->mpp->failback_tick = - pp->mpp->pgfailback + 1; - else if (pp->mpp->pgfailback == -FAILBACK_IMMEDIATE && - need_switch_pathgroup(pp->mpp, 1)) - switch_pathgroup(pp->mpp); + /* + * path prio refreshing + */ + condlog(4, "path prio refresh"); + pathinfo(pp, conf->hwtable, DI_PRIO); - /* - * if at least one path is up in a group, and - * the group is disabled, re-enable it - */ - if (newstate == PATH_UP) - enable_group(pp); - } - else if (newstate == PATH_UP || newstate == PATH_GHOST) { - LOG_MSG(4, checker_message(&pp->checker)); - /* - * double the next check delay. - * max at conf->max_checkint - */ - if (pp->checkint < (conf->max_checkint / 2)) - pp->checkint = 2 * pp->checkint; - else - pp->checkint = conf->max_checkint; + /* + * pathgroup failback policy + */ + if (need_switch_pathgroup(pp->mpp, 0)) { + if (pp->mpp->pgfailback > 0 && + pp->mpp->failback_tick <= 0) + pp->mpp->failback_tick = + pp->mpp->pgfailback + 1; + else if (pp->mpp->pgfailback == + -FAILBACK_IMMEDIATE) + switch_pathgroup(pp->mpp); + } +} - pp->tick = pp->checkint; - condlog(4, "%s: delay next check %is", - pp->dev_t, pp->tick); - } - else if (newstate == PATH_DOWN) - LOG_MSG(2, checker_message(&pp->checker)); +static void * +checkerloop (void *ap) +{ + struct vectors *vecs; + struct path *pp; + int count = 0; + unsigned int i; + + mlockall(MCL_CURRENT | MCL_FUTURE); + vecs = (struct vectors *)ap; + condlog(2, "path checkers start up"); - pp->state = newstate; + /* + * init the path check interval + */ + vector_foreach_slot (vecs->pathvec, pp, i) { + pp->checkint = conf->checkint; + } - /* - * path prio refreshing - */ - condlog(4, "path prio refresh"); - pathinfo(pp, conf->hwtable, DI_PRIO); - - if (need_switch_pathgroup(pp->mpp, 0)) { - if (pp->mpp->pgfailback > 0 && - pp->mpp->failback_tick <= 0) - pp->mpp->failback_tick = - pp->mpp->pgfailback + 1; - else if (pp->mpp->pgfailback == - -FAILBACK_IMMEDIATE) - switch_pathgroup(pp->mpp); - } + while (1) { + pthread_cleanup_push(cleanup_lock, vecs->lock); + lock(vecs->lock); + condlog(4, "tick"); + + vector_foreach_slot (vecs->pathvec, pp, i) { + check_path(vecs, pp); } defered_failback_tick(vecs->mpvec); retry_count_tick(vecs->mpvec); @@ -1336,7 +1034,7 @@ checkerloop (void *ap) mpvec_garbage_collector(vecs); count = MAPGCINT; } - + lock_cleanup_pop(vecs->lock); sleep(1); } @@ -1351,12 +1049,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 +1064,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) > 0){ vector_del_slot(vecs->pathvec, i); free_path(pp); i--; - } + } else pp->checkint = conf->checkint; } @@ -1394,18 +1092,15 @@ 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 */ - remove_maps(vecs, NULL); + remove_maps(vecs); /* * save new set of maps formed by considering current path state */ + vector_free(vecs->mpvec); vecs->mpvec = mpvec; /* @@ -1430,11 +1125,12 @@ reconfigure (struct vectors * vecs) * free old map and path vectors ... they use old conf state */ if (VECTOR_SIZE(vecs->mpvec)) - remove_maps(vecs, stop_waiter_thread); + remove_maps_and_stop_waiters(vecs); if (VECTOR_SIZE(vecs->pathvec)) free_pathvec(vecs->pathvec, FREE_PATHS); + vecs->pathvec = NULL; conf = NULL; if (load_config(DEFAULT_CONFIGFILE)) @@ -1461,30 +1157,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 +1225,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 +1256,7 @@ set_oom_adj (int val) fprintf(fp, "%i", val); fclose(fp); } - + static int child (void * param) { @@ -1590,6 +1272,14 @@ child (void * param) condlog(2, "--------start up--------"); condlog(2, "read " DEFAULT_CONFIGFILE); + if (init_checkers()) { + condlog(0, "failed to initialize checkers"); + exit(1); + } + if (init_prio()) { + condlog(0, "failed to initialize prioritizers"); + exit(1); + } if (load_config(DEFAULT_CONFIGFILE)) exit(1); @@ -1603,6 +1293,21 @@ child (void * param) conf->max_checkint = MAX_CHECKINT(conf->checkint); } + if (conf->max_fds) { + struct rlimit fd_limit; + if (conf->max_fds > 0) { + fd_limit.rlim_cur = conf->max_fds; + fd_limit.rlim_max = conf->max_fds; + } + else { + fd_limit.rlim_cur = RLIM_INFINITY; + fd_limit.rlim_max = RLIM_INFINITY; + } + if (setrlimit(RLIMIT_NOFILE, &fd_limit) < 0) + condlog(0, "can't set open fds limit to %d : %s\n", + conf->max_fds, strerror(errno)); + } + if (pidfile_create(DEFAULT_PIDFILE, getpid())) { if (logsink) log_thread_stop(); @@ -1617,7 +1322,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 +1341,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); @@ -1647,13 +1352,15 @@ child (void * param) * exit path */ lock(vecs->lock); - remove_maps(vecs, stop_waiter_thread); + remove_maps_and_stop_waiters(vecs); free_pathvec(vecs->pathvec, FREE_PATHS); pthread_cancel(check_thr); pthread_cancel(uevent_thr); pthread_cancel(uxlsnr_thr); + sysfs_cleanup(); + free_keys(keys); keys = NULL; free_handlers(handlers); @@ -1670,7 +1377,7 @@ child (void * param) conf = NULL; condlog(2, "--------shut down-------"); - + if (logsink) log_thread_stop(); @@ -1738,8 +1445,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 +1488,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, and others. +.B multipathd +was developed by Christophe Varoqui, and others. diff --git a/multipathd/multipathd.init.suse b/multipathd/multipathd.init.suse new file mode 100644 index 0000000..d851354 --- /dev/null +++ b/multipathd/multipathd.init.suse @@ -0,0 +1,132 @@ +#! /bin/sh +# Copyright (c) 1995-2001 SuSE GmbH Nuernberg, Germany. +# +# Author: Hannes Reinecke +# +# init.d/routed +# +# and symbolic its link +# +# /usr/sbin/rcrouted +# +### BEGIN INIT INFO +# Provides: multipathd +# Required-Start: $syslog +# Required-Stop: +# Default-Start: 3 5 +# Default-Stop: 0 1 2 4 6 +# Description: Starts multipath daemon +### END INIT INFO + +PATH=/bin:/usr/bin:/sbin:/usr/sbin +DAEMON=/sbin/multipathd +PIDFILE=/var/run/multipathd.pid + +# Set the maximum number of open files +MAX_OPEN_FDS=4096 + +test -x $DAEMON || exit 5 + +. /etc/rc.status + +# First reset status of this service +rc_reset + +case "$1" in + start) + echo -n "Starting multipathd" + + modprobe dm-multipath + + # Set the maximum number of open files + if [ -n "$MAX_OPEN_FDS" ] ; then + ulimit -n $MAX_OPEN_FDS + fi + + if [ -f $PIDFILE ]; then + PID="$(cat $PIDFILE)" + PROCNAME="$(ps -o cmd --no-headers $PID)" + fi + + if [ "$PROCNAME" != "$DAEMON" ]; then + $DAEMON + fi + + # Remember status and be verbose + rc_status -v + sleep 1 + ;; + stop) + echo -n "Shutting down multipathd" + # Because of the way how multipathd sets up its own namespace + # and chroots to it, killproc cannot be used with this process. + # So implement a cruder version: + if [ -f $PIDFILE ]; then + PID="$(cat $PIDFILE)" + PROCNAME="$(ps -o cmd --no-headers $PID)" + fi + + if [ "$PROCNAME" == "$DAEMON" ]; then + kill -TERM $PID + fi + + # Remember status and be verbose + rc_status -v + ;; + try-restart) + ## Stop the service and if this succeeds (i.e. the + ## service was running before), start it again. + $0 status >/dev/null && $0 restart + + # Remember status and be quiet + rc_status + ;; + restart|force-reload) + ## Stop the service and regardless of whether it was + ## running or not, start it again. + $0 stop + $0 start + + # Remember status and be quiet + rc_status + ;; + reload) + ## Like force-reload, but if daemon does not support + ## signalling, do nothing (!) + + # If it does not support reload: + exit 3 + ;; + status) + echo -n "Checking for multipathd: " + + # Status has a slightly different for the status command: + # 0 - service running + # 1 - service dead, but /var/run/ pid file exists + # 2 - service dead, but /var/lock/ lock file exists + # 3 - service not running + + if [ -f $PIDFILE ]; then + PID="$(cat $PIDFILE)" + PROCNAME="$(ps -o cmd --no-headers $PID)" + if [ "$PROCNAME" == "$DAEMON" ]; then + (exit 0) + else + (exit 1) + fi + else + (exit 3) + fi + + rc_status -v + ;; + probe) + ## Optional: Probe for the necessity of a reload, + ## give out the argument which is required for a reload. + ;; + *) + echo "Usage: $0 {start|stop|status|try-restart|restart|force-reload|reload|probe}" + exit 1 + ;; +esac +rc_exit diff --git a/multipathd/uxclnt.c b/multipathd/uxclnt.c index ff7b578..009e5cb 100644 --- a/multipathd/uxclnt.c +++ b/multipathd/uxclnt.c @@ -21,6 +21,9 @@ #include #include +#include +#include "cli.h" + /* * process the client */ @@ -29,6 +32,9 @@ static void process(int fd) char *line; char *reply; + cli_init(); + rl_readline_name = "multipathd"; + rl_completion_entry_function = key_generator; while ((line = readline("multipathd> "))) { size_t len; size_t llen = strlen(line); diff --git a/path_priority/pp_alua/LICENSE b/path_priority/pp_alua/LICENSE deleted file mode 100644 index 9e31bbf..0000000 --- a/path_priority/pp_alua/LICENSE +++ /dev/null @@ -1,483 +0,0 @@ - - GNU LIBRARY GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1991 Free Software Foundation, Inc. - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - -[This is the first released version of the library GPL. It is - numbered 2 because it goes with version 2 of the ordinary GPL.] - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -Licenses are intended to guarantee your freedom to share and change -free software--to make sure the software is free for all its users. - - This license, the Library General Public License, applies to some -specially designated Free Software Foundation software, and to any -other libraries whose authors decide to use it. You can use it for -your libraries, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if -you distribute copies of the library, or if you modify it. - - For example, if you distribute copies of the library, whether gratis -or for a fee, you must give the recipients all the rights that we gave -you. You must make sure that they, too, receive or can get the source -code. If you link a program with the library, you must provide -complete object files to the recipients so that they can relink them -with the library, after making changes to the library and recompiling -it. And you must show them these terms so they know their rights. - - Our method of protecting your rights has two steps: (1) copyright -the library, and (2) offer you this license which gives you legal -permission to copy, distribute and/or modify the library. - - Also, for each distributor's protection, we want to make certain -that everyone understands that there is no warranty for this free -library. If the library is modified by someone else and passed on, we -want its recipients to know that what they have is not the original -version, so that any problems introduced by others will not reflect on -the original authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that companies distributing free -software will individually obtain patent licenses, thus in effect -transforming the program into proprietary software. To prevent this, -we have made it clear that any patent must be licensed for everyone's -free use or not licensed at all. - - Most GNU software, including some libraries, is covered by the ordinary -GNU General Public License, which was designed for utility programs. This -license, the GNU Library General Public License, applies to certain -designated libraries. This license is quite different from the ordinary -one; be sure to read it in full, and don't assume that anything in it is -the same as in the ordinary license. - - The reason we have a separate public license for some libraries is that -they blur the distinction we usually make between modifying or adding to a -program and simply using it. Linking a program with a library, without -changing the library, is in some sense simply using the library, and is -analogous to running a utility program or application program. However, in -a textual and legal sense, the linked executable is a combined work, a -derivative of the original library, and the ordinary General Public License -treats it as such. - - Because of this blurred distinction, using the ordinary General -Public License for libraries did not effectively promote software -sharing, because most developers did not use the libraries. We -concluded that weaker conditions might promote sharing better. - - However, unrestricted linking of non-free programs would deprive the -users of those programs of all benefit from the free status of the -libraries themselves. This Library General Public License is intended to -permit developers of non-free programs to use free libraries, while -preserving your freedom as a user of such programs to change the free -libraries that are incorporated in them. (We have not seen how to achieve -this as regards changes in header files, but we have achieved it as regards -changes in the actual functions of the Library.) The hope is that this -will lead to faster development of free libraries. - - The precise terms and conditions for copying, distribution and -modification follow. Pay close attention to the difference between a -"work based on the library" and a "work that uses the library". The -former contains code derived from the library, while the latter only -works together with the library. - - Note that it is possible for a library to be covered by the ordinary -General Public License rather than by this special one. - - GNU LIBRARY GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License Agreement applies to any software library which -contains a notice placed by the copyright holder or other authorized -party saying it may be distributed under the terms of this Library -General Public License (also called "this License"). Each licensee is -addressed as "you". - - A "library" means a collection of software functions and/or data -prepared so as to be conveniently linked with application programs -(which use some of those functions and data) to form executables. - - The "Library", below, refers to any such software library or work -which has been distributed under these terms. A "work based on the -Library" means either the Library or any derivative work under -copyright law: that is to say, a work containing the Library or a -portion of it, either verbatim or with modifications and/or translated -straightforwardly into another language. (Hereinafter, translation is -included without limitation in the term "modification".) - - "Source code" for a work means the preferred form of the work for -making modifications to it. For a library, complete source code means -all the source code for all modules it contains, plus any associated -interface definition files, plus the scripts used to control compilation -and installation of the library. - - Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running a program using the Library is not restricted, and output from -such a program is covered only if its contents constitute a work based -on the Library (independent of the use of the Library in a tool for -writing it). Whether that is true depends on what the Library does -and what the program that uses the Library does. - - 1. You may copy and distribute verbatim copies of the Library's -complete source code as you receive it, in any medium, provided that -you conspicuously and appropriately publish on each copy an -appropriate copyright notice and disclaimer of warranty; keep intact -all the notices that refer to this License and to the absence of any -warranty; and distribute a copy of this License along with the -Library. - - You may charge a fee for the physical act of transferring a copy, -and you may at your option offer warranty protection in exchange for a -fee. - - 2. You may modify your copy or copies of the Library or any portion -of it, thus forming a work based on the Library, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) The modified work must itself be a software library. - - b) You must cause the files modified to carry prominent notices - stating that you changed the files and the date of any change. - - c) You must cause the whole of the work to be licensed at no - charge to all third parties under the terms of this License. - - d) If a facility in the modified Library refers to a function or a - table of data to be supplied by an application program that uses - the facility, other than as an argument passed when the facility - is invoked, then you must make a good faith effort to ensure that, - in the event an application does not supply such function or - table, the facility still operates, and performs whatever part of - its purpose remains meaningful. - - (For example, a function in a library to compute square roots has - a purpose that is entirely well-defined independent of the - application. Therefore, Subsection 2d requires that any - application-supplied function or table used by this function must - be optional: if the application does not supply it, the square - root function must still compute square roots.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Library, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Library, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote -it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Library. - -In addition, mere aggregation of another work not based on the Library -with the Library (or with a work based on the Library) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may opt to apply the terms of the ordinary GNU General Public -License instead of this License to a given copy of the Library. To do -this, you must alter all the notices that refer to this License, so -that they refer to the ordinary GNU General Public License, version 2, -instead of to this License. (If a newer version than version 2 of the -ordinary GNU General Public License has appeared, then you can specify -that version instead if you wish.) Do not make any other change in -these notices. - - Once this change is made in a given copy, it is irreversible for -that copy, so the ordinary GNU General Public License applies to all -subsequent copies and derivative works made from that copy. - - This option is useful when you wish to copy part of the code of -the Library into a program that is not a library. - - 4. You may copy and distribute the Library (or a portion or -derivative of it, under Section 2) in object code or executable form -under the terms of Sections 1 and 2 above provided that you accompany -it with the complete corresponding machine-readable source code, which -must be distributed under the terms of Sections 1 and 2 above on a -medium customarily used for software interchange. - - If distribution of object code is made by offering access to copy -from a designated place, then offering equivalent access to copy the -source code from the same place satisfies the requirement to -distribute the source code, even though third parties are not -compelled to copy the source along with the object code. - - 5. A program that contains no derivative of any portion of the -Library, but is designed to work with the Library by being compiled or -linked with it, is called a "work that uses the Library". Such a -work, in isolation, is not a derivative work of the Library, and -therefore falls outside the scope of this License. - - However, linking a "work that uses the Library" with the Library -creates an executable that is a derivative of the Library (because it -contains portions of the Library), rather than a "work that uses the -library". The executable is therefore covered by this License. -Section 6 states terms for distribution of such executables. - - When a "work that uses the Library" uses material from a header file -that is part of the Library, the object code for the work may be a -derivative work of the Library even though the source code is not. -Whether this is true is especially significant if the work can be -linked without the Library, or if the work is itself a library. The -threshold for this to be true is not precisely defined by law. - - If such an object file uses only numerical parameters, data -structure layouts and accessors, and small macros and small inline -functions (ten lines or less in length), then the use of the object -file is unrestricted, regardless of whether it is legally a derivative -work. (Executables containing this object code plus portions of the -Library will still fall under Section 6.) - - Otherwise, if the work is a derivative of the Library, you may -distribute the object code for the work under the terms of Section 6. -Any executables containing that work also fall under Section 6, -whether or not they are linked directly with the Library itself. - - 6. As an exception to the Sections above, you may also compile or -link a "work that uses the Library" with the Library to produce a -work containing portions of the Library, and distribute that work -under terms of your choice, provided that the terms permit -modification of the work for the customer's own use and reverse -engineering for debugging such modifications. - - You must give prominent notice with each copy of the work that the -Library is used in it and that the Library and its use are covered by -this License. You must supply a copy of this License. If the work -during execution displays copyright notices, you must include the -copyright notice for the Library among them, as well as a reference -directing the user to the copy of this License. Also, you must do one -of these things: - - a) Accompany the work with the complete corresponding - machine-readable source code for the Library including whatever - changes were used in the work (which must be distributed under - Sections 1 and 2 above); and, if the work is an executable linked - with the Library, with the complete machine-readable "work that - uses the Library", as object code and/or source code, so that the - user can modify the Library and then relink to produce a modified - executable containing the modified Library. (It is understood - that the user who changes the contents of definitions files in the - Library will not necessarily be able to recompile the application - to use the modified definitions.) - - b) Accompany the work with a written offer, valid for at - least three years, to give the same user the materials - specified in Subsection 6a, above, for a charge no more - than the cost of performing this distribution. - - c) If distribution of the work is made by offering access to copy - from a designated place, offer equivalent access to copy the above - specified materials from the same place. - - d) Verify that the user has already received a copy of these - materials or that you have already sent this user a copy. - - For an executable, the required form of the "work that uses the -Library" must include any data and utility programs needed for -reproducing the executable from it. However, as a special exception, -the source code distributed need not include anything that is normally -distributed (in either source or binary form) with the major -components (compiler, kernel, and so on) of the operating system on -which the executable runs, unless that component itself accompanies -the executable. - - It may happen that this requirement contradicts the license -restrictions of other proprietary libraries that do not normally -accompany the operating system. Such a contradiction means you cannot -use both them and the Library together in an executable that you -distribute. - - 7. You may place library facilities that are a work based on the -Library side-by-side in a single library together with other library -facilities not covered by this License, and distribute such a combined -library, provided that the separate distribution of the work based on -the Library and of the other library facilities is otherwise -permitted, and provided that you do these two things: - - a) Accompany the combined library with a copy of the same work - based on the Library, uncombined with any other library - facilities. This must be distributed under the terms of the - Sections above. - - b) Give prominent notice with the combined library of the fact - that part of it is a work based on the Library, and explaining - where to find the accompanying uncombined form of the same work. - - 8. You may not copy, modify, sublicense, link with, or distribute -the Library except as expressly provided under this License. Any -attempt otherwise to copy, modify, sublicense, link with, or -distribute the Library is void, and will automatically terminate your -rights under this License. However, parties who have received copies, -or rights, from you under this License will not have their licenses -terminated so long as such parties remain in full compliance. - - 9. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Library or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Library (or any work based on the -Library), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Library or works based on it. - - 10. Each time you redistribute the Library (or any work based on the -Library), the recipient automatically receives a license from the -original licensor to copy, distribute, link with or modify the Library -subject to these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 11. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Library at all. For example, if a patent -license would not permit royalty-free redistribution of the Library by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Library. - -If any portion of this section is held invalid or unenforceable under any -particular circumstance, the balance of the section is intended to apply, -and the section as a whole is intended to apply in other circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 12. If the distribution and/or use of the Library is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Library under this License may add -an explicit geographical distribution limitation excluding those countries, -so that distribution is permitted only in or among countries not thus -excluded. In such case, this License incorporates the limitation as if -written in the body of this License. - - 13. The Free Software Foundation may publish revised and/or new -versions of the Library General Public License from time to time. -Such new versions will be similar in spirit to the present version, -but may differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Library -specifies a version number of this License which applies to it and -"any later version", you have the option of following the terms and -conditions either of that version or of any later version published by -the Free Software Foundation. If the Library does not specify a -license version number, you may choose any version ever published by -the Free Software Foundation. - - 14. If you wish to incorporate parts of the Library into other free -programs whose distribution conditions are incompatible with these, -write to the author to ask for permission. For software which is -copyrighted by the Free Software Foundation, write to the Free -Software Foundation; we sometimes make exceptions for this. Our -decision will be guided by the two goals of preserving the free status -of all derivatives of our free software and of promoting the sharing -and reuse of software generally. - - NO WARRANTY - - 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO -WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. -EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR -OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY -KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE -LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME -THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN -WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY -AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU -FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR -CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE -LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING -RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A -FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF -SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGES. - - END OF TERMS AND CONDITIONS - - Appendix: How to Apply These Terms to Your New Libraries - - If you develop a new library, and you want it to be of the greatest -possible use to the public, we recommend making it free software that -everyone can redistribute and change. You can do so by permitting -redistribution under these terms (or, alternatively, under the terms of the -ordinary General Public License). - - To apply these terms, attach the following notices to the library. It is -safest to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least the -"copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this library; if not, write to the Free - Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - MA 02111-1307, USA - -Also add information on how to contact you by electronic and paper mail. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the library, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the - library `Frob' (a library for tweaking knobs) written by James Random Hacker. - - , 1 April 1990 - Ty Coon, President of Vice - -That's all there is to it! diff --git a/path_priority/pp_alua/Makefile b/path_priority/pp_alua/Makefile deleted file mode 100644 index 983ffe3..0000000 --- a/path_priority/pp_alua/Makefile +++ /dev/null @@ -1,54 +0,0 @@ -#============================================================================== -# (C) Copyright IBM Corp. 2004, 2005 All Rights Reserved. -# -# Makefile -# -# Tool to make use of a SCSI-feature called Asymmetric Logical Unit Access. -# It determines the ALUA state of a device and prints a priority value to -# stdout. -# -# Author(s): Jan Kunigk -# S. Bader -# -# This file is released under the GPL. -#============================================================================== -EXEC = mpath_prio_alua -BUILD = glibc -DEBUG = 0 -DEBUG_DUMPHEX = 0 -OBJS = main.o rtpg.o -INSTALL = install -D - -TOPDIR = ../.. - -ifneq ($(shell ls $(TOPDIR)/Makefile.inc 2>/dev/null),) -include $(TOPDIR)/Makefile.inc -endif - -CFLAGS += -DDEBUG=$(DEBUG) - -all: $(BUILD) - -glibc: $(OBJS) - $(CC) -o $(EXEC) $(OBJS) $(LDFLAGS) - -klibc: $(OBJS) - $(CC) -static -o $(EXEC) $(OBJS) - -install: $(BUILD) $(EXEC).8.gz - $(INSTALL) -s -m 755 $(EXEC) $(DESTDIR)$(bindir)/$(EXEC) - $(INSTALL) -m 644 $(EXEC).8.gz $(DESTDIR)$(mandir)/$(EXEC).8.gz - -uninstall: - rm $(DESTDIR)$(bindir)/$(EXEC) - rm $(DESTDIR)$(mandir)/$(EXEC).8.gz - -clean: - rm -f *.o *.gz $(EXEC) - -$(EXEC).8.gz: $(EXEC).8 - $(GZIP) $< >$@ - -main.o: main.c rtpg.h spc3.h - -rtpg.o: rtpg.c rtpg.h spc3.h diff --git a/path_priority/pp_alua/main.c b/path_priority/pp_alua/main.c deleted file mode 100644 index 190fbdc..0000000 --- a/path_priority/pp_alua/main.c +++ /dev/null @@ -1,281 +0,0 @@ -/* - * (C) Copyright IBM Corp. 2004, 2005 All Rights Reserved. - * - * main.c - * - * Tool to make use of a SCSI-feature called Asymmetric Logical Unit Access. - * It determines the ALUA state of a device and prints a priority value to - * stdout. - * - * Author(s): Jan Kunigk - * S. Bader - * - * This file is released under the GPL. - */ -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "rtpg.h" - -#define ALUA_PRIO_SUCCESS 0 -#define ALUA_PRIO_INVALID_COMMANDLINE 1 -#define ALUA_PRIO_OPEN_FAILED 2 -#define ALUA_PRIO_NOT_SUPPORTED 3 -#define ALUA_PRIO_RTPG_FAILED 4 -#define ALUA_PRIO_GETAAS_FAILED 5 - -#define ALUA_PRIO_MAJOR 0 -#define ALUA_PRIO_MINOR 6 - -#define PRINT_ERROR(f, a...) \ - if (verbose) \ - fprintf(stderr, "ERROR: " f, ##a) -#define PRINT_VERBOSE(f, a...) \ - if (verbose) \ - printf(f, ##a) - -char * devicename = NULL; -int verbose = 0; - -char *basename(char *p) -{ - char *r; - - for(r = p; *r != '\0'; r++); - for(; r > p && *(r - 1) != '/'; r--); - - return r; -} - -void -print_help(char *command) -{ - printf("Usage: %s [ [...]]\n\n", - basename(command)); - printf("Options are:\n"); - - printf("\t-d \n"); - printf("\t\tSets the directory prefix for relative path names and"); - printf(" created\n\t\tpath names. (default = \"/dev\")\n"); - - printf("\t-h\n"); - printf("\t\tPrint this help.\n"); - - printf("\t-v\n"); - printf("\t\tTurn on verbose output.\n"); - - printf("\t-V\n"); - printf("\t\tPrints the version number and exits.\n"); - - printf("\nDevice may be an absolute or relative path to a device "); - printf("node or a major and\nminor number seperated by a colon (:)."); - printf(" In this case a temporary device node\nwill be created in "); - printf("the device directory.\n"); -} - -void -print_version(char *command) -{ - printf("(C) Copyright IBM Corp. 2004, 2005 All Rights Reserved.\n"); - printf("This is %s version %u.%u\n", - basename(command), - ALUA_PRIO_MAJOR, - ALUA_PRIO_MINOR - ); -} - -int -open_block_device(char *name) -{ - int fd; - struct stat st; - - if (stat(name, &st) != 0) { - PRINT_ERROR("Cannot get file status from %s (errno = %i)!\n", - name, errno); - return -ALUA_PRIO_OPEN_FAILED; - } - if (!S_ISBLK(st.st_mode)) { - PRINT_ERROR("%s is not a block device!\n", name); - return -ALUA_PRIO_OPEN_FAILED; - } - fd = open(name, O_RDONLY); - if (fd < 0) { - PRINT_ERROR("Couldn't open %s (errno = %i)!\n", name, errno); - return -ALUA_PRIO_OPEN_FAILED; - } - return fd; -} - -int -close_block_device(int fd) -{ - return close(fd); -} - -int -get_alua_info(int fd) -{ - char * aas_string[] = { - [AAS_OPTIMIZED] = "active/optimized", - [AAS_NON_OPTIMIZED] = "active/non-optimized", - [AAS_STANDBY] = "standby", - [AAS_UNAVAILABLE] = "unavailable", - [AAS_TRANSITIONING] = "transitioning between states", - }; - int rc; - int tpg; - - rc = get_target_port_group_support(fd); - if (rc < 0) - return rc; - - if (verbose) { - printf("Target port groups are "); - switch(rc) { - case TPGS_NONE: - printf("not"); - break; - case TPGS_IMPLICIT: - printf("implicitly"); - break; - case TPGS_EXPLICIT: - printf("explicitly"); - break; - case TPGS_BOTH: - printf("implicitly and explicitly"); - break; - } - printf(" supported.\n"); - } - - if (rc == TPGS_NONE) - return -ALUA_PRIO_NOT_SUPPORTED; - - tpg = get_target_port_group(fd); - if (tpg < 0) { - PRINT_ERROR("Couldn't get target port group!\n"); - return -ALUA_PRIO_RTPG_FAILED; - } - PRINT_VERBOSE("Reported target port group is %i", tpg); - - rc = get_asymmetric_access_state(fd, tpg); - if (rc < 0) { - PRINT_VERBOSE(" [get AAS failed]\n"); - PRINT_ERROR("Couln't get asymmetric access state!\n"); - return -ALUA_PRIO_GETAAS_FAILED; - } - PRINT_VERBOSE(" [%s]\n", - (aas_string[rc]) ? aas_string[rc] : "invalid/reserved" - ); - - return rc; -} - -int -main (int argc, char **argv) -{ - char devicepath[PATH_MAX]; - char * devicedir; - char * s_opts = "d:hvV"; - char * pos; - int fd; - int rc; - int c; - - devicedir = "/dev"; - while ((c = getopt(argc, argv, s_opts)) >= 0) { - switch(c) { - case 'd': - devicedir = optarg; - break; - case 'h': - print_help(argv[0]); - return ALUA_PRIO_SUCCESS; - case 'V': - print_version(argv[0]); - return ALUA_PRIO_SUCCESS; - case 'v': - verbose = 1; - break; - case '?': - case ':': - default: - return ALUA_PRIO_INVALID_COMMANDLINE; - } - } - - if (optind == argc) { - print_help(argv[0]); - printf("\n"); - PRINT_ERROR("No device specified!\n"); - return ALUA_PRIO_INVALID_COMMANDLINE; - } - - rc = ALUA_PRIO_SUCCESS; - for(c = optind; c < argc && !rc; c++) { - if (argv[c][0] == '/') { - pos = NULL; - sprintf(devicepath, "%s", argv[c]); - } else if ((pos = index(argv[c], ':')) == NULL) { - sprintf(devicepath, "%s/%s", devicedir, argv[c]); - } else { - int major; - int minor; - - major = atoi(argv[c]); - minor = atoi(++pos); - sprintf(devicepath, "%s/tmpdev-%u:%u-%u", - devicedir, major, minor, getpid() - ); - mknod( - devicepath, - S_IFBLK|S_IRUSR|S_IWUSR, - MKDEV(major, minor) - ); - - } - - fd = open_block_device(devicepath); - if (fd < 0) { - if (pos != NULL) - unlink(devicepath); - return -fd; - } - rc = get_alua_info(fd); - if (rc >= 0) { - switch(rc) { - case AAS_OPTIMIZED: - rc = 50; - break; - case AAS_NON_OPTIMIZED: - rc = 10; - break; - case AAS_STANDBY: - rc = 1; - break; - default: - rc = 0; - } - printf("%u\n", rc); - rc = ALUA_PRIO_SUCCESS; - } - close_block_device(fd); - - /* The path was created before. */ - if (pos != NULL) - unlink(devicepath); - } - - return -rc; -} diff --git a/path_priority/pp_alua/mpath_prio_alua.8 b/path_priority/pp_alua/mpath_prio_alua.8 deleted file mode 100644 index 4843bcd..0000000 --- a/path_priority/pp_alua/mpath_prio_alua.8 +++ /dev/null @@ -1,162 +0,0 @@ -.TH MPATH_PRIO_ALUA 8 "7. June 2005" "multipath-tools" \ -"Linux Administrator's Manual" -.SH NAME -mpath_prio_alua \- Path priority tool based on Asymmetric LUn Access -.SH SYNOPSIS -.B mpath_prio_alua -.RB [\| \-d\ \c -.IR directory \|] -.RB [\| \-h \|] -.RB [\| \-v \|] -.RB [\| \-V \|] -.IR device " \|[ " device " \|[ " ... " \|]\|]" -.SH DESCRIPTION -.B mpath_prio_alua -is used as a priority callout for the multipath command. It returns a number -that is used by multipath to group devices with the same priority together. -.SH OPTIONS -.TP -.BI \-d " directory" -target directory for devices given as relative device names or devices given -as -.IR major : minor \c - number. -Default is "/dev". -.TP -.B \-h -displays the command line help. -.TP -.B \-v -turns on verbose output. This shows all results in human readable format. -This includes information about the port group the device is in and its -current state. -.TP -.B \-V -shows the version number and exits. -.TP -.BI device -specifies the device to query (the device must be a SCSI device that supports -the \*[lq]Report Target Port Groups\*[rq] command). -One of the following three formats may be used: -.RS -.IP \(bu 2 -The full path name that starts with '/' (e.g. /dev/sda). -.IP \(bu -The device name only. This will prefix the directory name given by the -\-d option (e.g. sda). -.IP \(bu -The major and minor number of the device separated by ':'. This will -create a temporary device node in the device directory (e.g. 8:0). The -temporary name will be -.RB \*[lq] tmpdev-:- \*[rq]. -.SH "RETURN VALUE" -The mpath_prio_alua command returns the following values: -.IP \fB0 -on success. In this case the priority for the device is printed to -stdout. The priority value is: -.RS -.IP \fB50\fP -for devices that are in the active, optimized group -.IP \fB10 -for devices that are in an active but non-optimized group -.IP \fB1 -for devices that are in the standby group -.IP \fB0 -for all other groups -.RE -.IP "" -The reason for the widely spaced priority values is the way multipath handles -them. It will multiply the number of paths in a group with the priority value -and select the group with the highest result. Thus, if there are six paths in -the active, non-optimized group and only one in the active, optimized one, -the non-optimized group would be used. -.IP \fB1 -Indicates an error parsing the command line. -.IP \fB2 -The given devices could not be opened for reading. -.IP \fB3 -The device does not support target port groups. -.IP \fB4 -The inquiry command did not return a target port group for the given device. -.IP \fB5 -The report target port group command failed or did not return a target -port group that was obtained from the inquiry command. -.SH "EXAMPLES" -This example queries a device directly and returns the priority string: -.P -.RS -.B #> mpath_prio_alua /dev/sda -.br -50 -.RE -.P -Now the major and minor number is used to specify the device and verbose -output is selected: -.P -.RS -.B #> mpath_prio_alua -v 8:0 -.br -Target port groups are implicitly supported. -.br -Reported target port group is 0 [active/optimized] -.br -50 -.RE -.P -The following example shows the entries in the devices section of the -.RI "multipath-tool configuration file (" /etc/multipath.conf ) -to support an IBM DS6000 storage system: -.P -.RS -.PD 0 -device { -.RS -.TP 22 -.B vendor -"IBM " -.TP -.B product -"1750500 " -.TP -.B path_grouping_policy -group_by_prio -.TP -.B prio_callout -"/sbin/mpath_prio_alua -d/tmp %d" -.TP -.B features -"1 queue_if_no_path" -.TP -.B path_checker -tur -.RE -} -.PD -.RE -.TP -.B Notes: -.IP \(bu 2 -Depending on your default configuration not all keywords are required -.RB "(e.g. if your " path_checker " is set to tur you don't have to" -.RB "use the " path_checker " statement in the device section)." -.IP \(bu -.RB "The entries for " vendor " and " product " must be strings that are 8" -.RB "characters long (for " vendor ") and 16 characters long (for " product ")." -The strings have to be padded with blanks if necessary. -.IP \(bu -If you are working with hotpluggable devices whose device nodes are created -by udev you should use the %d flag in the -.BR prio_callout " statement." -This is because a short time elapses between the devices being available -and udev creating the device nodes. -.IP \(bu -If under certain circumstances your storage subsystem temporarily reports -.RB "failures on all paths, you should use the " features " statement showed" -in the example. -This will configure the multipath volume to requeue I/O until a path becomes -available again, instead of reporting failures in that case. -.SH "SEE ALSO" -.BR multipath (8), -.SH AUTHORS -.B mpath_prio_alua -was developed by Jan Kunigk and adapted by Stefan Bader diff --git a/path_priority/pp_alua/rtpg.c b/path_priority/pp_alua/rtpg.c deleted file mode 100644 index 9aea560..0000000 --- a/path_priority/pp_alua/rtpg.c +++ /dev/null @@ -1,280 +0,0 @@ -/* - * (C) Copyright IBM Corp. 2004, 2005 All Rights Reserved. - * - * rtpg.c - * - * Tool to make use of a SCSI-feature called Asymmetric Logical Unit Access. - * It determines the ALUA state of a device and prints a priority value to - * stdout. - * - * Author(s): Jan Kunigk - * S. Bader - * - * This file is released under the GPL. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define __user -#include - -#include "rtpg.h" - -#define SENSE_BUFF_LEN 32 -#define DEF_TIMEOUT 60000 - -/* - * Macro used to print debug messaged. - */ -#if DEBUG > 0 -#define PRINT_DEBUG(f, a...) \ - fprintf(stderr, "DEBUG: " f, ##a) -#else -#define PRINT_DEBUG(f, a...) -#endif - -/* - * Optionally print the commands sent and the data received a hex dump. - */ -#if DEBUG > 0 -#if DEBUG_DUMPHEX > 0 -#define PRINT_HEX(p, l) print_hex(p, l) -void -print_hex(unsigned char *p, unsigned long len) -{ - int i; - - for(i = 0; i < len; i++) { - if (i % 16 == 0) - printf("%04x: ", i); - printf("%02x%s", p[i], (((i + 1) % 16) == 0) ? "\n" : " "); - } - printf("\n"); -} -#else -#define PRINT_HEX(p, l) -#endif -#else -#define PRINT_HEX(p, l) -#endif - -/* - * Returns 0 if the SCSI command either was successful or if the an error was - * recovered, otherwise 1. (definitions taken from sg_err.h) - */ -#define SCSI_CHECK_CONDITION 0x2 -#define SCSI_COMMAND_TERMINATED 0x22 -#define SG_ERR_DRIVER_SENSE 0x08 -#define RECOVERED_ERROR 0x01 - -static int -scsi_error(struct sg_io_hdr *hdr) -{ - /* Treat SG_ERR here to get rid of sg_err.[ch] */ - hdr->status &= 0x7e; - - if ( - (hdr->status == 0) && - (hdr->host_status == 0) && - (hdr->driver_status == 0) - ) { - return 0; - } - - if ( - (hdr->status == SCSI_CHECK_CONDITION) || - (hdr->status == SCSI_COMMAND_TERMINATED) || - ((hdr->driver_status & 0xf) == SG_ERR_DRIVER_SENSE) - ) { - if (hdr->sbp && (hdr->sb_len_wr > 2)) { - int sense_key; - unsigned char * sense_buffer = hdr->sbp; - - if (sense_buffer[0] & 0x2) - sense_key = sense_buffer[1] & 0xf; - else - sense_key = sense_buffer[2] & 0xf; - - if (sense_key == RECOVERED_ERROR) - return 0; - } - } - - return 1; -} - -/* - * Helper function to setup and run a SCSI inquiry command. - */ -int -do_inquiry(int fd, int evpd, unsigned int codepage, void *resp, int resplen) -{ - struct inquiry_command cmd; - struct sg_io_hdr hdr; - unsigned char sense[SENSE_BUFF_LEN]; - - memset(&cmd, 0, sizeof(cmd)); - cmd.op = OPERATION_CODE_INQUIRY; - if (evpd) { - inquiry_command_set_evpd(&cmd); - cmd.page = codepage; - } - set_uint16(cmd.length, resplen); - PRINT_HEX((unsigned char *) &cmd, sizeof(cmd)); - - memset(&hdr, 0, sizeof(hdr)); - hdr.interface_id = 'S'; - hdr.cmdp = (unsigned char *) &cmd; - hdr.cmd_len = sizeof(cmd); - hdr.dxfer_direction = SG_DXFER_FROM_DEV; - hdr.dxferp = resp; - hdr.dxfer_len = resplen; - hdr.sbp = sense; - hdr.mx_sb_len = sizeof(sense); - hdr.timeout = DEF_TIMEOUT; - - if (ioctl(fd, SG_IO, &hdr) < 0) { - PRINT_DEBUG("do_inquiry: IOCTL failed!\n"); - return -RTPG_INQUIRY_FAILED; - } - - if (scsi_error(&hdr)) { - PRINT_DEBUG("do_inquiry: SCSI error!\n"); - return -RTPG_INQUIRY_FAILED; - } - PRINT_HEX((unsigned char *) resp, resplen); - - return 0; -} - -/* - * This function returns the support for target port groups by evaluating the - * data returned by the standard inquiry command. - */ -int -get_target_port_group_support(int fd) -{ - struct inquiry_data inq; - int rc; - - rc = do_inquiry(fd, 0, 0x00, &inq, sizeof(inq)); - if (!rc) { - rc = inquiry_data_get_tpgs(&inq); - } - - return rc; -} - -int -get_target_port_group(int fd) -{ - unsigned char buf[128]; - struct vpd83_data * vpd83; - struct vpd83_dscr * dscr; - int rc; - - rc = do_inquiry(fd, 1, 0x83, buf, sizeof(buf)); - if (!rc) { - vpd83 = (struct vpd83_data *) buf; - - rc = -RTPG_NO_TPG_IDENTIFIER; - FOR_EACH_VPD83_DSCR(vpd83, dscr) { - if ((((char *) dscr) - ((char *) vpd83)) > sizeof(buf)) - break; - - if (vpd83_dscr_istype(dscr, IDTYPE_TARGET_PORT_GROUP)) { - struct vpd83_tpg_dscr * p; - - if (rc != -RTPG_NO_TPG_IDENTIFIER) { - PRINT_DEBUG("get_target_port_group: " - "more than one TPG identifier " - "found!\n"); - continue; - } - - p = (struct vpd83_tpg_dscr *) dscr->data; - rc = get_uint16(p->tpg); - } - } - if (rc == -RTPG_NO_TPG_IDENTIFIER) { - PRINT_DEBUG("get_target_port_group: " - "no TPG identifier found!\n"); - } - } - - return rc; -} - -int -do_rtpg(int fd, void* resp, long resplen) -{ - struct rtpg_command cmd; - struct sg_io_hdr hdr; - unsigned char sense[SENSE_BUFF_LEN]; - - memset(&cmd, 0, sizeof(cmd)); - cmd.op = OPERATION_CODE_RTPG; - rtpg_command_set_service_action(&cmd); - set_uint32(cmd.length, resplen); - PRINT_HEX((unsigned char *) &cmd, sizeof(cmd)); - - memset(&hdr, 0, sizeof(hdr)); - hdr.interface_id = 'S'; - hdr.cmdp = (unsigned char *) &cmd; - hdr.cmd_len = sizeof(cmd); - hdr.dxfer_direction = SG_DXFER_FROM_DEV; - hdr.dxferp = resp; - hdr.dxfer_len = resplen; - hdr.mx_sb_len = sizeof(sense); - hdr.sbp = sense; - hdr.timeout = DEF_TIMEOUT; - - if (ioctl(fd, SG_IO, &hdr) < 0) - return -RTPG_RTPG_FAILED; - - if (scsi_error(&hdr)) { - PRINT_DEBUG("do_rtpg: SCSI error!\n"); - return -RTPG_RTPG_FAILED; - } - PRINT_HEX(resp, resplen); - - return 0; -} - -int -get_asymmetric_access_state(int fd, unsigned int tpg) -{ - unsigned char buf[128]; - struct rtpg_data * tpgd; - struct rtpg_tpg_dscr * dscr; - int rc; - - rc = do_rtpg(fd, buf, sizeof(buf)); - if (rc < 0) - return rc; - - tpgd = (struct rtpg_data *) buf; - rc = -RTPG_TPG_NOT_FOUND; - RTPG_FOR_EACH_PORT_GROUP(tpgd, dscr) { - if (get_uint16(dscr->tpg) == tpg) { - if (rc != -RTPG_TPG_NOT_FOUND) { - PRINT_DEBUG("get_asymmetric_access_state: " - "more than one entry with same port " - "group.\n"); - } else { - PRINT_DEBUG("pref=%i\n", dscr->pref); - rc = rtpg_tpg_dscr_get_aas(dscr); - } - } - } - - return rc; -} - diff --git a/path_priority/pp_alua/rtpg.h b/path_priority/pp_alua/rtpg.h deleted file mode 100644 index 3c5dcf1..0000000 --- a/path_priority/pp_alua/rtpg.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * (C) Copyright IBM Corp. 2004, 2005 All Rights Reserved. - * - * rtpg.h - * - * Tool to make use of a SCSI-feature called Asymmetric Logical Unit Access. - * It determines the ALUA state of a device and prints a priority value to - * stdout. - * - * Author(s): Jan Kunigk - * S. Bader - * - * This file is released under the GPL. - */ -#ifndef __RTPG_H__ -#define __RTPG_H__ -#include "spc3.h" - -#define RTPG_SUCCESS 0 -#define RTPG_INQUIRY_FAILED 1 -#define RTPG_NO_TPG_IDENTIFIER 2 -#define RTPG_RTPG_FAILED 3 -#define RTPG_TPG_NOT_FOUND 4 - -int get_target_port_group_support(int fd); -int get_target_port_group(int fd); -int get_asymmetric_access_state(int fd, unsigned int tpg); - -#endif /* __RTPG_H__ */ - diff --git a/path_priority/pp_alua/spc3.h b/path_priority/pp_alua/spc3.h deleted file mode 100644 index 11f5dbd..0000000 --- a/path_priority/pp_alua/spc3.h +++ /dev/null @@ -1,322 +0,0 @@ -/* - * (C) Copyright IBM Corp. 2004, 2005 All Rights Reserved. - * - * spc3.h - * - * Tool to make use of a SCSI-feature called Asymmetric Logical Unit Access. - * It determines the ALUA state of a device and prints a priority value to - * stdout. - * - * Author(s): Jan Kunigk - * S. Bader - * - * This file is released under the GPL. - */ -#ifndef __SPC3_H__ -#define __SPC3_H__ -/*============================================================================= - * Some helper functions for getting and setting 16 and 32 bit values. - *============================================================================= - */ -static inline unsigned short -get_uint16(unsigned char *p) -{ - return (p[0] << 8) + p[1]; -} - -static inline void -set_uint16(unsigned char *p, unsigned short v) -{ - p[0] = (v >> 8) & 0xff; - p[1] = v & 0xff; -} - -static inline unsigned int -get_uint32(unsigned char *p) -{ - return (p[0] << 24) + (p[1] << 16) + (p[2] << 8) + p[3]; -} - -static inline void -set_uint32(unsigned char *p, unsigned int v) -{ - p[0] = (v >> 24) & 0xff; - p[1] = (v >> 16) & 0xff; - p[2] = (v >> 8) & 0xff; - p[3] = v & 0xff; -} - -/*============================================================================= - * Definitions to support the standard inquiry command as defined in SPC-3. - * If the evpd (enable vital product data) bit is set the data that will be - * returned is selected by the page field. This field must be 0 if the evpd - * bit is not set. - *============================================================================= - */ -#define OPERATION_CODE_INQUIRY 0x12 - -struct inquiry_command { - unsigned char op; - unsigned char b1; /* xxxxxx.. = reserved */ - /* ......x. = obsolete */ - /* .......x = evpd */ - unsigned char page; - unsigned char length[2]; - unsigned char control; -} __attribute__((packed)); - -static inline void -inquiry_command_set_evpd(struct inquiry_command *ic) -{ - ic->b1 |= 1; -} - -/*----------------------------------------------------------------------------- - * Data returned by the standard inquiry command. - *----------------------------------------------------------------------------- - * - * Peripheral qualifier codes. - */ -#define PQ_CONNECTED 0x0 -#define PQ_DISCONNECTED 0x1 -#define PQ_UNSUPPORTED 0x3 - -/* Defined peripheral device types. */ -#define PDT_DIRECT_ACCESS 0x00 -#define PDT_SEQUENTIAL_ACCESS 0x01 -#define PDT_PRINTER 0x02 -#define PDT_PROCESSOR 0x03 -#define PDT_WRITE_ONCE 0x04 -#define PDT_CD_DVD 0x05 -#define PDT_SCANNER 0x06 -#define PDT_OPTICAL_MEMORY 0x07 -#define PDT_MEDIUM_CHANGER 0x08 -#define PDT_COMMUNICATIONS 0x09 -#define PDT_STORAGE_ARRAY_CONTROLLER 0x0c -#define PDT_ENCLOSURE_SERVICES 0x0d -#define PDT_SIMPLIFIED_DIRECT_ACCESS 0x0e -#define PDT_OPTICAL_CARD_READER_WRITER 0x0f -#define PDT_BRIDGE_CONTROLLER 0x10 -#define PDT_OBJECT_BASED 0x11 -#define PDT_AUTOMATION_INTERFACE 0x12 -#define PDT_LUN 0x1e -#define PDT_UNKNOWN 0x1f - -/* Defined version codes. */ -#define VERSION_NONE 0x00 -#define VERSION_SPC 0x03 -#define VERSION_SPC2 0x04 -#define VERSION_SPC3 0x05 - -/* Defined TPGS field values. */ -#define TPGS_NONE 0x0 -#define TPGS_IMPLICIT 0x1 -#define TPGS_EXPLICIT 0x2 -#define TPGS_BOTH 0x3 - -struct inquiry_data { - unsigned char b0; /* xxx..... = peripheral_qualifier */ - /* ...xxxxx = peripheral_device_type */ - unsigned char b1; /* x....... = removable medium */ - /* .xxxxxxx = reserverd */ - unsigned char version; - unsigned char b3; /* xx...... = obsolete */ - /* ..x..... = normal aca supported */ - /* ...x.... = hirarchichal lun supp. */ - /* ....xxxx = response format */ - /* 2 is spc-3 format */ - unsigned char length; - unsigned char b5; /* x....... = storage controller */ - /* component supported */ - /* .x...... = access controls coord. */ - /* ..xx.... = target port group supp.*/ - /* ....x... = third party copy supp. */ - /* .....xx. = reserved */ - /* .......x = protection info supp. */ - unsigned char b6; /* x....... = bque */ - /* .x...... = enclosure services sup.*/ - /* ..x..... = vs1 */ - /* ...x.... = multiport support */ - /* ....x... = medium changer */ - /* .....xx. = obsolete */ - /* .......x = add16 */ - unsigned char b7; /* xx...... = obsolete */ - /* ..x..... = wbus16 */ - /* ...x.... = sync */ - /* ....x... = linked commands supp. */ - /* .....x.. = obsolete */ - /* ......x. = command queue support */ - /* .......x = vs2 */ - unsigned char vendor_identification[8]; - unsigned char product_identification[8]; - unsigned char product_revision[4]; - unsigned char vendor_specific[20]; - unsigned char b48; /* xxxx.... = reserved */ - /* ....xx.. = clocking */ - /* ......x. = qas */ - /* .......x = ius */ - unsigned char reserved4; - unsigned char version_descriptor[8][2]; - unsigned char reserved5[22]; - unsigned char vendor_parameters[0]; -} __attribute__((packed)); - -static inline int -inquiry_data_get_tpgs(struct inquiry_data *id) -{ - return (id->b5 >> 4) & 3; -} - -/*----------------------------------------------------------------------------- - * Inquiry data returned when requesting vital product data page 0x83. - *----------------------------------------------------------------------------- - */ -#define CODESET_BINARY 0x1 -#define CODESET_ACSII 0x2 -#define CODESET_UTF8 0x3 - -#define ASSOCIATION_UNIT 0x0 -#define ASSOCIATION_PORT 0x1 -#define ASSOCIATION_DEVICE 0x2 - -#define IDTYPE_VENDOR_SPECIFIC 0x0 -#define IDTYPE_T10_VENDOR_ID 0x1 -#define IDTYPE_EUI64 0x2 -#define IDTYPE_NAA 0x3 -#define IDTYPE_RELATIVE_TPG_ID 0x4 -#define IDTYPE_TARGET_PORT_GROUP 0x5 -#define IDTYPE_LUN_GROUP 0x6 -#define IDTYPE_MD5_LUN_ID 0x7 -#define IDTYPE_SCSI_NAME_STRING 0x8 - -struct vpd83_tpg_dscr { - unsigned char reserved1[2]; - unsigned char tpg[2]; -} __attribute__((packed)); - -struct vpd83_dscr { - unsigned char b0; /* xxxx.... = protocol id */ - /* ....xxxx = codeset */ - unsigned char b1; /* x....... = protocol id valid */ - /* .x...... = reserved */ - /* ..xx.... = association */ - /* ....xxxx = id type */ - unsigned char reserved2; - unsigned char length; /* size-4 */ - unsigned char data[0]; -} __attribute__((packed)); - -static inline int -vpd83_dscr_istype(struct vpd83_dscr *d, unsigned char type) -{ - return ((d->b1 & 7) == type); -} - -struct vpd83_data { - unsigned char b0; /* xxx..... = peripheral_qualifier */ - /* ...xxxxx = peripheral_device_type */ - unsigned char page_code; /* 0x83 */ - unsigned char length[2]; /* size-4 */ - struct vpd83_dscr data[0]; -} __attribute__((packed)); - -/*----------------------------------------------------------------------------- - * This macro should be used to walk through all identification descriptors - * defined in the code page 0x83. - * The argument p is a pointer to the code page 0x83 data and d is used to - * point to the current descriptor. - *----------------------------------------------------------------------------- - */ -#define FOR_EACH_VPD83_DSCR(p, d) \ - for( \ - d = p->data; \ - (((char *) d) - ((char *) p)) < \ - get_uint16(p->length); \ - d = (struct vpd83_dscr *) \ - ((char *) d + d->length + 4) \ - ) - -/*============================================================================= - * The following stuctures and macros are used to call the report target port - * groups command defined in SPC-3. - * This command is used to get information about the target port groups (which - * states are supported, which ports belong to this group, and so on) and the - * current state of each target port group. - *============================================================================= - */ -#define OPERATION_CODE_RTPG 0xa3 -#define SERVICE_ACTION_RTPG 0x0a - -struct rtpg_command { - unsigned char op; /* 0xa3 */ - unsigned char b1; /* xxx..... = reserved */ - /* ...xxxxx = service action (0x0a) */ - unsigned char reserved2[4]; - unsigned char length[4]; - unsigned char reserved3; - unsigned char control; -} __attribute__((packed)); - -static inline void -rtpg_command_set_service_action(struct rtpg_command *cmd) -{ - cmd->b1 = (cmd->b1 & 0xe0) | SERVICE_ACTION_RTPG; -} - -struct rtpg_tp_dscr { - unsigned char obsolete1[2]; - /* The Relative Target Port Identifier of a target port. */ - unsigned char rtpi[2]; -} __attribute__((packed)); - -#define AAS_OPTIMIZED 0x0 -#define AAS_NON_OPTIMIZED 0x1 -#define AAS_STANDBY 0x2 -#define AAS_UNAVAILABLE 0x3 -#define AAS_TRANSITIONING 0xf - -#define TPG_STATUS_NONE 0x0 -#define TPG_STATUS_SET 0x1 -#define TPG_STATUS_IMPLICIT_CHANGE 0x2 - -struct rtpg_tpg_dscr { - unsigned char b0; /* x....... = pref(ered) port */ - /* .xxx.... = reserved */ - /* ....xxxx = asymetric access state */ - unsigned char b1; /* xxxx.... = reserved */ - /* ....x... = unavailable support */ - /* .....x.. = standby support */ - /* ......x. = non-optimized support */ - /* .......x = optimized support */ - unsigned char tpg[2]; - unsigned char reserved3; - unsigned char status; - unsigned char vendor_unique; - unsigned char port_count; - struct rtpg_tp_dscr data[0]; -} __attribute__((packed)); - -static inline int -rtpg_tpg_dscr_get_aas(struct rtpg_tpg_dscr *d) -{ - return (d->b0 & 0x0f); -} - -struct rtpg_data { - unsigned char length[4]; /* size-4 */ - struct rtpg_tpg_dscr data[0]; -} __attribute__((packed)); - -#define RTPG_FOR_EACH_PORT_GROUP(p, g) \ - for( \ - g = &(p->data[0]); \ - (((char *) g) - ((char *) p)) < get_uint32(p->length); \ - g = (struct rtpg_tpg_dscr *) ( \ - ((char *) g) + \ - sizeof(struct rtpg_tpg_dscr) + \ - g->port_count * sizeof(struct rtpg_tp_dscr) \ - ) \ - ) - -#endif /* __SPC3_H__ */ - diff --git a/path_priority/pp_balance_units/Makefile b/path_priority/pp_balance_units/Makefile deleted file mode 100644 index bed7fb0..0000000 --- a/path_priority/pp_balance_units/Makefile +++ /dev/null @@ -1,44 +0,0 @@ -# Makefile -# -# Copyright (C) 2003 Christophe Varoqui, -# -BUILD = glibc -DEBUG = 0 - -TOPDIR = ../.. -include $(TOPDIR)/Makefile.inc - -ifeq ($(strip $(BUILD)),klibc) - CFLAGS += -I/usr/include -DDEBUG=$(DEBUG) - OBJS = pp_balance_units.o $(MULTIPATHLIB)-$(BUILD).a -else - CFLAGS += -I$(multipathdir) -DDEBUG=$(DEBUG) - LDFLAGS = -ldevmapper - OBJS = pp_balance_units.o $(MULTIPATHLIB)-$(BUILD).a -endif - -EXEC = mpath_prio_balance_units - -all: $(BUILD) - -prepare: - rm -f core *.o *.gz - -glibc: prepare $(OBJS) - $(CC) -o $(EXEC) $(OBJS) $(LDFLAGS) - -klibc: prepare $(OBJS) - $(CC) -static -o $(EXEC) $(CRT0) $(OBJS) $(KLIBC) $(LIBGCC) - -$(MULTIPATHLIB)-$(BUILD).a: - make -C $(multipathdir) BUILD=$(BUILD) $(BUILD) - -install: - install -d $(DESTDIR)$(bindir) - install -s -m 755 $(EXEC) $(DESTDIR)$(bindir)/ - -uninstall: - rm $(DESTDIR)$(bindir)/$(EXEC) - -clean: - rm -f core *.o $(EXEC) *.gz diff --git a/path_priority/pp_balance_units/pp_balance_units.c b/path_priority/pp_balance_units/pp_balance_units.c deleted file mode 100644 index 307a959..0000000 --- a/path_priority/pp_balance_units/pp_balance_units.c +++ /dev/null @@ -1,474 +0,0 @@ -/* - * Christophe Varoqui (2004) - * This code is GPLv2, see license file - * - * This path prioritizer aims to balance logical units over all - * controlers 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 - * - 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) - * - else, or if anything goes wrong, return 1 as a default prio - * - */ -#define __user - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#define SERIAL_SIZE 255 -#define WORD_SIZE 255 -#define PARAMS_SIZE 255 -#define FILE_NAME_SIZE 255 -#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 SCSI_CHECK_CONDITION 0x2 -#define SCSI_COMMAND_TERMINATED 0x22 -#define SG_ERR_DRIVER_SENSE 0x08 - -#if DEBUG -#define debug(format, arg...) fprintf(stderr, format "\n", ##arg) -#else -#define debug(format, arg...) do {} while(0) -#endif - -#define safe_sprintf(var, format, args...) \ - snprintf(var, sizeof(var), format, ##args) >= sizeof(var) -#define safe_snprintf(var, size, format, args...) \ - snprintf(var, size, format, ##args) >= size - -struct path { - char dev_t[WORD_SIZE]; - char serial[SERIAL_SIZE]; -}; - -struct controler { - char serial[SERIAL_SIZE]; - int path_count; -}; - -static int -exit_tool (int ret) -{ - printf("1\n"); - exit(ret); -} - -static int -opennode (char * devt, int mode) -{ - char devpath[FILE_NAME_SIZE]; - unsigned int major; - unsigned int minor; - int fd; - - sscanf(devt, "%u:%u", &major, &minor); - memset(devpath, 0, FILE_NAME_SIZE); - - if (safe_sprintf(devpath, "/tmp/.pp_balance.%u.%u.devnode", - major, minor)) { - fprintf(stderr, "devpath too small\n"); - return -1; - } - unlink (devpath); - mknod(devpath, S_IFBLK|S_IRUSR|S_IWUSR, makedev(major, minor)); - fd = open(devpath, mode); - - if (fd < 0) - unlink(devpath); - - return fd; - -} - -static void -closenode (char * devt, int fd) -{ - char devpath[FILE_NAME_SIZE]; - unsigned int major; - unsigned int minor; - - if (fd >= 0) - close(fd); - - sscanf(devt, "%u:%u", &major, &minor); - if (safe_sprintf(devpath, "/tmp/.pp_balance.%u.%u.devnode", - major, minor)) { - fprintf(stderr, "devpath too small\n"); - return; - } - unlink(devpath); -} - -static int -do_inq(int sg_fd, int cmddt, int evpd, unsigned int pg_op, - void *resp, int mx_resp_len, int noisy) -{ - unsigned char inqCmdBlk[INQUIRY_CMDLEN] = - { 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) - inqCmdBlk[1] |= 1; - inqCmdBlk[2] = (unsigned char) pg_op; - inqCmdBlk[3] = (unsigned char)((mx_resp_len >> 8) & 0xff); - 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 = 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; -} - -static int -get_serial (char * str, char * devt) -{ - int fd; - int len; - char buff[MX_ALLOC_LEN + 1]; - - fd = opennode(devt, O_RDONLY); - - if (fd < 0) - return 0; - - if (0 == do_inq(fd, 0, 1, 0x80, buff, MX_ALLOC_LEN, 0)) { - len = buff[3]; - if (len > 0) { - memcpy(str, buff + 4, len); - buff[len] = '\0'; - } - close(fd); - return 1; - } - - closenode(devt, fd); - return 0; -} - -static void * -get_params (void) -{ - struct dm_task *dmt, *dmt1; - struct dm_names *names = NULL; - unsigned next = 0; - void *nexttgt; - uint64_t start, length; - char *target_type = NULL; - char *params; - char *pp; - vector paramsvec = NULL; - - if (!(dmt = dm_task_create(DM_DEVICE_LIST))) - return NULL; - - if (!dm_task_run(dmt)) - goto out; - - if (!(names = dm_task_get_names(dmt))) - goto out; - - if (!names->dev) { - debug("no devmap found"); - goto out; - } - do { - /* - * keep only multipath maps - */ - names = (void *) names + next; - nexttgt = NULL; - debug("devmap %s :", names->name); - - if (!(dmt1 = dm_task_create(DM_DEVICE_TABLE))) - goto out; - - if (!dm_task_set_name(dmt1, names->name)) - goto out1; - - if (!dm_task_run(dmt1)) - goto out1; - - do { - nexttgt = dm_get_next_target(dmt1, nexttgt, - &start, - &length, - &target_type, - ¶ms); - debug("\\_ %lu %lu %s", (unsigned long) start, - (unsigned long) length, - target_type); - - if (!target_type) { - debug("unknown target type"); - goto out1; - } - - if (!strncmp(target_type, "multipath", 9)) { - if (!paramsvec) - paramsvec = vector_alloc(); - - pp = malloc(PARAMS_SIZE); - strncpy(pp, params, PARAMS_SIZE); - vector_alloc_slot(paramsvec); - vector_set_slot(paramsvec, pp); - } else - debug("skip non multipath target"); - } while (nexttgt); -out1: - dm_task_destroy(dmt1); - next = names->next; - } while (next); -out: - dm_task_destroy(dmt); - return paramsvec; -} - -static int -get_word (char *sentence, char *word) -{ - char *p; - int skip = 0; - - while (*sentence == ' ') { - sentence++; - skip++; - } - p = sentence; - - while (*p != ' ' && *p != '\0') - p++; - - skip += (p - sentence); - - if (p - sentence > WORD_SIZE) { - fprintf(stderr, "word too small\n"); - exit_tool(1); - } - strncpy(word, sentence, WORD_SIZE); - word += p - sentence; - *word = '\0'; - - if (*p == '\0') - return 0; - - return skip; -} - -static int -is_path (char * word) -{ - char *p; - - if (!word) - return 0; - - p = word; - - while (*p != '\0') { - if (*p == ':') - return 1; - p++; - } - return 0; -} - -static int -get_paths (vector pathvec) -{ - vector paramsvec = NULL; - char * str; - struct path * pp; - int i; - enum where {BEFOREPG, INPG, AFTERPG}; - int pos = BEFOREPG; - - if (!pathvec) - return 1; - - if (!(paramsvec = get_params())) - exit_tool(0); - - vector_foreach_slot (paramsvec, str, i) { - debug("params %s", str); - while (pos != AFTERPG) { - pp = zalloc(sizeof(struct path)); - str += get_word(str, pp->dev_t); - - if (!is_path(pp->dev_t)) { - debug("skip \"%s\"", pp->dev_t); - free(pp); - - if (pos == INPG) - pos = AFTERPG; - - continue; - } - if (pos == BEFOREPG) - pos = INPG; - - get_serial(pp->serial, pp->dev_t); - vector_alloc_slot(pathvec); - vector_set_slot(pathvec, pp); - debug("store %s [%s]", - pp->dev_t, pp->serial); - } - pos = BEFOREPG; - } - return 0; -} - -static void * -find_controler (vector controlers, char * serial) -{ - int i; - struct controler * cp; - - if (!controlers) - return NULL; - - vector_foreach_slot (controlers, cp, i) - if (!strncmp(cp->serial, serial, SERIAL_SIZE)) - return cp; - return NULL; -} - -static void -get_controlers (vector controlers, vector pathvec) -{ - int i; - struct path * pp; - struct controler * cp; - - if (!controlers) - return; - - vector_foreach_slot (pathvec, pp, i) { - if (!pp || !strlen(pp->serial)) - continue; - - cp = find_controler(controlers, pp->serial); - - if (!cp) { - cp = zalloc(sizeof(struct controler)); - vector_alloc_slot(controlers); - vector_set_slot(controlers, cp); - strncpy(cp->serial, pp->serial, SERIAL_SIZE); - } - cp->path_count++; - } -} - -static int -get_max_path_count (vector controlers) -{ - int i; - int max = 0; - struct controler * cp; - - if (!controlers) - return 0; - - vector_foreach_slot (controlers, cp, i) { - debug("controler %s : %i paths", cp->serial, cp->path_count); - if(cp->path_count > max) - max = cp->path_count; - } - debug("max_path_count = %i", max); - return max; -} - -int -main (int argc, char **argv) -{ - vector pathvec = NULL; - vector controlers = NULL; - struct path * ref_path = NULL; - struct controler * cp = NULL; - int max_path_count = 0; - - ref_path = zalloc(sizeof(struct path)); - - if (!ref_path) - exit_tool(1); - - if (argc != 2) - exit_tool(1); - - if (optinddev_t, argv[optind], WORD_SIZE); - - get_serial(ref_path->serial, ref_path->dev_t); - - if (!ref_path->serial || !strlen(ref_path->serial)) - exit_tool(0); - - pathvec = vector_alloc(); - controlers = vector_alloc(); - - get_paths(pathvec); - get_controlers(controlers, pathvec); - max_path_count = get_max_path_count(controlers); - cp = find_controler(controlers, ref_path->serial); - - if (!cp) { - debug("no other active path on serial %s\n", - ref_path->serial); - exit_tool(0); - } - - printf("%i\n", max_path_count - cp->path_count + 1); - - return(0); -} diff --git a/path_priority/pp_emc/Makefile b/path_priority/pp_emc/Makefile deleted file mode 100644 index 651bdcd..0000000 --- a/path_priority/pp_emc/Makefile +++ /dev/null @@ -1,25 +0,0 @@ -EXEC = mpath_prio_emc -BUILD = glibc -OBJS = pp_emc.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) - -%.o: %.c - $(CC) $(CFLAGS) -c -o $@ $< diff --git a/path_priority/pp_emc/pp_emc.c b/path_priority/pp_emc/pp_emc.c deleted file mode 100644 index dd58424..0000000 --- a/path_priority/pp_emc/pp_emc.c +++ /dev/null @@ -1,97 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../../libmultipath/sg_include.h" - -#define INQUIRY_CMD 0x12 -#define INQUIRY_CMDLEN 6 - -int emc_clariion_prio(const char *dev) -{ - unsigned char sense_buffer[256]; - unsigned char sb[128]; - unsigned char inqCmdBlk[INQUIRY_CMDLEN] = {INQUIRY_CMD, 1, 0xC0, 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 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 query command failed\n"); - goto out; - } - if (io_hdr.info & SG_INFO_OK_MASK) { - fprintf(stderr, "query command indicates error"); - goto out; - } - - close(fd); - - if (/* Verify the code page - right page & revision */ - sense_buffer[1] != 0xc0 || sense_buffer[9] != 0x00) { - fprintf(stderr, "Path unit report page in unknown format"); - goto out; - } - - if ( /* Effective initiator type */ - sense_buffer[27] != 0x03 - /* Failover mode should be set to 1 */ - || (sense_buffer[28] & 0x07) != 0x04 - /* Arraycommpath should be set to 1 */ - || (sense_buffer[30] & 0x04) != 0x04) { - fprintf(stderr, "Path not correctly configured for failover"); - } - - if ( /* LUN operations should indicate normal operations */ - sense_buffer[48] != 0x00) { - fprintf(stderr, "Path not available for normal operations"); - } - - /* Is the default owner equal to this path? */ - /* Note this will switch to the default priority group, even if - * it is not the currently active one. */ - ret = (sense_buffer[5] == sense_buffer[8]) ? 1 : 0; - -out: - return(ret); -} - -int -main (int argc, char **argv) -{ - int prio; - if (argc != 2) { - fprintf(stderr, "Arguments wrong!\n"); - prio = 0; - } else - prio = emc_clariion_prio(argv[1]); - - printf("%d\n", prio); - exit(0); -} - diff --git a/path_priority/pp_netapp/Makefile b/path_priority/pp_netapp/Makefile deleted file mode 100644 index 9e7d3a3..0000000 --- a/path_priority/pp_netapp/Makefile +++ /dev/null @@ -1,22 +0,0 @@ -EXEC = mpath_prio_netapp -BUILD = glibc -OBJS = pp_netapp.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_netapp/pp_netapp.c b/path_priority/pp_netapp/pp_netapp.c deleted file mode 100644 index 8562a95..0000000 --- a/path_priority/pp_netapp/pp_netapp.c +++ /dev/null @@ -1,268 +0,0 @@ -/* - * Copyright 2005 Network Appliance, Inc., All Rights Reserved - * Author: David Wysochanski available at davidw@netapp.com - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * 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 v2 for more details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../../libmultipath/sg_include.h" - -#define INQUIRY_CMD 0x12 -#define INQUIRY_CMDLEN 6 -#define DEFAULT_PRIO 10 -#define RESULTS_MAX 256 -#define SG_TIMEOUT 30000 - - -static void dump_cdb(unsigned char *cdb, int size) -{ - int i; - - fprintf(stderr, "- SCSI CDB: "); - for (i=0; imasked_status, - io_hdr->host_status, io_hdr->driver_status); - if (io_hdr->sb_len_wr > 0) { - fprintf(stderr, "- SCSI sense data: "); - for (i=0; isb_len_wr; i++) { - fprintf(stderr, "0x%02x ", io_hdr->sbp[i]); - } - fprintf(stderr, "\n"); - } -} - -/* - * Returns: - * -1: error, errno set - * 0: success - */ -static int send_gva(const char *dev, unsigned char pg, - unsigned char *results, int *results_size) -{ - unsigned char sb[128]; - unsigned char cdb[10] = {0xc0, 0, 0x1, 0xa, 0x98, 0xa, - pg, sizeof(sb), 0, 0}; - struct sg_io_hdr io_hdr; - int ret = -1; - int fd; - - fd = open(dev, O_RDWR|O_NONBLOCK); - - if (fd <= 0) { - fprintf(stderr, "Opening %s failed, errno=%d.\n", dev, errno); - goto out_no_close; - } - - memset(&io_hdr, 0, sizeof (struct sg_io_hdr)); - io_hdr.interface_id = 'S'; - io_hdr.cmd_len = sizeof (cdb); - io_hdr.mx_sb_len = sizeof (sb); - io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; - io_hdr.dxfer_len = *results_size; - io_hdr.dxferp = results; - io_hdr.cmdp = cdb; - io_hdr.sbp = sb; - io_hdr.timeout = SG_TIMEOUT; - io_hdr.pack_id = 0; - if (ioctl(fd, SG_IO, &io_hdr) < 0) { - fprintf(stderr, "SG_IO ioctl failed, errno=%d\n", errno); - dump_cdb(cdb, sizeof(cdb)); - goto out; - } - if (io_hdr.info & SG_INFO_OK_MASK) { - fprintf(stderr, "SCSI error\n"); - dump_cdb(cdb, sizeof(cdb)); - process_sg_error(&io_hdr); - goto out; - } - - if (results[4] != 0x0a || results[5] != 0x98 || - results[6] != 0x0a ||results[7] != 0x01) { - dump_cdb(cdb, sizeof(cdb)); - fprintf(stderr, "GVA return wrong format "); - fprintf(stderr, "results[4-7] = 0x%02x 0x%02x 0x%02x 0x%02x\n", - results[4], results[5], results[6], results[7]); - goto out; - } - ret = 0; - out: - close(fd); - out_no_close: - return(ret); -} - -/* - * Retuns: - * -1: Unable to obtain proxy info - * 0: Device _not_ proxy path - * 1: Device _is_ proxy path - */ -static int get_proxy(const char *dev) -{ - unsigned char results[256]; - unsigned char sb[128]; - unsigned char cdb[INQUIRY_CMDLEN] = {INQUIRY_CMD, 1, 0xc1, 0, - sizeof(sb), 0}; - struct sg_io_hdr io_hdr; - int ret = -1; - int fd; - - fd = open(dev, O_RDWR|O_NONBLOCK); - - if (fd <= 0) { - fprintf(stderr, "Opening %s failed, errno=%d.\n", dev, errno); - goto out_no_close; - } - - memset(&results, 0, sizeof (results)); - memset(&io_hdr, 0, sizeof (struct sg_io_hdr)); - io_hdr.interface_id = 'S'; - io_hdr.cmd_len = sizeof (cdb); - io_hdr.mx_sb_len = sizeof (sb); - io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; - io_hdr.dxfer_len = sizeof (results); - io_hdr.dxferp = results; - io_hdr.cmdp = cdb; - io_hdr.sbp = sb; - io_hdr.timeout = SG_TIMEOUT; - io_hdr.pack_id = 0; - if (ioctl(fd, SG_IO, &io_hdr) < 0) { - fprintf(stderr, "ioctl sending inquiry command failed, " - "errno=%d\n", errno); - dump_cdb(cdb, sizeof(cdb)); - goto out; - } - if (io_hdr.info & SG_INFO_OK_MASK) { - fprintf(stderr, "SCSI error\n"); - dump_cdb(cdb, sizeof(cdb)); - process_sg_error(&io_hdr); - goto out; - } - - if (results[1] != 0xc1 || results[8] != 0x0a || - results[9] != 0x98 || results[10] != 0x0a || - results[11] != 0x0 || results[12] != 0xc1 || - results[13] != 0x0) { - fprintf(stderr,"Proxy info page in unknown format - "); - fprintf(stderr,"results[8-13]=0x%02x 0x%02x 0x%02x 0x%02x " - "0x%02x 0x%02x\n", - results[8], results[9], results[10], - results[11], results[12], results[13]); - dump_cdb(cdb, sizeof(cdb)); - goto out; - } - ret = (results[19] & 0x02) >> 1; - - out: - close(fd); - out_no_close: - return(ret); -} - -/* - * Returns priority of device based on device info. - * - * 4: FCP non-proxy, FCP proxy unknown, or unable to determine protocol - * 3: iSCSI HBA - * 2: iSCSI software - * 1: FCP proxy - */ -static int netapp_prio(const char *dev) -{ - unsigned char results[RESULTS_MAX]; - int results_size=RESULTS_MAX; - int rc; - int is_proxy; - int is_iscsi_software; - int is_iscsi_hardware; - int tot_len; - - is_iscsi_software = is_iscsi_hardware = is_proxy = 0; - - memset(&results, 0, sizeof (results)); - rc = send_gva(dev, 0x41, results, &results_size); - if (rc == 0) { - tot_len = results[0] << 24 | results[1] << 16 | - results[2] << 8 | results[3]; - if (tot_len <= 8) { - goto try_fcp_proxy; - } - if (results[8] != 0x41) { - fprintf(stderr, "GVA page 0x41 error - " - "results[8] = 0x%x\n", results[8]); - goto try_fcp_proxy; - } - if ((strncmp((char *)&results[12], "ism_sw", 6) == 0) || - (strncmp((char *)&results[12], "iswt", 4) == 0)) { - is_iscsi_software = 1; - goto prio_select; - } - else if (strncmp((char *)&results[12], "ism_sn", 6) == 0) { - is_iscsi_hardware = 1; - goto prio_select; - } - } - - try_fcp_proxy: - rc = get_proxy(dev); - if (rc >= 0) { - is_proxy = rc; - } - - prio_select: - if (is_iscsi_hardware) { - return 3; - } else if (is_iscsi_software) { - return 2; - } else { - if (is_proxy) { - return 1; - } else { - /* Either non-proxy, or couldn't get proxy info */ - return 4; - } - } -} - -int -main (int argc, char **argv) -{ - int prio; - if (argc != 2) { - fprintf(stderr, "Arguments wrong!\n"); - prio = 0; - } else - prio = netapp_prio(argv[1]); - - printf("%d\n", prio); - exit(0); -} - diff --git a/path_priority/pp_random/Makefile b/path_priority/pp_random/Makefile deleted file mode 100644 index 85f42a2..0000000 --- a/path_priority/pp_random/Makefile +++ /dev/null @@ -1,22 +0,0 @@ -EXEC = mpath_prio_random -BUILD = glibc -OBJS = pp_random.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_random/pp_random.c b/path_priority/pp_random/pp_random.c deleted file mode 100644 index 05c4b8d..0000000 --- a/path_priority/pp_random/pp_random.c +++ /dev/null @@ -1,14 +0,0 @@ -#include -#include -#include -#include - -int main(void) -{ - struct timeval tv; - - gettimeofday(&tv, NULL); - srand((unsigned int)tv.tv_usec); - printf("%i\n", 1+(int) (10.0*rand()/(RAND_MAX+1.0))); - return 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 -#include -#include -#include -#include -#include -#include -#include -#include - -#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); -}