diff --git a/Makefile b/Makefile index 83ae2fe..ee554e7 100644 --- a/Makefile +++ b/Makefile @@ -20,9 +20,13 @@ endif export KRNLSRC export KRNLOBJ -BUILDDIRS = $(shell find . -mindepth 2 -name Makefile -exec dirname {} \; | grep -v ^lib) +BUILDDIRS = $(shell find . -mindepth 2 -name Makefile -exec dirname {} \; | grep -vE '^lib|/\.') +ifeq ($(MULTIPATH_VERSION),) VERSION = $(shell basename ${PWD} | cut -d'-' -f3) +else +VERSION = $(MULTIPATH_VERSION) +endif all: recurse diff --git a/Makefile.inc b/Makefile.inc index 4a705aa..7e2d4e6 100644 --- a/Makefile.inc +++ b/Makefile.inc @@ -17,15 +17,16 @@ ifeq ($(strip $(BUILD)),klibc) CC = klcc klibcdir = /usr/lib/klibc libdm = $(klibcdir)/lib/libdevmapper.a - libsysfs = $(klibcdir)/lib/libsysfs.a endif prefix = exec_prefix = $(prefix) bindir = $(exec_prefix)/sbin +libudevdir = ${prefix}/lib/udev checkersdir = $(TOPDIR)/libcheckers multipathdir = $(TOPDIR)/libmultipath mandir = $(prefix)/usr/share/man/man8 +man5dir = $(prefix)/usr/share/man/man5 rcdir = $(prefix)/etc/init.d GZIP = /bin/gzip -9 -c @@ -33,6 +34,8 @@ GZIP = /bin/gzip -9 -c CHECKERSLIB = $(checkersdir)/libcheckers MULTIPATHLIB = $(multipathdir)/libmultipath +INSTALL_PROGRAM = install -s + OPTFLAGS = -pipe -g -Wall -Wunused -Wstrict-prototypes CFLAGS = $(OPTFLAGS) diff --git a/devmap_name/Makefile b/devmap_name/Makefile index 380c85b..d8d8b09 100644 --- a/devmap_name/Makefile +++ b/devmap_name/Makefile @@ -28,9 +28,9 @@ klibc: prepare $(OBJS) $(CC) -static -o $(EXEC) $(OBJS) $(GZIP) $(EXEC).8 > $(EXEC).8.gz -install: +install: $(EXEC) $(EXEC).8 install -d $(DESTDIR)$(bindir) - install -s -m 755 $(EXEC) $(DESTDIR)$(bindir)/ + install -m 755 $(EXEC) $(DESTDIR)$(bindir)/ install -d $(DESTDIR)$(mandir) install -m 644 $(EXEC).8.gz $(DESTDIR)$(mandir) diff --git a/devmap_name/devmap_name.8 b/devmap_name/devmap_name.8 index f4f03c3..86d0931 100644 --- a/devmap_name/devmap_name.8 +++ b/devmap_name/devmap_name.8 @@ -1,4 +1,4 @@ -.TH DEVMAP_NAME 8 "February 2004" "" "Linux Administrator's Manual" +.TH DEVMAP_NAME 8 "July 2006" "" "Linux Administrator's Manual" .SH NAME devmap_name \- Query device-mapper name .SH SYNOPSIS diff --git a/kpartx/Makefile b/kpartx/Makefile index bf6e6c1..b4cca6c 100644 --- a/kpartx/Makefile +++ b/kpartx/Makefile @@ -6,15 +6,15 @@ BUILD=glibc include ../Makefile.inc -CFLAGS += -I. -D_LARGEFILE64_SOURCE +CFLAGS += -I. -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 ifeq ($(strip $(BUILD)),klibc) OBJS = bsd.o dos.o kpartx.o solaris.o unixware.o gpt.o crc32.o \ - lopart.o xstrncpy.o devmapper.o dasd.o mac.o \ + lopart.o xstrncpy.o devmapper.o dasd.o mac.o sun.o \ $(MULTIPATHLIB)-$(BUILD).a $(libdm) else LDFLAGS = -ldevmapper - OBJS = bsd.o dos.o kpartx.o solaris.o unixware.o dasd.o \ + OBJS = bsd.o dos.o kpartx.o solaris.o unixware.o dasd.o sun.o \ gpt.o mac.o crc32.o lopart.o xstrncpy.o devmapper.o endif @@ -36,9 +36,13 @@ klibc: prepare $(OBJS) $(MULTIPATHLIB)-$(BUILD).a: make -C $(multipathdir) BUILD=$(BUILD) -install: +install: $(EXEC) $(EXEC).8 install -d $(DESTDIR)$(bindir) - install -s -m 755 $(EXEC) $(DESTDIR)$(bindir) + $(INSTALL_PROGRAM) -m 755 $(EXEC) $(DESTDIR)$(bindir) + install -d $(DESTDIR)$(libudevdir) + install -m 755 kpartx_id $(DESTDIR)$(libudevdir) + install -d $(DESTDIR)/etc/udev/rules.d + install -m 644 kpartx.rules $(DESTDIR)/etc/udev/rules.d/ install -d $(DESTDIR)$(mandir) install -m 644 $(EXEC).8.gz $(DESTDIR)$(mandir) diff --git a/kpartx/bsd.c b/kpartx/bsd.c index 3ae2dc4..f87175e 100644 --- a/kpartx/bsd.c +++ b/kpartx/bsd.c @@ -10,7 +10,7 @@ struct bsd_disklabel { short int d_type; /* drive type */ short int d_subtype; /* controller/d_type specific */ char d_typename[16]; /* type name, e.g. "eagle" */ - char d_packname[16]; /* pack identifier */ + char d_packname[16]; /* pack identifier */ unsigned int d_secsize; /* # of bytes per sector */ unsigned int d_nsectors; /* # of data sectors per track */ unsigned int d_ntracks; /* # of tracks per cylinder */ @@ -50,12 +50,12 @@ int read_bsd_pt(int fd, struct slice all, struct slice *sp, int ns) { struct bsd_disklabel *l; struct bsd_partition *p; - unsigned int offset = all.start; + unsigned int offset = all.start, end; int max_partitions; char *bp; - int n = 0; + int n = 0, i, j; - bp = getblock(fd, offset+1); /* 1 sector suffices */ + bp = getblock(fd, offset+1); /* 1 sector suffices */ if (bp == NULL) return -1; @@ -79,5 +79,36 @@ read_bsd_pt(int fd, struct slice all, struct slice *sp, int ns) { break; } } + /* + * Convention has it that the bsd disklabel will always have + * the 'c' partition spanning the entire disk. + * So we have to check for contained slices. + */ + for(i = 0; i < n; i++) { + if (sp[i].size == 0) + continue; + + end = sp[i].start + sp[i].size; + for(j = 0; j < n; j ++) { + if ( i == j ) + continue; + if (sp[j].size == 0) + continue; + + if (sp[i].start < sp[j].start) { + if (end > sp[j].start && + end < sp[j].start + sp[j].size) { + /* Invalid slice */ + fprintf(stderr, + "bsd_disklabel: slice %d overlaps with %d\n", i , j); + sp[i].size = 0; + } + } else { + if (end <= sp[j].start + sp[j].size) { + sp[i].container = j + 1; + } + } + } + } return n; } diff --git a/kpartx/dasd.c b/kpartx/dasd.c index 69b9807..f31111f 100644 --- a/kpartx/dasd.c +++ b/kpartx/dasd.c @@ -40,14 +40,20 @@ #include "byteorder.h" #include "dasd.h" +unsigned long sectors512(unsigned long sectors, int blocksize) +{ + return sectors * (blocksize >> 9); +} + /* */ int read_dasd_pt(int fd, struct slice all, struct slice *sp, int ns) { int retval = -1; - int blocksize, offset, size; + int blocksize; long disksize; + unsigned long offset, size; dasd_information_t info; struct hd_geometry geo; char type[5] = {0,}; @@ -172,13 +178,13 @@ read_dasd_pt(int fd, struct slice all, struct slice *sp, int ns) /* disk is reserved minidisk */ blocksize = label[3]; offset = label[13]; - size = (label[7] - 1)*(blocksize >> 9); + size = sectors512(label[7] - 1, blocksize); } else { - offset = (info.label_block + 1) * (blocksize >> 9); - size = disksize - offset; + offset = info.label_block + 1; + size = disksize; } - sp[0].start = offset * (blocksize >> 9); - sp[0].size = size - offset * (blocksize >> 9); + sp[0].start = sectors512(offset, blocksize); + sp[0].size = size - sp[0].start; retval = 1; } else if ((strncmp(type, "VOL1", 4) == 0) && (!info.FBA_layout) && (!strcmp(info.type, "ECKD"))) { @@ -214,8 +220,8 @@ read_dasd_pt(int fd, struct slice all, struct slice *sp, int ns) offset = cchh2blk(&f1.DS1EXT1.llimit, &geo); size = cchh2blk(&f1.DS1EXT1.ulimit, &geo) - offset + geo.sectors; - sp[counter].start = offset * (blocksize >> 9); - sp[counter].size = size * (blocksize >> 9); + sp[counter].start = sectors512(offset, blocksize); + sp[counter].size = sectors512(size, blocksize); counter++; blk++; } @@ -224,10 +230,8 @@ read_dasd_pt(int fd, struct slice all, struct slice *sp, int ns) /* * Old style LNX1 or unlabeled disk */ - offset = (info.label_block + 1) * (blocksize >> 9); - size = disksize - offset; - sp[0].start = offset * (blocksize >> 9); - sp[0].size = size - offset * (blocksize >> 9); + sp[0].start = sectors512(info.label_block + 1, blocksize); + sp[0].size = disksize - sp[0].start; retval = 1; } diff --git a/kpartx/devmapper.c b/kpartx/devmapper.c index 1253941..6e3e198 100644 --- a/kpartx/devmapper.c +++ b/kpartx/devmapper.c @@ -7,6 +7,10 @@ #include #include #include +#include + +#define UUID_PREFIX "part%d-" +#define MAX_PREFIX_LEN 8 extern int dm_prereq (char * str, int x, int y, int z) @@ -68,9 +72,10 @@ dm_simplecmd (int task, const char *name) { extern int dm_addmap (int task, const char *name, const char *target, - const char *params, unsigned long size) { + const char *params, unsigned long size, const char *uuid, int part) { int r = 0; struct dm_task *dmt; + char *prefixed_uuid; if (!(dmt = dm_task_create (task))) return 0; @@ -81,12 +86,25 @@ dm_addmap (int task, const char *name, const char *target, if (!dm_task_add_target (dmt, 0, size, target, params)) goto addout; + if (task == DM_DEVICE_CREATE && uuid) { + prefixed_uuid = malloc(MAX_PREFIX_LEN + strlen(uuid) + 1); + if (!prefixed_uuid) { + fprintf(stderr, "cannot create prefixed uuid : %s\n", + strerror(errno)); + goto addout; + } + sprintf(prefixed_uuid, UUID_PREFIX "%s", part, uuid); + if (!dm_task_set_uuid(dmt, prefixed_uuid)) + goto addout; + } + dm_task_no_open_count(dmt); r = dm_task_run (dmt); addout: dm_task_destroy (dmt); + return r; } @@ -178,3 +196,56 @@ out: return ret; } +char * +dm_mapuuid(int major, int minor) +{ + struct dm_task *dmt; + const char *tmp; + char *uuid = NULL; + + if (!(dmt = dm_task_create(DM_DEVICE_INFO))) + return NULL; + + dm_task_no_open_count(dmt); + dm_task_set_major(dmt, major); + dm_task_set_minor(dmt, minor); + + if (!dm_task_run(dmt)) + goto out; + + tmp = dm_task_get_uuid(dmt); + if (tmp[0] != '\0') + uuid = strdup(tmp); +out: + dm_task_destroy(dmt); + return uuid; +} + +int +dm_devn (char * mapname, int *major, int *minor) +{ + int r = 1; + struct dm_task *dmt; + struct dm_info info; + + if (!(dmt = dm_task_create(DM_DEVICE_INFO))) + return 0; + + if (!dm_task_set_name(dmt, mapname)) + goto out; + + if (!dm_task_run(dmt)) + goto out; + + if (!dm_task_get_info(dmt, &info)) + goto out; + + *major = info.major; + *minor = info.minor; + + r = 0; +out: + dm_task_destroy(dmt); + return r; +} + diff --git a/kpartx/devmapper.h b/kpartx/devmapper.h index 9607476..ccdbead 100644 --- a/kpartx/devmapper.h +++ b/kpartx/devmapper.h @@ -1,6 +1,9 @@ int dm_prereq (char *, int, int, int); int dm_simplecmd (int, const char *); -int dm_addmap (int, const char *, const char *, const char *, unsigned long); +int dm_addmap (int, const char *, const char *, const char *, unsigned long, + char *, int); int dm_map_present (char *); char * dm_mapname(int major, int minor); dev_t dm_get_first_dep(char *devname); +char * dm_mapuuid(int major, int minor); +int dm_devn (char * mapname, int *major, int *minor); diff --git a/kpartx/dos.c b/kpartx/dos.c index a707423..1691105 100644 --- a/kpartx/dos.c +++ b/kpartx/dos.c @@ -16,7 +16,7 @@ is_extended(int type) { } static int -read_extended_partition(int fd, struct partition *ep, +read_extended_partition(int fd, struct partition *ep, int en, struct slice *sp, int ns) { struct partition p; @@ -53,6 +53,7 @@ read_extended_partition(int fd, struct partition *ep, if (n < ns) { sp[n].start = here + le32_to_cpu(p.start_sect); sp[n].size = le32_to_cpu(p.nr_sects); + sp[n].container = en + 1; n++; } else { fprintf(stderr, @@ -97,9 +98,7 @@ read_dos_pt(int fd, struct slice all, struct slice *sp, int ns) { break; } if (is_extended(p.sys_type)) { - n += read_extended_partition(fd, &p, sp+n, ns-n); - /* hide the extended partition itself */ - sp[i].size = 0; + n += read_extended_partition(fd, &p, i, sp+n, ns-n); } } return n; diff --git a/kpartx/kpartx.8 b/kpartx/kpartx.8 index 259ce3f..87b07ce 100644 --- a/kpartx/kpartx.8 +++ b/kpartx/kpartx.8 @@ -1,4 +1,4 @@ -.TH KPARTX 8 "February 2004" "" "Linux Administrator's Manual" +.TH KPARTX 8 "July 2006" "" "Linux Administrator's Manual" .SH NAME kpartx \- Create device maps from partition tables .SH SYNOPSIS diff --git a/kpartx/kpartx.c b/kpartx/kpartx.c index 2198302..dbe2ee2 100644 --- a/kpartx/kpartx.c +++ b/kpartx/kpartx.c @@ -79,6 +79,7 @@ initpts(void) addpts("unixware", read_unixware_pt); addpts("dasd", read_dasd_pt); addpts("mac", read_mac_pt); + addpts("sun", read_sun_pt); } static char short_opts[] = "ladgvnp:t:"; @@ -115,7 +116,7 @@ strip_slash (char * device) char * p = device; while (*(p++) != 0x0) { - + if (*p == '/') *p = '!'; } @@ -125,9 +126,9 @@ static int find_devname_offset (char * device) { char *p, *q = NULL; - + p = device; - + while (*p++) if (*p == '/') q = p; @@ -182,7 +183,7 @@ get_hotplug_device(void) int main(int argc, char **argv){ - int fd, i, j, k, n, op, off, arg; + int fd, i, j, m, n, op, off, arg, c, d; struct slice all; struct pt *ptp; enum action what = LIST; @@ -192,6 +193,8 @@ main(int argc, char **argv){ char partname[PARTNAME_SIZE], params[PARTNAME_SIZE + 16]; char * loopdev = NULL; char * delim = NULL; + char *uuid = NULL; + char *mapname = NULL; int loopro = 0; int hotplug = 0; struct stat buf; @@ -203,7 +206,7 @@ main(int argc, char **argv){ type = device = diskdevice = NULL; memset(&all, 0, sizeof(all)); memset(&partname, 0, sizeof(partname)); - + /* Check whether hotplug mode. */ progname = strrchr(argv[0], '/'); @@ -284,17 +287,12 @@ main(int argc, char **argv){ } if (S_ISREG (buf.st_mode)) { - loopdev = malloc(LO_NAME_SIZE * sizeof(char)); - - if (!loopdev) - exit(1); - /* already looped file ? */ loopdev = find_loop_by_file(device); if (!loopdev && what == DELETE) exit (0); - + if (!loopdev) { loopdev = find_unused_loop_device(); @@ -311,8 +309,22 @@ main(int argc, char **argv){ memset(delim, 0, DELIM_SIZE); set_delimiter(device, delim); } - + off = find_devname_offset(device); + + if (!loopdev) { + uuid = dm_mapuuid((unsigned int)MAJOR(buf.st_rdev), + (unsigned int)MINOR(buf.st_rdev)); + mapname = dm_mapname((unsigned int)MAJOR(buf.st_rdev), + (unsigned int)MINOR(buf.st_rdev)); + } + + if (!uuid) + uuid = device + off; + + if (!mapname) + mapname = device + off; + fd = open(device, O_RDONLY); if (fd == -1) { @@ -328,7 +340,7 @@ main(int argc, char **argv){ if (type && strcmp(type, ptp->type)) continue; - + /* here we get partitions */ n = ptp->fn(fd, all, slices, SIZE(slices)); @@ -342,36 +354,56 @@ main(int argc, char **argv){ else continue; - /* - * test for overlap, as in the case of an extended partition - * zero their size to avoid mapping - */ - for (j=0; j slices[j].start && - slices[k].start < slices[j].start + - slices[j].size) - slices[j].size = 0; - } - } - switch(what) { case LIST: - for (j = 0; j < n; j++) { + for (j = 0, c = 0, m = 0; j < n; j++) { if (slices[j].size == 0) continue; + if (slices[j].container > 0) { + c++; + continue; + } + + slices[j].minor = m++; printf("%s%s%d : 0 %lu %s %lu\n", - device + off, delim, j+1, - (unsigned long) slices[j].size, device, - (unsigned long) slices[j].start); + mapname, delim, j+1, + (unsigned long) slices[j].size, device, + (unsigned long) slices[j].start); } + /* Loop to resolve contained slices */ + d = c; + while (c) { + for (j = 0; j < n; j++) { + unsigned long start; + int k = slices[j].container - 1; + + if (slices[j].size == 0) + continue; + if (slices[j].minor > 0) + continue; + if (slices[j].container == 0) + continue; + slices[j].minor = m++; + + start = slices[j].start - slices[k].start; + printf("%s%s%d : 0 %lu /dev/dm-%d %lu\n", + mapname, delim, j+1, + (unsigned long) slices[j].size, + slices[k].minor, start); + c--; + } + /* Terminate loop if nothing more to resolve */ + if (d == c) + break; + } + break; case DELETE: for (j = 0; j < n; j++) { if (safe_sprintf(partname, "%s%s%d", - device + off , delim, j+1)) { + mapname, delim, j+1)) { fprintf(stderr, "partname too small\n"); exit(1); } @@ -390,7 +422,7 @@ main(int argc, char **argv){ if (S_ISREG (buf.st_mode)) { if (del_loop(device)) { if (verbose) - printf("can't del loop : %s\n", + printf("can't del loop : %s\n", device); exit(1); } @@ -399,17 +431,23 @@ main(int argc, char **argv){ break; case ADD: - for (j=0; j 0) { + c++; + continue; + } + if (safe_sprintf(partname, "%s%s%d", - device + off , delim, j+1)) { + mapname, delim, j+1)) { fprintf(stderr, "partname too small\n"); exit(1); } strip_slash(partname); - + if (safe_sprintf(params, "%s %lu", device, (unsigned long)slices[j].start)) { fprintf(stderr, "params too small\n"); @@ -420,16 +458,80 @@ main(int argc, char **argv){ DM_DEVICE_RELOAD : DM_DEVICE_CREATE); dm_addmap(op, partname, DM_TARGET, params, - slices[j].size); + slices[j].size, uuid, j+1); if (op == DM_DEVICE_RELOAD) dm_simplecmd(DM_DEVICE_RESUME, partname); + dm_devn(partname, &slices[j].major, + &slices[j].minor); + if (verbose) - printf("add map %s : 0 %lu %s %s\n", - partname, slices[j].size, - DM_TARGET, params); + printf("add map %s (%d:%d): 0 %lu %s %s\n", + partname, slices[j].major, + slices[j].minor, slices[j].size, + DM_TARGET, params); + } + /* Loop to resolve contained slices */ + d = c; + while (c) { + for (j = 0; j < n; j++) { + int k = slices[j].container - 1; + + if (slices[j].size == 0) + continue; + + /* Skip all existing slices */ + if (slices[j].minor > 0) + continue; + + /* Skip all simple slices */ + if (k < 0) + continue; + + /* Check container slice */ + if (slices[k].size == 0) + fprintf(stderr, "Invalid slice %d\n", + k); + + if (safe_sprintf(partname, "%s%s%d", + mapname, delim, j+1)) { + fprintf(stderr, "partname too small\n"); + exit(1); + } + strip_slash(partname); + + if (safe_sprintf(params, "%d:%d %lu", + slices[k].major, + slices[k].minor, + (unsigned long)slices[j].start)) { + fprintf(stderr, "params too small\n"); + exit(1); + } + + op = (dm_map_present(partname) ? + DM_DEVICE_RELOAD : DM_DEVICE_CREATE); + + dm_addmap(op, partname, DM_TARGET, params, + slices[j].size, uuid, j+1); + + if (op == DM_DEVICE_RELOAD) + dm_simplecmd(DM_DEVICE_RESUME, + partname); + + dm_devn(partname, &slices[j].major, + &slices[j].minor); + + if (verbose) + printf("add map %s : 0 %lu %s %s\n", + partname, slices[j].size, + DM_TARGET, params); + c--; + } + /* Terminate loop */ + if (d == c) + break; } break; @@ -505,7 +607,7 @@ getblock (int fd, unsigned int secnr) { bp->next = blockhead; blockhead = bp; bp->block = (char *) xmalloc(READ_SIZE); - + if (read(fd, bp->block, READ_SIZE) != READ_SIZE) { fprintf(stderr, "read error, sector %d\n", secnr); bp->block = NULL; diff --git a/kpartx/kpartx.h b/kpartx/kpartx.h index 6a715de..9b3aeca 100644 --- a/kpartx/kpartx.h +++ b/kpartx/kpartx.h @@ -22,6 +22,9 @@ struct slice { unsigned long start; unsigned long size; + int container; + int major; + int minor; }; typedef int (ptreader)(int fd, struct slice all, struct slice *sp, int ns); @@ -33,6 +36,7 @@ extern ptreader read_unixware_pt; extern ptreader read_gpt_pt; extern ptreader read_dasd_pt; extern ptreader read_mac_pt; +extern ptreader read_sun_pt; char *getblock(int fd, unsigned int secnr); diff --git a/kpartx/kpartx.rules b/kpartx/kpartx.rules new file mode 100644 index 0000000..f32c718 --- /dev/null +++ b/kpartx/kpartx.rules @@ -0,0 +1,36 @@ +# +# persistent links for device-mapper devices +# only hardware-backed device-mapper devices (ie multipath, dmraid, +# and kpartx) have meaningful persistent device names +# + +KERNEL!="dm-*", GOTO="kpartx_end" +ACTION=="remove", GOTO="kpartx_end" + +ENV{DM_TABLE_STATE}!="LIVE", GOTO="kpartx_end" + +ENV{DM_UUID}=="?*", IMPORT{program}=="/lib/udev/kpartx_id %M %m $env{DM_UUID}" + +OPTIONS="link_priority=50" + +# Create persistent links for multipath tables +ENV{DM_UUID}=="mpath-*", \ + SYMLINK+="disk/by-id/$env{DM_TYPE}-$env{DM_NAME}" + +# Create persistent links for dmraid tables +ENV{DM_UUID}=="mpath-*", \ + SYMLINK+="disk/by-id/$env{DM_TYPE}-$env{DM_NAME}" + +# Create persistent links for partitions +ENV{DM_PART}=="?*", \ + SYMLINK+="disk/by-id/$env{DM_TYPE}-$env{DM_NAME}-part$env{DM_PART}" + +# Create dm tables for partitions +ENV{DM_STATE}=="ACTIVE", ENV{DM_UUID}=="mpath-*", \ + RUN+="/sbin/kpartx -a -p -part /dev/$kernel" +ENV{DM_STATE}=="ACTIVE", ENV{DM_UUID}=="dmraid-*", \ + RUN+="/sbin/kpartx -a -p -part /dev/$kernel" + +LABEL="kpartx_end" + + diff --git a/kpartx/kpartx_id b/kpartx/kpartx_id new file mode 100644 index 0000000..81f32bf --- /dev/null +++ b/kpartx/kpartx_id @@ -0,0 +1,93 @@ +#!/bin/sh +# +# kpartx_id +# +# Generates ID information for device-mapper tables. +# +# Copyright (C) 2006 SUSE Linux Products GmbH +# Author: +# Hannes Reinecke +# +# +# 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 index ec8c10d..6340a68 100644 --- a/libcheckers/Makefile +++ b/libcheckers/Makefile @@ -6,7 +6,7 @@ BUILD = glibc include ../Makefile.inc -OBJS = checkers.o readsector0.o tur.o directio.o emc_clariion.o hp_sw.o +OBJS = libsg.o checkers.o readsector0.o tur.o directio.o emc_clariion.o hp_sw.o rdac.o all: $(BUILD) diff --git a/libcheckers/checkers.c b/libcheckers/checkers.c index 53770a6..d7728a5 100644 --- a/libcheckers/checkers.c +++ b/libcheckers/checkers.c @@ -7,11 +7,13 @@ #include "tur.h" #include "hp_sw.h" #include "emc_clariion.h" +#include "rdac.h" #include "readsector0.h" static struct checker checkers[] = { { .fd = 0, + .sync = 1, .name = DIRECTIO, .message = "", .context = NULL, @@ -21,6 +23,7 @@ static struct checker checkers[] = { }, { .fd = 0, + .sync = 1, .name = TUR, .message = "", .context = NULL, @@ -30,6 +33,7 @@ static struct checker checkers[] = { }, { .fd = 0, + .sync = 1, .name = HP_SW, .message = "", .context = NULL, @@ -39,6 +43,7 @@ static struct checker checkers[] = { }, { .fd = 0, + .sync = 1, .name = EMC_CLARIION, .message = "", .context = NULL, @@ -48,6 +53,17 @@ static struct checker checkers[] = { }, { .fd = 0, + .sync = 1, + .name = RDAC, + .message = "", + .context = NULL, + .check = rdac, + .init = rdac_init, + .free = rdac_free + }, + { + .fd = 0, + .sync = 1, .name = READSECTOR0, .message = "", .context = NULL, @@ -55,7 +71,7 @@ static struct checker checkers[] = { .init = readsector0_init, .free = readsector0_free }, - {0, "", "", NULL, NULL, NULL, NULL}, + {0, 1, "", "", NULL, NULL, NULL, NULL}, }; void checker_set_fd (struct checker * c, int fd) @@ -63,6 +79,16 @@ void checker_set_fd (struct checker * c, int fd) c->fd = fd; } +void checker_set_sync (struct checker * c) +{ + c->sync = 1; +} + +void checker_set_async (struct checker * c) +{ + c->sync = 0; +} + struct checker * checker_lookup (char * name) { struct checker * c = &checkers[0]; @@ -75,8 +101,9 @@ struct checker * checker_lookup (char * name) return NULL; } -int checker_init (struct checker * c) +int checker_init (struct checker * c, void ** mpctxt_addr) { + c->mpcontext = mpctxt_addr; return c->init(c); } @@ -123,6 +150,7 @@ struct checker * checker_default (void) void checker_get (struct checker * dst, struct checker * src) { dst->fd = src->fd; + dst->sync = src->sync; strncpy(dst->name, src->name, CHECKER_NAME_LEN); strncpy(dst->message, src->message, CHECKER_MSG_LEN); dst->check = src->check; diff --git a/libcheckers/checkers.h b/libcheckers/checkers.h index ac795ed..9b270eb 100644 --- a/libcheckers/checkers.h +++ b/libcheckers/checkers.h @@ -2,7 +2,47 @@ #define _CHECKERS_H /* - * path states + * + * Userspace (multipath/multipathd) path states + * + * PATH_WILD: + * - Use: None of the checkers (returned if we don't have an fd) + * - Description: Corner case where "fd <= 0" for path fd (see checker_check()) + * + * PATH_UNCHECKED: + * - Use: Only in directio checker + * - Description: set when fcntl(F_GETFL) fails to return flags or O_DIRECT + * not include in flags, or O_DIRECT read fails + * - Notes: + * - multipathd: uses it to skip over paths in sync_map_state() + * - multipath: used in update_paths(); if state==PATH_UNCHECKED, call + * pathinfo() + * + * PATH_DOWN: + * - Use: All checkers (directio, emc_clariion, hp_sw, readsector0, tur) + * - Description: Either a) SG_IO ioctl failed, or b) check condition on some + * SG_IO ioctls that succeed (tur, readsector0 checkers); path is down and + * you shouldn't try to send commands to it + * + * PATH_UP: + * - Use: All checkers (directio, emc_clariion, hp_sw, readsector0, tur) + * - Description: Path is up and I/O can be sent to it + * + * PATH_SHAKY: + * - Use: Only emc_clariion + * - Description: Indicates path not available for "normal" operations + * + * PATH_GHOST: + * - Use: Only hp_sw + * - Description: Indicates a "passive/standby" path on active/passive HP + * arrays. These paths will return valid answers to certain SCSI commands + * (tur, read_capacity, inquiry, start_stop), but will fail I/O commands. + * The path needs an initialization command to be sent to it in order for + * I/Os to succeed. + * + * PATH_PENDING: + * - Use: All async checkers + * - Description: Indicates a check IO is in flight. */ #define PATH_WILD -1 #define PATH_UNCHECKED 0 @@ -10,14 +50,32 @@ #define PATH_UP 2 #define PATH_SHAKY 3 #define PATH_GHOST 4 +#define PATH_PENDING 5 #define DIRECTIO "directio" #define TUR "tur" #define HP_SW "hp_sw" +#define RDAC "rdac" #define EMC_CLARIION "emc_clariion" #define READSECTOR0 "readsector0" -#define DEFAULT_CHECKER READSECTOR0 +#define DEFAULT_CHECKER DIRECTIO + +/* + * Overloaded storage response time can be very long. + * SG_IO timouts after DEF_TIMEOUT milliseconds, and checkers interprets this + * as a path failure. multipathd then proactively evicts the path from the DM + * multipath table in this case. + * + * This generaly snow balls and ends up in full eviction and IO errors for end + * users. Bad. This may also cause SCSI bus resets, causing disruption for all + * local and external storage hardware users. + * + * Provision a long timeout. Longer than any real-world application would cope + * with. + */ +#define DEF_TIMEOUT 300000 +#define ASYNC_TIMEOUT_SEC 30 /* * strings lengths @@ -28,19 +86,24 @@ struct checker { int fd; + int sync; char name[CHECKER_NAME_LEN]; char message[CHECKER_MSG_LEN]; /* comm with callers */ void * context; /* store for persistent data */ + void ** mpcontext; /* store for persistent data + shared multipath-wide */ int (*check)(struct checker *); int (*init)(struct checker *); /* to allocate the context */ void (*free)(struct checker *); /* to free the context */ }; -#define MSG(c, a) snprintf((c)->message, CHECKER_MSG_LEN, a); +#define MSG(c, fmt, args...) snprintf((c)->message, CHECKER_MSG_LEN, fmt, ##args); -int checker_init (struct checker *); +int checker_init (struct checker *, void **); void checker_put (struct checker *); void checker_reset (struct checker * c); +void checker_set_sync (struct checker * c); +void checker_set_async (struct checker * c); void checker_set_fd (struct checker *, int); struct checker * checker_lookup (char *); int checker_check (struct checker *); diff --git a/libcheckers/directio.c b/libcheckers/directio.c index b53c1c3..ee09af7 100644 --- a/libcheckers/directio.c +++ b/libcheckers/directio.c @@ -12,28 +12,47 @@ #include #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 blksize; - unsigned char *buf; - unsigned char *ptr; + int running; + int reset_flags; + int blksize; + unsigned char * buf; + unsigned char * ptr; + io_context_t ioctx; + struct iocb io; }; + int directio_init (struct checker * c) { unsigned long pgsize = getpagesize(); struct directio_context * ct; + long flags; ct = malloc(sizeof(struct directio_context)); if (!ct) return 1; - c->context = (void *)ct; + memset(ct, 0, sizeof(struct directio_context)); + + if (io_setup(1, &ct->ioctx) != 0) { + condlog(1, "io_setup failed"); + free(ct); + return 1; + } if (ioctl(c->fd, BLKBSZGET, &ct->blksize) < 0) { MSG(c, "cannot get blocksize, set default"); @@ -50,11 +69,28 @@ int directio_init (struct checker * c) ct->buf = (unsigned char *)malloc(ct->blksize + pgsize); if (!ct->buf) goto out; - ct->ptr = (unsigned char *)(((unsigned long)ct->buf + pgsize - 1) & - (~(pgsize - 1))); + flags = fcntl(c->fd, F_GETFL); + if (flags < 0) + goto out; + if (!(flags & O_DIRECT)) { + flags |= O_DIRECT; + if (fcntl(c->fd, F_SETFL, flags) < 0) + goto out; + ct->reset_flags = 1; + } + + ct->ptr = (unsigned char *) (((unsigned long)ct->buf + pgsize - 1) & + (~(pgsize - 1))); + + /* Sucessfully initialized, return the context. */ + c->context = (void *) ct; return 0; + out: + if (ct->buf) + free(ct->buf); + io_destroy(ct->ioctx); free(ct); return 1; } @@ -62,56 +98,72 @@ out: void directio_free (struct checker * c) { struct directio_context * ct = (struct directio_context *)c->context; + long flags; if (!ct) return; + + if (ct->reset_flags) { + if ((flags = fcntl(c->fd, F_GETFL)) >= 0) { + flags &= ~O_DIRECT; + /* No point in checking for errors */ + fcntl(c->fd, F_SETFL, flags); + } + } + if (ct->buf) free(ct->buf); + io_destroy(ct->ioctx); free(ct); } static int -direct_read (int fd, unsigned char * buff, int size) +check_state(int fd, struct directio_context *ct, int sync) { - long flags; - int reset_flags = 0; - int res, retval; - - flags = fcntl(fd,F_GETFL); - - if (flags < 0) { - return PATH_UNCHECKED; + struct timespec timeout = { .tv_nsec = 5 }; + struct io_event event; + struct stat sb; + int rc = PATH_UNCHECKED; + long r; + + if (fstat(fd, &sb) == 0) { + LOG(4, "called for %x", (unsigned) sb.st_rdev); + } + if (sync) { + LOG(4, "called in synchronous mode"); + timeout.tv_sec = ASYNC_TIMEOUT_SEC; + timeout.tv_nsec = 0; } - if (!(flags & O_DIRECT)) { - flags |= O_DIRECT; - if (fcntl(fd,F_SETFL,flags) < 0) { + if (!ct->running) { + struct iocb *ios[1] = { &ct->io }; + + LOG(3, "starting new request"); + memset(&ct->io, 0, sizeof(struct iocb)); + io_prep_pread(&ct->io, fd, ct->ptr, ct->blksize, 0); + if (io_submit(ct->ioctx, 1, ios) != 1) { + LOG(3, "io_submit error %i", errno); return PATH_UNCHECKED; } - reset_flags = 1; } + ct->running++; - while ( (res = read(fd,buff,size)) < 0 && errno == EINTR ); - if (res < 0) { - if (errno == EINVAL) { - /* O_DIRECT is not available */ - retval = PATH_UNCHECKED; - } else if (errno == ENOMEM) { - retval = PATH_UP; - } else { - retval = PATH_DOWN; - } + r = io_getevents(ct->ioctx, 1L, 1L, &event, &timeout); + LOG(3, "async io getevents returns %li (errno=%s)", r, strerror(errno)); + + if (r < 1L) { + if (ct->running > ASYNC_TIMEOUT_SEC || sync) { + LOG(3, "abort check on timeout"); + rc = PATH_DOWN; + } else + rc = PATH_PENDING; } else { - retval = PATH_UP; - } - - if (reset_flags) { - flags &= ~O_DIRECT; - /* No point in checking for errors */ - fcntl(fd,F_SETFL,flags); + LOG(3, "io finished %lu/%lu", event.res, event.res2); + ct->running = 0; + rc = (event.res == ct->blksize) ? PATH_UP : PATH_DOWN; } - return retval; + return rc; } int directio (struct checker * c) @@ -119,7 +171,10 @@ int directio (struct checker * c) int ret; struct directio_context * ct = (struct directio_context *)c->context; - ret = direct_read(c->fd, ct->ptr, ct->blksize); + if (!ct) + return PATH_UNCHECKED; + + ret = check_state(c->fd, ct, c->sync); switch (ret) { @@ -132,6 +187,9 @@ int directio (struct checker * c) case PATH_UP: MSG(c, MSG_DIRECTIO_UP); break; + case PATH_PENDING: + MSG(c, MSG_DIRECTIO_PENDING); + break; default: break; } diff --git a/libcheckers/emc_clariion.c b/libcheckers/emc_clariion.c index 1d7b684..6c7167e 100644 --- a/libcheckers/emc_clariion.c +++ b/libcheckers/emc_clariion.c @@ -11,25 +11,76 @@ #include #include -#include "checkers.h" - #include "../libmultipath/sg_include.h" +#include "libsg.h" +#include "checkers.h" #define INQUIRY_CMD 0x12 #define INQUIRY_CMDLEN 6 #define HEAVY_CHECK_COUNT 10 -struct emc_clariion_checker_context { +/* + * Mechanism to track CLARiiON inactive snapshot LUs. + * This is done so that we can fail passive paths + * to an inactive snapshot LU even though since a + * simple read test would return 02/04/03 instead + * of 05/25/01 sensekey/ASC/ASCQ data. + */ +#define IS_INACTIVE_SNAP(c) (c->mpcontext ? \ + ((struct emc_clariion_checker_LU_context *) \ + (*c->mpcontext))->inactive_snap \ + : 0) + +#define SET_INACTIVE_SNAP(c) if (c->mpcontext) \ + ((struct emc_clariion_checker_LU_context *)\ + (*c->mpcontext))->inactive_snap = 1 + +#define CLR_INACTIVE_SNAP(c) if (c->mpcontext) \ + ((struct emc_clariion_checker_LU_context *)\ + (*c->mpcontext))->inactive_snap = 0 + +struct emc_clariion_checker_path_context { char wwn[16]; unsigned wwn_set; }; +struct emc_clariion_checker_LU_context { + int inactive_snap; +}; + +extern void +hexadecimal_to_ascii(char * wwn, char *wwnstr) +{ + int i,j, nbl; + + for (i=0,j=0;i<16;i++) { + wwnstr[j++] = ((nbl = ((wwn[i]&0xf0) >> 4)) <= 9) ? + '0' + nbl : 'a' + (nbl - 10); + wwnstr[j++] = ((nbl = (wwn[i]&0x0f)) <= 9) ? + '0' + nbl : 'a' + (nbl - 10); + } + wwnstr[32]=0; +} + int emc_clariion_init (struct checker * c) { - c->context = malloc(sizeof(struct emc_clariion_checker_context)); + /* + * Allocate and initialize the path specific context. + */ + c->context = malloc(sizeof(struct emc_clariion_checker_path_context)); if (!c->context) return 1; - ((struct emc_clariion_checker_context *)c->context)->wwn_set = 0; + ((struct emc_clariion_checker_path_context *)c->context)->wwn_set = 0; + + /* + * Allocate and initialize the multi-path global context. + */ + if (c->mpcontext) { + void * mpctxt = malloc(sizeof(int)); + *c->mpcontext = mpctxt; + CLR_INACTIVE_SNAP(c); + } + return 0; } @@ -40,13 +91,15 @@ void emc_clariion_free (struct checker * c) int emc_clariion(struct checker * c) { - unsigned char sense_buffer[256] = { 0, }; - unsigned char sb[128] = { 0, }; + unsigned char sense_buffer[128] = { 0, }; + unsigned char sb[SENSE_BUFF_LEN] = { 0, }, *sbb; unsigned char inqCmdBlk[INQUIRY_CMDLEN] = {INQUIRY_CMD, 1, 0xC0, 0, - sizeof(sb), 0}; + sizeof(sense_buffer), 0}; struct sg_io_hdr io_hdr; - struct emc_clariion_checker_context * ct = - (struct emc_clariion_checker_context *)c->context; + struct emc_clariion_checker_path_context * ct = + (struct emc_clariion_checker_path_context *)c->context; + char wwnstr[33]; + int ret; memset(&io_hdr, 0, sizeof (struct sg_io_hdr)); io_hdr.interface_id = 'S'; @@ -57,7 +110,7 @@ int emc_clariion(struct checker * c) io_hdr.dxferp = sense_buffer; io_hdr.cmdp = inqCmdBlk; io_hdr.sbp = sb; - io_hdr.timeout = 60000; + io_hdr.timeout = DEF_TIMEOUT; io_hdr.pack_id = 0; if (ioctl(c->fd, SG_IO, &io_hdr) < 0) { MSG(c, "emc_clariion_checker: sending query command failed"); @@ -69,34 +122,39 @@ int emc_clariion(struct checker * c) } if (/* Verify the code page - right page & revision */ sense_buffer[1] != 0xc0 || sense_buffer[9] != 0x00) { - MSG(c, "emc_clariion_checker: Path unit report page in unknown format"); + MSG(c, "emc_clariion_checker: Path unit report page in " + "unknown format"); return PATH_DOWN; } if ( /* Effective initiator type */ sense_buffer[27] != 0x03 - /* Failover mode should be set to 1 */ - || (sense_buffer[28] & 0x07) != 0x04 + /* + * Failover mode should be set to 1 (PNR failover mode) + * or 4 (ALUA failover mode). + */ + || (((sense_buffer[28] & 0x07) != 0x04) && + ((sense_buffer[28] & 0x07) != 0x06)) /* Arraycommpath should be set to 1 */ || (sense_buffer[30] & 0x04) != 0x04) { - MSG(c, "emc_clariion_checker: Path not correctly configured for failover"); + MSG(c, "emc_clariion_checker: Path not correctly configured " + "for failover"); return PATH_DOWN; } if ( /* LUN operations should indicate normal operations */ sense_buffer[48] != 0x00) { - MSG(c, "emc_clariion_checker: Path not available for normal operations"); + MSG(c, "emc_clariion_checker: Path not available for normal " + "operations"); return PATH_SHAKY; } -#if 0 - /* This is not actually an error as the failover to this group - * _would_ bind the path */ - if ( /* LUN should at least be bound somewhere */ - sense_buffer[4] != 0x00) { - return PATH_UP; + if ( /* LUN should at least be bound somewhere and not be LUNZ */ + sense_buffer[4] == 0x00) { + MSG(c, "emc_clariion_checker: Logical Unit is unbound " + "or LUNZ"); + return PATH_DOWN; } -#endif /* * store the LUN WWN there and compare that it indeed did not @@ -105,7 +163,8 @@ int emc_clariion(struct checker * c) */ if (ct->wwn_set) { if (memcmp(ct->wwn, &sense_buffer[10], 16) != 0) { - MSG(c, "emc_clariion_checker: Logical Unit WWN has changed!"); + MSG(c, "emc_clariion_checker: Logical Unit WWN " + "has changed!"); return PATH_DOWN; } } else { @@ -113,7 +172,59 @@ int emc_clariion(struct checker * c) ct->wwn_set = 1; } - - MSG(c, "emc_clariion_checker: Path healthy"); - return PATH_UP; + /* + * Issue read on active path to determine if inactive snapshot. + */ + if (sense_buffer[4] == 2) {/* if active path */ + unsigned char buf[4096]; + + ret = sg_read(c->fd, &buf[0], sbb = &sb[0]); + if (ret == PATH_DOWN) { + hexadecimal_to_ascii(ct->wwn, wwnstr); + + /* + * Check for inactive snapshot LU this way. Must + * fail these. + */ + if (((sbb[2]&0xf) == 5) && (sbb[12] == 0x25) && + (sbb[13]==1)) { + /* + * Do this so that we can fail even the + * passive paths which will return + * 02/04/03 not 05/25/01 on read. + */ + SET_INACTIVE_SNAP(c); + MSG(c, "emc_clariion_checker: Active " + "path to inactive snapshot WWN %s.", + wwnstr); + } else + MSG(c, "emc_clariion_checker: Read " + "error for WWN %s. Sense data are " + "0x%x/0x%x/0x%x.", wwnstr, + sbb[2]&0xf, sbb[12], sbb[13]); + } else { + MSG(c, "emc_clariion_checker: Active path is " + "healthy."); + /* + * Remove the path from the set of paths to inactive + * snapshot LUs if it was in this list since the + * snapshot is no longer inactive. + */ + CLR_INACTIVE_SNAP(c); + } + } else { + if (IS_INACTIVE_SNAP(c)) { + hexadecimal_to_ascii(ct->wwn, wwnstr); + MSG(c, "emc_clariion_checker: Passive " + "path to inactive snapshot WWN %s.", + wwnstr); + ret = PATH_DOWN; + } else { + MSG(c, + "emc_clariion_checker: Passive path is healthy."); + ret = PATH_UP; /* not ghost */ + } + } + + return ret; } diff --git a/libcheckers/hp_sw.c b/libcheckers/hp_sw.c index 509e9c4..b9731ff 100644 --- a/libcheckers/hp_sw.c +++ b/libcheckers/hp_sw.c @@ -19,7 +19,6 @@ #define INQUIRY_CMDLEN 6 #define INQUIRY_CMD 0x12 #define SENSE_BUFF_LEN 32 -#define DEF_TIMEOUT 60000 #define SCSI_CHECK_CONDITION 0x2 #define SCSI_COMMAND_TERMINATED 0x22 #define SG_ERR_DRIVER_SENSE 0x08 @@ -111,7 +110,7 @@ do_tur (int fd) io_hdr.dxfer_direction = SG_DXFER_NONE; io_hdr.cmdp = turCmdBlk; io_hdr.sbp = sense_buffer; - io_hdr.timeout = 20000; + io_hdr.timeout = DEF_TIMEOUT; io_hdr.pack_id = 0; if (ioctl(fd, SG_IO, &io_hdr) < 0) diff --git a/libcheckers/libsg.c b/libcheckers/libsg.c new file mode 100644 index 0000000..9171b10 --- /dev/null +++ b/libcheckers/libsg.c @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2004, 2005 Christophe Varoqui + */ +#include +#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/libcheckers/libsg.h b/libcheckers/libsg.h new file mode 100644 index 0000000..97c4491 --- /dev/null +++ b/libcheckers/libsg.h @@ -0,0 +1,8 @@ +#ifndef _LIBSG_H +#define _LIBSG_H + +#define SENSE_BUFF_LEN 32 + +int sg_read (int sg_fd, unsigned char * buff, unsigned char * senseBuff); + +#endif /* _LIBSG_H */ diff --git a/libcheckers/rdac.c b/libcheckers/rdac.c new file mode 100644 index 0000000..f430488 --- /dev/null +++ b/libcheckers/rdac.c @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2005 Christophe Varoqui + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "checkers.h" + +#include "../libmultipath/sg_include.h" + +#define INQUIRY_CMDLEN 6 +#define INQUIRY_CMD 0x12 +#define SENSE_BUFF_LEN 32 +#define RDAC_DEF_TIMEOUT 60000 +#define SCSI_CHECK_CONDITION 0x2 +#define SCSI_COMMAND_TERMINATED 0x22 +#define SG_ERR_DRIVER_SENSE 0x08 +#define RECOVERED_ERROR 0x01 + +#define MSG_RDAC_UP "rdac checker reports path is up" +#define MSG_RDAC_DOWN "rdac checker reports path is down" +#define MSG_RDAC_GHOST "rdac checker reports path is ghost" + +struct rdac_checker_context { + void * dummy; +}; + +int rdac_init (struct checker * c) +{ + return 0; +} + +void rdac_free (struct checker * c) +{ + return; +} + +static int +do_inq(int sg_fd, unsigned int pg_op, void *resp, int mx_resp_len) +{ + unsigned char inqCmdBlk[INQUIRY_CMDLEN] = { INQUIRY_CMD, 1, 0, 0, 0, 0 }; + unsigned char sense_b[SENSE_BUFF_LEN]; + struct sg_io_hdr io_hdr; + + inqCmdBlk[2] = (unsigned char) pg_op; + inqCmdBlk[4] = (unsigned char) (mx_resp_len & 0xff); + memset(&io_hdr, 0, sizeof (struct sg_io_hdr)); + + io_hdr.interface_id = 'S'; + io_hdr.cmd_len = sizeof (inqCmdBlk); + io_hdr.mx_sb_len = sizeof (sense_b); + io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; + io_hdr.dxfer_len = mx_resp_len; + io_hdr.dxferp = resp; + io_hdr.cmdp = inqCmdBlk; + io_hdr.sbp = sense_b; + io_hdr.timeout = RDAC_DEF_TIMEOUT; + + if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) + return 1; + + /* treat SG_ERR here to get rid of sg_err.[ch] */ + io_hdr.status &= 0x7e; + if ((0 == io_hdr.status) && (0 == io_hdr.host_status) && + (0 == io_hdr.driver_status)) + return 0; + if ((SCSI_CHECK_CONDITION == io_hdr.status) || + (SCSI_COMMAND_TERMINATED == io_hdr.status) || + (SG_ERR_DRIVER_SENSE == (0xf & io_hdr.driver_status))) { + if (io_hdr.sbp && (io_hdr.sb_len_wr > 2)) { + int sense_key; + unsigned char * sense_buffer = io_hdr.sbp; + if (sense_buffer[0] & 0x2) + sense_key = sense_buffer[1] & 0xf; + else + sense_key = sense_buffer[2] & 0xf; + if (RECOVERED_ERROR == sense_key) + return 0; + } + } + return 1; +} + +struct volume_access_inq +{ + char dontcare0[8]; + char avtcvp; + char dontcare1[39]; +}; + +extern int +rdac(struct checker * c) +{ + struct volume_access_inq inq; + + if (0 != do_inq(c->fd, 0xC9, &inq, sizeof(struct volume_access_inq))) { + MSG(c, MSG_RDAC_DOWN); + return PATH_DOWN; + } + + return ((inq.avtcvp & 0x1) ? PATH_UP : PATH_GHOST); +} diff --git a/libcheckers/rdac.h b/libcheckers/rdac.h new file mode 100644 index 0000000..d7bf812 --- /dev/null +++ b/libcheckers/rdac.h @@ -0,0 +1,8 @@ +#ifndef _RDAC_H +#define _RDAC_H + +int rdac(struct checker *); +int rdac_init(struct checker *); +void rdac_free(struct checker *); + +#endif /* _RDAC_H */ diff --git a/libcheckers/readsector0.c b/libcheckers/readsector0.c index e368fb4..bef0eb6 100644 --- a/libcheckers/readsector0.c +++ b/libcheckers/readsector0.c @@ -2,21 +2,9 @@ * 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 +#include "libsg.h" #define MSG_READSECTOR0_UP "readsector0 checker reports path is up" #define MSG_READSECTOR0_DOWN "readsector0 checker reports path is down" @@ -35,72 +23,14 @@ void readsector0_free (struct checker * c) return; } -static int -sg_read (int sg_fd, unsigned char * buff) -{ - /* defaults */ - int blocks = 1; - long long start_block = 0; - int bs = 512; - int cdbsz = 10; - int * diop = NULL; - - unsigned char rdCmd[cdbsz]; - unsigned char senseBuff[SENSE_BUFF_LEN]; - struct sg_io_hdr io_hdr; - int res; - int rd_opcode[] = {0x8, 0x28, 0xa8, 0x88}; - int sz_ind; - - memset(rdCmd, 0, cdbsz); - sz_ind = 1; - rdCmd[0] = rd_opcode[sz_ind]; - rdCmd[2] = (unsigned char)((start_block >> 24) & 0xff); - rdCmd[3] = (unsigned char)((start_block >> 16) & 0xff); - rdCmd[4] = (unsigned char)((start_block >> 8) & 0xff); - rdCmd[5] = (unsigned char)(start_block & 0xff); - rdCmd[7] = (unsigned char)((blocks >> 8) & 0xff); - rdCmd[8] = (unsigned char)(blocks & 0xff); - - memset(&io_hdr, 0, sizeof(struct sg_io_hdr)); - io_hdr.interface_id = 'S'; - io_hdr.cmd_len = cdbsz; - io_hdr.cmdp = rdCmd; - io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; - io_hdr.dxfer_len = bs * blocks; - io_hdr.dxferp = buff; - io_hdr.mx_sb_len = SENSE_BUFF_LEN; - io_hdr.sbp = senseBuff; - io_hdr.timeout = DEF_TIMEOUT; - io_hdr.pack_id = (int)start_block; - if (diop && *diop) - io_hdr.flags |= SG_FLAG_DIRECT_IO; - - while (((res = ioctl(sg_fd, SG_IO, &io_hdr)) < 0) && (EINTR == errno)); - - if (res < 0) { - if (ENOMEM == errno) { - return PATH_UP; - } - return PATH_DOWN; - } - - if ((0 == io_hdr.status) && - (0 == io_hdr.host_status) && - (0 == io_hdr.driver_status)) { - return PATH_UP; - } else { - return PATH_DOWN; - } -} - extern int readsector0 (struct checker * c) { - unsigned char buf[512]; + unsigned char buf[4096]; + unsigned char sbuf[SENSE_BUFF_LEN]; int ret; - ret = sg_read(c->fd, &buf[0]); + ret = sg_read(c->fd, &buf[0], &sbuf[0]); switch (ret) { diff --git a/libcheckers/tur.c b/libcheckers/tur.c index d40a273..e79bc0a 100644 --- a/libcheckers/tur.c +++ b/libcheckers/tur.c @@ -51,7 +51,7 @@ tur (struct checker * c) io_hdr.dxfer_direction = SG_DXFER_NONE; io_hdr.cmdp = turCmdBlk; io_hdr.sbp = sense_buffer; - io_hdr.timeout = 20000; + io_hdr.timeout = DEF_TIMEOUT; io_hdr.pack_id = 0; if (ioctl(c->fd, SG_IO, &io_hdr) < 0) { MSG(c, MSG_TUR_DOWN); diff --git a/libmultipath/Makefile b/libmultipath/Makefile index 8a14b04..511f5ad 100644 --- a/libmultipath/Makefile +++ b/libmultipath/Makefile @@ -6,24 +6,31 @@ BUILD = glibc include ../Makefile.inc -CFLAGS = -I$(checkersdir) +CFLAGS += -I$(checkersdir) OBJS = memory.o parser.o vector.o devmapper.o callout.o \ hwtable.o blacklist.o util.o dmparser.o config.o \ structs.o discovery.o propsel.o dict.o \ pgpolicies.o debug.o regex.o defaults.o uevent.o \ switchgroup.o uxsock.o print.o alias.o log_pthread.o \ - log.o configure.o structs_vec.o + log.o configure.o structs_vec.o sysfs.o PREVBUILD = $(shell nm debug.o 2> /dev/null|grep log_safe) ifeq ($(strip $(DAEMON)),1) + OBJS += lock.o waiter.o CFLAGS += -DDAEMON CLEAN = $(shell if [ "x$(PREVBUILD)" = "x" ]; then echo clean; fi) else CLEAN = $(shell if [ ! "x$(PREVBUILD)" = "x" ]; then echo clean; fi) endif +LIBDM_API_FLUSH = $(shell objdump -T /lib/libdevmapper.so.* | grep -c dm_task_no_flush) + +ifeq ($(strip $(LIBDM_API_FLUSH)),1) + CFLAGS += -DLIBDM_API_FLUSH +endif + all: $(BUILD) prepare: $(CLEAN) diff --git a/libmultipath/alias.c b/libmultipath/alias.c index 6d103d7..ca434fe 100644 --- a/libmultipath/alias.c +++ b/libmultipath/alias.c @@ -120,21 +120,34 @@ lock_bindings_file(int fd) static int -open_bindings_file(char *file) +open_bindings_file(char *file, int *can_write) { int fd; struct stat s; if (ensure_directories_exist(file, 0700)) return -1; + *can_write = 1; fd = open(file, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); if (fd < 0) { - condlog(0, "Cannot open bindings file [%s] : %s", file, - strerror(errno)); - return -1; + if (errno == EROFS) { + *can_write = 0; + condlog(3, "Cannot open bindings file [%s] read/write. " + " trying readonly", file); + fd = open(file, O_RDONLY); + if (fd < 0) { + condlog(0, "Cannot open bindings file [%s] " + "readonly : %s", file, strerror(errno)); + return -1; + } + } + else { + condlog(0, "Cannot open bindings file [%s] : %s", file, + strerror(errno)); + return -1; + } } - - if (lock_bindings_file(fd) < 0) + if (*can_write && lock_bindings_file(fd) < 0) goto fail; memset(&s, 0, sizeof(s)); @@ -143,6 +156,8 @@ open_bindings_file(char *file) goto fail; } if (s.st_size == 0) { + if (*can_write == 0) + goto fail; /* If bindings file is empty, write the header */ size_t len = strlen(BINDINGS_FILE_HEADER); if (write_all(fd, BINDINGS_FILE_HEADER, len) != len) { @@ -166,28 +181,14 @@ fail: static int -lookup_binding(int fd, char *map_wwid, char **map_alias) +lookup_binding(FILE *f, char *map_wwid, char **map_alias) { char buf[LINE_MAX]; - FILE *f; unsigned int line_nr = 0; - int scan_fd; int id = 0; *map_alias = NULL; - scan_fd = dup(fd); - if (scan_fd < 0) { - condlog(0, "Cannot dup bindings file descriptor : %s", - strerror(errno)); - return -1; - } - f = fdopen(scan_fd, "r"); - if (!f) { - condlog(0, "cannot fdopen on bindings file descriptor : %s", - strerror(errno)); - close(scan_fd); - return -1; - } + while (fgets(buf, LINE_MAX, f)) { char *c, *alias, *wwid; int curr_id; @@ -210,43 +211,27 @@ lookup_binding(int fd, char *map_wwid, char **map_alias) } if (strcmp(wwid, map_wwid) == 0){ condlog(3, "Found matching wwid [%s] in bindings file." - "\nSetting alias to %s", wwid, alias); + " Setting alias to %s", wwid, alias); *map_alias = strdup(alias); if (*map_alias == NULL) condlog(0, "Cannot copy alias from bindings " "file : %s", strerror(errno)); - fclose(f); return id; } } condlog(3, "No matching wwid [%s] in bindings file.", map_wwid); - fclose(f); return id; } static int -rlookup_binding(int fd, char **map_wwid, char *map_alias) +rlookup_binding(FILE *f, char **map_wwid, char *map_alias) { char buf[LINE_MAX]; - FILE *f; unsigned int line_nr = 0; - int scan_fd; int id = 0; *map_wwid = NULL; - scan_fd = dup(fd); - if (scan_fd < 0) { - condlog(0, "Cannot dup bindings file descriptor : %s", - strerror(errno)); - return -1; - } - f = fdopen(scan_fd, "r"); - if (!f) { - condlog(0, "cannot fdopen on bindings file descriptor : %s", - strerror(errno)); - close(scan_fd); - return -1; - } + while (fgets(buf, LINE_MAX, f)) { char *c, *alias, *wwid; int curr_id; @@ -274,12 +259,10 @@ rlookup_binding(int fd, char **map_wwid, char *map_alias) if (*map_wwid == NULL) condlog(0, "Cannot copy alias from bindings " "file : %s", strerror(errno)); - fclose(f); return id; } } condlog(3, "No matching alias [%s] in bindings file.", map_alias); - fclose(f); return id; } @@ -327,24 +310,49 @@ char * get_user_friendly_alias(char *wwid, char *file) { char *alias; - int fd, id; + int fd, scan_fd, id; + FILE *f; + int can_write; if (!wwid || *wwid == '\0') { condlog(3, "Cannot find binding for empty WWID"); return NULL; } - fd = open_bindings_file(file); + fd = open_bindings_file(file, &can_write); if (fd < 0) return NULL; - id = lookup_binding(fd, wwid, &alias); + + scan_fd = dup(fd); + if (scan_fd < 0) { + condlog(0, "Cannot dup bindings file descriptor : %s", + strerror(errno)); + close(fd); + return NULL; + } + + f = fdopen(scan_fd, "r"); + if (!f) { + condlog(0, "cannot fdopen on bindings file descriptor : %s", + strerror(errno)); + close(scan_fd); + close(fd); + return NULL; + } + + id = lookup_binding(f, wwid, &alias); if (id < 0) { + fclose(f); + close(scan_fd); close(fd); return NULL; } - if (!alias) + + if (!alias && can_write) alias = allocate_binding(fd, wwid, id); + fclose(f); + close(scan_fd); close(fd); return alias; } @@ -353,22 +361,45 @@ char * get_user_friendly_wwid(char *alias, char *file) { char *wwid; - int fd, id; + int fd, scan_fd, id, unused; + FILE *f; if (!alias || *alias == '\0') { condlog(3, "Cannot find binding for empty alias"); return NULL; } - fd = open_bindings_file(file); + fd = open_bindings_file(file, &unused); if (fd < 0) return NULL; - id = rlookup_binding(fd, &wwid, alias); + + scan_fd = dup(fd); + if (scan_fd < 0) { + condlog(0, "Cannot dup bindings file descriptor : %s", + strerror(errno)); + close(fd); + return NULL; + } + + f = fdopen(scan_fd, "r"); + if (!f) { + condlog(0, "cannot fdopen on bindings file descriptor : %s", + strerror(errno)); + close(scan_fd); + close(fd); + return NULL; + } + + id = rlookup_binding(f, &wwid, alias); if (id < 0) { + fclose(f); + close(scan_fd); close(fd); return NULL; } + fclose(f); + close(scan_fd); close(fd); return wwid; } diff --git a/libmultipath/blacklist.c b/libmultipath/blacklist.c index 6a8a681..0c277cb 100644 --- a/libmultipath/blacklist.c +++ b/libmultipath/blacklist.c @@ -2,7 +2,6 @@ * Copyright (c) 2004, 2005 Christophe Varoqui */ #include - #include #include "memory.h" @@ -14,7 +13,7 @@ #include "blacklist.h" extern int -store_ble (vector blist, char * str) +store_ble (vector blist, char * str, int origin) { struct blentry * ble; @@ -36,6 +35,7 @@ store_ble (vector blist, char * str) goto out1; ble->str = str; + ble->origin = origin; vector_set_slot(blist, ble); return 0; out1: @@ -63,7 +63,7 @@ alloc_ble_device (vector blist) } extern int -set_ble_device (vector blist, char * vendor, char * product) +set_ble_device (vector blist, char * vendor, char * product, int origin) { struct blentry_device * ble; @@ -91,6 +91,7 @@ set_ble_device (vector blist, char * vendor, char * product) } ble->product = product; } + ble->origin = origin; return 0; } @@ -105,19 +106,19 @@ setup_default_blist (struct config * conf) str = STRDUP("^(ram|raw|loop|fd|md|dm-|sr|scd|st)[0-9]*"); if (!str) return 1; - if (store_ble(conf->blist_devnode, str)) + if (store_ble(conf->blist_devnode, str, ORIGIN_DEFAULT)) return 1; str = STRDUP("^hd[a-z]"); if (!str) return 1; - if (store_ble(conf->blist_devnode, str)) + if (store_ble(conf->blist_devnode, str, ORIGIN_DEFAULT)) return 1; - - str = STRDUP("^cciss!c[0-9]d[0-9]*"); + + str = STRDUP("^dcssblk[0-9]*"); if (!str) return 1; - if (store_ble(conf->blist_devnode, str)) + if (store_ble(conf->blist_devnode, str, ORIGIN_DEFAULT)) return 1; vector_foreach_slot (conf->hwtable, hwe, i) { @@ -128,63 +129,194 @@ setup_default_blist (struct config * conf) VECTOR_SIZE(conf->blist_device) -1); if (set_ble_device(conf->blist_device, STRDUP(hwe->vendor), - STRDUP(hwe->bl_product))) { + STRDUP(hwe->bl_product), + ORIGIN_DEFAULT)) { FREE(ble); return 1; } } } - return 0; } int -blacklist (vector blist, char * str) +_blacklist_exceptions (vector elist, char * str) +{ + int i; + struct blentry * ele; + + vector_foreach_slot (elist, ele, i) { + if (!regexec(&ele->regex, str, 0, NULL, 0)) + return 1; + } + return 0; +} + +int +_blacklist (vector blist, char * str) { int i; struct blentry * ble; vector_foreach_slot (blist, ble, i) { - if (!regexec(&ble->regex, str, 0, NULL, 0)) { - condlog(3, "%s: blacklisted", str); + if (!regexec(&ble->regex, str, 0, NULL, 0)) + return 1; + } + return 0; +} + +int +_blacklist_exceptions_device(vector elist, char * vendor, char * product) +{ + int i; + struct blentry_device * ble; + + vector_foreach_slot (elist, ble, i) { + if (!regexec(&ble->vendor_reg, vendor, 0, NULL, 0) && + !regexec(&ble->product_reg, product, 0, NULL, 0)) return 1; - } } return 0; } int -blacklist_device (vector blist, char * vendor, char * product) +_blacklist_device (vector blist, char * vendor, char * product) { int i; struct blentry_device * ble; vector_foreach_slot (blist, ble, i) { if (!regexec(&ble->vendor_reg, vendor, 0, NULL, 0) && - !regexec(&ble->product_reg, product, 0, NULL, 0)) { - condlog(3, "%s:%s: blacklisted", vendor, product); + !regexec(&ble->product_reg, product, 0, NULL, 0)) return 1; - } } return 0; } +#define LOG_BLIST(M) \ + if (vendor && product) \ + condlog(3, "%s: (%s:%s) %s", dev, vendor, product, (M)); \ + else if (wwid) \ + condlog(3, "%s: (%s) %s", dev, wwid, (M)); \ + else \ + condlog(3, "%s: %s", dev, (M)) + +void +log_filter (char *dev, char *vendor, char *product, char *wwid, int r) +{ + /* + * Try to sort from most likely to least. + */ + switch (r) { + case MATCH_NOTHING: + break; + case MATCH_DEVICE_BLIST: + LOG_BLIST("vendor/product blacklisted"); + break; + case MATCH_WWID_BLIST: + LOG_BLIST("wwid blacklisted"); + break; + case MATCH_DEVNODE_BLIST: + LOG_BLIST("device node name blacklisted"); + break; + case MATCH_DEVICE_BLIST_EXCEPT: + LOG_BLIST("vendor/product whitelisted"); + break; + case MATCH_WWID_BLIST_EXCEPT: + LOG_BLIST("wwid whitelisted"); + break; + case MATCH_DEVNODE_BLIST_EXCEPT: + LOG_BLIST("device node name whitelisted"); + break; + } +} + int -blacklist_path (struct config * conf, struct path * pp) +_filter_device (vector blist, vector elist, char * vendor, char * product) { - if (blacklist(conf->blist_devnode, pp->dev)) - return 1; + if (!vendor || !product) + return 0; + if (_blacklist_exceptions_device(elist, vendor, product)) + return MATCH_DEVICE_BLIST_EXCEPT; + if (_blacklist_device(blist, vendor, product)) + return MATCH_DEVICE_BLIST; + return 0; +} - if (blacklist(conf->blist_wwid, pp->wwid)) - return 1; +int +filter_device (vector blist, vector elist, char * vendor, char * product) +{ + int r = _filter_device(blist, elist, vendor, product); + log_filter(NULL, vendor, product, NULL, r); + return r; +} - if (pp->vendor_id && pp->product_id && - blacklist_device(conf->blist_device, pp->vendor_id, pp->product_id)) - return 1; +int +_filter_devnode (vector blist, vector elist, char * dev) +{ + if (!dev) + return 0; + if (_blacklist_exceptions(elist, dev)) + return MATCH_DEVNODE_BLIST_EXCEPT; + if (_blacklist(blist, dev)) + return MATCH_DEVNODE_BLIST; + return 0; +} + +int +filter_devnode (vector blist, vector elist, char * dev) +{ + int r = _filter_devnode(blist, elist, dev); + log_filter(dev, NULL, NULL, NULL, r); + return r; +} +int +_filter_wwid (vector blist, vector elist, char * wwid) +{ + if (!wwid) + return 0; + if (_blacklist_exceptions(elist, wwid)) + return MATCH_WWID_BLIST_EXCEPT; + if (_blacklist(blist, wwid)) + return MATCH_WWID_BLIST; + return 0; +} + +int +filter_wwid (vector blist, vector elist, char * wwid) +{ + int r = _filter_wwid(blist, elist, wwid); + log_filter(NULL, NULL, NULL, wwid, r); + return r; +} + +int +_filter_path (struct config * conf, struct path * pp) +{ + int r; + + r = _filter_devnode(conf->blist_devnode, conf->elist_devnode,pp->dev); + if (r) + return r; + r = _filter_wwid(conf->blist_wwid, conf->elist_devnode, pp->wwid); + if (r) + return r; + r = _filter_device(conf->blist_device, conf->elist_device, + pp->vendor_id, pp->product_id); + if (r) + return r; return 0; } +int +filter_path (struct config * conf, struct path * pp) +{ + int r=_filter_path(conf, pp); + log_filter(pp->dev, pp->vendor_id, pp->product_id, pp->wwid, r); + return r; +} + void free_blacklist (vector blist) { diff --git a/libmultipath/blacklist.h b/libmultipath/blacklist.h index 9e2b63c..cdbebef 100644 --- a/libmultipath/blacklist.h +++ b/libmultipath/blacklist.h @@ -3,9 +3,18 @@ #include "regex.h" +#define MATCH_NOTHING 0 +#define MATCH_WWID_BLIST 1 +#define MATCH_DEVICE_BLIST 2 +#define MATCH_DEVNODE_BLIST 3 +#define MATCH_WWID_BLIST_EXCEPT -MATCH_WWID_BLIST +#define MATCH_DEVICE_BLIST_EXCEPT -MATCH_DEVICE_BLIST +#define MATCH_DEVNODE_BLIST_EXCEPT -MATCH_DEVNODE_BLIST + struct blentry { char * str; regex_t regex; + int origin; }; struct blentry_device { @@ -13,15 +22,17 @@ struct blentry_device { char * product; regex_t vendor_reg; regex_t product_reg; + int origin; }; int setup_default_blist (struct config *); int alloc_ble_device (vector); -int blacklist (vector, char *); -int blacklist_device (vector, char *, char *); -int blacklist_path (struct config *, struct path *); -int store_ble (vector, char *); -int set_ble_device (vector, char *, char *); +int filter_devnode (vector, vector, char *); +int filter_wwid (vector, vector, char *); +int filter_device (vector, vector, char *, char *); +int filter_path (struct config *, struct path *); +int store_ble (vector, char *, int); +int set_ble_device (vector, char *, char *, int); void free_blacklist (vector); void free_blacklist_device (vector); diff --git a/libmultipath/config.c b/libmultipath/config.c index 1068755..a39af8a 100644 --- a/libmultipath/config.c +++ b/libmultipath/config.c @@ -20,26 +20,61 @@ #include "blacklist.h" #include "defaults.h" +static struct hwentry * +find_hwe_strmatch (vector hwtable, char * vendor, char * product, char * revision) +{ + int i; + struct hwentry *hwe, *ret = NULL; + + vector_foreach_slot (hwtable, hwe, i) { + if (hwe->vendor && vendor && strcmp(hwe->vendor, vendor)) + continue; + + if (hwe->product && product && strcmp(hwe->product, product)) + continue; + + if (hwe->revision && revision && strcmp(hwe->revision, revision)) + continue; + + ret = hwe; + break; + } + return ret; +} + struct hwentry * -find_hwe (vector hwtable, char * vendor, char * product) +find_hwe (vector hwtable, char * vendor, char * product, char * revision) { int i; struct hwentry *hwe, *ret = NULL; - regex_t vre, pre; + regex_t vre, pre, rre; vector_foreach_slot (hwtable, hwe, i) { - if (regcomp(&vre, hwe->vendor, REG_EXTENDED|REG_NOSUB)) + if (hwe->vendor && + regcomp(&vre, hwe->vendor, REG_EXTENDED|REG_NOSUB)) + break; + if (hwe->product && + regcomp(&pre, hwe->product, REG_EXTENDED|REG_NOSUB)) { + regfree(&vre); break; - if (regcomp(&pre, hwe->product, REG_EXTENDED|REG_NOSUB)) { + } + if (hwe->revision && + regcomp(&rre, hwe->revision, REG_EXTENDED|REG_NOSUB)) { regfree(&vre); + regfree(&pre); break; } - if (!regexec(&vre, vendor, 0, NULL, 0) && - !regexec(&pre, product, 0, NULL, 0)) + if ((!hwe->vendor || !regexec(&vre, vendor, 0, NULL, 0)) && + (!hwe->product || !regexec(&pre, product, 0, NULL, 0)) && + (!hwe->revision || !regexec(&rre, revision, 0, NULL, 0))) ret = hwe; - - regfree(&pre); - regfree(&vre); + + if (hwe->revision) + regfree(&rre); + if (hwe->product) + regfree(&pre); + if (hwe->vendor) + regfree(&vre); if (ret) break; @@ -91,6 +126,9 @@ free_hwe (struct hwentry * hwe) if (hwe->product) FREE(hwe->product); + if (hwe->revision) + FREE(hwe->revision); + if (hwe->selector) FREE(hwe->selector); @@ -204,23 +242,12 @@ set_param_str(char * str) return dst; } -static int -dup_hwe (vector hwtable, char * vendor, char * product) -{ - struct hwentry * hwe = find_hwe(hwtable, vendor, product); - - if (hwe) - return 1; - - return 0; -} - int store_hwe (vector hwtable, struct hwentry * dhwe) { struct hwentry * hwe; - if (dup_hwe(hwtable, dhwe->vendor, dhwe->product)) + if (find_hwe_strmatch(hwtable, dhwe->vendor, dhwe->product, dhwe->revision)) return 0; if (!(hwe = alloc_hwe())) @@ -232,6 +259,9 @@ store_hwe (vector hwtable, struct hwentry * dhwe) if (!dhwe->product || !(hwe->product = set_param_str(dhwe->product))) goto out; + if (dhwe->revision && !(hwe->revision = set_param_str(dhwe->revision))) + goto out; + if (dhwe->getuid && !(hwe->getuid = set_param_str(dhwe->getuid))) goto out; @@ -303,9 +333,14 @@ free_config (struct config * conf) free_blacklist(conf->blist_devnode); free_blacklist(conf->blist_wwid); free_blacklist_device(conf->blist_device); + + free_blacklist(conf->elist_devnode); + free_blacklist(conf->elist_wwid); + free_blacklist_device(conf->elist_device); + free_mptable(conf->mptable); free_hwtable(conf->hwtable); - + free_keywords(conf->keywords); FREE(conf); } @@ -332,6 +367,7 @@ load_config (char * file) * read the config file */ if (filepresent(file)) { + set_current_keywords(&conf->keywords); if (init_data(file, init_keywords)) { condlog(0, "error parsing config file"); goto out; @@ -372,6 +408,26 @@ load_config (char * file) if (setup_default_blist(conf)) goto out; + if (conf->elist_devnode == NULL) { + conf->elist_devnode = vector_alloc(); + + if (!conf->elist_devnode) + goto out; + } + if (conf->elist_wwid == NULL) { + conf->elist_wwid = vector_alloc(); + + if (!conf->elist_wwid) + goto out; + } + + if (conf->elist_device == NULL) { + conf->elist_device = vector_alloc(); + + if (!conf->elist_device) + goto out; + } + if (conf->mptable == NULL) { conf->mptable = vector_alloc(); diff --git a/libmultipath/config.h b/libmultipath/config.h index 6e5633a..a25b3ad 100644 --- a/libmultipath/config.h +++ b/libmultipath/config.h @@ -1,6 +1,9 @@ #ifndef _CONFIG_H #define _CONFIG_H +#define ORIGIN_DEFAULT 0 +#define ORIGIN_CONFIG 1 + enum devtypes { DEV_NONE, DEV_DEVT, @@ -11,6 +14,7 @@ enum devtypes { struct hwentry { char * vendor; char * product; + char * revision; char * getuid; char * getprio; char * features; @@ -23,6 +27,7 @@ struct hwentry { int rr_weight; int no_path_retry; int minio; + int pg_timeout; struct checker * checker; char * bl_product; }; @@ -38,6 +43,7 @@ struct mpentry { int rr_weight; int no_path_retry; int minio; + int pg_timeout; }; struct config { @@ -48,7 +54,7 @@ struct config { int with_sysfs; int pgpolicy; struct checker * checker; - int dev_type; + enum devtypes dev_type; int minio; int checkint; int max_checkint; @@ -57,8 +63,10 @@ struct config { int rr_weight; int no_path_retry; int user_friendly_names; + int pg_timeout; char * dev; + char * sysfs_dir; char * udev_dir; char * selector; char * getuid; @@ -67,17 +75,21 @@ struct config { char * hwhandler; char * bindings_file; + vector keywords; vector mptable; vector hwtable; vector blist_devnode; vector blist_wwid; vector blist_device; + vector elist_devnode; + vector elist_wwid; + vector elist_device; }; struct config * conf; -struct hwentry * find_hwe (vector hwtable, char * vendor, char * product); +struct hwentry * find_hwe (vector hwtable, char * vendor, char * product, char *revision); struct mpentry * find_mpe (char * wwid); char * get_mpe_wwid (char * alias); diff --git a/libmultipath/configure.c b/libmultipath/configure.c index 1ba4356..3cd6041 100644 --- a/libmultipath/configure.c +++ b/libmultipath/configure.c @@ -60,6 +60,7 @@ setup_map (struct multipath * mpp) select_rr_weight(mpp); select_minio(mpp); select_no_path_retry(mpp); + select_pg_timeout(mpp); /* * assign paths to path groups -- start with no groups and all paths @@ -145,11 +146,6 @@ select_action (struct multipath * mpp, vector curmp) mpp->action = ACT_RENAME; return; } - else { - condlog(3, "%s: set ACT_CREATE (map does not exist)", - mpp->alias); - mpp->action = ACT_CREATE; - } mpp->action = ACT_CREATE; condlog(3, "%s: set ACT_CREATE (map does not exist)", mpp->alias); @@ -179,8 +175,9 @@ select_action (struct multipath * mpp, vector curmp) mpp->alias); return; } - if (!mpp->no_path_retry && /* let features be handled by the daemon */ - strncmp(cmpp->features, mpp->features, strlen(mpp->features))) { + if (!mpp->no_path_retry && !mpp->pg_timeout && + (strlen(cmpp->features) != strlen(mpp->features) || + strcmp(cmpp->features, mpp->features))) { mpp->action = ACT_RELOAD; condlog(3, "%s: set ACT_RELOAD (features change)", mpp->alias); @@ -286,11 +283,13 @@ lock_multipath (struct multipath * mpp, int lock) /* * Return value: - * -1: Retry - * 0: DM_DEVICE_CREATE or DM_DEVICE_RELOAD failed, or dry_run mode. - * 1: DM_DEVICE_CREATE or DM_DEVICE_RELOAD succeeded. - * 2: Map is already existing. */ +#define DOMAP_RETRY -1 +#define DOMAP_FAIL 0 +#define DOMAP_OK 1 +#define DOMAP_EXIST 2 +#define DOMAP_DRY 3 + extern int domap (struct multipath * mpp) { @@ -299,15 +298,15 @@ domap (struct multipath * mpp) /* * last chance to quit before touching the devmaps */ - if (conf->dry_run) { + if (conf->dry_run && mpp->action != ACT_NOTHING) { print_multipath_topology(mpp, conf->verbosity); - return 0; + return DOMAP_DRY; } switch (mpp->action) { case ACT_REJECT: case ACT_NOTHING: - return 2; + return DOMAP_EXIST; case ACT_SWITCHPG: dm_switchgroup(mpp->alias, mpp->bestpg); @@ -317,18 +316,19 @@ domap (struct multipath * mpp) * retry. */ reinstate_paths(mpp); - return 2; + return DOMAP_EXIST; case ACT_CREATE: if (lock_multipath(mpp, 1)) { condlog(3, "%s: failed to create map (in use)", mpp->alias); - return -1; + return DOMAP_RETRY; } - dm_shut_log(); - if (dm_map_present(mpp->alias)) + if (dm_map_present(mpp->alias)) { + condlog(3, "%s: map already present", mpp->alias); break; + } r = dm_addmap(DM_DEVICE_CREATE, mpp->alias, DEFAULT_TARGET, mpp->params, mpp->size, mpp->wwid); @@ -346,7 +346,6 @@ domap (struct multipath * mpp) } lock_multipath(mpp, 0); - dm_restore_log(); break; case ACT_RELOAD: @@ -377,9 +376,9 @@ domap (struct multipath * mpp) condlog(2, "%s: load table [0 %llu %s %s]", mpp->alias, mpp->size, DEFAULT_TARGET, mpp->params); #endif + return DOMAP_OK; } - - return r; + return DOMAP_FAIL; } static int @@ -423,7 +422,7 @@ coalesce_paths (struct vectors * vecs, vector newmp, char * refwwid) /* 1. if path has no unique id or wwid blacklisted */ if (memcmp(empty_buff, pp1->wwid, WWID_SIZE) == 0 || - blacklist_path(conf, pp1)) + filter_path(conf, pp1) > 0) continue; /* 2. if path already coalesced */ @@ -444,7 +443,7 @@ coalesce_paths (struct vectors * vecs, vector newmp, char * refwwid) if ((mpp = add_map_with_path(vecs, pp1, 0)) == NULL) return 1; - if (pp1->priority < 0) + if (pp1->priority == PRIO_UNDEF) mpp->action = ACT_REJECT; if (!mpp->paths) { @@ -471,7 +470,7 @@ coalesce_paths (struct vectors * vecs, vector newmp, char * refwwid) mpp->size); mpp->action = ACT_REJECT; } - if (pp2->priority < 0) + if (pp2->priority == PRIO_UNDEF) mpp->action = ACT_REJECT; } verify_paths(mpp, vecs, NULL); @@ -486,19 +485,18 @@ coalesce_paths (struct vectors * vecs, vector newmp, char * refwwid) r = domap(mpp); - if (!r) { - condlog(3, "%s: domap (%u) failure " - "for create/reload map", - mpp->alias, r); - remove_map(mpp, vecs, NULL, 0); - continue; - } - else if (r < 0) { + if (r == DOMAP_FAIL || r == DOMAP_RETRY) { condlog(3, "%s: domap (%u) failure " "for create/reload map", mpp->alias, r); - return r; + if (r == DOMAP_FAIL) { + remove_map(mpp, vecs, NULL, 0); + continue; + } else /* if (r == DOMAP_RETRY) */ + return r; } + if (r == DOMAP_DRY) + continue; if (mpp->no_path_retry != NO_PATH_RETRY_UNDEF) { if (mpp->no_path_retry == NO_PATH_RETRY_FAIL) @@ -506,6 +504,12 @@ coalesce_paths (struct vectors * vecs, vector newmp, char * refwwid) else dm_queue_if_no_path(mpp->alias, 1); } + if (mpp->pg_timeout != PGTIMEOUT_UNDEF) { + if (mpp->pg_timeout == -PGTIMEOUT_NONE) + dm_set_pg_timeout(mpp->alias, 0); + else + dm_set_pg_timeout(mpp->alias, mpp->pg_timeout); + } if (newmp) { if (mpp->action != ACT_REJECT) { @@ -547,11 +551,11 @@ coalesce_paths (struct vectors * vecs, vector newmp, char * refwwid) } extern char * -get_refwwid (char * dev, int dev_type, vector pathvec) +get_refwwid (char * dev, enum devtypes dev_type, vector pathvec) { struct path * pp; char buff[FILE_NAME_SIZE]; - char * refwwid; + char * refwwid = NULL, tmpwwid[WWID_SIZE]; if (dev_type == DEV_NONE) return NULL; @@ -606,6 +610,12 @@ get_refwwid (char * dev, int dev_type, vector pathvec) goto out; } if (dev_type == DEV_DEVMAP) { + + if (((dm_get_uuid(dev, tmpwwid)) == 0) && (strlen(tmpwwid))) { + refwwid = tmpwwid; + goto out; + } + /* * may be a binding */ diff --git a/libmultipath/configure.h b/libmultipath/configure.h index 42ee9b0..1cbbe82 100644 --- a/libmultipath/configure.h +++ b/libmultipath/configure.h @@ -25,5 +25,5 @@ int setup_map (struct multipath * mpp); int domap (struct multipath * mpp); int reinstate_paths (struct multipath *mpp); int coalesce_paths (struct vectors *vecs, vector curmp, char * refwwid); -char * get_refwwid (char * dev, int dev_type, vector pathvec); +char * get_refwwid (char * dev, enum devtypes dev_type, vector pathvec); diff --git a/libmultipath/debug.c b/libmultipath/debug.c index 099ab52..05dfb06 100644 --- a/libmultipath/debug.c +++ b/libmultipath/debug.c @@ -14,7 +14,7 @@ #include "vector.h" #include "config.h" -void dlog (int sink, int prio, char * fmt, ...) +void dlog (int sink, int prio, const char * fmt, ...) { va_list ap; int thres; @@ -29,17 +29,16 @@ void dlog (int sink, int prio, char * fmt, ...) struct tm *tb = localtime(&t); char buff[16]; - strftime(buff, 16, "%b %d %H:%M:%S", tb); + strftime(buff, sizeof(buff), "%b %d %H:%M:%S", tb); + buff[sizeof(buff)-1] = '\0'; fprintf(stdout, "%s | ", buff); vfprintf(stdout, fmt, ap); - fprintf(stdout, "\n"); } else log_safe(prio + 3, fmt, ap); #else vfprintf(stdout, fmt, ap); - fprintf(stdout, "\n"); #endif } va_end(ap); diff --git a/libmultipath/debug.h b/libmultipath/debug.h index e7612a9..082fff1 100644 --- a/libmultipath/debug.h +++ b/libmultipath/debug.h @@ -1,4 +1,4 @@ -void dlog (int sink, int prio, char * fmt, ...) +void dlog (int sink, int prio, const char * fmt, ...) __attribute__((format(printf, 3, 4))); #if DAEMON @@ -11,11 +11,11 @@ void dlog (int sink, int prio, char * fmt, ...) int logsink; #define condlog(prio, fmt, args...) \ - dlog(logsink, prio, fmt, ##args) + dlog(logsink, prio, fmt "\n", ##args) #else /* DAEMON */ #define condlog(prio, fmt, args...) \ - dlog(0, prio, fmt, ##args) + dlog(0, prio, fmt "\n", ##args) #endif /* DAEMON */ diff --git a/libmultipath/defaults.h b/libmultipath/defaults.h index ab65492..df7d971 100644 --- a/libmultipath/defaults.h +++ b/libmultipath/defaults.h @@ -1,4 +1,4 @@ -#define DEFAULT_GETUID "/sbin/scsi_id -g -u -s /block/%n" +#define DEFAULT_GETUID "/lib/udev/scsi_id -g -u -s /block/%n" #define DEFAULT_UDEVDIR "/dev" #define DEFAULT_SELECTOR "round-robin 0" #define DEFAULT_FEATURES "0" @@ -9,6 +9,7 @@ #define DEFAULT_FAILBACK -FAILBACK_MANUAL #define DEFAULT_RR_WEIGHT RR_WEIGHT_NONE #define DEFAULT_NO_PATH_RETRY NO_PATH_RETRY_UNDEF +#define DEFAULT_PGTIMEOUT -PGTIMEOUT_NONE #define DEFAULT_USER_FRIENDLY_NAMES 0 #define DEFAULT_CHECKINT 5 diff --git a/libmultipath/devmapper.c b/libmultipath/devmapper.c index 4328036..d6991ba 100644 --- a/libmultipath/devmapper.c +++ b/libmultipath/devmapper.c @@ -6,11 +6,13 @@ */ #include #include +#include #include #include #include #include #include +#include #include @@ -19,35 +21,96 @@ #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 +121,44 @@ dm_prereq (char * str, int x, int y, int z) condlog(0, "Can not communicate with kernel DM"); goto out; } - target = dm_task_get_versions(dmt); do { last_target = target; - if (!strncmp(str, target->name, strlen(str))) { - r--; - - if (target->version[0] >= x && - target->version[1] >= y && - target->version[2] >= z) - r--; - + r = 1; break; } - target = (void *) target + target->next; } while (last_target != target); - if (r == 2) + if (r == 2) { condlog(0, "DM multipath kernel driver not loaded"); - else if (r == 1) - condlog(0, "DM multipath kernel driver version too old"); - + goto out; + } + v = target->version; + if ((v[0] > minv[0]) || + ((v[0] == minv[0]) && (v[1] > minv[1])) || + ((v[0] == minv[0]) && (v[1] == minv[1]) && (v[2] >= minv[2]))) { + r = 0; + goto out; + } + condlog(0, "DM multipath kernel driver must be >= %u.%.2u.%.2u", + minv[0], minv[1], minv[2]); out: dm_task_destroy(dmt); return r; } extern int +dm_prereq (char * str) +{ + if (dm_libprereq()) + return 1; + return dm_drvprereq(str); +} + +extern int dm_simplecmd (int task, const char *name) { int r = 0; struct dm_task *dmt; @@ -100,6 +170,10 @@ dm_simplecmd (int task, const char *name) { goto out; dm_task_no_open_count(dmt); + dm_task_skip_lockfs(dmt); /* for DM_DEVICE_RESUME */ +#ifdef LIBDM_API_FLUSH + dm_task_no_flush(dmt); /* for DM_DEVICE_SUSPEND/RESUME */ +#endif r = dm_task_run (dmt); @@ -113,6 +187,7 @@ dm_addmap (int task, const char *name, const char *target, const char *params, unsigned long long size, const char *uuid) { int r = 0; struct dm_task *dmt; + char *prefixed_uuid = NULL; if (!(dmt = dm_task_create (task))) return 0; @@ -123,13 +198,26 @@ dm_addmap (int task, const char *name, const char *target, if (!dm_task_add_target (dmt, 0, size, target, params)) goto addout; - if (uuid && !dm_task_set_uuid(dmt, uuid)) - goto addout; + if (uuid){ + prefixed_uuid = MALLOC(UUID_PREFIX_LEN + strlen(uuid) + 1); + if (!prefixed_uuid) { + condlog(0, "cannot create prefixed uuid : %s\n", + strerror(errno)); + goto addout; + } + sprintf(prefixed_uuid, UUID_PREFIX "%s", uuid); + if (!dm_task_set_uuid(dmt, prefixed_uuid)) + goto freeout; + } dm_task_no_open_count(dmt); r = dm_task_run (dmt); + freeout: + if (prefixed_uuid) + free(prefixed_uuid); + addout: dm_task_destroy (dmt); return r; @@ -203,6 +291,7 @@ dm_get_uuid(char *name, char *uuid) { struct dm_task *dmt; const char *uuidtmp; + int r = 1; dmt = dm_task_create(DM_DEVICE_INFO); if (!dmt) @@ -215,15 +304,19 @@ dm_get_uuid(char *name, char *uuid) goto uuidout; uuidtmp = dm_task_get_uuid(dmt); - if (uuidtmp) - strcpy(uuid, uuidtmp); + if (uuidtmp) { + if (!strncmp(uuidtmp, UUID_PREFIX, UUID_PREFIX_LEN)) + strcpy(uuid, uuidtmp + UUID_PREFIX_LEN); + else + strcpy(uuid, uuidtmp); + } else uuid[0] = '\0'; + r = 0; uuidout: dm_task_destroy(dmt); - - return 0; + return r; } extern int @@ -509,6 +602,16 @@ dm_queue_if_no_path(char *mapname, int enable) return dm_message(mapname, message); } +int +dm_set_pg_timeout(char *mapname, int timeout_val) +{ + char message[24]; + + if (snprintf(message, 24, "set_pg_timeout %d", timeout_val) >= 24) + return 1; + return dm_message(mapname, message); +} + static int dm_groupmsg (char * msg, char * mapname, int index) { @@ -591,6 +694,7 @@ dm_get_maps (vector mp, char * type) goto out1; dm_get_uuid(names->name, mpp->wwid); + dm_get_info(names->name, &mpp->dmi); } if (!vector_alloc_slot(mp)) @@ -697,9 +801,7 @@ dm_mapname(int major, int minor) * daemon uev_trigger -> uev_add_map */ while (--loop) { - dm_shut_log(); r = dm_task_run(dmt); - dm_restore_log(); if (r) break; diff --git a/libmultipath/devmapper.h b/libmultipath/devmapper.h index c7879a7..8438034 100644 --- a/libmultipath/devmapper.h +++ b/libmultipath/devmapper.h @@ -1,6 +1,5 @@ -void dm_shut_log(void); -void dm_restore_log(void); -int dm_prereq (char *, int, int, int); +void dm_init(void); +int dm_prereq (char *); int dm_simplecmd (int, const char *); int dm_addmap (int, const char *, const char *, const char *, unsigned long long, const char *uuid); @@ -13,6 +12,7 @@ int dm_flush_maps (char *); int dm_fail_path(char * mapname, char * path); int dm_reinstate_path(char * mapname, char * path); int dm_queue_if_no_path(char *mapname, int enable); +int dm_set_pg_timeout(char *mapname, int timeout_val); int dm_switchgroup(char * mapname, int index); int dm_enablegroup(char * mapname, int index); int dm_disablegroup(char * mapname, int index); diff --git a/libmultipath/dict.c b/libmultipath/dict.c index 9ca228a..4572a7d 100644 --- a/libmultipath/dict.c +++ b/libmultipath/dict.c @@ -201,6 +201,32 @@ def_no_path_retry_handler(vector strvec) } static int +def_pg_timeout_handler(vector strvec) +{ + int pg_timeout; + char * buff; + + buff = set_value(strvec); + + if (!buff) + return 1; + + if (strlen(buff) == 4 && !strcmp(buff, "none")) + conf->pg_timeout = -PGTIMEOUT_NONE; + else if (sscanf(buff, "%d", &pg_timeout) == 1 && pg_timeout >= 0) { + if (pg_timeout == 0) + conf->pg_timeout = -PGTIMEOUT_NONE; + else + conf->pg_timeout = pg_timeout; + } + else + conf->pg_timeout = PGTIMEOUT_UNDEF; + + FREE(buff); + return 0; +} + +static int names_handler(vector strvec) { char * buff; @@ -238,6 +264,19 @@ blacklist_handler(vector strvec) } static int +blacklist_exceptions_handler(vector strvec) +{ + conf->elist_devnode = vector_alloc(); + conf->elist_wwid = vector_alloc(); + conf->elist_device = vector_alloc(); + + if (!conf->elist_devnode || !conf->elist_wwid || !conf->elist_device) + return 1; + + return 0; +} + +static int ble_devnode_handler(vector strvec) { char * buff; @@ -247,7 +286,20 @@ ble_devnode_handler(vector strvec) if (!buff) return 1; - return store_ble(conf->blist_devnode, buff); + return store_ble(conf->blist_devnode, buff, ORIGIN_CONFIG); +} + +static int +ble_except_devnode_handler(vector strvec) +{ + char * buff; + + buff = set_value(strvec); + + if (!buff) + return 1; + + return store_ble(conf->elist_devnode, buff, ORIGIN_CONFIG); } static int @@ -260,7 +312,20 @@ ble_wwid_handler(vector strvec) if (!buff) return 1; - return store_ble(conf->blist_wwid, buff); + return store_ble(conf->blist_wwid, buff, ORIGIN_CONFIG); +} + +static int +ble_except_wwid_handler(vector strvec) +{ + char * buff; + + buff = set_value(strvec); + + if (!buff) + return 1; + + return store_ble(conf->elist_wwid, buff, ORIGIN_CONFIG); } static int @@ -270,6 +335,12 @@ ble_device_handler(vector strvec) } static int +ble_except_device_handler(vector strvec) +{ + return alloc_ble_device(conf->elist_device); +} + +static int ble_vendor_handler(vector strvec) { char * buff; @@ -279,7 +350,20 @@ ble_vendor_handler(vector strvec) if (!buff) return 1; - return set_ble_device(conf->blist_device, buff, NULL); + return set_ble_device(conf->blist_device, buff, NULL, ORIGIN_CONFIG); +} + +static int +ble_except_vendor_handler(vector strvec) +{ + char * buff; + + buff = set_value(strvec); + + if (!buff) + return 1; + + return set_ble_device(conf->elist_device, buff, NULL, ORIGIN_CONFIG); } static int @@ -292,7 +376,20 @@ ble_product_handler(vector strvec) if (!buff) return 1; - return set_ble_device(conf->blist_device, NULL, buff); + return set_ble_device(conf->blist_device, NULL, buff, ORIGIN_CONFIG); +} + +static int +ble_except_product_handler(vector strvec) +{ + char * buff; + + buff = set_value(strvec); + + if (!buff) + return 1; + + return set_ble_device(conf->elist_device, NULL, buff, ORIGIN_CONFIG); } /* @@ -585,6 +682,36 @@ hw_minio_handler(vector strvec) return 0; } +static int +hw_pg_timeout_handler(vector strvec) +{ + int pg_timeout; + struct hwentry *hwe = VECTOR_LAST_SLOT(conf->hwtable); + char *buff; + + if (!hwe) + return 1; + + buff = set_value(strvec); + + if (!buff) + return 1; + + if (strlen(buff) == 4 && !strcmp(buff, "none")) + hwe->pg_timeout = -PGTIMEOUT_NONE; + else if (sscanf(buff, "%d", &pg_timeout) == 1 && pg_timeout >= 0) { + if (pg_timeout == 0) + hwe->pg_timeout = -PGTIMEOUT_NONE; + else + hwe->pg_timeout = pg_timeout; + } + else + hwe->pg_timeout = PGTIMEOUT_UNDEF; + + FREE(buff); + return 0; +} + /* * multipaths block handlers */ @@ -777,6 +904,35 @@ mp_minio_handler(vector strvec) return 0; } +static int +mp_pg_timeout_handler(vector strvec) +{ + int pg_timeout; + struct mpentry *mpe = VECTOR_LAST_SLOT(conf->mptable); + char *buff; + + if (!mpe) + return 1; + + buff = set_value(strvec); + + if (!buff) + return 1; + if (strlen(buff) == 4 && !strcmp(buff, "none")) + mpe->pg_timeout = -PGTIMEOUT_NONE; + else if (sscanf(buff, "%d", &pg_timeout) == 1 && pg_timeout >= 0) { + if (pg_timeout == 0) + mpe->pg_timeout = -PGTIMEOUT_NONE; + else + mpe->pg_timeout = pg_timeout; + } + else + mpe->pg_timeout = PGTIMEOUT_UNDEF; + + FREE(buff); + return 0; +} + /* * config file keywords printing */ @@ -896,6 +1052,22 @@ snprint_mp_rr_min_io (char * buff, int len, void * data) } static int +snprint_mp_pg_timeout (char * buff, int len, void * data) +{ + struct mpentry * mpe = (struct mpentry *)data; + + switch (mpe->pg_timeout) { + case PGTIMEOUT_UNDEF: + break; + case -PGTIMEOUT_NONE: + return snprintf(buff, len, "none"); + default: + return snprintf(buff, len, "%i", mpe->pg_timeout); + } + return 0; +} + +static int snprint_hw_vendor (char * buff, int len, void * data) { struct hwentry * hwe = (struct hwentry *)data; @@ -1097,6 +1269,27 @@ snprint_hw_rr_min_io (char * buff, int len, void * data) } static int +snprint_hw_pg_timeout (char * buff, int len, void * data) +{ + struct hwentry * hwe = (struct hwentry *)data; + + if (!hwe->pg_timeout) + return 0; + if (hwe->pg_timeout == conf->pg_timeout) + return 0; + + switch (hwe->pg_timeout) { + case PGTIMEOUT_UNDEF: + break; + case -PGTIMEOUT_NONE: + return snprintf(buff, len, "none"); + default: + return snprintf(buff, len, "%i", hwe->pg_timeout); + } + return 0; +} + +static int snprint_hw_path_checker (char * buff, int len, void * data) { struct hwentry * hwe = (struct hwentry *)data; @@ -1268,6 +1461,23 @@ snprint_def_no_path_retry (char * buff, int len, void * data) } static int +snprint_def_pg_timeout (char * buff, int len, void * data) +{ + if (conf->pg_timeout == DEFAULT_PGTIMEOUT) + return 0; + + switch (conf->pg_timeout) { + case PGTIMEOUT_UNDEF: + break; + case -PGTIMEOUT_NONE: + return snprintf(buff, len, "none"); + default: + return snprintf(buff, len, "%i", conf->pg_timeout); + } + return 0; +} + +static int snprint_def_user_friendly_names (char * buff, int len, void * data) { if (conf->user_friendly_names == DEFAULT_USER_FRIENDLY_NAMES) @@ -1320,6 +1530,7 @@ init_keywords(void) install_keyword("rr_min_io", &def_minio_handler, &snprint_def_rr_min_io); install_keyword("rr_weight", &def_weight_handler, &snprint_def_rr_weight); install_keyword("no_path_retry", &def_no_path_retry_handler, &snprint_def_no_path_retry); + install_keyword("pg_timeout", &def_pg_timeout_handler, &snprint_def_pg_timeout); install_keyword("user_friendly_names", &names_handler, &snprint_def_user_friendly_names); __deprecated install_keyword("default_selector", &def_selector_handler, NULL); __deprecated install_keyword("default_path_grouping_policy", &def_pgpolicy_handler, NULL); @@ -1336,6 +1547,14 @@ init_keywords(void) install_keyword("vendor", &ble_vendor_handler, &snprint_bled_vendor); install_keyword("product", &ble_product_handler, &snprint_bled_product); install_sublevel_end(); + install_keyword_root("blacklist_exceptions", &blacklist_exceptions_handler); + install_keyword("devnode", &ble_except_devnode_handler, &snprint_ble_simple); + install_keyword("wwid", &ble_except_wwid_handler, &snprint_ble_simple); + install_keyword("device", &ble_except_device_handler, NULL); + install_sublevel(); + install_keyword("vendor", &ble_except_vendor_handler, &snprint_bled_vendor); + install_keyword("product", &ble_except_product_handler, &snprint_bled_product); + install_sublevel_end(); #if 0 __deprecated install_keyword_root("devnode_blacklist", &blacklist_handler); @@ -1365,6 +1584,7 @@ init_keywords(void) install_keyword("rr_weight", &hw_weight_handler, &snprint_hw_rr_weight); install_keyword("no_path_retry", &hw_no_path_retry_handler, &snprint_hw_no_path_retry); install_keyword("rr_min_io", &hw_minio_handler, &snprint_hw_rr_min_io); + install_keyword("pg_timeout", &hw_pg_timeout_handler, &snprint_hw_pg_timeout); install_sublevel_end(); install_keyword_root("multipaths", &multipaths_handler); @@ -1378,5 +1598,6 @@ init_keywords(void) install_keyword("rr_weight", &mp_weight_handler, &snprint_mp_rr_weight); install_keyword("no_path_retry", &mp_no_path_retry_handler, &snprint_mp_no_path_retry); install_keyword("rr_min_io", &mp_minio_handler, &snprint_mp_rr_min_io); + install_keyword("pg_timeout", &mp_pg_timeout_handler, &snprint_mp_pg_timeout); install_sublevel_end(); } diff --git a/libmultipath/discovery.c b/libmultipath/discovery.c index cf8289c..c842eb0 100644 --- a/libmultipath/discovery.c +++ b/libmultipath/discovery.c @@ -8,9 +8,8 @@ #include #include #include +#include #include -#include -#include #include @@ -24,6 +23,7 @@ #include "debug.h" #include "propsel.h" #include "sg_include.h" +#include "sysfs.h" #include "discovery.h" struct path * @@ -61,7 +61,8 @@ path_discover (vector pathvec, struct config * conf, char * devname, int flag) if (!devname) return 0; - if (blacklist(conf->blist_devnode, devname)) + if (filter_devnode(conf->blist_devnode, conf->elist_devnode, + devname) > 0) return 0; if(safe_sprintf(path, "%s/block/%s/device", sysfs_path, @@ -70,8 +71,10 @@ path_discover (vector pathvec, struct config * conf, char * devname, int flag) return 1; } - if (!filepresent(path)) + if (strncmp(devname,"cciss",5) && !filepresent(path)) { + condlog(4, "path %s not present", path); return 0; + } pp = find_path_by_dev(pathvec, devname); @@ -86,127 +89,117 @@ path_discover (vector pathvec, struct config * conf, char * devname, int flag) int path_discovery (vector pathvec, struct config * conf, int flag) { - struct dlist * ls; - struct sysfs_class * class; - struct sysfs_class_device * dev; - int r = 1; - - if (!(class = sysfs_open_class("block"))) + DIR *blkdir; + struct dirent *blkdev; + struct stat statbuf; + char devpath[PATH_MAX]; + char *devptr; + int r = 0; + + if (!(blkdir = opendir("/sys/block"))) return 1; - if (!(ls = sysfs_get_class_devices(class))) - goto out; - - r = 0; + strcpy(devpath,"/sys/block"); + while ((blkdev = readdir(blkdir)) != NULL) { + if ((strcmp(blkdev->d_name,".") == 0) || + (strcmp(blkdev->d_name,"..") == 0)) + continue; - dlist_for_each_data(ls, dev, struct sysfs_class_device) - r += path_discover(pathvec, conf, dev->name, flag); + devptr = devpath + 10; + *devptr = '\0'; + strcat(devptr,"/"); + strcat(devptr,blkdev->d_name); + if (stat(devpath, &statbuf) < 0) + continue; -out: - sysfs_close_class(class); - return r; -} + if (S_ISDIR(statbuf.st_mode) == 0) + continue; -/* - * the daemon can race udev upon path add, - * not multipath(8), ran by udev - */ -#if DAEMON -#define WAIT_MAX_SECONDS 5 -#define WAIT_LOOP_PER_SECOND 5 + condlog(4, "Discover device %s", devpath); -static int -wait_for_file (char * filename) -{ - int loop; - struct stat stats; - - loop = WAIT_MAX_SECONDS * WAIT_LOOP_PER_SECOND; - - while (--loop) { - if (stat(filename, &stats) == 0) - return 0; - - if (errno != ENOENT) - return 1; - - usleep(1000 * 1000 / WAIT_LOOP_PER_SECOND); + r += path_discover(pathvec, conf, blkdev->d_name, flag); } - return 1; -} -#else -static int -wait_for_file (char * filename) -{ - return 0; + closedir(blkdir); + condlog(4, "Discovery status %d", r); + return r; } -#endif -#define declare_sysfs_get_str(fname, fmt) \ +#define declare_sysfs_get_str(fname) \ extern int \ -sysfs_get_##fname (char * sysfs_path, char * dev, char * buff, int len) \ +sysfs_get_##fname (struct sysfs_device * dev, char * buff, size_t len) \ { \ - struct sysfs_attribute * attr; \ - char attr_path[SYSFS_PATH_SIZE]; \ + char *attr; \ \ - if (safe_sprintf(attr_path, fmt, sysfs_path, dev)) \ + attr = sysfs_attr_get_value(dev->devpath, #fname); \ + if (!attr) \ return 1; \ \ - if (wait_for_file(attr_path)) \ - return 1; \ -\ - if (!(attr = sysfs_open_attribute(attr_path))) \ - return 1; \ -\ - if (0 > sysfs_read_attribute(attr)) \ - goto out; \ -\ - if (attr->len < 2 || attr->len - 1 > len) \ - goto out; \ -\ - strncpy(buff, attr->value, attr->len - 1); \ - buff[attr->len - 1] = '\0'; \ - sysfs_close_attribute(attr); \ + if (strlcpy(buff, attr, len) != strlen(attr)) \ + return 2; \ return 0; \ -out: \ - sysfs_close_attribute(attr); \ - return 1; \ } -declare_sysfs_get_str(devtype, "%s/block/%s/device/devtype"); -declare_sysfs_get_str(cutype, "%s/block/%s/device/cutype"); -declare_sysfs_get_str(vendor, "%s/block/%s/device/vendor"); -declare_sysfs_get_str(model, "%s/block/%s/device/model"); -declare_sysfs_get_str(rev, "%s/block/%s/device/rev"); -declare_sysfs_get_str(dev, "%s/block/%s/dev"); +declare_sysfs_get_str(devtype); +declare_sysfs_get_str(cutype); +declare_sysfs_get_str(vendor); +declare_sysfs_get_str(model); +declare_sysfs_get_str(rev); int -sysfs_get_size (char * sysfs_path, char * dev, unsigned long long * size) +sysfs_get_dev (struct sysfs_device * dev, char * buff, size_t len) { - struct sysfs_attribute * attr; - char attr_path[SYSFS_PATH_SIZE]; - int r; + char *attr; - if (safe_sprintf(attr_path, "%s/block/%s/size", sysfs_path, dev)) + attr = sysfs_attr_get_value(dev->devpath, "dev"); + if (!attr) { + condlog(3, "%s: no 'dev' attribute in sysfs", dev->kernel); return 1; + } + if (strlcpy(buff, attr, len) != strlen(attr)) { + condlog(3, "%s: overflow in 'dev' attribute", dev->kernel); + return 2; + } + return 0; +} - attr = sysfs_open_attribute(attr_path); +int +sysfs_get_size (struct sysfs_device * dev, unsigned long long * size) +{ + char *attr; + int r; + attr = sysfs_attr_get_value(dev->devpath, "size"); if (!attr) return 1; - if (0 > sysfs_read_attribute(attr)) - goto out; - - r = sscanf(attr->value, "%llu\n", size); - sysfs_close_attribute(attr); + r = sscanf(attr, "%llu\n", size); if (r != 1) return 1; return 0; -out: - sysfs_close_attribute(attr); +} + +int +sysfs_get_fc_nodename (struct sysfs_device * dev, char * node, + unsigned int host, unsigned int channel, + unsigned int target) +{ + char attr_path[SYSFS_PATH_SIZE], *attr; + + if (safe_sprintf(attr_path, + "/class/fc_transport/target%i:%i:%i", + host, channel, target)) { + condlog(0, "attr_path too small"); + return 1; + } + + attr = sysfs_attr_get_value(attr_path, "node_name"); + if (attr) { + strlcpy(node, attr, strlen(attr)); + return 0; + } + return 1; } @@ -216,78 +209,74 @@ out: static int opennode (char * dev, int mode) { - char devpath[FILE_NAME_SIZE]; + char devpath[FILE_NAME_SIZE], *ptr; if (safe_sprintf(devpath, "%s/%s", conf->udev_dir, dev)) { condlog(0, "devpath too small"); return -1; } - - if (wait_for_file(devpath)) { - condlog(3, "failed to open %s", devpath); - return -1; + /* + * Translate '!' into '/' + */ + ptr = devpath; + while ((ptr = strchr(ptr, '!'))) { + *ptr = '/'; + ptr++; } - return open(devpath, mode); } extern int devt2devname (char *devname, char *devt) { - struct dlist * ls; - char attr_path[FILE_NAME_SIZE]; + FILE *fd; + unsigned int tmpmaj, tmpmin, major, minor; + char dev[FILE_NAME_SIZE]; char block_path[FILE_NAME_SIZE]; - struct sysfs_attribute * attr; - struct sysfs_class * class; - struct sysfs_class_device * dev; + struct stat statbuf; - if(safe_sprintf(block_path, "%s/block", sysfs_path)) { - condlog(0, "block_path too small"); + if (sscanf(devt, "%u:%u", &major, &minor) != 2) { + condlog(0, "Invalid device number %s", devt); return 1; } - if (!(class = sysfs_open_class("block"))) - return 1; - - if (!(ls = sysfs_get_class_devices(class))) - goto err; - dlist_for_each_data(ls, dev, struct sysfs_class_device) { - if(safe_sprintf(attr_path, "%s/%s/dev", - block_path, dev->name)) { - condlog(0, "attr_path too small"); - goto err; + if ((fd = fopen("/proc/partitions", "r")) < 0) { + condlog(0, "Cannot open /proc/partitions"); + return 1; + } + + while (!feof(fd)) { + int r = fscanf(fd,"%u %u %*d %s",&tmpmaj, &tmpmin, dev); + if (!r) { + fscanf(fd,"%*s\n"); + continue; } - if (!(attr = sysfs_open_attribute(attr_path))) - goto err; - - if (sysfs_read_attribute(attr)) - goto err1; - - /* discard newline */ - if (attr->len > 1) attr->len--; - - if (strlen(devt) == attr->len && - strncmp(attr->value, devt, attr->len) == 0) { - if(safe_sprintf(attr_path, "%s/%s", - block_path, dev->name)) { - condlog(0, "attr_path too small"); - goto err1; - } - sysfs_get_name_from_path(attr_path, devname, - FILE_NAME_SIZE); - sysfs_close_attribute(attr); - sysfs_close_class(class); - return 0; + if (r != 3) + continue; + + if ((major == tmpmaj) && (minor == tmpmin)) { + sprintf(block_path, "/sys/block/%s", dev); + break; } } -err1: - sysfs_close_attribute(attr); -err: - sysfs_close_class(class); - return 1; + fclose(fd); + + if (strncmp(block_path,"/sys/block", 10)) + return 1; + + if (stat(block_path, &statbuf) < 0) { + condlog(0, "No sysfs entry for %s\n", block_path); + return 1; + } + + if (S_ISDIR(statbuf.st_mode) == 0) { + condlog(0, "sysfs entry %s is not a directory\n", block_path); + return 1; + } + return 0; } -static int +int do_inq(int sg_fd, int cmddt, int evpd, unsigned int pg_op, void *resp, int mx_resp_len, int noisy) { @@ -295,7 +284,7 @@ do_inq(int sg_fd, int cmddt, int evpd, unsigned int pg_op, { INQUIRY_CMD, 0, 0, 0, 0, 0 }; unsigned char sense_b[SENSE_BUFF_LEN]; struct sg_io_hdr io_hdr; - + if (cmddt) inqCmdBlk[1] |= 2; if (evpd) @@ -313,10 +302,10 @@ do_inq(int sg_fd, int cmddt, int evpd, unsigned int pg_op, io_hdr.cmdp = inqCmdBlk; io_hdr.sbp = sense_b; io_hdr.timeout = DEF_TIMEOUT; - + if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) return -1; - + /* treat SG_ERR here to get rid of sg_err.[ch] */ io_hdr.status &= 0x7e; if ((0 == io_hdr.status) && (0 == io_hdr.host_status) && @@ -339,100 +328,64 @@ do_inq(int sg_fd, int cmddt, int evpd, unsigned int pg_op, return -1; } -int -get_serial (char * str, int fd) +static int +get_serial (char * str, int maxlen, int fd) { int len = 0; char buff[MX_ALLOC_LEN + 1] = {0}; if (fd < 0) - return 0; + return 1; if (0 == do_inq(fd, 0, 1, 0x80, buff, MX_ALLOC_LEN, 0)) { len = buff[3]; + if (len >= maxlen) + return 1; if (len > 0) { memcpy(str, buff + 4, len); str[len] = '\0'; } - return 1; + return 0; } - return 0; + return 1; } static int -sysfs_get_bus (char * sysfs_path, struct path * pp) +get_inq (char * vendor, char * product, char * rev, int fd) { - struct sysfs_device *sdev; - char attr_path[FILE_NAME_SIZE]; - char attr_buff[FILE_NAME_SIZE]; - - pp->bus = SYSFS_BUS_UNDEF; + char buff[MX_ALLOC_LEN + 1] = {0}; - /* - * This is ugly : we should be able to do a simple - * get_link("%s/block/%s/device/bus", ...) but it just - * won't work - */ - if(safe_sprintf(attr_path, "%s/block/%s/device", - sysfs_path, pp->dev)) { - condlog(0, "attr_path too small"); - return 1; - } - - if (0 > sysfs_get_link(attr_path, attr_buff, sizeof(attr_buff))) + if (fd < 0) return 1; -#if DAEMON - int loop = WAIT_MAX_SECONDS * WAIT_LOOP_PER_SECOND; - - while (loop--) { - sdev = sysfs_open_device_path(attr_buff); - - if (strlen(sdev->bus)) - break; - - sysfs_close_device(sdev); - usleep(1000 * 1000 / WAIT_LOOP_PER_SECOND); + if (0 == do_inq(fd, 0, 0, 0, buff, MX_ALLOC_LEN, 0)) { + memcpy(vendor, buff + 8, 8); + vendor[8] = '\0'; + memcpy(product, buff + 16, 16); + product[16] = '\0'; + memcpy(rev, buff + 32, 4); + rev[4] = '\0'; + return 0; } -#else - sdev = sysfs_open_device_path(attr_buff); -#endif - - if (!strncmp(sdev->bus, "scsi", 4)) - pp->bus = SYSFS_BUS_SCSI; - else if (!strncmp(sdev->bus, "ide", 3)) - pp->bus = SYSFS_BUS_IDE; - else if (!strncmp(sdev->bus, "ccw", 3)) - pp->bus = SYSFS_BUS_CCW; - else - return 1; - - sysfs_close_device(sdev); - - return 0; + return 1; } static int -scsi_sysfs_pathinfo (struct path * pp) +scsi_sysfs_pathinfo (struct path * pp, struct sysfs_device * parent) { char attr_path[FILE_NAME_SIZE]; - char attr_buff[FILE_NAME_SIZE]; - struct sysfs_attribute * attr; - if (sysfs_get_vendor(sysfs_path, pp->dev, - pp->vendor_id, SCSI_VENDOR_SIZE)) + if (sysfs_get_vendor(parent, pp->vendor_id, SCSI_VENDOR_SIZE)) return 1; condlog(3, "%s: vendor = %s", pp->dev, pp->vendor_id); - if (sysfs_get_model(sysfs_path, pp->dev, - pp->product_id, SCSI_PRODUCT_SIZE)) + if (sysfs_get_model(parent, pp->product_id, SCSI_PRODUCT_SIZE)) return 1; condlog(3, "%s: product = %s", pp->dev, pp->product_id); - if (sysfs_get_rev(sysfs_path, pp->dev, - pp->rev, SCSI_REV_SIZE)) + if (sysfs_get_rev(parent, pp->rev, SCSI_REV_SIZE)) return 1; condlog(3, "%s: rev = %s", pp->dev, pp->rev); @@ -440,20 +393,12 @@ scsi_sysfs_pathinfo (struct path * pp) /* * set the hwe configlet pointer */ - pp->hwe = find_hwe(conf->hwtable, pp->vendor_id, pp->product_id); + pp->hwe = find_hwe(conf->hwtable, pp->vendor_id, pp->product_id, pp->rev); /* * host / bus / target / lun */ - if(safe_sprintf(attr_path, "%s/block/%s/device", - sysfs_path, pp->dev)) { - condlog(0, "attr_path too small"); - return 1; - } - if (0 > sysfs_get_link(attr_path, attr_buff, sizeof(attr_buff))) - return 1; - - basename(attr_buff, attr_path); + basename(parent->devpath, attr_path); sscanf(attr_path, "%i:%i:%i:%i", &pp->sg_id.host_no, @@ -470,35 +415,19 @@ scsi_sysfs_pathinfo (struct path * pp) /* * target node name */ - if(safe_sprintf(attr_path, - "%s/class/fc_transport/target%i:%i:%i/node_name", - sysfs_path, - pp->sg_id.host_no, - pp->sg_id.channel, - pp->sg_id.scsi_id)) { - condlog(0, "attr_path too small"); - return 1; + if(!sysfs_get_fc_nodename(parent, pp->tgt_node_name, + pp->sg_id.host_no, + pp->sg_id.channel, + pp->sg_id.scsi_id)) { + condlog(3, "%s: tgt_node_name = %s", + pp->dev, pp->tgt_node_name); } - if (!(attr = sysfs_open_attribute(attr_path))) - return 0; - - if (sysfs_read_attribute(attr)) - goto err; - - if (attr->len > 0) - strncpy(pp->tgt_node_name, attr->value, attr->len - 1); - - condlog(3, "%s: tgt_node_name = %s", - pp->dev, pp->tgt_node_name); return 0; -err: - sysfs_close_attribute(attr); - return 1; } static int -ccw_sysfs_pathinfo (struct path * pp) +ccw_sysfs_pathinfo (struct path * pp, struct sysfs_device * parent) { char attr_path[FILE_NAME_SIZE]; char attr_buff[FILE_NAME_SIZE]; @@ -507,8 +436,7 @@ ccw_sysfs_pathinfo (struct path * pp) condlog(3, "%s: vendor = %s", pp->dev, pp->vendor_id); - if (sysfs_get_devtype(sysfs_path, pp->dev, - attr_buff, FILE_NAME_SIZE)) + if (sysfs_get_devtype(parent, attr_buff, FILE_NAME_SIZE)) return 1; if (!strncmp(attr_buff, "3370", 4)) { @@ -524,20 +452,12 @@ ccw_sysfs_pathinfo (struct path * pp) /* * set the hwe configlet pointer */ - pp->hwe = find_hwe(conf->hwtable, pp->vendor_id, pp->product_id); + pp->hwe = find_hwe(conf->hwtable, pp->vendor_id, pp->product_id, NULL); /* * host / bus / target / lun - */ - if(safe_sprintf(attr_path, "%s/block/%s/device", - sysfs_path, pp->dev)) { - condlog(0, "attr_path too small"); - return 1; - } - if (0 > sysfs_get_link(attr_path, attr_buff, sizeof(attr_buff))) - return 1; - - basename(attr_buff, attr_path); + */ + basename(parent->devpath, attr_path); pp->sg_id.lun = 0; sscanf(attr_path, "%i.%i.%x", &pp->sg_id.host_no, @@ -554,20 +474,43 @@ ccw_sysfs_pathinfo (struct path * pp) } static int -common_sysfs_pathinfo (struct path * pp) +cciss_sysfs_pathinfo (struct path * pp, struct sysfs_device * dev) { - if (sysfs_get_bus(sysfs_path, pp)) - return 1; + char attr_path[FILE_NAME_SIZE]; - condlog(3, "%s: bus = %i", pp->dev, pp->bus); + /* + * host / bus / target / lun + */ + basename(dev->devpath, attr_path); + pp->sg_id.lun = 0; + pp->sg_id.channel = 0; + sscanf(attr_path, "cciss!c%id%i", + &pp->sg_id.host_no, + &pp->sg_id.scsi_id); + condlog(3, "%s: h:b:t:l = %i:%i:%i:%i", + pp->dev, + pp->sg_id.host_no, + pp->sg_id.channel, + pp->sg_id.scsi_id, + pp->sg_id.lun); + return 0; +} + +static int +common_sysfs_pathinfo (struct path * pp, struct sysfs_device *dev) +{ + char *attr; - if (sysfs_get_dev(sysfs_path, pp->dev, - pp->dev_t, BLK_DEV_SIZE)) + attr = sysfs_attr_get_value(dev->devpath, "dev"); + if (!attr) { + condlog(3, "%s: no 'dev' attribute in sysfs", pp->dev); return 1; + } + strlcpy(pp->dev_t, attr, BLK_DEV_SIZE); condlog(3, "%s: dev_t = %s", pp->dev, pp->dev_t); - if (sysfs_get_size(sysfs_path, pp->dev, &pp->size)) + if (sysfs_get_size(dev, &pp->size)) return 1; condlog(3, "%s: size = %llu", pp->dev, pp->size); @@ -575,19 +518,53 @@ common_sysfs_pathinfo (struct path * pp) return 0; } +struct sysfs_device *sysfs_device_from_path(struct path *pp) +{ + char sysdev[FILE_NAME_SIZE]; + + strlcpy(sysdev,"/block/", FILE_NAME_SIZE); + strlcat(sysdev,pp->dev, FILE_NAME_SIZE); + + return sysfs_device_get(sysdev); +} + extern int sysfs_pathinfo(struct path * pp) { - if (common_sysfs_pathinfo(pp)) + struct sysfs_device *parent; + + pp->sysdev = sysfs_device_from_path(pp); + if (!pp->sysdev) { + condlog(1, "%s: failed to get sysfs information", pp->dev); + return 1; + } + + if (common_sysfs_pathinfo(pp, pp->sysdev)) return 1; + parent = sysfs_device_get_parent(pp->sysdev); + if (!parent) + parent = pp->sysdev; + + condlog(3, "%s: subsystem = %s", pp->dev, parent->subsystem); + + if (!strncmp(parent->subsystem, "scsi",4)) + pp->bus = SYSFS_BUS_SCSI; + if (!strncmp(parent->subsystem, "ccw",3)) + pp->bus = SYSFS_BUS_CCW; + if (!strncmp(pp->dev,"cciss",5)) + pp->bus = SYSFS_BUS_CCISS; + if (pp->bus == SYSFS_BUS_UNDEF) return 0; else if (pp->bus == SYSFS_BUS_SCSI) { - if (scsi_sysfs_pathinfo(pp)) + if (scsi_sysfs_pathinfo(pp, parent)) return 1; } else if (pp->bus == SYSFS_BUS_CCW) { - if (ccw_sysfs_pathinfo(pp)) + if (ccw_sysfs_pathinfo(pp, parent)) + return 1; + } else if (pp->bus == SYSFS_BUS_CCISS) { + if (cciss_sysfs_pathinfo(pp, pp->sysdev)) return 1; } return 0; @@ -597,7 +574,7 @@ static int scsi_ioctl_pathinfo (struct path * pp, int mask) { if (mask & DI_SERIAL) { - get_serial(pp->serial, pp->fd); + get_serial(pp->serial, SERIAL_SIZE, pp->fd); condlog(3, "%s: serial = %s", pp->dev, pp->serial); } @@ -605,16 +582,37 @@ scsi_ioctl_pathinfo (struct path * pp, int mask) } static int +cciss_ioctl_pathinfo (struct path * pp, int mask) +{ + if (mask & DI_SYSFS) { + get_inq(pp->vendor_id, pp->product_id, pp->rev, pp->fd); + condlog(3, "%s: vendor = %s", pp->dev, pp->vendor_id); + condlog(3, "%s: product = %s", pp->dev, pp->product_id); + condlog(3, "%s: revision = %s", pp->dev, pp->rev); + /* + * set the hwe configlet pointer + */ + pp->hwe = find_hwe(conf->hwtable, pp->vendor_id, + pp->product_id, pp->rev); + + } + return 0; +} + +static int get_state (struct path * pp) { struct checker * c = &pp->checker; + if (!pp->mpp) + return 0; + if (!checker_selected(c)) { select_checker(pp); if (!checker_selected(c)) return 1; checker_set_fd(c, pp->fd); - if (checker_init(c)) + if (checker_init(c, &pp->mpp->mpcontext)) return 1; } pp->state = checker_check(c); @@ -636,14 +634,14 @@ get_prio (struct path * pp) pp->getprio_selected = 1; } if (!pp->getprio) { - pp->priority = 1; + pp->priority = PRIO_DEFAULT; } else if (apply_format(pp->getprio, &buff[0], pp)) { condlog(0, "error formatting prio callout command"); - pp->priority = -1; + pp->priority = PRIO_UNDEF; return 1; } else if (execute_program(buff, prio, 16)) { condlog(0, "error calling out %s", buff); - pp->priority = -1; + pp->priority = PRIO_UNDEF; return 1; } else pp->priority = atoi(prio); @@ -689,17 +687,29 @@ pathinfo (struct path *pp, vector hwtable, int mask) if (pp->fd < 0) pp->fd = opennode(pp->dev, O_RDONLY); - if (pp->fd < 0) + if (pp->fd < 0) { + condlog(4, "Couldn't open node for %s: %s", + pp->dev, strerror(errno)); goto blank; + } if (pp->bus == SYSFS_BUS_SCSI && scsi_ioctl_pathinfo(pp, mask)) goto blank; + if (pp->bus == SYSFS_BUS_CCISS && + cciss_ioctl_pathinfo(pp, mask)) + goto blank; + if (mask & DI_CHECKER && get_state(pp)) goto blank; - - if (mask & DI_PRIO && pp->state != PATH_DOWN) + + /* + * Retrieve path priority, even for PATH_DOWN paths if it has never + * been successfully obtained before. + */ + if (mask & DI_PRIO && + (pp->state != PATH_DOWN || pp->priority == PRIO_UNDEF)) get_prio(pp); if (mask & DI_WWID && !strlen(pp->wwid)) diff --git a/libmultipath/discovery.h b/libmultipath/discovery.h index cdc9627..c7cf7e8 100644 --- a/libmultipath/discovery.h +++ b/libmultipath/discovery.h @@ -5,7 +5,6 @@ #define INQUIRY_CMDLEN 6 #define INQUIRY_CMD 0x12 #define SENSE_BUFF_LEN 32 -#define DEF_TIMEOUT 60000 #define RECOVERED_ERROR 0x01 #define MX_ALLOC_LEN 255 #define TUR_CMD_LEN 6 @@ -14,6 +13,10 @@ #define BLKGETSIZE _IO(0x12,96) #endif +#ifndef DEF_TIMEOUT +#define DEF_TIMEOUT 300000 +#endif + /* * exerpt from sg_err.h */ @@ -21,16 +24,10 @@ #define SCSI_COMMAND_TERMINATED 0x22 #define SG_ERR_DRIVER_SENSE 0x08 -int sysfs_get_vendor (char * sysfs_path, char * dev, char * buff, int len); -int sysfs_get_model (char * sysfs_path, char * dev, char * buff, int len); -int sysfs_get_rev (char * sysfs_path, char * dev, char * buff, int len); -int sysfs_get_dev (char * sysfs_path, char * dev, char * buff, int len); - -int sysfs_get_size (char * sysfs_path, char * dev, unsigned long long *); +int sysfs_get_dev (struct sysfs_device * dev, char * buff, size_t len); int path_discovery (vector pathvec, struct config * conf, int flag); void basename (char *, char *); -int get_serial (char * buff, int fd); int do_tur (char *); int devt2devname (char *, char *); int pathinfo (struct path *, vector hwtable, int mask); diff --git a/libmultipath/dmparser.c b/libmultipath/dmparser.c index 2b170c6..631933d 100644 --- a/libmultipath/dmparser.c +++ b/libmultipath/dmparser.c @@ -84,11 +84,14 @@ assemble_map (struct multipath * mp) freechar -= shift; vector_foreach_slot (pgp->paths, pp, j) { - if (mp->rr_weight == RR_WEIGHT_PRIO && pp->priority) - minio *= pp->priority; + int tmp_minio = minio; + + if (mp->rr_weight == RR_WEIGHT_PRIO + && pp->priority > 0) + tmp_minio = minio * pp->priority; shift = snprintf(p, freechar, " %s %d", - pp->dev_t, minio); + pp->dev_t, tmp_minio); if (shift >= freechar) { fprintf(stderr, "mp->params too small\n"); return 1; @@ -117,6 +120,7 @@ disassemble_map (vector pathvec, char * params, struct multipath * mpp) int num_pg_args = 0; int num_paths = 0; int num_paths_args = 0; + int def_minio = 0; struct path * pp; struct pathgroup * pgp; @@ -305,12 +309,15 @@ disassemble_map (vector pathvec, char * params, struct multipath * mpp) if (k == 0 && !strncmp(mpp->selector, "round-robin", 11)) { p += get_word(p, &word); - mpp->minio = atoi(word); + def_minio = atoi(word); - if (mpp->rr_weight) - mpp->minio /= mpp->rr_weight; + if (mpp->rr_weight == RR_WEIGHT_PRIO + && pp->priority > 0) + def_minio /= pp->priority; FREE(word); + if (def_minio != mpp->minio) + mpp->minio = def_minio; } else p += get_word(p, NULL); diff --git a/libmultipath/hwtable.c b/libmultipath/hwtable.c index 5c7d625..ef761d7 100644 --- a/libmultipath/hwtable.c +++ b/libmultipath/hwtable.c @@ -12,13 +12,34 @@ * Tuning suggestions on these parameters should go to * dm-devel@redhat.com * - * You are welcome to claim maintainership over a controler + * You are welcome to claim maintainership over a controller * family. Please mail the currently enlisted maintainer and * the upstream package maintainer. */ static struct hwentry default_hw[] = { /* - * StorageWorks controler family + * Apple controller family + * + * Maintainer : Shyam Sundar + * Mail : g.shyamsundar@yahoo.co.in + */ + { + .vendor = "APPLE*", + .product = "Xserve RAID ", + .getuid = DEFAULT_GETUID, + .getprio = NULL, + .features = DEFAULT_FEATURES, + .hwhandler = DEFAULT_HWHANDLER, + .selector = DEFAULT_SELECTOR, + .pgpolicy = MULTIBUS, + .pgfailback = FAILBACK_UNDEF, + .rr_weight = RR_WEIGHT_NONE, + .no_path_retry = NO_PATH_RETRY_UNDEF, + .minio = DEFAULT_MINIO, + .checker_name = DEFAULT_CHECKER, + }, + /* + * StorageWorks controller family * * Maintainer : Christophe Varoqui * Mail : christophe.varoqui@free.fr @@ -42,11 +63,11 @@ static struct hwentry default_hw[] = { .vendor = "DEC", .product = "HSG80", .getuid = DEFAULT_GETUID, - .getprio = NULL, - .features = DEFAULT_FEATURES, + .getprio = "/sbin/mpath_prio_hp_sw /dev/%n", + .features = "1 queue_if_no_path", .hwhandler = "1 hp_sw", .selector = DEFAULT_SELECTOR, - .pgpolicy = GROUP_BY_SERIAL, + .pgpolicy = GROUP_BY_PRIO, .pgfailback = FAILBACK_UNDEF, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_UNDEF, @@ -54,14 +75,30 @@ static struct hwentry default_hw[] = { .checker_name = HP_SW, }, { - .vendor = "{COMPAQ,HP}", - .product = "{MSA,HSV}1*", + .vendor = "HP", + .product = "A6189A", .getuid = DEFAULT_GETUID, .getprio = NULL, .features = DEFAULT_FEATURES, + .hwhandler = DEFAULT_HWHANDLER, + .selector = DEFAULT_SELECTOR, + .pgpolicy = MULTIBUS, + .pgfailback = FAILBACK_UNDEF, + .rr_weight = RR_WEIGHT_NONE, + .no_path_retry = NO_PATH_RETRY_UNDEF, + .minio = DEFAULT_MINIO, + .checker_name = READSECTOR0, + }, + { + /* MSA 1000/MSA1500 EVA 3000/5000 with old firmware */ + .vendor = "(COMPAQ|HP)", + .product = "(MSA|HSV)1.0.*", + .getuid = DEFAULT_GETUID, + .getprio = "/sbin/mpath_prio_hp_sw /dev/%n", + .features = "1 queue_if_no_path", .hwhandler = "1 hp_sw", .selector = DEFAULT_SELECTOR, - .pgpolicy = GROUP_BY_SERIAL, + .pgpolicy = GROUP_BY_PRIO, .pgfailback = FAILBACK_UNDEF, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_UNDEF, @@ -69,24 +106,58 @@ static struct hwentry default_hw[] = { .checker_name = HP_SW, }, { + /* MSA 1000/1500 with new firmware */ .vendor = "HP", - .product = "HSV2*", + .product = "MSA VOLUME", .getuid = DEFAULT_GETUID, - .getprio = NULL, + .getprio = "/sbin/mpath_prio_alua /dev/%n", .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, - .pgpolicy = MULTIBUS, - .pgfailback = FAILBACK_UNDEF, + .pgpolicy = GROUP_BY_PRIO, + .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = DEFAULT_MINIO, - .checker_name = READSECTOR0, + .checker_name = TUR, + }, + { + /* EVA 3000/5000 with new firmware */ + .vendor = "(COMPAQ|HP)", + .product = "(MSA|HSV)1.1.*", + .getuid = DEFAULT_GETUID, + .getprio = "/sbin/mpath_prio_alua /dev/%n", + .features = DEFAULT_FEATURES, + .hwhandler = DEFAULT_HWHANDLER, + .selector = DEFAULT_SELECTOR, + .pgpolicy = GROUP_BY_PRIO, + .pgfailback = -FAILBACK_IMMEDIATE, + .rr_weight = RR_WEIGHT_NONE, + .no_path_retry = NO_PATH_RETRY_UNDEF, + .minio = DEFAULT_MINIO, + .checker_name = TUR, }, { + /* EVA 4000/6000/8000 */ .vendor = "HP", - .product = "DF[456]00", + .product = "HSV2.*", .getuid = DEFAULT_GETUID, + .getprio = "/sbin/mpath_prio_alua /dev/%n", + .features = DEFAULT_FEATURES, + .hwhandler = DEFAULT_HWHANDLER, + .selector = DEFAULT_SELECTOR, + .pgpolicy = GROUP_BY_PRIO, + .pgfailback = -FAILBACK_IMMEDIATE, + .rr_weight = RR_WEIGHT_NONE, + .no_path_retry = NO_PATH_RETRY_UNDEF, + .minio = DEFAULT_MINIO, + .checker_name = TUR, + }, + { + /* HP Smart Array */ + .vendor = "HP", + .product = "LOGICAL VOLUME.*", + .getuid = "/lib/udev/scsi_id -n -g -u -s /block/%n", .getprio = NULL, .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, @@ -96,10 +167,10 @@ static struct hwentry default_hw[] = { .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = DEFAULT_MINIO, - .checker_name = READSECTOR0, + .checker_name = TUR, }, /* - * DDN controler family + * DDN controller family * * Maintainer : Christophe Varoqui * Mail : christophe.varoqui@free.fr @@ -117,10 +188,10 @@ static struct hwentry default_hw[] = { .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = DEFAULT_MINIO, - .checker_name = READSECTOR0, + .checker_name = DIRECTIO, }, /* - * EMC / Clariion controler family + * EMC / Clariion controller family * * Maintainer : Edward Goggin, EMC * Mail : egoggin@emc.com @@ -128,7 +199,7 @@ static struct hwentry default_hw[] = { { .vendor = "EMC", .product = "SYMMETRIX", - .getuid = "/sbin/scsi_id -g -u -ppre-spc3-83 -s /block/%n", + .getuid = "/lib/udev/scsi_id -g -u -ppre-spc3-83 -s /block/%n", .getprio = NULL, .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, @@ -142,7 +213,7 @@ static struct hwentry default_hw[] = { }, { .vendor = "DGC", - .product = "*", + .product = ".*", .bl_product = "LUNZ", .getuid = DEFAULT_GETUID, .getprio = "/sbin/mpath_prio_emc /dev/%n", @@ -157,7 +228,7 @@ static struct hwentry default_hw[] = { .checker_name = EMC_CLARIION, }, /* - * Fujitsu controler family + * Fujitsu controller family * * Maintainer : Christophe Varoqui * Mail : christophe.varoqui@free.fr @@ -178,17 +249,17 @@ static struct hwentry default_hw[] = { .checker_name = READSECTOR0, }, /* - * Hitachi controler family + * Hitachi controller family * - * Maintainer : Christophe Varoqui - * Mail : christophe.varoqui@free.fr + * Maintainer : Matthias Rudolph + * Mail : matthias.rudolph@hds.com */ { - .vendor = "HITACHI", - .product = "{A6189A,OPEN-}", + .vendor = "(HITACHI|HP)", + .product = "OPEN-.*", .getuid = DEFAULT_GETUID, .getprio = NULL, - .features = DEFAULT_FEATURES, + .features = "1 queue_if_no_path", .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, .pgpolicy = MULTIBUS, @@ -196,12 +267,27 @@ static struct hwentry default_hw[] = { .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = DEFAULT_MINIO, - .checker_name = READSECTOR0, + .checker_name = TUR, + }, + { + .vendor = "HITACHI", + .product = "DF.*", + .getuid = DEFAULT_GETUID, + .getprio = "/sbin/mpath_prio_hds_modular /dev/%n", + .features = "1 queue_if_no_path", + .hwhandler = DEFAULT_HWHANDLER, + .selector = DEFAULT_SELECTOR, + .pgpolicy = GROUP_BY_PRIO, + .pgfailback = -FAILBACK_IMMEDIATE, + .rr_weight = RR_WEIGHT_NONE, + .no_path_retry = NO_PATH_RETRY_UNDEF, + .minio = DEFAULT_MINIO, + .checker_name = TUR, }, /* - * IBM controler family + * IBM controller family * - * Maintainer : Hannes Reinecke, Suse + * Maintainer : Hannes Reinecke, SuSE * Mail : hare@suse.de */ { @@ -224,7 +310,23 @@ static struct hwentry default_hw[] = { .vendor = "IBM", .product = "1742", .getuid = DEFAULT_GETUID, - .getprio = "/sbin/mpath_prio_tpc /dev/%n", + .getprio = "/sbin/mpath_prio_rdac /dev/%n", + .features = DEFAULT_FEATURES, + .hwhandler = DEFAULT_HWHANDLER, + .selector = DEFAULT_SELECTOR, + .pgpolicy = GROUP_BY_PRIO, + .pgfailback = -FAILBACK_IMMEDIATE, + .rr_weight = RR_WEIGHT_NONE, + .no_path_retry = NO_PATH_RETRY_UNDEF, + .minio = DEFAULT_MINIO, + .checker_name = TUR, + }, + { + /* IBM Netfinity Fibre Channel RAID Controller Unit */ + .vendor = "IBM", + .product = "3526", + .getuid = DEFAULT_GETUID, + .getprio = "/sbin/mpath_prio_rdac /dev/%n", .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, @@ -254,7 +356,7 @@ static struct hwentry default_hw[] = { { /* IBM ESS F20 aka Shark */ .vendor = "IBM", - .product = "2105F20", + .product = "2105(800|F20)", .getuid = DEFAULT_GETUID, .getprio = NULL, .features = "1 queue_if_no_path", @@ -268,9 +370,9 @@ static struct hwentry default_hw[] = { .checker_name = TUR, }, { - /* IBM DS6000 / SAN Volume Controller */ + /* IBM DS6000 */ .vendor = "IBM", - .product = "{1750500,2145}", + .product = "1750500", .getuid = DEFAULT_GETUID, .getprio = "/sbin/mpath_prio_alua /dev/%n", .features = "1 queue_if_no_path", @@ -292,7 +394,7 @@ static struct hwentry default_hw[] = { .features = "1 queue_if_no_path", .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, - .pgpolicy = GROUP_BY_SERIAL, + .pgpolicy = MULTIBUS, .pgfailback = FAILBACK_UNDEF, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_UNDEF, @@ -300,12 +402,29 @@ static struct hwentry default_hw[] = { .checker_name = TUR, }, { + /* IBM SAN Volume Controller */ + .vendor = "IBM", + .product = "2145", + .getuid = DEFAULT_GETUID, + .getprio = "/sbin/mpath_prio_alua /dev/%n", + .features = "1 queue_if_no_path", + .hwhandler = DEFAULT_HWHANDLER, + .selector = DEFAULT_SELECTOR, + .pgpolicy = GROUP_BY_PRIO, + .pgfailback = -FAILBACK_IMMEDIATE, + .rr_weight = RR_WEIGHT_NONE, + .no_path_retry = NO_PATH_RETRY_UNDEF, + .minio = DEFAULT_MINIO, + .checker_name = TUR, + }, + { /* IBM S/390 ECKD DASD */ .vendor = "IBM", .product = "S/390 DASD ECKD", - .getuid = "/sbin/dasdview -j /dev/%n", + .bl_product = "S/390.*", + .getuid = "/sbin/dasdinfo -u -b %n", .getprio = NULL, - .features = DEFAULT_FEATURES, + .features = "1 queue_if_no_path", .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, .pgpolicy = MULTIBUS, @@ -315,38 +434,59 @@ static struct hwentry default_hw[] = { .minio = DEFAULT_MINIO, .checker_name = DIRECTIO, }, - /* - * NETAPP controler family + /* + * NETAPP controller family * - * Maintainer : Igor Feoktistov - * Mail : igorf@netapp.com + * Maintainer : Dave Wysochanski + * Mail : davidw@netapp.com */ { .vendor = "NETAPP", - .product = "LUN", + .product = "LUN.*", .getuid = DEFAULT_GETUID, .getprio = "/sbin/mpath_prio_netapp /dev/%n", .features = "1 queue_if_no_path", .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, .pgpolicy = GROUP_BY_PRIO, - .pgfailback = FAILBACK_UNDEF, + .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_UNDEF, - .minio = DEFAULT_MINIO, + .minio = 128, + .checker_name = READSECTOR0, + }, + /* + * IBM NSeries (NETAPP) controller family + * + * Maintainer : Dave Wysochanski + * Mail : davidw@netapp.com + */ + { + .vendor = "IBM", + .product = "Nseries.*", + .getuid = DEFAULT_GETUID, + .getprio = "/sbin/mpath_prio_netapp /dev/%n", + .features = "1 queue_if_no_path", + .hwhandler = DEFAULT_HWHANDLER, + .selector = DEFAULT_SELECTOR, + .pgpolicy = GROUP_BY_PRIO, + .pgfailback = -FAILBACK_IMMEDIATE, + .rr_weight = RR_WEIGHT_NONE, + .no_path_retry = NO_PATH_RETRY_UNDEF, + .minio = 128, .checker_name = READSECTOR0, }, /* - * Pillar Data controler family + * Pillar Data controller family * - * Maintainer : Christophe Varoqui - * Mail : christophe.varoqui@free.fr + * Maintainer : Srinivasan Ramani + * Mail : sramani@pillardata.com */ { .vendor = "Pillar", - .product = "Axiom 500", + .product = "Axiom.*", .getuid = DEFAULT_GETUID, - .getprio = "/sbin/mpath_prio_alua %d", + .getprio = "/sbin/mpath_prio_alua %n", .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, @@ -382,16 +522,31 @@ static struct hwentry default_hw[] = { .vendor = "SGI", .product = "TP9[45]00", .getuid = DEFAULT_GETUID, - .getprio = "/sbin/mpath_prio_tpc /dev/%n", + .getprio = "/sbin/mpath_prio_rdac /dev/%n", .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, - .no_path_retry = NO_PATH_RETRY_UNDEF, + .no_path_retry = NO_PATH_RETRY_QUEUE, .minio = DEFAULT_MINIO, - .checker_name = TUR, + .checker_name = RDAC, + }, + { + .vendor = "SGI", + .product = "IS.*", + .getuid = DEFAULT_GETUID, + .getprio = "/sbin/mpath_prio_rdac /dev/%n", + .features = DEFAULT_FEATURES, + .hwhandler = DEFAULT_HWHANDLER, + .selector = DEFAULT_SELECTOR, + .pgpolicy = GROUP_BY_PRIO, + .pgfailback = -FAILBACK_IMMEDIATE, + .rr_weight = RR_WEIGHT_NONE, + .no_path_retry = NO_PATH_RETRY_QUEUE, + .minio = DEFAULT_MINIO, + .checker_name = RDAC, }, /* * STK arrays @@ -403,7 +558,7 @@ static struct hwentry default_hw[] = { .vendor = "STK", .product = "OPENstorage D280", .getuid = DEFAULT_GETUID, - .getprio = "/sbin/mpath_prio_tpc /dev/%n", + .getprio = "/sbin/mpath_prio_rdac /dev/%n", .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, @@ -422,7 +577,7 @@ static struct hwentry default_hw[] = { */ { .vendor = "SUN", - .product = "{StorEdge 3510,T4}", + .product = "(StorEdge 3510|T4)", .getuid = DEFAULT_GETUID, .getprio = NULL, .features = DEFAULT_FEATURES, diff --git a/libmultipath/list.h b/libmultipath/list.h new file mode 100644 index 0000000..8626630 --- /dev/null +++ b/libmultipath/list.h @@ -0,0 +1,289 @@ +/* + * Copied from the Linux kernel source tree, version 2.6.0-test1. + * + * Licensed under the GPL v2 as per the whole kernel source tree. + * + */ + +#ifndef _LIST_H +#define _LIST_H + +/** + * container_of - cast a member of a structure out to the containing structure + * + * @ptr: the pointer to the member. + * @type: the type of the container struct this is embedded in. + * @member: the name of the member within the struct. + * + */ +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) + +/* + * These are non-NULL pointers that will result in page faults + * under normal circumstances, used to verify that nobody uses + * non-initialized list entries. + */ +#define LIST_POISON1 ((void *) 0x00100100) +#define LIST_POISON2 ((void *) 0x00200200) + +/* + * Simple doubly linked list implementation. + * + * Some of the internal functions ("__xxx") are useful when + * manipulating whole lists rather than single entries, as + * sometimes we already know the next/prev entries and we can + * generate better code by using them directly rather than + * using the generic single-entry routines. + */ + +struct list_head { + struct list_head *next, *prev; +}; + +#define LIST_HEAD_INIT(name) { &(name), &(name) } + +#define LIST_HEAD(name) \ + struct list_head name = LIST_HEAD_INIT(name) + +#define INIT_LIST_HEAD(ptr) do { \ + (ptr)->next = (ptr); (ptr)->prev = (ptr); \ +} while (0) + +/* + * Insert a new entry between two known consecutive entries. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_add(struct list_head *new, + struct list_head *prev, + struct list_head *next) +{ + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; +} + +/** + * list_add - add a new entry + * @new: new entry to be added + * @head: list head to add it after + * + * Insert a new entry after the specified head. + * This is good for implementing stacks. + */ +static inline void list_add(struct list_head *new, struct list_head *head) +{ + __list_add(new, head, head->next); +} + +/** + * list_add_tail - add a new entry + * @new: new entry to be added + * @head: list head to add it before + * + * Insert a new entry before the specified head. + * This is useful for implementing queues. + */ +static inline void list_add_tail(struct list_head *new, struct list_head *head) +{ + __list_add(new, head->prev, head); +} + +/* + * Delete a list entry by making the prev/next entries + * point to each other. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_del(struct list_head * prev, struct list_head * next) +{ + next->prev = prev; + prev->next = next; +} + +/** + * list_del - deletes entry from list. + * @entry: the element to delete from the list. + * Note: list_empty on entry does not return true after this, the entry is + * in an undefined state. + */ +static inline void list_del(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + entry->next = LIST_POISON1; + entry->prev = LIST_POISON2; +} + +/** + * list_del_init - deletes entry from list and reinitialize it. + * @entry: the element to delete from the list. + */ +static inline void list_del_init(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + INIT_LIST_HEAD(entry); +} + +/** + * list_move - delete from one list and add as another's head + * @list: the entry to move + * @head: the head that will precede our entry + */ +static inline void list_move(struct list_head *list, struct list_head *head) +{ + __list_del(list->prev, list->next); + list_add(list, head); +} + +/** + * list_move_tail - delete from one list and add as another's tail + * @list: the entry to move + * @head: the head that will follow our entry + */ +static inline void list_move_tail(struct list_head *list, + struct list_head *head) +{ + __list_del(list->prev, list->next); + list_add_tail(list, head); +} + +/** + * list_empty - tests whether a list is empty + * @head: the list to test. + */ +static inline int list_empty(struct list_head *head) +{ + return head->next == head; +} + +static inline void __list_splice(struct list_head *list, + struct list_head *head) +{ + struct list_head *first = list->next; + struct list_head *last = list->prev; + struct list_head *at = head->next; + + first->prev = head; + head->next = first; + + last->next = at; + at->prev = last; +} + +/** + * list_splice - join two lists + * @list: the new list to add. + * @head: the place to add it in the first list. + */ +static inline void list_splice(struct list_head *list, struct list_head *head) +{ + if (!list_empty(list)) + __list_splice(list, head); +} + +/** + * list_splice_init - join two lists and reinitialise the emptied list. + * @list: the new list to add. + * @head: the place to add it in the first list. + * + * The list at @list is reinitialised + */ +static inline void list_splice_init(struct list_head *list, + struct list_head *head) +{ + if (!list_empty(list)) { + __list_splice(list, head); + INIT_LIST_HEAD(list); + } +} + +/** + * list_entry - get the struct for this entry + * @ptr: the &struct list_head pointer. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_struct within the struct. + */ +#define list_entry(ptr, type, member) \ + container_of(ptr, type, member) + +/** + * list_for_each - iterate over a list + * @pos: the &struct list_head to use as a loop counter. + * @head: the head for your list. + */ +#define list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); \ + pos = pos->next) + +/** + * __list_for_each - iterate over a list + * @pos: the &struct list_head to use as a loop counter. + * @head: the head for your list. + * + * This variant differs from list_for_each() in that it's the + * simplest possible list iteration code. + * Use this for code that knows the list to be very short (empty + * or 1 entry) most of the time. + */ +#define __list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) + +/** + * list_for_each_prev - iterate over a list backwards + * @pos: the &struct list_head to use as a loop counter. + * @head: the head for your list. + */ +#define list_for_each_prev(pos, head) \ + for (pos = (head)->prev; pos != (head); pos = pos->prev) + +/** + * list_for_each_safe - iterate over a list safe against removal of list entry + * @pos: the &struct list_head to use as a loop counter. + * @n: another &struct list_head to use as temporary storage + * @head: the head for your list. + */ +#define list_for_each_safe(pos, n, head) \ + for (pos = (head)->next, n = pos->next; pos != (head); \ + pos = n, n = pos->next) + +/** + * list_for_each_entry - iterate over list of given type + * @pos: the type * to use as a loop counter. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry(pos, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.next, typeof(*pos), member)) + +/** + * list_for_each_entry_reverse - iterate backwards over list of given type. + * @pos: the type * to use as a loop counter. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_reverse(pos, head, member) \ + for (pos = list_entry((head)->prev, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.prev, typeof(*pos), member)) + +/** + * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry + * @pos: the type * to use as a loop counter. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_safe(pos, n, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member), \ + n = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, typeof(*n), member)) + +#endif /* _LIST_H */ diff --git a/libmultipath/lock.c b/libmultipath/lock.c new file mode 100644 index 0000000..0ca8783 --- /dev/null +++ b/libmultipath/lock.c @@ -0,0 +1,8 @@ +#include +#include "lock.h" + +void cleanup_lock (void * data) +{ + unlock((pthread_mutex_t *)data); +} + diff --git a/libmultipath/lock.h b/libmultipath/lock.h new file mode 100644 index 0000000..6afecda --- /dev/null +++ b/libmultipath/lock.h @@ -0,0 +1,22 @@ +#ifndef _LOCK_H +#define _LOCK_H + +#ifdef LCKDBG +#define lock(a) \ + fprintf(stderr, "%s:%s(%i) lock %p\n", __FILE__, __FUNCTION__, __LINE__, a); \ + pthread_mutex_lock(a) +#define unlock(a) \ + fprintf(stderr, "%s:%s(%i) unlock %p\n", __FILE__, __FUNCTION__, __LINE__, a); \ + pthread_mutex_unlock(a) +#define lock_cleanup_pop(a) \ + fprintf(stderr, "%s:%s(%i) unlock %p\n", __FILE__, __FUNCTION__, __LINE__, a); \ + pthread_cleanup_pop(1); +#else +#define lock(a) pthread_mutex_lock(a) +#define unlock(a) pthread_mutex_unlock(a) +#define lock_cleanup_pop(a) pthread_cleanup_pop(1); +#endif + +void cleanup_lock (void * data); + +#endif /* _LOCK_H */ diff --git a/libmultipath/log.c b/libmultipath/log.c index 8b339d7..90e4d1f 100644 --- a/libmultipath/log.c +++ b/libmultipath/log.c @@ -118,6 +118,11 @@ int log_enqueue (int prio, const char * fmt, va_list ap) /* not enough space on tail : rewind */ if (la->head <= la->tail && len > (la->end - la->tail)) { logdbg(stderr, "enqueue: rewind tail to %p\n", la->tail); + if (la->head == la->start ) { + logdbg(stderr, "enqueue: can not rewind tail, drop msg\n"); + la->tail = lastmsg; + return 1; /* can't reuse */ + } la->tail = la->start; if (la->empty) diff --git a/libmultipath/log_pthread.c b/libmultipath/log_pthread.c index f98cfa4..5a82b6a 100644 --- a/libmultipath/log_pthread.c +++ b/libmultipath/log_pthread.c @@ -12,7 +12,7 @@ #include "log_pthread.h" #include "log.h" -void log_safe (int prio, char * fmt, va_list ap) +void log_safe (int prio, const char * fmt, va_list ap) { pthread_mutex_lock(logq_lock); //va_start(ap, fmt); diff --git a/libmultipath/log_pthread.h b/libmultipath/log_pthread.h index 7c902c7..2b18f59 100644 --- a/libmultipath/log_pthread.h +++ b/libmultipath/log_pthread.h @@ -7,7 +7,7 @@ pthread_mutex_t *logq_lock; pthread_mutex_t *logev_lock; pthread_cond_t *logev_cond; -void log_safe(int prio, char * fmt, va_list ap); +void log_safe(int prio, const char * fmt, va_list ap); void log_thread_start(void); void log_thread_stop(void); diff --git a/libmultipath/parser.c b/libmultipath/parser.c index 9b0b5c2..f9c555e 100644 --- a/libmultipath/parser.c +++ b/libmultipath/parser.c @@ -25,6 +25,13 @@ /* local vars */ static int sublevel = 0; vector keywords = NULL; +vector *keywords_addr = NULL; + +void set_current_keywords (vector *k) +{ + keywords_addr = k; + keywords = NULL; +} int keyword_alloc(vector keywords, char *string, int (*handler) (vector), @@ -53,7 +60,10 @@ keyword_alloc(vector keywords, char *string, int (*handler) (vector), int install_keyword_root(char *string, int (*handler) (vector)) { - return keyword_alloc(keywords, string, handler, NULL); + int r = keyword_alloc(keywords, string, handler, NULL); + if (!r) + *keywords_addr = keywords; + return r; } void @@ -100,6 +110,9 @@ free_keywords(vector keywords) struct keyword *keyword; int i; + if (!keywords) + return; + for (i = 0; i < VECTOR_SIZE(keywords); i++) { keyword = VECTOR_SLOT(keywords, i); if (keyword->sub) diff --git a/libmultipath/parser.h b/libmultipath/parser.h index f0fdd94..95d4e6f 100644 --- a/libmultipath/parser.h +++ b/libmultipath/parser.h @@ -76,6 +76,7 @@ extern void *set_value(vector strvec); extern int process_stream(vector keywords); extern int init_data(char *conf_file, void (*init_keywords) (void)); extern struct keyword * find_keyword(vector v, char * name); +void set_current_keywords (vector *k); int snprint_keyword(char *buff, int len, char *fmt, struct keyword *kw, void *data); diff --git a/libmultipath/pgpolicies.h b/libmultipath/pgpolicies.h index 66c3c29..1f010a3 100644 --- a/libmultipath/pgpolicies.h +++ b/libmultipath/pgpolicies.h @@ -9,7 +9,7 @@ #define POLICY_NAME_SIZE 32 -/* Storage controlers capabilities */ +/* Storage controllers capabilities */ enum iopolicies { IOPOLICY_UNDEF, FAILOVER, diff --git a/libmultipath/print.c b/libmultipath/print.c index 6cc63e2..01a157a 100644 --- a/libmultipath/print.c +++ b/libmultipath/print.c @@ -5,6 +5,8 @@ #include #include #include +#include +#include #include @@ -13,11 +15,12 @@ #include "structs_vec.h" #include "print.h" #include "dmparser.h" -#include "configure.h" #include "config.h" +#include "configure.h" #include "pgpolicies.h" #include "defaults.h" #include "parser.h" +#include "blacklist.h" #define MAX(x,y) (x > y) ? x : y #define TAIL (line + len - 1 - c) @@ -52,16 +55,26 @@ snprint_uint (char * buff, size_t len, unsigned int val) static int snprint_size (char * buff, size_t len, unsigned long long size) { - if (size < (1 << 11)) - return snprintf(buff, len, "%llu kB", size >> 1); - else if (size < (1 << 21)) - return snprintf(buff, len, "%llu MB", size >> 11); - else if (size < (1 << 31)) - return snprintf(buff, len, "%llu GB", size >> 21); + float s = (float)(size >> 1); /* start with KB */ + char fmt[6] = {}; + char units[] = {'K','M','G','T','P'}; + char *u = units; + + while (s >= 1024 && *u != 'P') { + s = s / 1024; + u++; + } + if (s < 10) + snprintf(fmt, 6, "%%.1f%c", *u); else - return snprintf(buff, len, "%llu TB", size >> 31); + snprintf(fmt, 6, "%%.0f%c", *u); + + return snprintf(buff, len, fmt, s); } +/* + * multipath info printing functions + */ static int snprint_name (char * buff, size_t len, struct multipath * mpp) { @@ -211,6 +224,10 @@ static int snprint_action (char * buff, size_t len, struct multipath * mpp) { switch (mpp->action) { + case ACT_REJECT: + return snprint_str(buff, len, ACT_REJECT_STR); + case ACT_RENAME: + return snprint_str(buff, len, ACT_RENAME_STR); case ACT_RELOAD: return snprint_str(buff, len, ACT_RELOAD_STR); case ACT_CREATE: @@ -222,6 +239,9 @@ snprint_action (char * buff, size_t len, struct multipath * mpp) } } +/* + * path info printing functions + */ static int snprint_path_uuid (char * buff, size_t len, struct path * pp) { @@ -292,8 +312,8 @@ snprint_dm_path_state (char * buff, size_t len, struct path * pp) static int snprint_vpr (char * buff, size_t len, struct path * pp) { - return snprintf(buff, len, "%s/%s/%s", - pp->vendor_id, pp->product_id, pp->rev); + return snprintf(buff, len, "%s,%s", + pp->vendor_id, pp->product_id); } static int @@ -496,7 +516,7 @@ snprint_multipath (char * line, int len, char * format, char * f = format; /* format string cursor */ int fwd; struct multipath_data * data; - char buff[MAX_FIELD_LEN]; + char buff[MAX_FIELD_LEN] = {}; do { if (!TAIL) @@ -515,6 +535,7 @@ snprint_multipath (char * line, int len, char * format, data->snprint(buff, MAX_FIELD_LEN, mpp); PRINT(c, TAIL, buff); PAD(data->width); + buff[0] = '\0'; } while (*f++); line[c - line - 1] = '\n'; @@ -631,7 +652,7 @@ snprint_pathgroup (char * line, int len, char * format, extern void print_multipath_topology (struct multipath * mpp, int verbosity) { - char buff[MAX_LINE_LEN * MAX_LINES]; + char buff[MAX_LINE_LEN * MAX_LINES] = {}; snprint_multipath_topology(&buff[0], MAX_LINE_LEN * MAX_LINES, mpp, verbosity); @@ -662,7 +683,10 @@ snprint_multipath_topology (char * buff, int len, struct multipath * mpp, c += sprintf(c, "%%n"); if (strncmp(mpp->alias, mpp->wwid, WWID_SIZE)) - c += sprintf(c, " (%%w)"); + c += sprintf(c, " (%%w) "); + + c += sprintf(c, "%%d "); + c += snprint_vpr(c, 24, first_path(mpp)); fwd += snprint_multipath(buff + fwd, len - fwd, style, mpp); if (fwd > len) @@ -833,6 +857,110 @@ snprint_defaults (char * buff, int len) } +static int +snprint_blacklist_group (char *buff, int len, int *fwd, vector *vec) +{ + int threshold = MAX_LINE_LEN; + struct blentry * ble; + int pos; + int i; + + pos = *fwd; + if (!VECTOR_SIZE(*vec)) { + if ((len - pos - threshold) <= 0) + return 0; + pos += snprintf(buff + pos, len - pos, " \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 +1023,137 @@ snprint_blacklist (char * buff, int len) if (fwd > len) return len; } + fwd += snprintf(buff + fwd, len - fwd, "}\n"); + if (fwd > len) + return len; + return fwd; +} +extern int +snprint_blacklist_except (char * buff, int len) +{ + int i; + struct blentry * ele; + struct blentry_device * eled; + int fwd = 0; + struct keyword *rootkw; + struct keyword *kw; + + rootkw = find_keyword(NULL, "blacklist_exceptions"); + if (!rootkw) + return 0; + + fwd += snprintf(buff + fwd, len - fwd, "blacklist_exceptions {\n"); + if (fwd > len) + return len; + + vector_foreach_slot (conf->elist_devnode, ele, i) { + kw = find_keyword(rootkw->sub, "devnode"); + if (!kw) + return 0; + fwd += snprint_keyword(buff + fwd, len - fwd, "\t%k %v\n", + kw, ele); + if (fwd > len) + return len; + } + vector_foreach_slot (conf->elist_wwid, ele, i) { + kw = find_keyword(rootkw->sub, "wwid"); + if (!kw) + return 0; + fwd += snprint_keyword(buff + fwd, len - fwd, "\t%k %v\n", + kw, ele); + if (fwd > len) + return len; + } + rootkw = find_keyword(rootkw->sub, "device"); + if (!rootkw) + return 0; + + vector_foreach_slot (conf->elist_device, eled, i) { + fwd += snprintf(buff + fwd, len - fwd, "\tdevice {\n"); + if (fwd > len) + return len; + kw = find_keyword(rootkw->sub, "vendor"); + if (!kw) + return 0; + fwd += snprint_keyword(buff + fwd, len - fwd, "\t\t%k %v\n", + kw, eled); + if (fwd > len) + return len; + kw = find_keyword(rootkw->sub, "product"); + if (!kw) + return 0; + fwd += snprint_keyword(buff + fwd, len - fwd, "\t\t%k %v\n", + kw, eled); + if (fwd > len) + return len; + fwd += snprintf(buff + fwd, len - fwd, "\t}\n"); + if (fwd > len) + return len; + } fwd += snprintf(buff + fwd, len - fwd, "}\n"); if (fwd > len) return len; return fwd; - +} + +extern int +snprint_devices (char * buff, int len, struct vectors *vecs) +{ + DIR *blkdir; + struct dirent *blkdev; + struct stat statbuf; + char devpath[PATH_MAX]; + char *devptr; + int threshold = MAX_LINE_LEN; + int fwd = 0; + int r; + + struct path * pp; + + if (!(blkdir = opendir("/sys/block"))) + return 1; + + if ((len - fwd - threshold) <= 0) + return len; + fwd += snprintf(buff + fwd, len - fwd, "available block devices:\n"); + + strcpy(devpath,"/sys/block"); + devptr = devpath + 10; + while ((blkdev = readdir(blkdir)) != NULL) { + if ((strcmp(blkdev->d_name,".") == 0) || + (strcmp(blkdev->d_name,"..") == 0)) + continue; + + strcat(devptr,blkdev->d_name); + if (stat(devptr, &statbuf) < 0) + continue; + + if (S_ISDIR(statbuf.st_mode) == 0) + continue; + + if ((len - fwd - threshold) <= 0) + return len; + + fwd += snprintf(buff + fwd, len - fwd, " %s", devpath); + pp = find_path_by_dev(vecs->pathvec, devpath); + if (!pp) { + r = filter_devnode(conf->blist_devnode, + conf->elist_devnode, devpath); + if (r > 0) + fwd += snprintf(buff + fwd, len - fwd, + " (blacklisted)"); + else if (r < 0) + fwd += snprintf(buff + fwd, len - fwd, + " (whitelisted)"); + } + fwd += snprintf(buff + fwd, len - fwd, "\n"); + } + closedir(blkdir); + + if (fwd > len) + return len; + return fwd; } extern int diff --git a/libmultipath/print.h b/libmultipath/print.h index 3dde45d..73c2f63 100644 --- a/libmultipath/print.h +++ b/libmultipath/print.h @@ -42,6 +42,9 @@ int snprint_multipath_topology (char *, int, struct multipath * mpp, int verbosity); int snprint_defaults (char *, int); int snprint_blacklist (char *, int); +int snprint_blacklist_except (char *, int); +int snprint_blacklist_report (char *, int); +int snprint_devices (char *, int, struct vectors *); int snprint_hwtable (char *, int, vector); int snprint_mptable (char *, int, vector); diff --git a/libmultipath/propsel.c b/libmultipath/propsel.c index 79cee8b..45a3728 100644 --- a/libmultipath/propsel.c +++ b/libmultipath/propsel.c @@ -15,6 +15,7 @@ #include "pgpolicies.h" #include "alias.h" #include "defaults.h" +#include "devmapper.h" pgpolicyfn *pgpolicies[] = { NULL, @@ -41,7 +42,7 @@ select_rr_weight (struct multipath * mp) } if (mp->hwe && mp->hwe->rr_weight) { mp->rr_weight = mp->hwe->rr_weight; - condlog(3, "%s: rr_weight = %i (controler setting)", + condlog(3, "%s: rr_weight = %i (controller setting)", mp->alias, mp->rr_weight); return 0; } @@ -68,7 +69,7 @@ select_pgfailback (struct multipath * mp) } if (mp->hwe && mp->hwe->pgfailback != FAILBACK_UNDEF) { mp->pgfailback = mp->hwe->pgfailback; - condlog(3, "%s: pgfailback = %i (controler setting)", + condlog(3, "%s: pgfailback = %i (controller setting)", mp->alias, mp->pgfailback); return 0; } @@ -112,7 +113,7 @@ select_pgpolicy (struct multipath * mp) mp->pgpolicyfn = pgpolicies[mp->pgpolicy]; get_pgpolicy_name(pgpolicy_name, POLICY_NAME_SIZE, mp->pgpolicy); - condlog(3, "%s: pgpolicy = %s (controler setting)", + condlog(3, "%s: pgpolicy = %s (controller setting)", mp->alias, pgpolicy_name); return 0; } @@ -144,7 +145,7 @@ select_selector (struct multipath * mp) } if (mp->hwe && mp->hwe->selector) { mp->selector = mp->hwe->selector; - condlog(3, "%s: selector = %s (controler setting)", + condlog(3, "%s: selector = %s (controller setting)", mp->alias, mp->selector); return 0; } @@ -164,6 +165,16 @@ select_alias (struct multipath * mp) if (conf->user_friendly_names) mp->alias = get_user_friendly_alias(mp->wwid, conf->bindings_file); + if (mp->alias == NULL){ + char *alias; + if ((alias = MALLOC(WWID_SIZE)) != NULL){ + if (dm_get_name(mp->wwid, DEFAULT_TARGET, + alias) == 1) + mp->alias = alias; + else + FREE(alias); + } + } if (mp->alias == NULL) mp->alias = mp->wwid; } @@ -176,7 +187,7 @@ select_features (struct multipath * mp) { if (mp->hwe && mp->hwe->features) { mp->features = mp->hwe->features; - condlog(3, "%s: features = %s (controler setting)", + condlog(3, "%s: features = %s (controller setting)", mp->alias, mp->features); return 0; } @@ -191,7 +202,7 @@ select_hwhandler (struct multipath * mp) { if (mp->hwe && mp->hwe->hwhandler) { mp->hwhandler = mp->hwe->hwhandler; - condlog(3, "%s: hwhandler = %s (controler setting)", + condlog(3, "%s: hwhandler = %s (controller setting)", mp->alias, mp->hwhandler); return 0; } @@ -208,7 +219,7 @@ select_checker(struct path *pp) if (pp->hwe && pp->hwe->checker) { checker_get(c, pp->hwe->checker); - condlog(3, "%s: path checker = %s (controler setting)", + condlog(3, "%s: path checker = %s (controller setting)", pp->dev, checker_name(c)); return 0; } @@ -229,7 +240,7 @@ select_getuid (struct path * pp) { if (pp->hwe && pp->hwe->getuid) { pp->getuid = pp->hwe->getuid; - condlog(3, "%s: getuid = %s (controler setting)", + condlog(3, "%s: getuid = %s (controller setting)", pp->dev, pp->getuid); return 0; } @@ -250,7 +261,7 @@ select_getprio (struct path * pp) { if (pp->hwe && pp->hwe->getprio) { pp->getprio = pp->hwe->getprio; - condlog(3, "%s: getprio = %s (controler setting)", + condlog(3, "%s: getprio = %s (controller setting)", pp->dev, pp->getprio); return 0; } @@ -276,7 +287,7 @@ select_no_path_retry(struct multipath *mp) } if (mp->hwe && mp->hwe->no_path_retry != NO_PATH_RETRY_UNDEF) { mp->no_path_retry = mp->hwe->no_path_retry; - condlog(3, "%s: no_path_retry = %i (controler setting)", + condlog(3, "%s: no_path_retry = %i (controller setting)", mp->alias, mp->no_path_retry); return 0; } @@ -303,7 +314,7 @@ select_minio (struct multipath * mp) } if (mp->hwe && mp->hwe->minio) { mp->minio = mp->hwe->minio; - condlog(3, "%s: minio = %i (controler setting)", + condlog(3, "%s: minio = %i (controller setting)", mp->alias, mp->minio); return 0; } @@ -319,3 +330,41 @@ select_minio (struct multipath * mp) return 0; } +extern int +select_pg_timeout(struct multipath *mp) +{ + if (mp->mpe && mp->mpe->pg_timeout != PGTIMEOUT_UNDEF) { + mp->pg_timeout = mp->mpe->pg_timeout; + if (mp->pg_timeout > 0) + condlog(3, "%s: pg_timeout = %d (multipath setting)", + mp->alias, mp->pg_timeout); + else + condlog(3, "%s: pg_timeout = NONE (multipath setting)", + mp->alias); + return 0; + } + if (mp->hwe && mp->hwe->pg_timeout != PGTIMEOUT_UNDEF) { + mp->pg_timeout = mp->hwe->pg_timeout; + if (mp->pg_timeout > 0) + condlog(3, "%s: pg_timeout = %d (controller setting)", + mp->alias, mp->pg_timeout); + else + condlog(3, "%s: pg_timeout = NONE (controller setting)", + mp->alias); + return 0; + } + if (conf->pg_timeout != PGTIMEOUT_UNDEF) { + mp->pg_timeout = conf->pg_timeout; + if (mp->pg_timeout > 0) + condlog(3, "%s: pg_timeout = %d (config file default)", + mp->alias, mp->pg_timeout); + else + condlog(3, + "%s: pg_timeout = NONE (config file default)", + mp->alias); + return 0; + } + mp->pg_timeout = PGTIMEOUT_UNDEF; + condlog(3, "pg_timeout = NONE (internal default)"); + return 0; +} diff --git a/libmultipath/propsel.h b/libmultipath/propsel.h index f66a598..afd1f88 100644 --- a/libmultipath/propsel.h +++ b/libmultipath/propsel.h @@ -9,4 +9,5 @@ int select_checker(struct path *pp); int select_getuid (struct path * pp); int select_getprio (struct path * pp); int select_no_path_retry(struct multipath *mp); +int select_pg_timeout(struct multipath *mp); int select_minio(struct multipath *mp); diff --git a/libmultipath/structs.c b/libmultipath/structs.c index 024e790..d36eaef 100644 --- a/libmultipath/structs.c +++ b/libmultipath/structs.c @@ -16,6 +16,7 @@ #include "debug.h" #include "structs_vec.h" #include "blacklist.h" +#include "waiter.h" struct path * alloc_path (void) @@ -30,6 +31,7 @@ alloc_path (void) pp->sg_id.scsi_id = -1; pp->sg_id.lun = -1; pp->fd = -1; + pp->priority = PRIO_UNDEF; } return pp; } @@ -115,9 +117,10 @@ alloc_multipath (void) mpp = (struct multipath *)MALLOC(sizeof(struct multipath)); - if (mpp) + if (mpp) { mpp->bestpg = 1; - + mpp->mpcontext = NULL; + } return mpp; } @@ -178,6 +181,7 @@ free_multipath (struct multipath * mpp, int free_paths) free_pathvec(mpp->paths, free_paths); free_pgvec(mpp->pg, free_paths); + FREE_PTR(mpp->mpcontext); FREE(mpp); } @@ -365,3 +369,10 @@ pathcount (struct multipath * mpp, int state) return count; } + +struct path * +first_path (struct multipath * mpp) +{ + struct pathgroup * pgp = VECTOR_SLOT(mpp->pg, 0); + return VECTOR_SLOT(pgp->paths, 0); +} diff --git a/libmultipath/structs.h b/libmultipath/structs.h index 2b96cfb..f821f87 100644 --- a/libmultipath/structs.h +++ b/libmultipath/structs.h @@ -1,14 +1,17 @@ #ifndef _STRUCTS_H #define _STRUCTS_H -#define WWID_SIZE 64 -#define SERIAL_SIZE 17 +#define WWID_SIZE 128 +#define SERIAL_SIZE 64 #define NODE_NAME_SIZE 19 #define PATH_STR_SIZE 16 #define PARAMS_SIZE 1024 #define FILE_NAME_SIZE 256 #define CALLOUT_MAX_SIZE 128 #define BLK_DEV_SIZE 33 +#define PATH_SIZE 512 +#define NAME_SIZE 128 + #define SCSI_VENDOR_SIZE 9 #define SCSI_PRODUCT_SIZE 17 @@ -18,6 +21,9 @@ #define NO_PATH_RETRY_FAIL -1 #define NO_PATH_RETRY_QUEUE -2 +#define PRIO_UNDEF -1 +#define PRIO_DEFAULT 1 + enum free_path_switch { KEEP_PATHS, FREE_PATHS @@ -40,6 +46,7 @@ enum sysfs_buses { SYSFS_BUS_SCSI, SYSFS_BUS_IDE, SYSFS_BUS_CCW, + SYSFS_BUS_CCISS, }; enum pathstates { @@ -55,6 +62,11 @@ enum pgstates { PGSTATE_ACTIVE }; +enum pgtimeouts { + PGTIMEOUT_UNDEF, + PGTIMEOUT_NONE +}; + struct scsi_idlun { int dev_id; int host_unique_id; @@ -78,9 +90,19 @@ struct scsi_dev { int host_no; }; +struct sysfs_device { + struct sysfs_device *parent; /* parent device */ + char devpath[PATH_SIZE]; + char subsystem[NAME_SIZE]; /* $class, $bus, drivers, module */ + char kernel[NAME_SIZE]; /* device instance name */ + char kernel_number[NAME_SIZE]; + char driver[NAME_SIZE]; /* device driver name */ +}; + struct path { char dev[FILE_NAME_SIZE]; char dev_t[BLK_DEV_SIZE]; + struct sysfs_device *sysdev; struct scsi_idlun scsi_id; struct sg_id sg_id; char wwid[WWID_SIZE]; @@ -127,6 +149,7 @@ struct multipath { int no_path_retry; /* number of retries after all paths are down */ int retry_tick; /* remaining times for retries */ int minio; + int pg_timeout; unsigned long long size; vector paths; vector pg; @@ -142,7 +165,7 @@ struct multipath { struct mpentry * mpe; struct hwentry * hwe; - /* daemon store a data blob for DM event waiter threads */ + /* threads */ void * waiter; /* stats */ @@ -151,6 +174,9 @@ struct multipath { unsigned int stat_map_loads; unsigned int stat_total_queueing_time; unsigned int stat_queueing_timeouts; + + /* checkers shared data */ + void * mpcontext; }; struct pathgroup { @@ -183,10 +209,11 @@ struct multipath * find_mp_by_minor (vector mp, int minor); struct path * find_path_by_devt (vector pathvec, char * devt); struct path * find_path_by_dev (vector pathvec, char * dev); +struct path * first_path (struct multipath * mpp); int pathcountgr (struct pathgroup *, int); int pathcount (struct multipath *, int); -char sysfs_path[FILE_NAME_SIZE]; +extern char sysfs_path[PATH_SIZE]; -#endif +#endif /* _STRUCTS_H */ diff --git a/libmultipath/structs_vec.c b/libmultipath/structs_vec.c index 86bf2a5..1cc6028 100644 --- a/libmultipath/structs_vec.c +++ b/libmultipath/structs_vec.c @@ -13,8 +13,9 @@ #include "dmparser.h" #include "config.h" #include "propsel.h" +#include "sysfs.h" #include "discovery.h" - +#include "waiter.h" /* * creates or updates mpp->paths reading mpp->pg @@ -58,8 +59,8 @@ adopt_paths (vector pathvec, struct multipath * mpp) vector_foreach_slot (pathvec, pp, i) { if (!strncmp(mpp->wwid, pp->wwid, WWID_SIZE)) { - condlog(3, "%s ownership set to %s", - pp->dev_t, mpp->alias); + condlog(3, "%s: ownership set to %s", + pp->dev, mpp->alias); pp->mpp = mpp; if (!mpp->paths && !(mpp->paths = vector_alloc())) @@ -96,7 +97,7 @@ orphan_paths (vector pathvec, struct multipath * mpp) vector_foreach_slot (pathvec, pp, i) { if (pp->mpp == mpp) { - condlog(4, "%s is orphaned", pp->dev_t); + condlog(4, "%s: orphaned", pp->dev); orphan_path(pp); } } @@ -117,6 +118,8 @@ remove_map (struct multipath * mpp, struct vectors * vecs, { int i; + condlog(4, "%s: remove multipath map", mpp->alias); + /* * stop the DM event waiter thread */ @@ -244,8 +247,17 @@ extern int setup_multipath (struct vectors * vecs, struct multipath * mpp) { retry: - if (dm_get_info(mpp->alias, &mpp->dmi)) + if (dm_get_info(mpp->alias, &mpp->dmi)) { + /* Error accessing table */ + condlog(3, "%s: cannot access table", mpp->alias); + goto out; + } + + if (!dm_map_present(mpp->alias)) { + /* Table has been removed */ + condlog(3, "%s: table does not exist", mpp->alias); goto out; + } set_multipath_wwid(mpp); mpp->mpe = find_mpe(mpp->wwid); @@ -269,6 +281,7 @@ retry: #endif goto retry; } + condlog(0, "%s: failed to setup multipath", mpp->alias); goto out; } @@ -277,10 +290,10 @@ retry: select_rr_weight(mpp); select_pgfailback(mpp); set_no_path_retry(mpp); + select_pg_timeout(mpp); return 0; out: - condlog(0, "%s: failed to setup multipath", mpp->alias); remove_map(mpp, vecs, NULL, 1); return 1; } @@ -361,10 +374,10 @@ verify_paths(struct multipath * mpp, struct vectors * vecs, vector rpvec) /* * see if path is in sysfs */ - if (!pp->dev || sysfs_get_dev(sysfs_path, - pp->dev, pp->dev_t, BLK_DEV_SIZE)) { + if (!pp->sysdev || sysfs_get_dev(pp->sysdev, + pp->dev_t, BLK_DEV_SIZE)) { condlog(0, "%s: failed to access path %s", mpp->alias, - pp->dev ? pp->dev : pp->dev_t); + pp->sysdev ? pp->sysdev->devpath : pp->dev_t); count++; vector_del_slot(mpp->paths, i); i--; @@ -382,3 +395,88 @@ verify_paths(struct multipath * mpp, struct vectors * vecs, vector rpvec) return count; } +int update_multipath (struct vectors *vecs, char *mapname) +{ + struct multipath *mpp; + struct pathgroup *pgp; + struct path *pp; + int i, j; + + mpp = find_mp_by_alias(vecs->mpvec, mapname); + + if (!mpp) { + condlog(3, "%s: multipath map not found\n", mapname); + return 2; + } + + free_pgvec(mpp->pg, KEEP_PATHS); + mpp->pg = NULL; + + if (setup_multipath(vecs, mpp)) + return 1; /* mpp freed in setup_multipath */ + + /* + * compare checkers states with DM states + */ + vector_foreach_slot (mpp->pg, pgp, i) { + vector_foreach_slot (pgp->paths, pp, j) { + if (pp->dmstate != PSTATE_FAILED) + continue; + + if (pp->state != PATH_DOWN) { + int oldstate = pp->state; + condlog(2, "%s: mark as failed", pp->dev_t); + mpp->stat_path_failures++; + pp->state = PATH_DOWN; + if (oldstate == PATH_UP || + oldstate == PATH_GHOST) + update_queue_mode_del_path(mpp); + + /* + * if opportune, + * schedule the next check earlier + */ + if (pp->tick > conf->checkint) + pp->tick = conf->checkint; + } + } + } + + return 0; +} + +/* + * mpp->no_path_retry: + * -2 (QUEUE) : queue_if_no_path enabled, never turned off + * -1 (FAIL) : fail_if_no_path + * 0 (UNDEF) : nothing + * >0 : queue_if_no_path enabled, turned off after polling n times + */ +void update_queue_mode_del_path(struct multipath *mpp) +{ + if (--mpp->nr_active == 0 && mpp->no_path_retry > 0) { + /* + * Enter retry mode. + * meaning of +1: retry_tick may be decremented in + * checkerloop before starting retry. + */ + mpp->stat_queueing_timeouts++; + mpp->retry_tick = mpp->no_path_retry * conf->checkint + 1; + condlog(1, "%s: Entering recovery mode: max_retries=%d", + mpp->alias, mpp->no_path_retry); + } + condlog(2, "%s: remaining active paths: %d", mpp->alias, mpp->nr_active); +} + +void update_queue_mode_add_path(struct multipath *mpp) +{ + if (mpp->nr_active++ == 0 && mpp->no_path_retry > 0) { + /* come back to normal mode from retry mode */ + mpp->retry_tick = 0; + dm_queue_if_no_path(mpp->alias, 1); + condlog(2, "%s: queue_if_no_path enabled", mpp->alias); + condlog(1, "%s: Recovered to normal mode", mpp->alias); + } + condlog(2, "%s: remaining active paths: %d", mpp->alias, mpp->nr_active); +} + diff --git a/libmultipath/structs_vec.h b/libmultipath/structs_vec.h index 348e9e5..81d9eaa 100644 --- a/libmultipath/structs_vec.h +++ b/libmultipath/structs_vec.h @@ -9,17 +9,6 @@ struct vectors { vector mpvec; }; -#if DAEMON -struct event_thread { - struct dm_task *dmt; - pthread_t thread; - int event_nr; - char mapname[WWID_SIZE]; - struct vectors *vecs; - struct multipath *mpp; -}; -#endif - typedef void (stop_waiter_thread_func) (struct multipath *, struct vectors *); typedef int (start_waiter_thread_func) (struct multipath *, struct vectors *); @@ -44,5 +33,8 @@ struct multipath * add_map_without_path (struct vectors * vecs, start_waiter_thread_func *start_waiter); struct multipath * add_map_with_path (struct vectors * vecs, struct path * pp, int add_vec); +int update_multipath (struct vectors *vecs, char *mapname); +void update_queue_mode_del_path(struct multipath *mpp); +void update_queue_mode_add_path(struct multipath *mpp); #endif /* _STRUCTS_VEC_H */ diff --git a/libmultipath/sysfs.c b/libmultipath/sysfs.c new file mode 100644 index 0000000..b9621ac --- /dev/null +++ b/libmultipath/sysfs.c @@ -0,0 +1,460 @@ +/* + * Copyright (C) 2005-2006 Kay Sievers + * + * 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", len); + dbg("sysfs_path='%s'", sysfs_path); + + INIT_LIST_HEAD(&attr_list); + INIT_LIST_HEAD(&sysfs_dev_list); + return 0; +} + +void sysfs_cleanup(void) +{ + struct sysfs_attr *attr_loop; + struct sysfs_attr *attr_temp; + + struct sysfs_dev *sysdev_loop; + struct sysfs_dev *sysdev_temp; + + list_for_each_entry_safe(attr_loop, attr_temp, &attr_list, node) { + list_del(&attr_loop->node); + free(attr_loop); + } + + list_for_each_entry_safe(sysdev_loop, sysdev_temp, &sysfs_dev_list, node) { + free(sysdev_loop); + } +} + +void sysfs_device_set_values(struct sysfs_device *dev, const char *devpath, + const char *subsystem, const char *driver) +{ + char *pos; + + strlcpy(dev->devpath, devpath, sizeof(dev->devpath)); + if (subsystem != NULL) + strlcpy(dev->subsystem, subsystem, sizeof(dev->subsystem)); + if (driver != NULL) + strlcpy(dev->driver, driver, sizeof(dev->driver)); + + /* set kernel name */ + pos = strrchr(dev->devpath, '/'); + if (pos == NULL) + return; + strlcpy(dev->kernel, &pos[1], sizeof(dev->kernel)); + dbg("kernel='%s'", dev->kernel); + + /* some devices have '!' in their name, change that to '/' */ + pos = dev->kernel; + while (pos[0] != '\0') { + if (pos[0] == '!') + pos[0] = '/'; + pos++; + } + + /* get kernel number */ + pos = &dev->kernel[strlen(dev->kernel)]; + while (isdigit(pos[-1])) + pos--; + strlcpy(dev->kernel_number, pos, sizeof(dev->kernel_number)); + dbg("kernel_number='%s'", dev->kernel_number); +} + +int sysfs_resolve_link(char *devpath, size_t size) +{ + char link_path[PATH_SIZE]; + char link_target[PATH_SIZE]; + int len; + int i; + int back; + + strlcpy(link_path, sysfs_path, sizeof(link_path)); + strlcat(link_path, devpath, sizeof(link_path)); + len = readlink(link_path, link_target, sizeof(link_target)); + if (len <= 0) + return -1; + link_target[len] = '\0'; + dbg("path link '%s' points to '%s'", devpath, link_target); + + for (back = 0; strncmp(&link_target[back * 3], "../", 3) == 0; back++) + ; + dbg("base '%s', tail '%s', back %i", devpath, &link_target[back * 3], back); + for (i = 0; i <= back; i++) { + char *pos = strrchr(devpath, '/'); + + if (pos == NULL) + return -1; + pos[0] = '\0'; + } + dbg("after moving back '%s'", devpath); + strlcat(devpath, "/", size); + strlcat(devpath, &link_target[back * 3], size); + return 0; +} + +struct sysfs_device *sysfs_device_get(const char *devpath) +{ + char path[PATH_SIZE]; + char devpath_real[PATH_SIZE]; + struct sysfs_device *dev = NULL; + struct sysfs_dev *sysdev_loop, *sysdev; + struct stat statbuf; + char link_path[PATH_SIZE]; + char link_target[PATH_SIZE]; + int len; + char *pos; + + dbg("open '%s'", devpath); + strlcpy(devpath_real, devpath, sizeof(devpath_real)); + remove_trailing_chars(devpath_real, '/'); + + /* if we got a link, resolve it to the real device */ + strlcpy(path, sysfs_path, sizeof(path)); + strlcat(path, devpath_real, sizeof(path)); + if (lstat(path, &statbuf) != 0) { + /* if stat fails look in the cache */ + dbg("stat '%s' failed: %s", path, strerror(errno)); + list_for_each_entry(sysdev_loop, &sysfs_dev_list, node) { + if (strcmp(sysdev_loop->dev.devpath, devpath_real) == 0) { + dbg("found vanished dev in cache '%s'", sysdev_loop->dev.devpath); + return &sysdev_loop->dev; + } + } + return NULL; + } + if (S_ISLNK(statbuf.st_mode)) { + if (sysfs_resolve_link(devpath_real, sizeof(devpath_real)) != 0) + return NULL; + + } + + list_for_each_entry(sysdev_loop, &sysfs_dev_list, node) { + if (strcmp(sysdev_loop->dev.devpath, devpath_real) == 0) { + dbg("found dev in cache '%s'", sysdev_loop->dev.devpath); + dev = &sysdev_loop->dev; + } + } + if(!dev) { + /* it is a new device */ + dbg("new device '%s'", devpath_real); + sysdev = malloc(sizeof(struct sysfs_dev)); + if (sysdev == NULL) + return NULL; + memset(sysdev, 0x00, sizeof(struct sysfs_dev)); + list_add(&sysdev->node, &sysfs_dev_list); + dev = &sysdev->dev; + } + + sysfs_device_set_values(dev, devpath_real, NULL, NULL); + + /* get subsystem name */ + strlcpy(link_path, sysfs_path, sizeof(link_path)); + strlcat(link_path, dev->devpath, sizeof(link_path)); + strlcat(link_path, "/subsystem", sizeof(link_path)); + len = readlink(link_path, link_target, sizeof(link_target)); + if (len > 0) { + /* get subsystem from "subsystem" link */ + link_target[len] = '\0'; + dbg("subsystem link '%s' points to '%s'", link_path, link_target); + pos = strrchr(link_target, '/'); + if (pos != NULL) + strlcpy(dev->subsystem, &pos[1], sizeof(dev->subsystem)); + } else if (strncmp(dev->devpath, "/class/", 7) == 0) { + /* get subsystem from class dir */ + strlcpy(dev->subsystem, &dev->devpath[7], sizeof(dev->subsystem)); + pos = strchr(dev->subsystem, '/'); + if (pos != NULL) + pos[0] = '\0'; + else + dev->subsystem[0] = '\0'; + } else if (strncmp(dev->devpath, "/block/", 7) == 0) { + strlcpy(dev->subsystem, "block", sizeof(dev->subsystem)); + } else if (strncmp(dev->devpath, "/devices/", 9) == 0) { + /* get subsystem from "bus" link */ + strlcpy(link_path, sysfs_path, sizeof(link_path)); + strlcat(link_path, dev->devpath, sizeof(link_path)); + strlcat(link_path, "/bus", sizeof(link_path)); + len = readlink(link_path, link_target, sizeof(link_target)); + if (len > 0) { + link_target[len] = '\0'; + dbg("bus link '%s' points to '%s'", link_path, link_target); + pos = strrchr(link_target, '/'); + if (pos != NULL) + strlcpy(dev->subsystem, &pos[1], sizeof(dev->subsystem)); + } + } else if (strstr(dev->devpath, "/drivers/") != NULL) { + strlcpy(dev->subsystem, "drivers", sizeof(dev->subsystem)); + } else if (strncmp(dev->devpath, "/module/", 8) == 0) { + strlcpy(dev->subsystem, "module", sizeof(dev->subsystem)); + } + + /* get driver name */ + strlcpy(link_path, sysfs_path, sizeof(link_path)); + strlcat(link_path, dev->devpath, sizeof(link_path)); + strlcat(link_path, "/driver", sizeof(link_path)); + len = readlink(link_path, link_target, sizeof(link_target)); + if (len > 0) { + link_target[len] = '\0'; + dbg("driver link '%s' points to '%s'", link_path, link_target); + pos = strrchr(link_target, '/'); + if (pos != NULL) + strlcpy(dev->driver, &pos[1], sizeof(dev->driver)); + } + return dev; +} + +struct sysfs_device *sysfs_device_get_parent(struct sysfs_device *dev) +{ + char parent_devpath[PATH_SIZE]; + char *pos; + + dbg("open '%s'", dev->devpath); + + /* look if we already know the parent */ + if (dev->parent != NULL) + return dev->parent; + + /* requesting a parent is only valid for devices */ + if ((strncmp(dev->devpath, "/devices/", 9) != 0) && + (strncmp(dev->devpath, "/subsystem/", 11) != 0) && + (strncmp(dev->devpath, "/class/", 7) != 0) && + (strncmp(dev->devpath, "/block/", 7) != 0)) + return NULL; + + strlcpy(parent_devpath, dev->devpath, sizeof(parent_devpath)); + dbg("'%s'", parent_devpath); + + /* strip last element */ + pos = strrchr(parent_devpath, '/'); + if (pos == NULL || pos == parent_devpath) + return NULL; + pos[0] = '\0'; + + /* are we at the top level of /devices */ + if (strcmp(parent_devpath, "/devices") == 0) { + dbg("/devices top level"); + return NULL; + } + + /* at the subsystems top level we want to follow the old-style "device" link */ + if (strncmp(parent_devpath, "/subsystem", 10) == 0) { + pos = strrchr(parent_devpath, '/'); + if (pos == &parent_devpath[10] || pos == parent_devpath || strcmp(pos, "/devices") == 0) { + dbg("/subsystem top level, look for device link"); + goto device_link; + } + } + if (strncmp(parent_devpath, "/class", 6) == 0) { + pos = strrchr(parent_devpath, '/'); + if (pos == &parent_devpath[6] || pos == parent_devpath) { + dbg("/class top level, look for device link"); + goto device_link; + } + } + if (strcmp(parent_devpath, "/block") == 0) { + dbg("/block top level, look for device link"); + goto device_link; + } + + /* get parent and remember it */ + dev->parent = sysfs_device_get(parent_devpath); + return dev->parent; + +device_link: + strlcpy(parent_devpath, dev->devpath, sizeof(parent_devpath)); + strlcat(parent_devpath, "/device", sizeof(parent_devpath)); + if (sysfs_resolve_link(parent_devpath, sizeof(parent_devpath)) != 0) + return NULL; + + /* get parent and remember it */ + dev->parent = sysfs_device_get(parent_devpath); + return dev->parent; +} + +struct sysfs_device *sysfs_device_get_parent_with_subsystem(struct sysfs_device *dev, const char *subsystem) +{ + struct sysfs_device *dev_parent; + + dev_parent = sysfs_device_get_parent(dev); + while (dev_parent != NULL) { + if (strcmp(dev_parent->subsystem, subsystem) == 0) + return dev_parent; + dev_parent = sysfs_device_get_parent(dev_parent); + } + return NULL; +} + +void sysfs_device_put(struct sysfs_device *dev) +{ + struct sysfs_dev *sysdev_loop; + + list_for_each_entry(sysdev_loop, &sysfs_dev_list, node) { + if (&sysdev_loop->dev == dev) { + dbg("removed dev '%s' from cache", + sysdev_loop->dev.devpath); + list_del(&sysdev_loop->node); + free(sysdev_loop); + return; + } + } + dbg("dev '%s' not found in cache", + sysdev_loop->dev.devpath); + + return; +} + +char *sysfs_attr_get_value(const char *devpath, const char *attr_name) +{ + char path_full[PATH_SIZE]; + const char *path; + char value[NAME_SIZE]; + struct sysfs_attr *attr_loop; + struct sysfs_attr *attr = NULL; + struct stat statbuf; + int fd; + ssize_t size; + size_t sysfs_len; + + dbg("open '%s'/'%s'", devpath, attr_name); + sysfs_len = strlcpy(path_full, sysfs_path, sizeof(path_full)); + path = &path_full[sysfs_len]; + strlcat(path_full, devpath, sizeof(path_full)); + strlcat(path_full, "/", sizeof(path_full)); + strlcat(path_full, attr_name, sizeof(path_full)); + + /* look for attribute in cache */ + list_for_each_entry(attr_loop, &attr_list, node) { + if (strcmp(attr_loop->path, path) == 0) { + dbg("found in cache '%s'", attr_loop->path); + attr = attr_loop; + } + } + if (!attr) { + /* store attribute in cache */ + dbg("new uncached attribute '%s'", path_full); + attr = malloc(sizeof(struct sysfs_attr)); + if (attr == NULL) + return NULL; + memset(attr, 0x00, sizeof(struct sysfs_attr)); + strlcpy(attr->path, path, sizeof(attr->path)); + dbg("add to cache '%s'", path_full); + list_add(&attr->node, &attr_list); + } else { + /* clear old value */ + if(attr->value) + memset(attr->value, 0x00, sizeof(attr->value)); + } + + if (lstat(path_full, &statbuf) != 0) { + dbg("stat '%s' failed: %s", path_full, strerror(errno)); + goto out; + } + + if (S_ISLNK(statbuf.st_mode)) { + /* links return the last element of the target path */ + char link_target[PATH_SIZE]; + int len; + const char *pos; + + len = readlink(path_full, link_target, sizeof(link_target)); + if (len > 0) { + link_target[len] = '\0'; + pos = strrchr(link_target, '/'); + if (pos != NULL) { + dbg("cache '%s' with link value '%s'", path_full, value); + strlcpy(attr->value_local, &pos[1], sizeof(attr->value_local)); + attr->value = attr->value_local; + } + } + goto out; + } + + /* skip directories */ + if (S_ISDIR(statbuf.st_mode)) + goto out; + + /* skip non-readable files */ + if ((statbuf.st_mode & S_IRUSR) == 0) + goto out; + + /* read attribute value */ + fd = open(path_full, O_RDONLY); + if (fd < 0) { + dbg("attribute '%s' does not exist", path_full); + goto out; + } + size = read(fd, value, sizeof(value)); + close(fd); + if (size < 0) + goto out; + if (size == sizeof(value)) + goto out; + + /* got a valid value, store and return it */ + value[size] = '\0'; + remove_trailing_chars(value, '\n'); + dbg("cache '%s' with attribute value '%s'", path_full, value); + strlcpy(attr->value_local, value, sizeof(attr->value_local)); + attr->value = attr->value_local; + +out: + return attr->value; +} diff --git a/libmultipath/sysfs.h b/libmultipath/sysfs.h new file mode 100644 index 0000000..e7fa3e7 --- /dev/null +++ b/libmultipath/sysfs.h @@ -0,0 +1,25 @@ +/* + * sysfs.h + */ + +#ifndef _LIBMULTIPATH_SYSFS_H +#define _LIBMULTIPATH_SYSFS_H + +#ifdef DEBUG +# define dbg(format, args...) printf(format "\n", ##args) +#else +# define dbg(format, args...) do {} while (0) +#endif + +int sysfs_init(char *path, size_t len); +void sysfs_cleanup(void); +void sysfs_device_set_values(struct sysfs_device *dev, const char *devpath, + const char *subsystem, const char *driver); +struct sysfs_device *sysfs_device_get(const char *devpath); +struct sysfs_device *sysfs_device_get_parent(struct sysfs_device *dev); +struct sysfs_device *sysfs_device_get_parent_with_subsystem(struct sysfs_device *dev, const char *subsystem); +void sysfs_device_put(struct sysfs_device *dev); +char *sysfs_attr_get_value(const char *devpath, const char *attr_name); +int sysfs_resolve_link(char *path, size_t size); + +#endif diff --git a/libmultipath/uevent.c b/libmultipath/uevent.c index 6482698..a4028d8 100644 --- a/libmultipath/uevent.c +++ b/libmultipath/uevent.c @@ -26,12 +26,14 @@ #include #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..d577ec9 --- /dev/null +++ b/libmultipath/version.h @@ -0,0 +1,37 @@ +/* + * Soft: multipath device mapper target autoconfig + * + * Version: $Id: main.h,v 0.0.1 2003/09/18 15:13:38 cvaroqui Exp $ + * + * Author: Christophe Varoqui + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Copyright (c) 2006 Christophe Varoqui + */ +#ifndef _VERSION_H +#define _VERSION_H + +#define VERSION_CODE 0x000407 +#define DATE_CODE 0x030c06 + +#define PROG "multipath-tools" + +#define MULTIPATH_VERSION(version) \ + (version >> 16) & 0xFF, \ + (version >> 8) & 0xFF, \ + version & 0xFF + +#define VERSION_STRING PROG" v%d.%d.%d (%.2d/%.2d, 20%.2d)\n", \ + MULTIPATH_VERSION(VERSION_CODE), \ + MULTIPATH_VERSION(DATE_CODE) + +#endif /* _VERSION_H */ diff --git a/libmultipath/waiter.c b/libmultipath/waiter.c new file mode 100644 index 0000000..d7af0d1 --- /dev/null +++ b/libmultipath/waiter.c @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2004, 2005 Christophe Varoqui + * Copyright (c) 2005 Kiyoshi Ueda, NEC + * Copyright (c) 2005 Benjamin Marzinski, Redhat + * Copyright (c) 2005 Edward Goggin, EMC + */ +#include +#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..e6cfe9a 100644 --- a/multipath.conf.annotated +++ b/multipath.conf.annotated @@ -11,7 +11,7 @@ # # # # name : udev_dir # # desc : directory where udev creates its device nodes -# # default : /udev +# # default : /dev # # # udev_dir /dev # @@ -47,9 +47,9 @@ # # scope : multipath # # desc : the default program and args to callout to obtain a unique # # path identifier. Absolute path required -# # default : /sbin/scsi_id -g -u -s +# # default : /lib/udev/scsi_id -g -u -s # # -# getuid_callout "/sbin/scsi_id -g -u -s /block/%n" +# getuid_callout "/lib/udev/scsi_id -g -u -s /block/%n" # # # # # name : prio_callout @@ -66,9 +66,9 @@ # # scope : multipath & multipathd # # desc : the default method used to determine the paths' state # # values : readsector0|tur|emc_clariion|hp_sw|directio -# # default : readsector0 +# # default : directio # # -# #path_checker readsector0 +# #path_checker directio # # # # # name : rr_min_io @@ -142,6 +142,22 @@ # product MSA[15]00 # } #} +## +## name : blacklist_exceptions +## scope : multipath & multipathd +## desc : list of device names to be treated as multipath candidates +## even if they are on the blacklist. +## Note: blacklist exceptions are only valid in the same class. +## It is not possible to blacklist devices using the devnode keyword +## and to exclude some devices of them using the wwid keyword. +## default : - +## +#blacklist_exceptions { +# devnode "^dasd[c-d]+[0-9]*" +# wwid "IBM.75000000092461.4d00.34" +# wwid "IBM.75000000092461.4d00.35" +# wwid "IBM.75000000092461.4d00.36" +#} # ## ## name : multipaths @@ -182,10 +198,10 @@ # # name : path_checker # # scope : multipathd # # desc : path checking alorithm to use to check path state -# # values : readsector0, tur -# # default : readsector0 +# # values : readsector0|tur|emc_clariion|hp_sw|directio +# # default : directio # # -# # path_checker readsector0 +# # path_checker directio # # # # # name : path_selector @@ -237,7 +253,7 @@ ## ## name : devices ## scope : multipath & multipathd -## desc : list of per storage controler settings +## desc : list of per storage controller settings ## overrides default settings (device_maps block) ## overriden by per multipath settings (multipaths block) ## @@ -245,7 +261,7 @@ # # # # name : device # # scope : multipath & multipathd -# # desc : settings for this specific storage controler +# # desc : settings for this specific storage controller # # # device { # # @@ -260,7 +276,7 @@ # # name : path_grouping_policy # # scope : multipath # # desc : path grouping policy to apply to multipath hosted -# # by this storage controler +# # by this storage controller # # values : failover = 1 path per priority group # # multibus = all valid paths in 1 priority # # group @@ -275,9 +291,9 @@ # # scope : multipath # # desc : the program and args to callout to obtain a unique # # path identifier. Absolute path required -# # default : /sbin/scsi_id -g -u -s +# # default : /lib/udev/scsi_id -g -u -s # # -# getuid_callout "/sbin/scsi_id -g -u -s /block/%n" +# getuid_callout "/lib/udev/scsi_id -g -u -s /block/%n" # # # # # name : prio_callout @@ -294,10 +310,10 @@ # # name : path_checker # # scope : multipathd # # desc : path checking alorithm to use to check path state -# # values : readsector0, tur -# # default : readsector0 +# # values : readsector0|tur|emc_clariion|hp_sw|directio +# # default : directio # # -# path_checker readsector0 +# path_checker directio # # # # # name : path_selector diff --git a/multipath.conf.synthetic b/multipath.conf.synthetic index 4a1f5a4..633d625 100644 --- a/multipath.conf.synthetic +++ b/multipath.conf.synthetic @@ -7,16 +7,16 @@ # polling_interval 10 # selector "round-robin 0" # path_grouping_policy multibus -# getuid_callout "/sbin/scsi_id -g -u -s /block/%n" +# getuid_callout "/lib/udev/scsi_id -g -u -s /block/%n" # prio_callout /bin/true -# path_checker readsector0 +# path_checker directio # rr_min_io 100 # rr_weight priorities # failback immediate # no_path_retry fail # user_friendly_names no #} -#devnode_blacklist { +#blacklist { # wwid 26353900f02796769 # devnode "^(ram|raw|loop|fd|md|dm-|sr|scd|st)[0-9]*" # devnode "^hd[a-z][[0-9]*]" @@ -26,12 +26,16 @@ # product MSA[15]00 # } #} +#blacklist_exceptions { +# devnode "^dasd[c-d]+[0-9]*" +# wwid "IBM.75000000092461.4d00.34" +#} #multipaths { # multipath { # wwid 3600508b4000156d700012000000b0000 # alias yellow # path_grouping_policy multibus -# path_checker readsector0 +# path_checker directio # path_selector "round-robin 0" # failback manual # rr_weight priorities @@ -48,8 +52,8 @@ # vendor "COMPAQ " # product "HSV110 (C)COMPAQ" # path_grouping_policy multibus -# getuid_callout "/sbin/scsi_id -g -u -s /block/%n" -# path_checker readsector0 +# getuid_callout "/lib/udev/scsi_id -g -u -s /block/%n" +# path_checker directio # path_selector "round-robin 0" # hardware_handler "0" # failback 15 diff --git a/multipath/02_multipath b/multipath/02_multipath index 1a5d5a1..067c582 100755 --- a/multipath/02_multipath +++ b/multipath/02_multipath @@ -12,10 +12,10 @@ cp /sbin/kpartx $INITRDDIR/sbin # feed the dependencies too # scsi_id is dynamicaly linked, so store the libs too # -cp /sbin/scsi_id $INITRDDIR/sbin +cp /lib/udev/scsi_id $INITRDDIR/lib/udev/ cp /bin/mountpoint $INITRDDIR/bin -PROGS="/sbin/scsi_id /bin/mountpoint" +PROGS="/lib/udev/scsi_id /bin/mountpoint" LIBS=`ldd $PROGS | grep -v linux-gate.so | sort -u | \ awk '{print $3}'` for i in $LIBS diff --git a/multipath/Makefile b/multipath/Makefile index 646dfc2..4923b2f 100644 --- a/multipath/Makefile +++ b/multipath/Makefile @@ -8,11 +8,12 @@ include ../Makefile.inc OBJS = main.o $(MULTIPATHLIB)-$(BUILD).a $(CHECKERSLIB)-$(BUILD).a CFLAGS += -I$(multipathdir) -I$(checkersdir) +LDFLAGS += -laio ifeq ($(strip $(BUILD)),klibc) - OBJS += $(libdm) $(libsysfs) + OBJS += $(libdm) else - LDFLAGS += -ldevmapper -lsysfs + LDFLAGS += -ldevmapper endif EXEC = multipath @@ -22,14 +23,14 @@ all: $(BUILD) prepare: make -C $(multipathdir) prepare rm -f core *.o *.gz + $(GZIP) $(EXEC).8 > $(EXEC).8.gz + $(GZIP) $(EXEC).conf.5 > $(EXEC).conf.5.gz glibc: prepare $(OBJS) $(CC) $(OBJS) -o $(EXEC) $(LDFLAGS) - $(GZIP) $(EXEC).8 > $(EXEC).8.gz klibc: prepare $(OBJS) $(CC) -static -o $(EXEC) $(CRT0) $(OBJS) $(KLIBC) $(LIBGCC) - $(GZIP) $(EXEC).8 > $(EXEC).8.gz $(CHECKERSLIB)-$(BUILD).a: make -C $(checkersdir) BUILD=$(BUILD) $(BUILD) @@ -39,16 +40,19 @@ $(MULTIPATHLIB)-$(BUILD).a: install: install -d $(DESTDIR)$(bindir) - install -s -m 755 $(EXEC) $(DESTDIR)$(bindir)/ + $(INSTALL_PROGRAM) -m 755 $(EXEC) $(DESTDIR)$(bindir)/ install -d $(DESTDIR)/etc/udev/rules.d install -m 644 multipath.rules $(DESTDIR)/etc/udev/rules.d/ install -d $(DESTDIR)$(mandir) install -m 644 $(EXEC).8.gz $(DESTDIR)$(mandir) + install -d $(DESTDIR)$(man5dir) + install -m 644 $(EXEC).conf.5.gz $(DESTDIR)$(man5dir) uninstall: rm $(DESTDIR)/etc/udev/rules.d/multipath.rules rm $(DESTDIR)$(bindir)/$(EXEC) rm $(DESTDIR)$(mandir)/$(EXEC).8.gz + rm $(DESTDIR)$(man5dir)/$(EXEC).conf.5.gz clean: rm -f core *.o $(EXEC) *.gz diff --git a/multipath/main.c b/multipath/main.c index 98f7207..815c307 100644 --- a/multipath/main.c +++ b/multipath/main.c @@ -25,7 +25,6 @@ #include #include #include -#include #include #include @@ -37,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -46,8 +46,7 @@ #include #include #include - -#include "main.h" +#include static int filter_pathvec (vector pathvec, char * refwwid) @@ -73,7 +72,7 @@ static void usage (char * progname) { fprintf (stderr, VERSION_STRING); - fprintf (stderr, "Usage: %s\t[-v level] [-d] [-l|-ll|-f|-F]\n", + fprintf (stderr, "Usage: %s\t[-v level] [-d] [-h|-l|-ll|-f|-F]\n", progname); fprintf (stderr, "\t\t\t[-p failover|multibus|group_by_serial|group_by_prio]\n" \ @@ -84,6 +83,7 @@ usage (char * progname) "\t 1\t\t\tprint created devmap names only\n" \ "\t 2\t\t\tdefault verbosity\n" \ "\t 3\t\t\tprint debug information\n" \ + "\t-h\t\tprint this usage text\n" \ "\t-b file\t\tbindings file location\n" \ "\t-d\t\tdry run, do not create or update devmaps\n" \ "\t-l\t\tshow multipath topology (sysfs and DM info)\n" \ @@ -128,13 +128,15 @@ update_paths (struct multipath * mpp) pp->state = PATH_DOWN; continue; } + pp->mpp = mpp; pathinfo(pp, conf->hwtable, DI_ALL); continue; } + pp->mpp = mpp; if (pp->state == PATH_UNCHECKED) pathinfo(pp, conf->hwtable, DI_CHECKER); - if (!pp->priority) + if (pp->priority == PRIO_UNDEF) pathinfo(pp, conf->hwtable, DI_PRIO); } } @@ -222,7 +224,7 @@ configure (void) vecs.mpvec = curmp; /* - * if we have a blacklisted device parameter, exit early + * dev is "/dev/" . "sysfs block dev" */ if (conf->dev) { if (!strncmp(conf->dev, "/dev/", 5) && @@ -232,8 +234,12 @@ configure (void) dev = conf->dev; } - if (dev && blacklist(conf->blist_devnode, dev)) - goto out; + /* + * if we have a blacklisted device parameter, exit early + */ + if (dev && + (filter_devnode(conf->blist_devnode, conf->elist_devnode, dev) > 0)) + goto out; /* * scope limiting must be translated into a wwid @@ -247,8 +253,8 @@ configure (void) goto out; } condlog(3, "scope limited to %s", refwwid); - - if (blacklist(conf->blist_wwid, refwwid)) + if (filter_wwid(conf->blist_wwid, conf->elist_wwid, + refwwid) > 0) goto out; } @@ -281,8 +287,10 @@ configure (void) filter_pathvec(pathvec, refwwid); - if (conf->list) + if (conf->list) { + r = 0; goto out; + } /* * core logic entry point @@ -305,24 +313,24 @@ main (int argc, char *argv[]) int arg; extern char *optarg; extern int optind; - int i, r; + int i, r = 1; if (getuid() != 0) { fprintf(stderr, "need to be root\n"); exit(1); } - if (dm_prereq(DEFAULT_TARGET, 1, 0, 3)) + if (dm_prereq(DEFAULT_TARGET)) exit(1); - if (sysfs_get_mnt_path(sysfs_path, FILE_NAME_SIZE)) { - condlog(0, "multipath tools need sysfs mounted"); - exit(1); - } if (load_config(DEFAULT_CONFIGFILE)) exit(1); - while ((arg = getopt(argc, argv, ":qdl::Ffi:M:v:p:b:")) != EOF ) { + if (sysfs_init(conf->sysfs_dir, FILE_NAME_SIZE)) { + condlog(0, "multipath tools need sysfs mounted"); + exit(1); + } + while ((arg = getopt(argc, argv, ":dhl::FfM:v:p:b:")) != EOF ) { switch(arg) { case 1: printf("optarg : %s\n",optarg); break; @@ -365,6 +373,8 @@ main (int argc, char *argv[]) usage(argv[0]); } break; + case 'h': + usage(argv[0]); case ':': fprintf(stderr, "Missing option arguement\n"); usage(argv[0]); @@ -391,6 +401,7 @@ main (int argc, char *argv[]) conf->dev_type = DEV_DEVMAP; } + dm_init(); if (conf->remove == FLUSH_ONE) { if (conf->dev_type == DEV_DEVMAP) @@ -408,6 +419,7 @@ main (int argc, char *argv[]) condlog(3, "restart multipath configuration process"); out: + sysfs_cleanup(); free_config(conf); dm_lib_release(); dm_lib_exit(); diff --git a/multipath/main.h b/multipath/main.h deleted file mode 100644 index 8d5b285..0000000 --- a/multipath/main.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Soft: Description here... - * - * Version: $Id: main.h,v 0.0.1 2003/09/18 15:13:38 cvaroqui Exp $ - * - * Author: Copyright (C) 2003 Christophe Varoqui - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - */ - -#ifndef _MAIN_H -#define _MAIN_H - -/* - * Build version - */ -#define PROG "multipath" - -#define VERSION_CODE 0x000407 -#define DATE_CODE 0x030c06 - -#define MULTIPATH_VERSION(version) \ - (version >> 16) & 0xFF, \ - (version >> 8) & 0xFF, \ - version & 0xFF - -#define VERSION_STRING PROG" v%d.%d.%d (%.2d/%.2d, 20%.2d)\n", \ - MULTIPATH_VERSION(VERSION_CODE), \ - MULTIPATH_VERSION(DATE_CODE) - -#endif diff --git a/multipath/multipath.8 b/multipath/multipath.8 index 7133598..693872b 100644 --- a/multipath/multipath.8 +++ b/multipath/multipath.8 @@ -1,4 +1,4 @@ -.TH MULTIPATH 8 "February 2004" "" "Linux Administrator's Manual" +.TH MULTIPATH 8 "July 2006" "" "Linux Administrator's Manual" .SH NAME multipath \- Device mapper target autoconfig .SH SYNOPSIS @@ -6,7 +6,7 @@ multipath \- Device mapper target autoconfig .RB [\| \-v\ \c .IR verbosity \|] .RB [\| \-d \|] -.RB [\| \-l | \-ll | \-f | \-F \|] +.RB [\| \-h | \-l | \-ll | \-f | \-F \|] .RB [\| \-p\ \c .BR failover | multibus | group_by_serial | group_by_prio | group_by_node_name \|] .RB [\| device \|] @@ -29,6 +29,9 @@ print the created or updated multipath names only, for use to feed other tools l print all info : detected paths, coalesced paths (ie multipaths) and device maps .RE .TP +.B \-h +print usage text +.TP .B \-d dry run, do not create or update devmaps .TP @@ -38,12 +41,6 @@ show the current multipath topology from information fetched in sysfs and the de .B \-ll show the current multipath topology from all available information (sysfs, the device mapper, path checkers ...) .TP -.TP -.BI \-D " major:minor" -update only the devmap the path pointed by -.I major:minor -is in -.TP .B \-f flush a multipath device map specified as parameter, if unused .TP @@ -64,7 +61,7 @@ all paths in 1 priority group 1 priority group per serial .TP .B group_by_prio -1 priority group per priority value. Priorities are determined by callout programs specified as a global, per-controler or per-multipath option in the configuration file +1 priority group per priority value. Priorities are determined by callout programs specified as a global, per-controller or per-multipath option in the configuration file .TP .B group_by_node_name 1 priority group per target node name. Target node names are fetched in /sys/class/fc_transport/target*/node_name. diff --git a/multipath/multipath.conf.5 b/multipath/multipath.conf.5 new file mode 100644 index 0000000..c8ab6b0 --- /dev/null +++ b/multipath/multipath.conf.5 @@ -0,0 +1,384 @@ +.TH MULTIPATH.CONF 5 "30 November 2006" +.SH NAME +multipath.conf \- multipath daemon configuration file +.SH DESCRIPTION +.B "multipath.conf" +is the configuration file for the multipath daemon. It is used to +overwrite the built-in configuration table of \fBmultipathd\fP. +Any line whose first non-white-space character is a '#' is considered +a comment line. Empty lines are ignored. +.SH SYNTAX +The configuration file contains entries of the form: +.RS +.nf +.ft B +.sp +
{ +.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 "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..b430b94 100644 --- a/multipathd/Makefile +++ b/multipathd/Makefile @@ -7,7 +7,7 @@ include ../Makefile.inc # basic flags setting # CFLAGS += -DDAEMON -I$(multipathdir) -I$(checkersdir) -LDFLAGS = -lpthread -ldevmapper -lsysfs -lreadline -lncurses +LDFLAGS = -lpthread -ldevmapper -lreadline -lncurses -laio # # debuging stuff @@ -45,7 +45,7 @@ $(MULTIPATHLIB)-glibc.a: install: install -d $(DESTDIR)$(bindir) - install -s -m 755 $(EXEC) $(DESTDIR)$(bindir) + $(INSTALL_PROGRAM) -m 755 $(EXEC) $(DESTDIR)$(bindir) install -d $(DESTDIR)$(rcdir) install -d $(DESTDIR)$(mandir) install -m 644 $(EXEC).8.gz $(DESTDIR)$(mandir) diff --git a/multipathd/cli.c b/multipathd/cli.c index 475819b..d786eef 100644 --- a/multipathd/cli.c +++ b/multipathd/cli.c @@ -4,6 +4,8 @@ #include #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..7bae02a 100644 --- a/multipathd/cli_handlers.c +++ b/multipathd/cli_handlers.c @@ -13,6 +13,7 @@ #include #include #include +#include #include "main.h" #include "cli.h" @@ -71,7 +72,7 @@ show_map_topology (char ** r, int * len, struct multipath * mpp) c = reply; - c += snprint_multipath_topology( c, reply + maxlen - c, mpp, 2); + c += snprint_multipath_topology(c, reply + maxlen - c, mpp, 2); again = ((c - reply) == (maxlen - 1)); if (again) @@ -92,7 +93,8 @@ show_maps_topology (char ** r, int * len, struct vectors * vecs) char * reply; unsigned int maxlen = INITIAL_REPLY_LEN; int again = 1; - + + get_path_layout(vecs->pathvec); reply = MALLOC(maxlen); while (again) { @@ -142,6 +144,12 @@ show_config (char ** r, int * len) reply = REALLOC(reply, maxlen *= 2); continue; } + c += snprint_blacklist_except(c, reply + maxlen - c); + again = ((c - reply) == maxlen); + if (again) { + reply = REALLOC(reply, maxlen *= 2); + continue; + } c += snprint_hwtable(c, reply + maxlen - c, conf->hwtable); again = ((c - reply) == maxlen); if (again) { @@ -183,6 +191,7 @@ cli_list_map_topology (void * v, char ** reply, int * len, void * data) struct vectors * vecs = (struct vectors *)data; char * param = get_keyparam(v, MAP); + get_path_layout(vecs->pathvec); mpp = find_mp_by_str(vecs->mpvec, param); if (!mpp) @@ -278,8 +287,8 @@ cli_add_path (void * v, char ** reply, int * len, void * data) condlog(2, "%s: add path (operator)", param); - if (blacklist(conf->blist_devnode, param) || - (r = ev_add_path(param, vecs)) == 2) { + if (filter_devnode(conf->blist_devnode, conf->elist_devnode, + param) > 0 || (r = ev_add_path(param, vecs)) == 2) { *reply = strdup("blacklisted"); *len = strlen(*reply) + 1; condlog(2, "%s: path blacklisted", param); @@ -304,16 +313,30 @@ cli_add_map (void * v, char ** reply, int * len, void * data) { struct vectors * vecs = (struct vectors *)data; char * param = get_keyparam(v, MAP); + int minor; + char dev_path[PATH_SIZE]; + struct sysfs_device *sysdev; condlog(2, "%s: add map (operator)", param); - if (blacklist(conf->blist_wwid, param)) { + if (filter_wwid(conf->blist_wwid, conf->elist_wwid, param) > 0) { *reply = strdup("blacklisted"); *len = strlen(*reply) + 1; condlog(2, "%s: map blacklisted", param); return 0; } - return ev_add_map(param, vecs); + minor = dm_get_minor(param); + if (minor < 0) { + condlog(2, "%s: not a device mapper table", param); + return 0; + } + sprintf(dev_path,"/block/dm-%d", minor); + sysdev = sysfs_device_get(dev_path); + if (!sysdev) { + condlog(2, "%s: not found in sysfs", param); + return 0; + } + return ev_add_map(sysdev, vecs); } int @@ -431,3 +454,79 @@ cli_fail(void * v, char ** reply, int * len, void * data) return dm_fail_path(pp->mpp->alias, pp->dev_t); } + +int +show_blacklist (char ** r, int * len) +{ + char *c = NULL; + char *reply = NULL; + unsigned int maxlen = INITIAL_REPLY_LEN; + int again = 1; + + while (again) { + reply = MALLOC(maxlen); + if (!reply) + return 1; + + c = reply; + c += snprint_blacklist_report(c, maxlen); + again = ((c - reply) == maxlen); + if (again) { + maxlen *= 2; + FREE(reply); + continue; + } + } + + *r = reply; + *len = (int)(c - reply + 1); + + return 0; +} + +int +cli_list_blacklist (void * v, char ** reply, int * len, void * data) +{ + condlog(3, "list blacklist (operator)"); + + return show_blacklist(reply, len); +} + +int +show_devices (char ** r, int * len, struct vectors *vecs) +{ + char *c = NULL; + char *reply = NULL; + unsigned int maxlen = INITIAL_REPLY_LEN; + int again = 1; + + while (again) { + reply = MALLOC(maxlen); + if (!reply) + return 1; + + c = reply; + c += snprint_devices(c, maxlen, vecs); + again = ((c - reply) == maxlen); + if (again) { + maxlen *= 2; + FREE(reply); + continue; + } + } + + *r = reply; + *len = (int)(c - reply + 1); + + return 0; +} + +int +cli_list_devices (void * v, char ** reply, int * len, void * data) +{ + struct vectors * vecs = (struct vectors *)data; + + condlog(3, "list devices (operator)"); + + return show_devices(reply, len, vecs); +} diff --git a/multipathd/cli_handlers.h b/multipathd/cli_handlers.h index 8768724..863694b 100644 --- a/multipathd/cli_handlers.h +++ b/multipathd/cli_handlers.h @@ -5,6 +5,8 @@ int cli_list_maps_stats (void * v, char ** reply, int * len, void * data); int cli_list_map_topology (void * v, char ** reply, int * len, void * data); int cli_list_maps_topology (void * v, char ** reply, int * len, void * data); int cli_list_config (void * v, char ** reply, int * len, void * data); +int cli_list_blacklist (void * v, char ** reply, int * len, void * data); +int cli_list_devices (void * v, char ** reply, int * len, void * data); int cli_add_path (void * v, char ** reply, int * len, void * data); int cli_del_path (void * v, char ** reply, int * len, void * data); int cli_add_map (void * v, char ** reply, int * len, void * data); diff --git a/multipathd/main.c b/multipathd/main.c index 55a2c49..da5fd8f 100644 --- a/multipathd/main.c +++ b/multipathd/main.c @@ -14,12 +14,6 @@ #include /* - * libsysfs - */ -#include -#include - -/* * libcheckers */ #include @@ -40,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -55,136 +50,29 @@ #include "uxclnt.h" #include "cli.h" #include "cli_handlers.h" +#include "lock.h" +#include "waiter.h" #define FILE_NAME_SIZE 256 #define CMDSIZE 160 #define LOG_MSG(a,b) \ - if (strlen(b)) condlog(a, "%s: %s", pp->dev_t, b); - -#ifdef LCKDBG -#define lock(a) \ - fprintf(stderr, "%s:%s(%i) lock %p\n", __FILE__, __FUNCTION__, __LINE__, a); \ - pthread_mutex_lock(a) -#define unlock(a) \ - fprintf(stderr, "%s:%s(%i) unlock %p\n", __FILE__, __FUNCTION__, __LINE__, a); \ - pthread_mutex_unlock(a) -#define lock_cleanup_pop(a) \ - fprintf(stderr, "%s:%s(%i) unlock %p\n", __FILE__, __FUNCTION__, __LINE__, a); \ - pthread_cleanup_pop(1); -#else -#define lock(a) pthread_mutex_lock(a) -#define unlock(a) pthread_mutex_unlock(a) -#define lock_cleanup_pop(a) pthread_cleanup_pop(1); -#endif + if (strlen(b)) condlog(a, "%s: %s", pp->dev, b); pthread_cond_t exit_cond = PTHREAD_COND_INITIALIZER; pthread_mutex_t exit_mutex = PTHREAD_MUTEX_INITIALIZER; /* - * structs - */ -struct vectors * gvecs; /* global copy of vecs for use in sig handlers */ - -static struct event_thread * -alloc_waiter (void) -{ - - struct event_thread * wp; - - wp = (struct event_thread *)MALLOC(sizeof(struct event_thread)); - - return wp; -} - -static void -free_waiter (void * data) -{ - struct event_thread * wp = (struct event_thread *)data; - - /* - * indicate in mpp that the wp is already freed storage - */ - lock(wp->vecs->lock); - - if (wp->mpp) - /* - * be careful, mpp may already be freed -- null if so - */ - wp->mpp->waiter = NULL; - else - condlog(3, "free_waiter, mpp freed before wp=%p,", wp); - - unlock(wp->vecs->lock); - - if (wp->dmt) - dm_task_destroy(wp->dmt); - - FREE(wp); -} - -static void -stop_waiter_thread (struct multipath * mpp, struct vectors * vecs) -{ - struct event_thread * wp = (struct event_thread *)mpp->waiter; - - if (!wp) { - condlog(3, "%s: no waiter thread", mpp->alias); - return; - } - condlog(2, "%s: stop event checker thread", wp->mapname); - pthread_kill((pthread_t)wp->thread, SIGUSR1); -} - -static void -cleanup_lock (void * data) -{ - unlock((pthread_mutex_t *)data); -} - -/* - * mpp->no_path_retry: - * -2 (QUEUE) : queue_if_no_path enabled, never turned off - * -1 (FAIL) : fail_if_no_path - * 0 (UNDEF) : nothing - * >0 : queue_if_no_path enabled, turned off after polling n times + * global copy of vecs for use in sig handlers */ -static void -update_queue_mode_del_path(struct multipath *mpp) -{ - if (--mpp->nr_active == 0 && mpp->no_path_retry > 0) { - /* - * Enter retry mode. - * meaning of +1: retry_tick may be decremented in - * checkerloop before starting retry. - */ - mpp->stat_queueing_timeouts++; - mpp->retry_tick = mpp->no_path_retry * conf->checkint + 1; - condlog(1, "%s: Entering recovery mode: max_retries=%d", - mpp->alias, mpp->no_path_retry); - } - condlog(2, "%s: remaining active paths: %d", mpp->alias, mpp->nr_active); -} - -static void -update_queue_mode_add_path(struct multipath *mpp) -{ - if (mpp->nr_active++ == 0 && mpp->no_path_retry > 0) { - /* come back to normal mode from retry mode */ - mpp->retry_tick = 0; - dm_queue_if_no_path(mpp->alias, 1); - condlog(2, "%s: queue_if_no_path enabled", mpp->alias); - condlog(1, "%s: Recovered to normal mode", mpp->alias); - } - condlog(2, "%s: remaining active paths: %d", mpp->alias, mpp->nr_active); -} +struct vectors * gvecs; static int need_switch_pathgroup (struct multipath * mpp, int refresh) { struct pathgroup * pgp; struct path * pp; - int i, j; + unsigned int i, j; if (!mpp || mpp->pgfailback == -FAILBACK_MANUAL) return 0; @@ -219,7 +107,8 @@ coalesce_maps(struct vectors *vecs, vector nmpv) { struct multipath * ompp; vector ompv = vecs->mpvec; - int i, j; + unsigned int i; + int j; vector_foreach_slot (ompv, ompp, i) { if (!find_mp_by_wwid(nmpv, ompp->wwid)) { @@ -253,234 +142,12 @@ coalesce_maps(struct vectors *vecs, vector nmpv) return 0; } -static int -update_multipath (struct vectors *vecs, char *mapname) -{ - struct multipath *mpp; - struct pathgroup *pgp; - struct path *pp; - int i, j; - int r = 1; - - mpp = find_mp_by_alias(vecs->mpvec, mapname); - - if (!mpp) - goto out; - - free_pgvec(mpp->pg, KEEP_PATHS); - mpp->pg = NULL; - - if (setup_multipath(vecs, mpp)) - goto out; /* mpp freed in setup_multipath */ - - /* - * compare checkers states with DM states - */ - vector_foreach_slot (mpp->pg, pgp, i) { - vector_foreach_slot (pgp->paths, pp, j) { - if (pp->dmstate != PSTATE_FAILED) - continue; - - if (pp->state != PATH_DOWN) { - int oldstate = pp->state; - condlog(2, "%s: mark as failed", pp->dev_t); - mpp->stat_path_failures++; - pp->state = PATH_DOWN; - if (oldstate == PATH_UP || - oldstate == PATH_GHOST) - update_queue_mode_del_path(mpp); - - /* - * if opportune, - * schedule the next check earlier - */ - if (pp->tick > conf->checkint) - pp->tick = conf->checkint; - } - } - } - r = 0; -out: - if (r) - condlog(0, "failed to update multipath"); - - return r; -} - -static sigset_t unblock_signals(void) -{ - sigset_t set, old; - - sigemptyset(&set); - sigaddset(&set, SIGHUP); - sigaddset(&set, SIGUSR1); - pthread_sigmask(SIG_UNBLOCK, &set, &old); - return old; -} - -/* - * returns the reschedule delay - * negative means *stop* - */ -static int -waiteventloop (struct event_thread * waiter) -{ - sigset_t set; - int event_nr; - int r; - - if (!waiter->event_nr) - waiter->event_nr = dm_geteventnr(waiter->mapname); - - if (!(waiter->dmt = dm_task_create(DM_DEVICE_WAITEVENT))) { - condlog(0, "%s: devmap event #%i dm_task_create error", - waiter->mapname, waiter->event_nr); - return 1; - } - - if (!dm_task_set_name(waiter->dmt, waiter->mapname)) { - condlog(0, "%s: devmap event #%i dm_task_set_name error", - waiter->mapname, waiter->event_nr); - dm_task_destroy(waiter->dmt); - return 1; - } - - if (waiter->event_nr && !dm_task_set_event_nr(waiter->dmt, - waiter->event_nr)) { - condlog(0, "%s: devmap event #%i dm_task_set_event_nr error", - waiter->mapname, waiter->event_nr); - dm_task_destroy(waiter->dmt); - return 1; - } - - dm_task_no_open_count(waiter->dmt); - - /* accept wait interruption */ - set = unblock_signals(); - - /* interruption spits messages */ - dm_shut_log(); - - /* wait */ - r = dm_task_run(waiter->dmt); - - /* wait is over : event or interrupt */ - pthread_sigmask(SIG_SETMASK, &set, NULL); - //dm_restore_log(); - - if (!r) /* wait interrupted by signal */ - return -1; - - dm_task_destroy(waiter->dmt); - waiter->dmt = NULL; - waiter->event_nr++; - - /* - * upon event ... - */ - while (1) { - condlog(3, "%s: devmap event #%i", - waiter->mapname, waiter->event_nr); - - /* - * event might be : - * - * 1) a table reload, which means our mpp structure is - * obsolete : refresh it through update_multipath() - * 2) a path failed by DM : mark as such through - * update_multipath() - * 3) map has gone away : stop the thread. - * 4) a path reinstate : nothing to do - * 5) a switch group : nothing to do - */ - pthread_cleanup_push(cleanup_lock, waiter->vecs->lock); - lock(waiter->vecs->lock); - r = update_multipath(waiter->vecs, waiter->mapname); - lock_cleanup_pop(waiter->vecs->lock); - - if (r) - return -1; /* stop the thread */ - - event_nr = dm_geteventnr(waiter->mapname); - - if (waiter->event_nr == event_nr) - return 1; /* upon problem reschedule 1s later */ - - waiter->event_nr = event_nr; - } - return -1; /* never reach there */ -} - -static void * -waitevent (void * et) -{ - int r; - struct event_thread *waiter; - - mlockall(MCL_CURRENT | MCL_FUTURE); - - waiter = (struct event_thread *)et; - pthread_cleanup_push(free_waiter, et); - - while (1) { - r = waiteventloop(waiter); - - if (r < 0) - break; - - sleep(r); - } - - pthread_cleanup_pop(1); - return NULL; -} - -static int -start_waiter_thread (struct multipath * mpp, struct vectors * vecs) -{ - pthread_attr_t attr; - struct event_thread * wp; - - if (!mpp) - return 0; - - if (pthread_attr_init(&attr)) - goto out; - - pthread_attr_setstacksize(&attr, 32 * 1024); - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - - wp = alloc_waiter(); - - if (!wp) - goto out; - - mpp->waiter = (void *)wp; - strncpy(wp->mapname, mpp->alias, WWID_SIZE); - wp->vecs = vecs; - wp->mpp = mpp; - - if (pthread_create(&wp->thread, &attr, waitevent, wp)) { - condlog(0, "%s: cannot create event checker", wp->mapname); - goto out1; - } - condlog(2, "%s: event checker started", wp->mapname); - - return 0; -out1: - free_waiter(wp); - mpp->waiter = NULL; -out: - condlog(0, "failed to start waiter thread"); - return 1; -} - static void sync_map_state(struct multipath *mpp) { - int i, j; struct pathgroup *pgp; - struct path *pp; + struct path *pp; + unsigned int i, j; vector_foreach_slot (mpp->pg, pgp, i){ vector_foreach_slot (pgp->paths, pp, j){ @@ -502,10 +169,10 @@ sync_map_state(struct multipath *mpp) static void sync_maps_state(vector mpvec) { - int i; + unsigned int i; struct multipath *mpp; - vector_foreach_slot (mpvec, mpp, i) + vector_foreach_slot (mpvec, mpp, i) sync_map_state(mpp); } @@ -536,52 +203,50 @@ flush_map(struct multipath * mpp, struct vectors * vecs) } static int -uev_add_map (char * devname, struct vectors * vecs) +uev_add_map (struct sysfs_device * dev, struct vectors * vecs) { - condlog(2, "%s: add map (uevent)", devname); - return ev_add_map(devname, vecs); + condlog(2, "%s: add map (uevent)", dev->kernel); + return ev_add_map(dev, vecs); } int -ev_add_map (char * devname, struct vectors * vecs) +ev_add_map (struct sysfs_device * dev, struct vectors * vecs) { - int major, minor; - char dev_t[BLK_DEV_SIZE]; char * alias; + char *dev_t; + int major, minor; char * refwwid; struct multipath * mpp; int map_present; int r = 1; - if (sscanf(devname, "dm-%d", &minor) == 1 && - !sysfs_get_dev(sysfs_path, devname, dev_t, BLK_DEV_SIZE) && - sscanf(dev_t, "%d:%d", &major, &minor) == 2) - alias = dm_mapname(major, minor); - else - alias = STRDUP(devname); - + dev_t = sysfs_attr_get_value(dev->devpath, "dev"); + + if (!dev_t || sscanf(dev_t, "%d:%d", &major, &minor) != 2) + return 1; + + alias = dm_mapname(major, minor); + if (!alias) return 1; - + map_present = dm_map_present(alias); if (map_present && dm_type(alias, DEFAULT_TARGET) <= 0) { condlog(4, "%s: not a multipath map", alias); - FREE(alias); return 0; } mpp = find_mp_by_alias(vecs->mpvec, alias); if (mpp) { - /* + /* * Not really an error -- we generate our own uevent * if we create a multipath mapped device as a result * of uev_add_path */ condlog(0, "%s: devmap already registered", - devname); - FREE(alias); + dev->kernel); return 0; } @@ -591,31 +256,30 @@ ev_add_map (char * devname, struct vectors * vecs) if (map_present && (mpp = add_map_without_path(vecs, minor, alias, start_waiter_thread))) { sync_map_state(mpp); - condlog(3, "%s: devmap %s added", alias, devname); + condlog(3, "%s: devmap %s added", alias, dev->kernel); return 0; } - refwwid = get_refwwid(devname, DEV_DEVMAP, vecs->pathvec); + refwwid = get_refwwid(dev->kernel, DEV_DEVMAP, vecs->pathvec); if (refwwid) { r = coalesce_paths(vecs, NULL, refwwid); dm_lib_release(); } - + if (!r) - condlog(3, "%s: devmap %s added", alias, devname); + condlog(3, "%s: devmap %s added", alias, dev->kernel); else - condlog(0, "%s: uev_add_map %s failed", alias, devname); + condlog(0, "%s: uev_add_map %s failed", alias, dev->kernel); FREE(refwwid); - FREE(alias); return r; } static int -uev_remove_map (char * devname, struct vectors * vecs) +uev_remove_map (struct sysfs_device * dev, struct vectors * vecs) { - condlog(2, "%s: remove map (uevent)", devname); - return ev_remove_map(devname, vecs); + condlog(2, "%s: remove map (uevent)", dev->kernel); + return ev_remove_map(dev->kernel, vecs); } int @@ -636,13 +300,13 @@ ev_remove_map (char * devname, struct vectors * vecs) } static int -uev_umount_map (char * devname, struct vectors * vecs) +uev_umount_map (struct sysfs_device * dev, struct vectors * vecs) { struct multipath * mpp; - condlog(2, "%s: umount map (uevent)", devname); + condlog(2, "%s: umount map (uevent)", dev->kernel); - mpp = find_mp_by_str(vecs->mpvec, devname); + mpp = find_mp_by_str(vecs->mpvec, dev->kernel); if (!mpp) return 0; @@ -655,12 +319,12 @@ uev_umount_map (char * devname, struct vectors * vecs) return 0; } - + static int -uev_add_path (char * devname, struct vectors * vecs) +uev_add_path (struct sysfs_device * dev, struct vectors * vecs) { - condlog(2, "%s: add path (uevent)", devname); - return (ev_add_path(devname, vecs) != 1)? 0 : 1; + condlog(2, "%s: add path (uevent)", dev->kernel); + return (ev_add_path(dev->kernel, vecs) != 1)? 0 : 1; } @@ -704,13 +368,13 @@ ev_add_path (char * devname, struct vectors * vecs) condlog(0, "%s: failed to get path uid", devname); return 1; /* leave path added to pathvec */ } - if (blacklist_path(conf, pp)){ + if (filter_path(conf, pp)){ int i = find_slot(vecs->pathvec, (void *)pp); if (i != -1) vector_del_slot(vecs->pathvec, i); free_path(pp); return 2; - } + } mpp = pp->mpp = find_mp_by_wwid(vecs->mpvec, pp->wwid); rescan: if (mpp) { @@ -742,8 +406,8 @@ rescan: condlog(0, "%s: failed in domap for addition of new " "path %s", mpp->alias, devname); /* - * deal with asynchronous uevents :(( - */ + * deal with asynchronous uevents :(( + */ if (mpp->action == ACT_RELOAD) { condlog(0, "%s: uev_add_path sleep", mpp->alias); sleep(1); @@ -776,10 +440,16 @@ out: } static int -uev_remove_path (char * devname, struct vectors * vecs) +uev_remove_path (struct sysfs_device * dev, struct vectors * vecs) { - condlog(2, "%s: remove path (uevent)", devname); - return ev_remove_path(devname, vecs); + int retval; + + condlog(2, "%s: remove path (uevent)", dev->kernel); + retval = ev_remove_path(dev->kernel, vecs); + if (!retval) + sysfs_device_put(dev); + + return retval; } int @@ -809,9 +479,9 @@ ev_remove_path (char * devname, struct vectors * vecs) vector rpvec = vector_alloc(); /* - * transform the mp->pg vector of vectors of paths - * into a mp->params string to feed the device-mapper - */ + * transform the mp->pg vector of vectors of paths + * into a mp->params string to feed the device-mapper + */ update_mpp_paths(mpp, vecs->pathvec); if ((i = find_slot(mpp->paths, (void *)pp)) != -1) vector_del_slot(mpp->paths, i); @@ -837,8 +507,8 @@ ev_remove_path (char * devname, struct vectors * vecs) goto out; } /* - * reload the map - */ + * reload the map + */ mpp->action = ACT_RELOAD; if (domap(mpp) <= 0) { condlog(0, "%s: failed in domap for " @@ -863,7 +533,7 @@ ev_remove_path (char * devname, struct vectors * vecs) } sync_map_state(mpp); - condlog(3, "%s path removed from devmap %s", + condlog(3, "%s: path removed from map %s", devname, mpp->alias); } free_pathvec(rpvec, KEEP_PATHS); @@ -898,8 +568,8 @@ out: static int map_discovery (struct vectors * vecs) { - int i; struct multipath * mpp; + unsigned int i; if (dm_get_maps(vecs->mpvec, "multipath")) return 1; @@ -916,7 +586,7 @@ uxsock_trigger (char * str, char ** reply, int * len, void * trigger_data) { struct vectors * vecs; int r; - + *reply = NULL; *len = 0; vecs = (struct vectors *)trigger_data; @@ -958,11 +628,11 @@ uev_discard(char * devpath) return 0; } -int +int uev_trigger (struct uevent * uev, void * trigger_data) { int r = 0; - char devname[32]; + struct sysfs_device *sysdev; struct vectors * vecs; vecs = (struct vectors *)trigger_data; @@ -970,40 +640,46 @@ uev_trigger (struct uevent * uev, void * trigger_data) if (uev_discard(uev->devpath)) return 0; - basename(uev->devpath, devname); + sysdev = sysfs_device_get(uev->devpath); + if(!sysdev) + return 0; + lock(vecs->lock); /* - * device map add/remove event + * device map event + * Add events are ignored here as the tables + * are not fully initialised then. */ - if (!strncmp(devname, "dm-", 3)) { - if (!strncmp(uev->action, "add", 3)) { - r = uev_add_map(devname, vecs); + if (!strncmp(sysdev->kernel, "dm-", 3)) { + if (!strncmp(uev->action, "change", 6)) { + r = uev_add_map(sysdev, vecs); goto out; } if (!strncmp(uev->action, "remove", 6)) { - r = uev_remove_map(devname, vecs); + r = uev_remove_map(sysdev, vecs); goto out; } if (!strncmp(uev->action, "umount", 6)) { - r = uev_umount_map(devname, vecs); + r = uev_umount_map(sysdev, vecs); goto out; } goto out; } - + /* * path add/remove event */ - if (blacklist(conf->blist_devnode, devname)) + if (filter_devnode(conf->blist_devnode, conf->elist_devnode, + sysdev->kernel) > 0) goto out; if (!strncmp(uev->action, "add", 3)) { - r = uev_add_path(devname, vecs); + r = uev_add_path(sysdev, vecs); goto out; } if (!strncmp(uev->action, "remove", 6)) { - r = uev_remove_path(devname, vecs); + r = uev_remove_path(sysdev, vecs); goto out; } @@ -1017,37 +693,36 @@ ueventloop (void * ap) { if (uevent_listen(&uev_trigger, ap)) fprintf(stderr, "error starting uevent listener"); - + return NULL; } static void * uxlsnrloop (void * ap) { - if (load_keys()) - return NULL; - - if (alloc_handlers()) + if (cli_init()) return NULL; - add_handler(LIST+PATHS, cli_list_paths); - add_handler(LIST+MAPS, cli_list_maps); - add_handler(LIST+MAPS+STATUS, cli_list_maps_status); - add_handler(LIST+MAPS+STATS, cli_list_maps_stats); - add_handler(LIST+MAPS+TOPOLOGY, cli_list_maps_topology); - add_handler(LIST+TOPOLOGY, cli_list_maps_topology); - add_handler(LIST+MAP+TOPOLOGY, cli_list_map_topology); - add_handler(LIST+CONFIG, cli_list_config); - add_handler(ADD+PATH, cli_add_path); - add_handler(DEL+PATH, cli_del_path); - add_handler(ADD+MAP, cli_add_map); - add_handler(DEL+MAP, cli_del_map); - add_handler(SWITCH+MAP+GROUP, cli_switch_group); - add_handler(RECONFIGURE, cli_reconfigure); - add_handler(SUSPEND+MAP, cli_suspend); - add_handler(RESUME+MAP, cli_resume); - add_handler(REINSTATE+PATH, cli_reinstate); - add_handler(FAIL+PATH, cli_fail); + set_handler_callback(LIST+PATHS, cli_list_paths); + set_handler_callback(LIST+MAPS, cli_list_maps); + set_handler_callback(LIST+MAPS+STATUS, cli_list_maps_status); + set_handler_callback(LIST+MAPS+STATS, cli_list_maps_stats); + set_handler_callback(LIST+MAPS+TOPOLOGY, cli_list_maps_topology); + set_handler_callback(LIST+TOPOLOGY, cli_list_maps_topology); + set_handler_callback(LIST+MAP+TOPOLOGY, cli_list_map_topology); + set_handler_callback(LIST+CONFIG, cli_list_config); + set_handler_callback(LIST+BLACKLIST, cli_list_blacklist); + set_handler_callback(LIST+DEVICES, cli_list_devices); + set_handler_callback(ADD+PATH, cli_add_path); + set_handler_callback(DEL+PATH, cli_del_path); + set_handler_callback(ADD+MAP, cli_add_map); + set_handler_callback(DEL+MAP, cli_del_map); + set_handler_callback(SWITCH+MAP+GROUP, cli_switch_group); + set_handler_callback(RECONFIGURE, cli_reconfigure); + set_handler_callback(SUSPEND+MAP, cli_suspend); + set_handler_callback(RESUME+MAP, cli_resume); + set_handler_callback(REINSTATE+PATH, cli_reinstate); + set_handler_callback(FAIL+PATH, cli_fail); uxsock_listen(&uxsock_trigger, ap); @@ -1113,12 +788,12 @@ enable_group(struct path * pp) * * we can safely return here, because upon map reload, all * PG will be enabled. - */ + */ if (!pp->mpp->pg || !pp->pgindex) return; pgp = VECTOR_SLOT(pp->mpp->pg, pp->pgindex - 1); - + if (pgp->status == PGSTATE_DISABLED) { condlog(2, "%s: enable group #%i", pp->mpp->alias, pp->pgindex); dm_enablegroup(pp->mpp->alias, pp->pgindex); @@ -1129,7 +804,7 @@ static void mpvec_garbage_collector (struct vectors * vecs) { struct multipath * mpp; - int i; + unsigned int i; vector_foreach_slot (vecs->mpvec, mpp, i) { if (mpp && mpp->alias && !dm_map_present(mpp->alias)) { @@ -1144,7 +819,7 @@ static void defered_failback_tick (vector mpvec) { struct multipath * mpp; - int i; + unsigned int i; vector_foreach_slot (mpvec, mpp, i) { /* @@ -1163,7 +838,7 @@ static void retry_count_tick(vector mpvec) { struct multipath *mpp; - int i; + unsigned int i; vector_foreach_slot (mpvec, mpp, i) { if (mpp->retry_tick) { @@ -1182,8 +857,9 @@ checkerloop (void *ap) { struct vectors *vecs; struct path *pp; - int i, count = 0; + int count = 0; int newstate; + unsigned int i; mlockall(MCL_CURRENT | MCL_FUTURE); vecs = (struct vectors *)ap; @@ -1213,24 +889,36 @@ checkerloop (void *ap) * in case we exit abnormaly from here */ pp->tick = conf->checkint; - + if (!checker_selected(&pp->checker)) { pathinfo(pp, conf->hwtable, DI_SYSFS); select_checker(pp); } - if (!checker_selected(&pp->checker)) { condlog(0, "%s: checker is not set", pp->dev); continue; } + /* + * Set checker in async mode. + * Honored only by checker implementing the said mode. + */ + checker_set_async(&pp->checker); + newstate = checker_check(&pp->checker); - + if (newstate < 0) { condlog(2, "%s: unusable path", pp->dev); pathinfo(pp, conf->hwtable, 0); continue; } - + /* + * Async IO in flight. Keep the previous path state + * and reschedule as soon as possible + */ + if (newstate == PATH_PENDING) { + pp->tick = 1; + continue; + } if (newstate != pp->state) { int oldstate = pp->state; pp->state = newstate; @@ -1245,7 +933,7 @@ checkerloop (void *ap) if (newstate == PATH_DOWN || newstate == PATH_SHAKY || update_multipath_strings(pp->mpp, - vecs->pathvec)) { + vecs->pathvec)) { /* * proactively fail path in the DM */ @@ -1336,7 +1024,7 @@ checkerloop (void *ap) mpvec_garbage_collector(vecs); count = MAPGCINT; } - + lock_cleanup_pop(vecs->lock); sleep(1); } @@ -1351,12 +1039,12 @@ configure (struct vectors * vecs, int start_waiters) vector mpvec; int i; - if (!(vecs->pathvec = vector_alloc())) + if (!vecs->pathvec && !(vecs->pathvec = vector_alloc())) return 1; - - if (!(vecs->mpvec = vector_alloc())) + + if (!vecs->mpvec && !(vecs->mpvec = vector_alloc())) return 1; - + if (!(mpvec = vector_alloc())) return 1; @@ -1366,11 +1054,11 @@ configure (struct vectors * vecs, int start_waiters) path_discovery(vecs->pathvec, conf, DI_ALL); vector_foreach_slot (vecs->pathvec, pp, i){ - if (blacklist_path(conf, pp)){ + if (filter_path(conf, pp)){ vector_del_slot(vecs->pathvec, i); free_path(pp); i--; - } + } else pp->checkint = conf->checkint; } @@ -1394,10 +1082,6 @@ configure (struct vectors * vecs, int start_waiters) sync_maps_state(mpvec); - if (conf->verbosity > 2) - vector_foreach_slot(mpvec, mpp, i) - print_map(mpp); - /* * purge dm of old maps */ @@ -1406,6 +1090,7 @@ configure (struct vectors * vecs, int start_waiters) /* * save new set of maps formed by considering current path state */ + vector_free(vecs->mpvec); vecs->mpvec = mpvec; /* @@ -1435,6 +1120,7 @@ reconfigure (struct vectors * vecs) if (VECTOR_SIZE(vecs->pathvec)) free_pathvec(vecs->pathvec, FREE_PATHS); + vecs->pathvec = NULL; conf = NULL; if (load_config(DEFAULT_CONFIGFILE)) @@ -1461,30 +1147,16 @@ init_vecs (void) if (!vecs) return NULL; - vecs->lock = + vecs->lock = (pthread_mutex_t *)MALLOC(sizeof(pthread_mutex_t)); if (!vecs->lock) goto out; - vecs->pathvec = vector_alloc(); - - if (!vecs->pathvec) - goto out1; - - vecs->mpvec = vector_alloc(); - - if (!vecs->mpvec) - goto out2; - pthread_mutex_init(vecs->lock, NULL); return vecs; -out2: - vector_free(vecs->pathvec); -out1: - FREE(vecs->lock); out: FREE(vecs); condlog(0, "failed to init paths"); @@ -1543,21 +1215,21 @@ signal_init(void) signal_set(SIGUSR1, sigusr1); signal_set(SIGINT, sigend); signal_set(SIGTERM, sigend); - signal_set(SIGKILL, sigend); + signal(SIGPIPE, SIG_IGN); } static void setscheduler (void) { - int res; + int res; static struct sched_param sched_param = { - sched_priority: 99 + .sched_priority = 99 }; - res = sched_setscheduler (0, SCHED_RR, &sched_param); + res = sched_setscheduler (0, SCHED_RR, &sched_param); - if (res == -1) - condlog(LOG_WARNING, "Could not set SCHED_RR at priority 99"); + if (res == -1) + condlog(LOG_WARNING, "Could not set SCHED_RR at priority 99"); return; } @@ -1574,7 +1246,7 @@ set_oom_adj (int val) fprintf(fp, "%i", val); fclose(fp); } - + static int child (void * param) { @@ -1617,7 +1289,7 @@ child (void * param) if (!vecs) exit(1); - if (sysfs_get_mnt_path(sysfs_path, FILE_NAME_SIZE)) { + if (sysfs_init(conf->sysfs_dir, FILE_NAME_SIZE)) { condlog(0, "can not find sysfs mount point"); exit(1); } @@ -1636,7 +1308,7 @@ child (void * param) pthread_attr_init(&attr); pthread_attr_setstacksize(&attr, 64 * 1024); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - + pthread_create(&check_thr, &attr, checkerloop, vecs); pthread_create(&uevent_thr, &attr, ueventloop, vecs); pthread_create(&uxlsnr_thr, &attr, uxlsnrloop, vecs); @@ -1654,6 +1326,8 @@ child (void * param) pthread_cancel(uevent_thr); pthread_cancel(uxlsnr_thr); + sysfs_cleanup(); + free_keys(keys); keys = NULL; free_handlers(handlers); @@ -1670,7 +1344,7 @@ child (void * param) conf = NULL; condlog(2, "--------shut down-------"); - + if (logsink) log_thread_stop(); @@ -1738,8 +1412,9 @@ main (int argc, char *argv[]) extern int optind; int arg; int err; - + logsink = 1; + dm_init(); if (getuid() != 0) { fprintf(stderr, "need to be root\n"); @@ -1780,7 +1455,7 @@ main (int argc, char *argv[]) err = 0; else err = daemonize(); - + if (err < 0) /* error */ exit(1); diff --git a/multipathd/main.h b/multipathd/main.h index d0cce3a..1a6dc55 100644 --- a/multipathd/main.h +++ b/multipathd/main.h @@ -7,7 +7,7 @@ int reconfigure (struct vectors *); int ev_add_path (char *, struct vectors *); int ev_remove_path (char *, struct vectors *); -int ev_add_map (char *, struct vectors *); +int ev_add_map (struct sysfs_device *, struct vectors *); int ev_remove_map (char *, struct vectors *); #endif /* MAIN_H */ diff --git a/multipathd/multipathd.8 b/multipathd/multipathd.8 index 48b1b04..480b8ed 100644 --- a/multipathd/multipathd.8 +++ b/multipathd/multipathd.8 @@ -1,22 +1,102 @@ -.TH MULTIPATHD 8 "October 2004" "Linux Administrator's Manual" +.TH MULTIPATHD 8 "November 2006" "Linux Administrator's Manual" .SH NAME multipathd \- multipath daemon -.SH SYNOPSYS + +.SH SYNOPSIS .B multipathd +.RB [\| options \|] -This daemon is in charge of checking for failed paths. When this happens, +.SH DESCRIPTION +The +.B multipathd +daemon is in charge of checking for failed paths. When this happens, it will reconfigure the multipath map the path belongs to, so that this map -regain its maximum performance and redundancy. +regains its maximum performance and redundancy. This daemon executes the external multipath config tool when events occur. -In turn, the multipath tool signals the multipathd daemon it is done with +In turn, the multipath tool signals the multipathd daemon when it is done with devmap reconfiguration, so that it can refresh its failed path list. +.SH OPTIONS +.TP +.B \-d +Forground Mode. Don't daemonize, and print all messages to stdout and stderr. +.TP +.B -v "level" +Verbosity level. Print additional information while running multipathd. A level of 0 means only print errors. A level of 3 or greater prints debugging information as well. +.TP +.B -k +multipathd will enter interactive mode. From this mode, the available commands can be viewed by entering "help". When you are finished entering commands, press CTRL-D to quit. + +.SH COMMANDS +.TP +The following commands can be used in interactive mode: +.TP +.B list|show paths +Show the paths that multipathd is monitoring, and their state. +.TP +.B list|show maps|multipaths +Show the multipath devices that the multipathd is monitoring. +.TP +.B list|show maps|multipaths status +Show the status of all multipath devices that the multipathd is monitoring. +.TP +.B list|show maps|multipaths stats +Show some statistics of all multipath devices that the multipathd is monitoring. +.TP +.B list|show maps|multipaths topology +Show the current multipath topology. Same as "multipath -ll". +.TP +.B list|show topology +Show the current multipath topology. Same as "multipath -ll". +.TP +.B list|show map|multipath $map topology +Show topology of a single multipath device specified by $map, e.g. 36005076303ffc56200000000000010aa. +This map could be obtained from "list maps". +.TP +.B list|show config +Show the currently used configuration, derived from default values and values specified within the configuration file /etc/multipath.conf. +.TP +.B list|show blacklist +Show the currently used blacklist rules, derived from default values and values specified within the configuration file /etc/multipath.conf. +.TP +.B list|show devices +Show all available block devices by name including the information if they are blacklisted or not. +.TP +.B add path $path +Add a path to the list of monitored paths. $path is as listed in /sys/block (e.g. sda). +.TP +.B remove|del path $path +Stop monitoring a path. $path is as listed in /sys/block (e.g. sda). +.TP +.B add map $map +Add a multipath device to the list of monitored devices. $map can either be a device-mapper device as listed in /sys/block (e.g. dm-0) or it can be the alias for the multipath device (e.g. mpath1) or the uid of the multipath device (e.g. 36005076303ffc56200000000000010aa). +.TP +.B remove|del map $map +Stop monitoring a multipath device. +.TP +.B switch|switchgroup map $map group $group +Force a multipath device to switch to a specific path group. $group is the path group index, starting with 1. +.TP +.B reconfigure +Reconfigures the multipaths. This should be triggered automatically after any hotplug event. +.TP +.B suspend map|multipath $map +Sets map $map into suspend state. +.TP +.B resume map|multipath $map +Resumes map $map from suspend state. +.TP +.B fail path $path +Sets path $path into failed state. +.TP +.B reinstate path $path +Resumes path $path from failed state. + .SH "SEE ALSO" .BR multipath (8) .BR kpartx (8) .BR hotplug (8) .SH "AUTHORS" -This man page was assembled by Patrick Caulfield -for the Debian project. From documentation provided -by the multipath author Christophe Varoqui, and others. +.B multipathd +was developed by Christophe Varoqui, and others. diff --git a/multipathd/uxclnt.c b/multipathd/uxclnt.c index ff7b578..009e5cb 100644 --- a/multipathd/uxclnt.c +++ b/multipathd/uxclnt.c @@ -21,6 +21,9 @@ #include #include +#include +#include "cli.h" + /* * process the client */ @@ -29,6 +32,9 @@ static void process(int fd) char *line; char *reply; + cli_init(); + rl_readline_name = "multipathd"; + rl_completion_entry_function = key_generator; while ((line = readline("multipathd> "))) { size_t len; size_t llen = strlen(line); diff --git a/path_priority/pp_alua/Makefile b/path_priority/pp_alua/Makefile index 983ffe3..6f356a1 100644 --- a/path_priority/pp_alua/Makefile +++ b/path_priority/pp_alua/Makefile @@ -35,7 +35,7 @@ glibc: $(OBJS) klibc: $(OBJS) $(CC) -static -o $(EXEC) $(OBJS) -install: $(BUILD) $(EXEC).8.gz +install: $(EXEC) $(EXEC).8.gz $(INSTALL) -s -m 755 $(EXEC) $(DESTDIR)$(bindir)/$(EXEC) $(INSTALL) -m 644 $(EXEC).8.gz $(DESTDIR)$(mandir)/$(EXEC).8.gz diff --git a/path_priority/pp_alua/main.c b/path_priority/pp_alua/main.c index 190fbdc..ba8da99 100644 --- a/path_priority/pp_alua/main.c +++ b/path_priority/pp_alua/main.c @@ -12,8 +12,6 @@ * * This file is released under the GPL. */ -#include - #include #include @@ -241,7 +239,7 @@ main (int argc, char **argv) mknod( devicepath, S_IFBLK|S_IRUSR|S_IWUSR, - MKDEV(major, minor) + makedev(major, minor) ); } diff --git a/path_priority/pp_alua/mpath_prio_alua.8 b/path_priority/pp_alua/mpath_prio_alua.8 index 4843bcd..58568a5 100644 --- a/path_priority/pp_alua/mpath_prio_alua.8 +++ b/path_priority/pp_alua/mpath_prio_alua.8 @@ -1,4 +1,4 @@ -.TH MPATH_PRIO_ALUA 8 "7. June 2005" "multipath-tools" \ +.TH MPATH_PRIO_ALUA 8 "July 2006" "multipath-tools" \ "Linux Administrator's Manual" .SH NAME mpath_prio_alua \- Path priority tool based on Asymmetric LUn Access diff --git a/path_priority/pp_alua/rtpg.c b/path_priority/pp_alua/rtpg.c index 9aea560..701f9d5 100644 --- a/path_priority/pp_alua/rtpg.c +++ b/path_priority/pp_alua/rtpg.c @@ -21,6 +21,7 @@ #include #include #include +#include #define __user #include @@ -28,7 +29,7 @@ #include "rtpg.h" #define SENSE_BUFF_LEN 32 -#define DEF_TIMEOUT 60000 +#define DEF_TIMEOUT 300000 /* * Macro used to print debug messaged. @@ -251,14 +252,38 @@ do_rtpg(int fd, void* resp, long resplen) int get_asymmetric_access_state(int fd, unsigned int tpg) { - unsigned char buf[128]; + unsigned char *buf; struct rtpg_data * tpgd; struct rtpg_tpg_dscr * dscr; int rc; - - rc = do_rtpg(fd, buf, sizeof(buf)); + int buflen; + uint32_t scsi_buflen; + + buflen = 128; /* Initial value from old code */ + buf = (unsigned char *)malloc(buflen); + if (!buf) { + PRINT_DEBUG ("malloc failed: could not allocate" + "%u bytes\n", buflen); + return -RTPG_RTPG_FAILED; + } + rc = do_rtpg(fd, buf, buflen); if (rc < 0) return rc; + scsi_buflen = buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3]; + if (buflen < (scsi_buflen + 4)) { + free(buf); + buf = (unsigned char *)malloc(scsi_buflen); + if (!buf) { + PRINT_DEBUG ("malloc failed: could not allocate" + "%u bytes\n", scsi_buflen); + return -RTPG_RTPG_FAILED; + } + buflen = scsi_buflen; + rc = do_rtpg(fd, buf, buflen); + if (rc < 0) + goto out; + } + tpgd = (struct rtpg_data *) buf; rc = -RTPG_TPG_NOT_FOUND; @@ -274,7 +299,8 @@ get_asymmetric_access_state(int fd, unsigned int tpg) } } } - +out: + free(buf); return rc; } diff --git a/path_priority/pp_alua/spc3.h b/path_priority/pp_alua/spc3.h index 11f5dbd..bddbbdd 100644 --- a/path_priority/pp_alua/spc3.h +++ b/path_priority/pp_alua/spc3.h @@ -148,10 +148,10 @@ struct inquiry_data { /* ......x. = command queue support */ /* .......x = vs2 */ unsigned char vendor_identification[8]; - unsigned char product_identification[8]; + unsigned char product_identification[16]; unsigned char product_revision[4]; unsigned char vendor_specific[20]; - unsigned char b48; /* xxxx.... = reserved */ + unsigned char b56; /* xxxx.... = reserved */ /* ....xx.. = clocking */ /* ......x. = qas */ /* .......x = ius */ diff --git a/path_priority/pp_balance_units/Makefile b/path_priority/pp_balance_units/Makefile index bed7fb0..cb1e6c6 100644 --- a/path_priority/pp_balance_units/Makefile +++ b/path_priority/pp_balance_units/Makefile @@ -35,7 +35,7 @@ $(MULTIPATHLIB)-$(BUILD).a: install: install -d $(DESTDIR)$(bindir) - install -s -m 755 $(EXEC) $(DESTDIR)$(bindir)/ + $(INSTALL_PROGRAM) -m 755 $(EXEC) $(DESTDIR)$(bindir)/ uninstall: rm $(DESTDIR)$(bindir)/$(EXEC) diff --git a/path_priority/pp_balance_units/pp_balance_units.c b/path_priority/pp_balance_units/pp_balance_units.c index 307a959..ea70f13 100644 --- a/path_priority/pp_balance_units/pp_balance_units.c +++ b/path_priority/pp_balance_units/pp_balance_units.c @@ -3,15 +3,15 @@ * This code is GPLv2, see license file * * This path prioritizer aims to balance logical units over all - * controlers available. The logic is : + * controllers available. The logic is : * * - list all paths in all primary path groups - * - for each path, get the controler's serial - * - compute the number of active paths attached to each controler - * - compute the max number of paths attached to the same controler + * - for each path, get the controller's serial + * - compute the number of active paths attached to each controller + * - compute the max number of paths attached to the same controller * - if sums are already balanced or if the path passed as parameter is - * attached to controler with less active paths, then return - * (max_path_attached_to_one_controler - number_of_paths_on_this_controler) + * attached to controller with less active paths, then return + * (max_path_attached_to_one_controller - number_of_paths_on_this_controller) * - else, or if anything goes wrong, return 1 as a default prio * */ @@ -38,7 +38,7 @@ #define INQUIRY_CMDLEN 6 #define INQUIRY_CMD 0x12 #define SENSE_BUFF_LEN 32 -#define DEF_TIMEOUT 60000 +#define DEF_TIMEOUT 300000 #define RECOVERED_ERROR 0x01 #define MX_ALLOC_LEN 255 #define SCSI_CHECK_CONDITION 0x2 @@ -61,7 +61,7 @@ struct path { char serial[SERIAL_SIZE]; }; -struct controler { +struct controller { char serial[SERIAL_SIZE]; int path_count; }; @@ -172,7 +172,7 @@ do_inq(int sg_fd, int cmddt, int evpd, unsigned int pg_op, } static int -get_serial (char * str, char * devt) +get_serial (char * str, int maxlen, char * devt) { int fd; int len; @@ -181,20 +181,22 @@ get_serial (char * str, char * devt) fd = opennode(devt, O_RDONLY); if (fd < 0) - return 0; + return 1; if (0 == do_inq(fd, 0, 1, 0x80, buff, MX_ALLOC_LEN, 0)) { len = buff[3]; + if (len >= maxlen) + return 1; if (len > 0) { memcpy(str, buff + 4, len); buff[len] = '\0'; } close(fd); - return 1; + return 0; } closenode(devt, fd); - return 0; + return 1; } static void * @@ -358,7 +360,7 @@ get_paths (vector pathvec) if (pos == BEFOREPG) pos = INPG; - get_serial(pp->serial, pp->dev_t); + get_serial(pp->serial, SERIAL_SIZE, pp->dev_t); vector_alloc_slot(pathvec); vector_set_slot(pathvec, pp); debug("store %s [%s]", @@ -370,40 +372,40 @@ get_paths (vector pathvec) } static void * -find_controler (vector controlers, char * serial) +find_controller (vector controllers, char * serial) { int i; - struct controler * cp; + struct controller * cp; - if (!controlers) + if (!controllers) return NULL; - vector_foreach_slot (controlers, cp, i) + vector_foreach_slot (controllers, cp, i) if (!strncmp(cp->serial, serial, SERIAL_SIZE)) return cp; return NULL; } static void -get_controlers (vector controlers, vector pathvec) +get_controllers (vector controllers, vector pathvec) { int i; struct path * pp; - struct controler * cp; + struct controller * cp; - if (!controlers) + if (!controllers) return; vector_foreach_slot (pathvec, pp, i) { if (!pp || !strlen(pp->serial)) continue; - cp = find_controler(controlers, pp->serial); + cp = find_controller(controllers, pp->serial); if (!cp) { - cp = zalloc(sizeof(struct controler)); - vector_alloc_slot(controlers); - vector_set_slot(controlers, cp); + cp = zalloc(sizeof(struct controller)); + vector_alloc_slot(controllers); + vector_set_slot(controllers, cp); strncpy(cp->serial, pp->serial, SERIAL_SIZE); } cp->path_count++; @@ -411,17 +413,17 @@ get_controlers (vector controlers, vector pathvec) } static int -get_max_path_count (vector controlers) +get_max_path_count (vector controllers) { int i; int max = 0; - struct controler * cp; + struct controller * cp; - if (!controlers) + if (!controllers) return 0; - vector_foreach_slot (controlers, cp, i) { - debug("controler %s : %i paths", cp->serial, cp->path_count); + vector_foreach_slot (controllers, cp, i) { + debug("controller %s : %i paths", cp->serial, cp->path_count); if(cp->path_count > max) max = cp->path_count; } @@ -433,9 +435,9 @@ int main (int argc, char **argv) { vector pathvec = NULL; - vector controlers = NULL; + vector controllers = NULL; struct path * ref_path = NULL; - struct controler * cp = NULL; + struct controller * cp = NULL; int max_path_count = 0; ref_path = zalloc(sizeof(struct path)); @@ -449,18 +451,18 @@ main (int argc, char **argv) if (optinddev_t, argv[optind], WORD_SIZE); - get_serial(ref_path->serial, ref_path->dev_t); + get_serial(ref_path->serial, SERIAL_SIZE, ref_path->dev_t); if (!ref_path->serial || !strlen(ref_path->serial)) exit_tool(0); pathvec = vector_alloc(); - controlers = vector_alloc(); + controllers = vector_alloc(); get_paths(pathvec); - get_controlers(controlers, pathvec); - max_path_count = get_max_path_count(controlers); - cp = find_controler(controlers, ref_path->serial); + get_controllers(controllers, pathvec); + max_path_count = get_max_path_count(controllers); + cp = find_controller(controllers, ref_path->serial); if (!cp) { debug("no other active path on serial %s\n", diff --git a/path_priority/pp_emc/Makefile b/path_priority/pp_emc/Makefile index 651bdcd..93e6075 100644 --- a/path_priority/pp_emc/Makefile +++ b/path_priority/pp_emc/Makefile @@ -14,7 +14,7 @@ klibc: $(OBJS) $(CC) -static -o $(EXEC) $(OBJS) install: $(EXEC) - install -s -m 755 $(EXEC) $(DESTDIR)$(bindir)/$(EXEC) + $(INSTALL_PROGRAM) -m 755 $(EXEC) $(DESTDIR)$(bindir)/$(EXEC) uninstall: rm $(DESTDIR)$(bindir)/$(EXEC) diff --git a/path_priority/pp_emc/pp_emc.c b/path_priority/pp_emc/pp_emc.c index dd58424..4031720 100644 --- a/path_priority/pp_emc/pp_emc.c +++ b/path_priority/pp_emc/pp_emc.c @@ -60,8 +60,12 @@ int emc_clariion_prio(const char *dev) if ( /* Effective initiator type */ sense_buffer[27] != 0x03 - /* Failover mode should be set to 1 */ - || (sense_buffer[28] & 0x07) != 0x04 + /* + * Failover mode should be set to 1 (PNR failover mode) + * or 4 (ALUA failover mode). + */ + || (((sense_buffer[28] & 0x07) != 0x04) && + ((sense_buffer[28] & 0x07) != 0x06)) /* Arraycommpath should be set to 1 */ || (sense_buffer[30] & 0x04) != 0x04) { fprintf(stderr, "Path not correctly configured for failover"); diff --git a/path_priority/pp_hds_modular/Makefile b/path_priority/pp_hds_modular/Makefile new file mode 100644 index 0000000..ca00ca7 --- /dev/null +++ b/path_priority/pp_hds_modular/Makefile @@ -0,0 +1,22 @@ +EXEC = mpath_prio_hds_modular +BUILD = glibc +OBJS = pp_hds_modular.o + +TOPDIR = ../.. +include $(TOPDIR)/Makefile.inc + +all: $(BUILD) + +glibc: $(OBJS) + $(CC) -o $(EXEC) $(OBJS) $(LDFLAGS) + +klibc: $(OBJS) + $(CC) -static -o $(EXEC) $(OBJS) + +install: $(EXEC) + $(INSTALL_PROGRAM) -m 755 $(EXEC) $(DESTDIR)$(bindir)/$(EXEC) + +uninstall: + rm $(DESTDIR)$(bindir)/$(EXEC) +clean: + rm -f *.o $(EXEC) diff --git a/path_priority/pp_hds_modular/pp_hds_modular.c b/path_priority/pp_hds_modular/pp_hds_modular.c new file mode 100644 index 0000000..7411508 --- /dev/null +++ b/path_priority/pp_hds_modular/pp_hds_modular.c @@ -0,0 +1,211 @@ +/* + * (C) Copyright HDS GmbH 2006. All Rights Reserved. + * + * pp_hds_modular.c + * Version 2.00 + * + * Prioritizer for Device Mapper Multipath and HDS Storage + * + * Hitachis Modular Storage contains two controllers for redundancy. The + * Storage internal LUN (LDEV) will normally allocated via two pathes to the + * server (one path per controller). For performance reasons should the server + * access to a LDEV only via one controller. The other path to the other + * controller is stand-by. It is also possible to allocate more as one path + * for a LDEV per controller. Here is active/active access allowed. The other + * pathes via the other controller are stand-by. + * + * This prioritizer checks with inquiry command the represented LDEV and + * Controller number and gives back a priority followed by this scheme: + * + * CONTROLLER ODD and LDEV ODD: PRIORITY 1 + * CONTROLLER ODD and LDEV EVEN: PRIORITY 0 + * CONTROLLER EVEN and LDEV ODD: PRIORITY 0 + * CONTROLLER EVEN and LDEV EVEN: PRIORITY 1 + * + * In the storage you can define for each LDEV a owner controller. If the + * server makes IOs via the other controller the storage will switch the + * ownership automatically. In this case you can see in the storage that the + * current controller is different from the default controller, but this is + * absolutely no problem. + * + * With this prioritizer it is possible to establish a static load balancing. + * Half of the LUNs are accessed via one HBA/storage controller and the other + * half via the other HBA/storage controller. + * + * In cluster environmemnts (RAC) it also guarantees that all cluster nodes have + * access to the LDEVs via the same controller. + * + * You can run the prioritizer manually in verbose mode: + * # pp_hds_modular -v 8:224 + * VENDOR: HITACHI + * PRODUCT: DF600F-CM + * SERIAL: 0x0105 + * LDEV: 0x00C6 + * CTRL: 1 + * PORT: B + * CTRL ODD, LDEV EVEN, PRIO 0 + * + * To compile this source please execute # cc pp_hds_modular.c -o /sbin/mpath_prio_hds_modular + * + * Changes 2006-07-16: + * - Changed to forward declaration of functions + * - The switch-statement was changed to a logical expression + * - unlinking of the devpath now also occurs at the end of + * hds_modular_prio to avoid old /tmp/.pp_balance.%u.%u.devnode + * entries in /tmp-Directory + * - The for-statements for passing variables where changed to + * snprintf-commands in verbose mode + * Changes 2006-08-10: + * - Back to the old switch statements because the regular expression does + * not work under RHEL4 U3 i386 + * Changes 2007-06-27: + * - switched from major:minor argument to device node argument + * + * This file is released under the GPL. + * + */ + +#include +#include +#include +#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 FILE_NAME_SIZE 255 + +int verbose=0; + +void print_help (void); +int hds_modular_prio (const char *); + +int main (int argc, char **argv) +{ + int prio; + if (argc == 2) + { + if (strcmp (argv[1], "-h") == 0) + { + print_help (); + exit (0); + } + else + { + verbose = 0; + prio = hds_modular_prio (argv[1]); + printf ("%d\n", prio); + exit (0); + } + } + if ((argc == 3) && (strcmp (argv[1], "-v")) == 0) + { + verbose = 1; + prio = hds_modular_prio (argv[2]); + printf ("%d\n", prio); + exit (0); + } + print_help (); + exit (1); +} + +int hds_modular_prio (const char *dev) +{ + int sg_fd, k; + char vendor[8]; + char product[32]; + char serial[32]; + char ldev[32]; + char ctrl[32]; + char port[32]; + unsigned char inqCmdBlk[INQ_CMD_LEN] = { INQ_CMD_CODE, 0, 0, 0, INQ_REPLY_LEN, 0 }; + unsigned char inqBuff[INQ_REPLY_LEN]; + unsigned char *inqBuffp = inqBuff; + unsigned char sense_buffer[32]; + sg_io_hdr_t io_hdr; + + if ((sg_fd = open (dev, O_RDONLY)) < 0) exit (1); + if ((ioctl (sg_fd, SG_GET_VERSION_NUM, &k) < 0) || (k < 30000)) exit (1); + + memset (&io_hdr, 0, sizeof (sg_io_hdr_t)); + io_hdr.interface_id = 'S'; + io_hdr.cmd_len = sizeof (inqCmdBlk); + io_hdr.mx_sb_len = sizeof (sense_buffer); + io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; + io_hdr.dxfer_len = INQ_REPLY_LEN; + io_hdr.dxferp = inqBuff; + io_hdr.cmdp = inqCmdBlk; + io_hdr.sbp = sense_buffer; + io_hdr.timeout = 2000; /* TimeOut = 2 seconds */ + + if (ioctl (sg_fd, SG_IO, &io_hdr) < 0) exit (1); + if ((io_hdr.info & SG_INFO_OK_MASK) != SG_INFO_OK) exit (1); + + snprintf (vendor, 9, "%.8s\n", inqBuffp + 8); + snprintf (product, 17, "%.16s", inqBuffp + 16); + snprintf (serial, 5, "%.4s", inqBuffp + 40); + snprintf (ldev, 5, "%.4s", inqBuffp + 44); + snprintf (ctrl, 2, "%.1s", inqBuffp + 49); + snprintf (port, 2, "%.1s", inqBuffp + 50); + + close (sg_fd); + + if (verbose) + { + printf ("VENDOR: %s\n", vendor); + printf ("PRODUCT: %s\n", product); + printf ("SERIAL: 0x%s\n", serial); + printf ("LDEV: 0x%s\n", ldev); + printf ("CTRL: %s\n", ctrl); + printf ("PORT: %s\n", port); + } + + switch (ctrl[0]) { + case '0': case '2': case '4': case '6': case '8': + switch (ldev[3]) { + case '0': case '2': case '4': case '6': case '8': case 'A': case 'C': case 'E': + if (1 == verbose) printf("CTRL EVEN, LDEV EVEN, PRIO 1\n"); + return 1; + break; + case '1': case '3': case '5': case '7': case '9': case 'B': case 'D': case 'F': + if (1 == verbose) printf("CTRL EVEN, LDEV ODD, PRIO 0\n"); + return 0; + break; + } + case '1': case '3': case '5': case '7': case '9': + switch (ldev[3]) { + case '0': case '2': case '4': case '6': case '8': case 'A': case 'C': case 'E': + if (1 == verbose) printf("CTRL ODD, LDEV EVEN, PRIO 0\n"); + return 0; + break; + case '1': case '3': case '5': case '7': case '9': case 'B': case 'D': case 'F': + if (1 == verbose) printf("CTRL ODD, LDEV ODD, PRIO 1\n"); + return 1; + break; + } + } + return 0; +} + +void print_help (void) +{ + printf ("\n"); + printf ("Usage: pp_hds_modular [-v] \n"); + printf ("Option: -v verbose mode\n"); + printf ("Description: Prioritizer for Device Mapper Multipath and HDS Storage\n"); + printf ("Version: 2.00\n"); + printf ("Author: Matthias Rudolph \n"); + printf ("Remarks: Prioritizer CTRL#1 corresponds to hardware CTRL#0\n"); + printf (" Prioritizer CTRL#2 corresponds to hardware CTRL#1\n"); + printf ("\n"); + return; +} + diff --git a/path_priority/pp_hp_sw/Makefile b/path_priority/pp_hp_sw/Makefile new file mode 100644 index 0000000..e7debf5 --- /dev/null +++ b/path_priority/pp_hp_sw/Makefile @@ -0,0 +1,25 @@ +EXEC = mpath_prio_hp_sw +BUILD = glibc +OBJS = pp_hp_sw.o + +TOPDIR = ../.. +include $(TOPDIR)/Makefile.inc + +all: $(BUILD) + +glibc: $(OBJS) + $(CC) -o $(EXEC) $(OBJS) $(LDFLAGS) + +klibc: $(OBJS) + $(CC) -static -o $(EXEC) $(OBJS) + +install: $(EXEC) + install -m 755 $(EXEC) $(DESTDIR)$(bindir)/$(EXEC) + +uninstall: + rm $(DESTDIR)$(bindir)/$(EXEC) +clean: + rm -f *.o $(EXEC) + +%.o: %.c + $(CC) $(CFLAGS) -c -o $@ $< diff --git a/path_priority/pp_hp_sw/pp_hp_sw.c b/path_priority/pp_hp_sw/pp_hp_sw.c new file mode 100644 index 0000000..e4a18b1 --- /dev/null +++ b/path_priority/pp_hp_sw/pp_hp_sw.c @@ -0,0 +1,119 @@ +/* + * Path priority checker for HP active/standby controller + * + * Check the path state and sort them into groups. + * There is actually a preferred path in the controller; + * we should ask HP on how to retrieve that information. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TUR_CMD_LEN 6 +#define SCSI_CHECK_CONDITION 0x2 +#define SCSI_COMMAND_TERMINATED 0x22 +#define SG_ERR_DRIVER_SENSE 0x08 +#define RECOVERED_ERROR 0x01 +#define NOT_READY 0x02 +#define UNIT_ATTENTION 0x06 + +#define HP_PATH_ACTIVE 0x04 +#define HP_PATH_STANDBY 0x02 +#define HP_PATH_FAILED 0x00 + +#include "../../libmultipath/sg_include.h" + +int hp_sw_prio(const char *dev) +{ + unsigned char turCmdBlk[TUR_CMD_LEN] = { 0x00, 0, 0, 0, 0, 0 }; + unsigned char sb[128]; + struct sg_io_hdr io_hdr; + int ret = HP_PATH_FAILED; + int fd; + + fd = open(dev, O_RDWR|O_NONBLOCK); + + if (fd <= 0) { + fprintf(stderr, "Opening the device failed.\n"); + goto out; + } + + memset(&io_hdr, 0, sizeof (struct sg_io_hdr)); + io_hdr.interface_id = 'S'; + io_hdr.cmd_len = sizeof (turCmdBlk); + io_hdr.mx_sb_len = sizeof (sb); + io_hdr.dxfer_direction = SG_DXFER_NONE; + io_hdr.cmdp = turCmdBlk; + io_hdr.sbp = sb; + io_hdr.timeout = 60000; + io_hdr.pack_id = 0; + retry: + if (ioctl(fd, SG_IO, &io_hdr) < 0) { + fprintf(stderr, "sending tur command failed\n"); + goto out; + } + io_hdr.status &= 0x7e; + if ((0 == io_hdr.status) && (0 == io_hdr.host_status) && + (0 == io_hdr.driver_status)) { + /* Command completed normally, path is active */ + ret = HP_PATH_ACTIVE; + } + + if ((SCSI_CHECK_CONDITION == io_hdr.status) || + (SCSI_COMMAND_TERMINATED == io_hdr.status) || + (SG_ERR_DRIVER_SENSE == (0xf & io_hdr.driver_status))) { + if (io_hdr.sbp && (io_hdr.sb_len_wr > 2)) { + int sense_key, asc, asq; + unsigned char * sense_buffer = io_hdr.sbp; + if (sense_buffer[0] & 0x2) { + sense_key = sense_buffer[1] & 0xf; + asc = sense_buffer[2]; + asq = sense_buffer[3]; + } else { + sense_key = sense_buffer[2] & 0xf; + asc = sense_buffer[12]; + asq = sense_buffer[13]; + } + if(RECOVERED_ERROR == sense_key) + ret = HP_PATH_ACTIVE; + if(NOT_READY == sense_key) { + if (asc == 0x04 && asq == 0x02) { + /* This is a standby path */ + ret = HP_PATH_STANDBY; + } + } + if(UNIT_ATTENTION == sense_key) { + if (asc == 0x29) { + /* Retry for device reset */ + goto retry; + } + } + } + } + + close(fd); + +out: + return(ret); +} + +int +main (int argc, char **argv) +{ + int prio; + if (argc != 2) { + fprintf(stderr, "Arguments wrong!\n"); + prio = 0; + } else + prio = hp_sw_prio(argv[1]); + + printf("%d\n", prio); + exit(0); +} + diff --git a/path_priority/pp_netapp/Makefile b/path_priority/pp_netapp/Makefile index 9e7d3a3..b29d002 100644 --- a/path_priority/pp_netapp/Makefile +++ b/path_priority/pp_netapp/Makefile @@ -14,7 +14,7 @@ klibc: $(OBJS) $(CC) -static -o $(EXEC) $(OBJS) install: $(EXEC) - install -s -m 755 $(EXEC) $(DESTDIR)$(bindir)/$(EXEC) + $(INSTALL_PROGRAM) -m 755 $(EXEC) $(DESTDIR)$(bindir)/$(EXEC) uninstall: rm $(DESTDIR)$(bindir)/$(EXEC) diff --git a/path_priority/pp_random/Makefile b/path_priority/pp_random/Makefile index 85f42a2..85d7c2f 100644 --- a/path_priority/pp_random/Makefile +++ b/path_priority/pp_random/Makefile @@ -14,7 +14,7 @@ klibc: $(OBJS) $(CC) -static -o $(EXEC) $(OBJS) install: $(EXEC) - install -s -m 755 $(EXEC) $(DESTDIR)$(bindir)/$(EXEC) + $(INSTALL_PROGRAM) -m 755 $(EXEC) $(DESTDIR)$(bindir)/$(EXEC) uninstall: rm $(DESTDIR)$(bindir)/$(EXEC) diff --git a/path_priority/pp_rdac/Makefile b/path_priority/pp_rdac/Makefile new file mode 100644 index 0000000..64ed4c3 --- /dev/null +++ b/path_priority/pp_rdac/Makefile @@ -0,0 +1,22 @@ +EXEC = mpath_prio_rdac +BUILD = glibc +OBJS = pp_rdac.o + +TOPDIR = ../.. +include $(TOPDIR)/Makefile.inc + +all: $(BUILD) + +glibc: $(OBJS) + $(CC) -o $(EXEC) $(OBJS) $(LDFLAGS) + +klibc: $(OBJS) + $(CC) -static -o $(EXEC) $(OBJS) + +install: $(EXEC) + $(INSTALL_PROGRAM) -m 755 $(EXEC) $(DESTDIR)$(bindir)/$(EXEC) + +uninstall: + rm $(DESTDIR)$(bindir)/$(EXEC) +clean: + rm -f *.o $(EXEC) diff --git a/path_priority/pp_rdac/pp_rdac.c b/path_priority/pp_rdac/pp_rdac.c new file mode 100644 index 0000000..49a13cf --- /dev/null +++ b/path_priority/pp_rdac/pp_rdac.c @@ -0,0 +1,112 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../../libmultipath/sg_include.h" + +#define INQUIRY_CMD 0x12 +#define INQUIRY_CMDLEN 6 + +int rdac_prio(const char *dev) +{ + unsigned char sense_buffer[256]; + unsigned char sb[128]; + unsigned char inqCmdBlk[INQUIRY_CMDLEN] = {INQUIRY_CMD, 1, 0xC9, 0, + sizeof(sb), 0}; + struct sg_io_hdr io_hdr; + int ret = 0; + int fd; + + fd = open(dev, O_RDWR|O_NONBLOCK); + + if (fd <= 0) { + fprintf(stderr, "opening of the device failed.\n"); + goto out; + } + + memset(&io_hdr, 0, sizeof (struct sg_io_hdr)); + io_hdr.interface_id = 'S'; + io_hdr.cmd_len = sizeof (inqCmdBlk); + io_hdr.mx_sb_len = sizeof (sb); + io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; + io_hdr.dxfer_len = sizeof (sense_buffer); + io_hdr.dxferp = sense_buffer; + io_hdr.cmdp = inqCmdBlk; + io_hdr.sbp = sb; + io_hdr.timeout = 60000; + io_hdr.pack_id = 0; + if (ioctl(fd, SG_IO, &io_hdr) < 0) { + fprintf(stderr, "sending inquiry command failed\n"); + goto out; + } + if (io_hdr.info & SG_INFO_OK_MASK) { + fprintf(stderr, "inquiry command indicates error"); + goto out; + } + + close(fd); + + if (/* Verify the code page - right page & page identifier */ + sense_buffer[1] != 0xc9 || + sense_buffer[3] != 0x2c || + sense_buffer[4] != 'v' || + sense_buffer[5] != 'a' || + sense_buffer[6] != 'c' ) { + fprintf(stderr, "Volume access control page in unknown format"); + goto out; + } + + if ( /* Current Volume Path Bit */ + ( sense_buffer[8] & 0x01) == 0x01 ) { + /* + * This volume was owned by the controller receiving + * the inquiry command. + */ + ret |= 0x01; + } + + /* Volume Preferred Path Priority */ + switch ( sense_buffer[9] & 0x0F ) { + case 0x01: + /* + * Access to this volume is most preferred through + * this path and other paths with this value. + */ + ret |= 0x02; + break; + case 0x02: + /* + * Access to this volume through this path is to be used + * as a secondary path. Typically this path would be used + * for fail-over situations. + */ + /* Fallthrough */ + default: + /* Reserved values */ + break; + } + +out: + return(ret); +} + +int +main (int argc, char **argv) +{ + int prio; + if (argc != 2) { + fprintf(stderr, "Wrong number of arguments.\n"); + fprintf(stderr, "Usage: %s device\n", argv[0]); + prio = 0; + } else + prio = rdac_prio(argv[1]); + + printf("%d\n", prio); + exit(0); +} diff --git a/path_priority/pp_tpc/Makefile b/path_priority/pp_tpc/Makefile deleted file mode 100644 index 86841dd..0000000 --- a/path_priority/pp_tpc/Makefile +++ /dev/null @@ -1,22 +0,0 @@ -EXEC = mpath_prio_tpc -BUILD = glibc -OBJS = pp_tpc.o - -TOPDIR = ../.. -include $(TOPDIR)/Makefile.inc - -all: $(BUILD) - -glibc: $(OBJS) - $(CC) -o $(EXEC) $(OBJS) $(LDFLAGS) - -klibc: $(OBJS) - $(CC) -static -o $(EXEC) $(OBJS) - -install: $(EXEC) - install -s -m 755 $(EXEC) $(DESTDIR)$(bindir)/$(EXEC) - -uninstall: - rm $(DESTDIR)$(bindir)/$(EXEC) -clean: - rm -f *.o $(EXEC) diff --git a/path_priority/pp_tpc/pp_tpc.c b/path_priority/pp_tpc/pp_tpc.c deleted file mode 100644 index 76e7c47..0000000 --- a/path_priority/pp_tpc/pp_tpc.c +++ /dev/null @@ -1,118 +0,0 @@ -#include -#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); -}