Makefile | 4 + devmap_name/devmap_name.8 | 2 +- kpartx/dasd.c | 28 +- kpartx/devmapper.c | 46 +++- kpartx/devmapper.h | 4 +- kpartx/kpartx.8 | 2 +- kpartx/kpartx.c | 15 +- libcheckers/Makefile | 2 +- libcheckers/checkers.c | 13 +- libcheckers/checkers.h | 61 +++- libcheckers/emc_clariion.c | 157 ++++++-- libcheckers/hp_sw.c | 3 +- libcheckers/libsg.c | 79 ++++ libcheckers/libsg.h | 8 + libcheckers/rdac.c | 109 +++++ libcheckers/rdac.h | 8 + libcheckers/readsector0.c | 76 +---- libcheckers/tur.c | 2 +- libmultipath/Makefile | 9 +- libmultipath/alias.c | 97 +++-- libmultipath/blacklist.c | 179 ++++++++-- libmultipath/blacklist.h | 21 +- libmultipath/config.c | 100 ++++- libmultipath/config.h | 15 +- libmultipath/configure.c | 74 ++-- libmultipath/configure.h | 2 +- libmultipath/debug.c | 5 +- libmultipath/debug.h | 4 +- libmultipath/defaults.h | 1 + libmultipath/devmapper.c | 111 +++++- libmultipath/devmapper.h | 3 +- libmultipath/dict.c | 229 +++++++++++- libmultipath/discovery.c | 51 ++- libmultipath/discovery.h | 6 +- libmultipath/dmparser.c | 19 +- libmultipath/hwtable.c | 198 ++++++++-- libmultipath/lock.c | 8 + libmultipath/lock.h | 22 + libmultipath/parser.c | 15 +- libmultipath/parser.h | 1 + libmultipath/pgpolicies.h | 2 +- libmultipath/print.c | 266 ++++++++++++- libmultipath/print.h | 3 + libmultipath/propsel.c | 71 +++- libmultipath/propsel.h | 1 + libmultipath/structs.c | 15 +- libmultipath/structs.h | 21 +- libmultipath/structs_vec.c | 95 +++++- libmultipath/structs_vec.h | 14 +- libmultipath/switchgroup.c | 2 +- libmultipath/util.c | 9 + libmultipath/util.h | 1 + libmultipath/version.h | 37 ++ libmultipath/waiter.c | 234 +++++++++++ libmultipath/waiter.h | 23 ++ multipath-tools.spec.in | 1 + multipath.conf.annotated | 22 +- multipath.conf.synthetic | 6 +- multipath/main.c | 36 ++- multipath/main.h | 39 -- multipath/multipath.8 | 15 +- multipathd/cli.c | 232 ++++++++++-- multipathd/cli.h | 7 + multipathd/cli_handlers.c | 94 +++++- multipathd/cli_handlers.h | 2 + multipathd/main.c | 439 +++------------------ multipathd/multipathd.8 | 96 ++++- multipathd/uxclnt.c | 6 + path_priority/pp_alua/main.c | 4 +- path_priority/pp_alua/mpath_prio_alua.8 | 2 +- path_priority/pp_alua/rtpg.c | 36 ++- path_priority/pp_alua/spc3.h | 4 +- path_priority/pp_balance_units/pp_balance_units.c | 74 ++-- path_priority/pp_hds_modular/Makefile | 22 + path_priority/pp_hds_modular/pp_hds_modular.c | 252 ++++++++++++ path_priority/pp_tpc/pp_tpc.c | 12 +- 76 files changed, 3026 insertions(+), 958 deletions(-) diff --git a/Makefile b/Makefile index 83ae2fe..aacede3 100644 --- a/Makefile +++ b/Makefile @@ -22,7 +22,11 @@ export KRNLOBJ BUILDDIRS = $(shell find . -mindepth 2 -name Makefile -exec dirname {} \; | grep -v ^lib) +ifeq ($(MULTIPATH_VERSION),) VERSION = $(shell basename ${PWD} | cut -d'-' -f3) +else +VERSION = $(MULTIPATH_VERSION) +endif all: recurse diff --git a/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/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, s /* 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, s 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, s /* * 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..5b27487 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,10 +86,26 @@ dm_addmap (int task, const char *name, c 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 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; @@ -178,3 +199,26 @@ out: return ret; } +char * +dm_mapuuid(int major, int minor) +{ + struct dm_task *dmt; + char *tmp, *uuid = NULL; + + if (!(dmt = dm_task_create(DM_DEVICE_INFO))) + return NULL; + + dm_task_no_open_count(dmt); + dm_task_set_major(dmt, major); + dm_task_set_minor(dmt, minor); + + if (!dm_task_run(dmt)) + goto out; + + tmp = dm_task_get_uuid(dmt); + if (tmp[0] != '\0') + uuid = strdup(tmp); +out: + dm_task_destroy(dmt); + return uuid; +} diff --git a/kpartx/devmapper.h b/kpartx/devmapper.h index 9607476..e20e456 100644 --- a/kpartx/devmapper.h +++ b/kpartx/devmapper.h @@ -1,6 +1,8 @@ int dm_prereq (char *, int, int, int); int dm_simplecmd (int, const char *); -int dm_addmap (int, const char *, const char *, const char *, unsigned long); +int dm_addmap (int, const char *, const char *, const char *, unsigned long, + char *, int); int dm_map_present (char *); char * dm_mapname(int major, int minor); dev_t dm_get_first_dep(char *devname); +char * dm_mapuuid(int major, int minor); diff --git a/kpartx/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..0ee0cb3 100644 --- a/kpartx/kpartx.c +++ b/kpartx/kpartx.c @@ -192,6 +192,7 @@ main(int argc, char **argv){ char partname[PARTNAME_SIZE], params[PARTNAME_SIZE + 16]; char * loopdev = NULL; char * delim = NULL; + char *uuid = NULL; int loopro = 0; int hotplug = 0; struct stat buf; @@ -284,11 +285,6 @@ main(int argc, char **argv){ } if (S_ISREG (buf.st_mode)) { - loopdev = malloc(LO_NAME_SIZE * sizeof(char)); - - if (!loopdev) - exit(1); - /* already looped file ? */ loopdev = find_loop_by_file(device); @@ -313,6 +309,13 @@ main(int argc, char **argv){ } off = find_devname_offset(device); + + if (!loopdev) + uuid = dm_mapuuid((unsigned int)MAJOR(buf.st_rdev), + (unsigned int)MINOR(buf.st_rdev)); + if (!uuid) + uuid = device + off; + fd = open(device, O_RDONLY); if (fd == -1) { @@ -420,7 +423,7 @@ main(int argc, char **argv){ DM_DEVICE_RELOAD : DM_DEVICE_CREATE); dm_addmap(op, partname, DM_TARGET, params, - slices[j].size); + slices[j].size, uuid, j+1); if (op == DM_DEVICE_RELOAD) dm_simplecmd(DM_DEVICE_RESUME, diff --git a/libcheckers/Makefile b/libcheckers/Makefile index ec8c10d..6340a68 100644 --- a/libcheckers/Makefile +++ b/libcheckers/Makefile @@ -6,7 +6,7 @@ BUILD = glibc include ../Makefile.inc -OBJS = checkers.o readsector0.o tur.o directio.o emc_clariion.o hp_sw.o +OBJS = libsg.o checkers.o readsector0.o tur.o directio.o emc_clariion.o hp_sw.o rdac.o all: $(BUILD) diff --git a/libcheckers/checkers.c b/libcheckers/checkers.c index 53770a6..a49ad59 100644 --- a/libcheckers/checkers.c +++ b/libcheckers/checkers.c @@ -7,6 +7,7 @@ #include "tur.h" #include "hp_sw.h" #include "emc_clariion.h" +#include "rdac.h" #include "readsector0.h" static struct checker checkers[] = { @@ -48,6 +49,15 @@ static struct checker checkers[] = { }, { .fd = 0, + .name = RDAC, + .message = "", + .context = NULL, + .check = rdac, + .init = rdac_init, + .free = rdac_free + }, + { + .fd = 0, .name = READSECTOR0, .message = "", .context = NULL, @@ -75,8 +85,9 @@ struct checker * checker_lookup (char * return NULL; } -int checker_init (struct checker * c) +int checker_init (struct checker * c, void ** mpctxt_addr) { + c->mpcontext = mpctxt_addr; return c->init(c); } diff --git a/libcheckers/checkers.h b/libcheckers/checkers.h index ac795ed..d4dad9c 100644 --- a/libcheckers/checkers.h +++ b/libcheckers/checkers.h @@ -2,7 +2,44 @@ #define _CHECKERS_H /* - * path states + * + * Userspace (multipath/multipathd) path states + * + * PATH_WILD: + * - Use: None of the checkers (returned if we don't have an fd) + * - Description: Corner case where "fd <= 0" for path fd (see checker_check()) + * + * PATH_UNCHECKED: + * - Use: Only in directio checker + * - Description: set when fcntl(F_GETFL) fails to return flags or O_DIRECT + * not include in flags, or O_DIRECT read fails + * - Notes: + * - multipathd: uses it to skip over paths in sync_map_state() + * - multipath: used in update_paths(); if state==PATH_UNCHECKED, call + * pathinfo() + * + * PATH_DOWN: + * - Use: All checkers (directio, emc_clariion, hp_sw, readsector0, tur) + * - Description: Either a) SG_IO ioctl failed, or b) check condition on some + * SG_IO ioctls that succeed (tur, readsector0 checkers); path is down and + * you shouldn't try to send commands to it + * + * PATH_UP: + * - Use: All checkers (directio, emc_clariion, hp_sw, readsector0, tur) + * - Description: Path is up and I/O can be sent to it + * + * PATH_SHAKY: + * - Use: Only emc_clariion + * - Description: Indicates path not available for "normal" operations + * + * PATH_GHOST: + * - Use: Only hp_sw + * - Description: Indicates a "passive/standby" path on active/passive HP + * arrays. These paths will return valid answers to certain SCSI commands + * (tur, read_capacity, inquiry, start_stop), but will fail I/O commands. + * The path needs an initialization command to be sent to it in order for + * I/Os to succeed. + * */ #define PATH_WILD -1 #define PATH_UNCHECKED 0 @@ -14,12 +51,28 @@ #define DIRECTIO "directio" #define TUR "tur" #define HP_SW "hp_sw" +#define RDAC "rdac" #define EMC_CLARIION "emc_clariion" #define READSECTOR0 "readsector0" #define DEFAULT_CHECKER READSECTOR0 /* + * Overloaded storage response time can be very long. + * SG_IO timouts after DEF_TIMEOUT milliseconds, and checkers interprets this + * as a path failure. multipathd then proactively evicts the path from the DM + * multipath table in this case. + * + * This generaly snow balls and ends up in full eviction and IO errors for end + * users. Bad. This may also cause SCSI bus resets, causing disruption for all + * local and external storage hardware users. + * + * Provision a long timeout. Longer than any real-world application would cope + * with. + */ +#define DEF_TIMEOUT 300000 + +/* * strings lengths */ #define CHECKER_NAME_LEN 16 @@ -31,14 +84,16 @@ struct checker { char name[CHECKER_NAME_LEN]; char message[CHECKER_MSG_LEN]; /* comm with callers */ void * context; /* store for persistent data */ + void ** mpcontext; /* store for persistent data + shared multipath-wide */ int (*check)(struct checker *); int (*init)(struct checker *); /* to allocate the context */ void (*free)(struct checker *); /* to free the context */ }; -#define MSG(c, a) snprintf((c)->message, CHECKER_MSG_LEN, a); +#define MSG(c, fmt, args...) snprintf((c)->message, CHECKER_MSG_LEN, fmt, ##args); -int checker_init (struct checker *); +int checker_init (struct checker *, void **); void checker_put (struct checker *); void checker_reset (struct checker * c); void checker_set_fd (struct checker *, int); diff --git a/libcheckers/emc_clariion.c b/libcheckers/emc_clariion.c index 1d7b684..384a943 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 * 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,7 +122,8 @@ 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; } @@ -79,24 +133,24 @@ int emc_clariion(struct checker * c) || (sense_buffer[28] & 0x07) != 0x04 /* Arraycommpath should be set to 1 */ || (sense_buffer[30] & 0x04) != 0x04) { - MSG(c, "emc_clariion_checker: Path not correctly configured for failover"); + 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 +159,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 +168,59 @@ int emc_clariion(struct checker * c) ct->wwn_set = 1; } - - MSG(c, "emc_clariion_checker: Path healthy"); - return PATH_UP; + /* + * Issue read on active path to determine if inactive snapshot. + */ + if (sense_buffer[4] == 2) {/* if active path */ + unsigned char buf[512]; + + ret = sg_read(c->fd, &buf[0], sbb = &sb[0]); + if (ret == PATH_DOWN) { + hexadecimal_to_ascii(ct->wwn, wwnstr); + + /* + * Check for inactive snapshot LU this way. Must + * fail these. + */ + if (((sbb[2]&0xf) == 5) && (sbb[12] == 0x25) && + (sbb[13]==1)) { + /* + * Do this so that we can fail even the + * passive paths which will return + * 02/04/03 not 05/25/01 on read. + */ + SET_INACTIVE_SNAP(c); + MSG(c, "emc_clariion_checker: Active " + "path to inactive snapshot WWN %s.", + wwnstr); + } else + MSG(c, "emc_clariion_checker: Read " + "error for WWN %s. Sense data are " + "0x%x/0x%x/0x%x.", wwnstr, + sbb[2]&0xf, sbb[12], sbb[13]); + } else { + MSG(c, "emc_clariion_checker: Active path is " + "healthy."); + /* + * Remove the path from the set of paths to inactive + * snapshot LUs if it was in this list since the + * snapshot is no longer inactive. + */ + CLR_INACTIVE_SNAP(c); + } + } else { + if (IS_INACTIVE_SNAP(c)) { + hexadecimal_to_ascii(ct->wwn, wwnstr); + MSG(c, "emc_clariion_checker: Passive " + "path to inactive snapshot WWN %s.", + wwnstr); + ret = PATH_DOWN; + } else { + MSG(c, + "emc_clariion_checker: Passive path is healthy."); + ret = PATH_UP; /* not ghost */ + } + } + + return ret; } diff --git a/libcheckers/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..f426aaf --- /dev/null +++ b/libcheckers/libsg.c @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2004, 2005 Christophe Varoqui + */ +#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; + int retry_count = 3; + + memset(rdCmd, 0, cdbsz); + sz_ind = 1; + rdCmd[0] = rd_opcode[sz_ind]; + rdCmd[2] = (unsigned char)((start_block >> 24) & 0xff); + rdCmd[3] = (unsigned char)((start_block >> 16) & 0xff); + rdCmd[4] = (unsigned char)((start_block >> 8) & 0xff); + rdCmd[5] = (unsigned char)(start_block & 0xff); + rdCmd[7] = (unsigned char)((blocks >> 8) & 0xff); + rdCmd[8] = (unsigned char)(blocks & 0xff); + + memset(&io_hdr, 0, sizeof(struct sg_io_hdr)); + io_hdr.interface_id = 'S'; + io_hdr.cmd_len = cdbsz; + io_hdr.cmdp = rdCmd; + io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; + io_hdr.dxfer_len = bs * blocks; + io_hdr.dxferp = buff; + io_hdr.mx_sb_len = SENSE_BUFF_LEN; + io_hdr.sbp = senseBuff; + io_hdr.timeout = DEF_TIMEOUT; + io_hdr.pack_id = (int)start_block; + if (diop && *diop) + io_hdr.flags |= SG_FLAG_DIRECT_IO; + +retry: + memset(senseBuff, 0, SENSE_BUFF_LEN); + while (((res = ioctl(sg_fd, SG_IO, &io_hdr)) < 0) && (EINTR == errno)); + + if (res < 0) { + if (ENOMEM == errno) { + return PATH_UP; + } + return PATH_DOWN; + } + + if ((0 == io_hdr.status) && + (0 == io_hdr.host_status) && + (0 == io_hdr.driver_status)) { + return PATH_UP; + } else { + /* + * Retry if UNIT_ATTENTION check condition. + */ + if ((sbb[2]&0xf) == 6) { + if (--retry_count) + goto retry; + } + return PATH_DOWN; + } +} diff --git a/libcheckers/libsg.h b/libcheckers/libsg.h new file mode 100644 index 0000000..97c4491 --- /dev/null +++ b/libcheckers/libsg.h @@ -0,0 +1,8 @@ +#ifndef _LIBSG_H +#define _LIBSG_H + +#define SENSE_BUFF_LEN 32 + +int sg_read (int sg_fd, unsigned char * buff, unsigned char * senseBuff); + +#endif /* _LIBSG_H */ diff --git a/libcheckers/rdac.c b/libcheckers/rdac.c new file mode 100644 index 0000000..f430488 --- /dev/null +++ b/libcheckers/rdac.c @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2005 Christophe Varoqui + */ +#include +#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..3cddfa8 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 * return; } -static int -sg_read (int sg_fd, unsigned char * buff) -{ - /* defaults */ - int blocks = 1; - long long start_block = 0; - int bs = 512; - int cdbsz = 10; - int * diop = NULL; - - unsigned char rdCmd[cdbsz]; - unsigned char senseBuff[SENSE_BUFF_LEN]; - struct sg_io_hdr io_hdr; - int res; - int rd_opcode[] = {0x8, 0x28, 0xa8, 0x88}; - int sz_ind; - - memset(rdCmd, 0, cdbsz); - sz_ind = 1; - rdCmd[0] = rd_opcode[sz_ind]; - rdCmd[2] = (unsigned char)((start_block >> 24) & 0xff); - rdCmd[3] = (unsigned char)((start_block >> 16) & 0xff); - rdCmd[4] = (unsigned char)((start_block >> 8) & 0xff); - rdCmd[5] = (unsigned char)(start_block & 0xff); - rdCmd[7] = (unsigned char)((blocks >> 8) & 0xff); - rdCmd[8] = (unsigned char)(blocks & 0xff); - - memset(&io_hdr, 0, sizeof(struct sg_io_hdr)); - io_hdr.interface_id = 'S'; - io_hdr.cmd_len = cdbsz; - io_hdr.cmdp = rdCmd; - io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; - io_hdr.dxfer_len = bs * blocks; - io_hdr.dxferp = buff; - io_hdr.mx_sb_len = SENSE_BUFF_LEN; - io_hdr.sbp = senseBuff; - io_hdr.timeout = DEF_TIMEOUT; - io_hdr.pack_id = (int)start_block; - if (diop && *diop) - io_hdr.flags |= SG_FLAG_DIRECT_IO; - - while (((res = ioctl(sg_fd, SG_IO, &io_hdr)) < 0) && (EINTR == errno)); - - if (res < 0) { - if (ENOMEM == errno) { - return PATH_UP; - } - return PATH_DOWN; - } - - if ((0 == io_hdr.status) && - (0 == io_hdr.host_status) && - (0 == io_hdr.driver_status)) { - return PATH_UP; - } else { - return PATH_DOWN; - } -} - extern int readsector0 (struct checker * c) { unsigned char buf[512]; + unsigned char sbuf[SENSE_BUFF_LEN]; int ret; - ret = sg_read(c->fd, &buf[0]); + ret = sg_read(c->fd, &buf[0], &sbuf[0]); switch (ret) { diff --git a/libcheckers/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..ef561a8 100644 --- a/libmultipath/Makefile +++ b/libmultipath/Makefile @@ -6,7 +6,7 @@ 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 \ @@ -18,12 +18,19 @@ OBJS = memory.o parser.o vector.o devmap PREVBUILD = $(shell nm debug.o 2> /dev/null|grep log_safe) ifeq ($(strip $(DAEMON)),1) + OBJS += lock.o waiter.o CFLAGS += -DDAEMON CLEAN = $(shell if [ "x$(PREVBUILD)" = "x" ]; then echo clean; fi) else CLEAN = $(shell if [ ! "x$(PREVBUILD)" = "x" ]; then echo clean; fi) endif +LIBDM_API_FLUSH = $(shell objdump -T /lib/libdevmapper.so.* | grep -c dm_task_no_flush) + +ifeq ($(strip $(LIBDM_API_FLUSH)),1) + CFLAGS += -DLIBDM_API_FLUSH +endif + all: $(BUILD) prepare: $(CLEAN) diff --git a/libmultipath/alias.c b/libmultipath/alias.c index 6d103d7..02ca087 100644 --- a/libmultipath/alias.c +++ b/libmultipath/alias.c @@ -166,28 +166,14 @@ fail: static int -lookup_binding(int fd, char *map_wwid, char **map_alias) +lookup_binding(FILE *f, char *map_wwid, char **map_alias) { char buf[LINE_MAX]; - FILE *f; unsigned int line_nr = 0; - int scan_fd; int id = 0; *map_alias = NULL; - scan_fd = dup(fd); - if (scan_fd < 0) { - condlog(0, "Cannot dup bindings file descriptor : %s", - strerror(errno)); - return -1; - } - f = fdopen(scan_fd, "r"); - if (!f) { - condlog(0, "cannot fdopen on bindings file descriptor : %s", - strerror(errno)); - close(scan_fd); - return -1; - } + while (fgets(buf, LINE_MAX, f)) { char *c, *alias, *wwid; int curr_id; @@ -210,43 +196,27 @@ lookup_binding(int fd, char *map_wwid, c } if (strcmp(wwid, map_wwid) == 0){ condlog(3, "Found matching wwid [%s] in bindings file." - "\nSetting alias to %s", wwid, alias); + " Setting alias to %s", wwid, alias); *map_alias = strdup(alias); if (*map_alias == NULL) condlog(0, "Cannot copy alias from bindings " "file : %s", strerror(errno)); - fclose(f); return id; } } condlog(3, "No matching wwid [%s] in bindings file.", map_wwid); - fclose(f); return id; } static int -rlookup_binding(int fd, char **map_wwid, char *map_alias) +rlookup_binding(FILE *f, char **map_wwid, char *map_alias) { char buf[LINE_MAX]; - FILE *f; unsigned int line_nr = 0; - int scan_fd; int id = 0; *map_wwid = NULL; - scan_fd = dup(fd); - if (scan_fd < 0) { - condlog(0, "Cannot dup bindings file descriptor : %s", - strerror(errno)); - return -1; - } - f = fdopen(scan_fd, "r"); - if (!f) { - condlog(0, "cannot fdopen on bindings file descriptor : %s", - strerror(errno)); - close(scan_fd); - return -1; - } + while (fgets(buf, LINE_MAX, f)) { char *c, *alias, *wwid; int curr_id; @@ -274,12 +244,10 @@ rlookup_binding(int fd, char **map_wwid, if (*map_wwid == NULL) condlog(0, "Cannot copy alias from bindings " "file : %s", strerror(errno)); - fclose(f); return id; } } condlog(3, "No matching alias [%s] in bindings file.", map_alias); - fclose(f); return id; } @@ -327,7 +295,8 @@ char * get_user_friendly_alias(char *wwid, char *file) { char *alias; - int fd, id; + int fd, scan_fd, id; + FILE *f; if (!wwid || *wwid == '\0') { condlog(3, "Cannot find binding for empty WWID"); @@ -337,14 +306,37 @@ get_user_friendly_alias(char *wwid, char fd = open_bindings_file(file); if (fd < 0) return NULL; - id = lookup_binding(fd, wwid, &alias); + + scan_fd = dup(fd); + if (scan_fd < 0) { + condlog(0, "Cannot dup bindings file descriptor : %s", + strerror(errno)); + close(fd); + return NULL; + } + + f = fdopen(scan_fd, "r"); + if (!f) { + condlog(0, "cannot fdopen on bindings file descriptor : %s", + strerror(errno)); + close(scan_fd); + close(fd); + return NULL; + } + + id = lookup_binding(f, wwid, &alias); if (id < 0) { + fclose(f); + close(scan_fd); close(fd); return NULL; } + if (!alias) alias = allocate_binding(fd, wwid, id); + fclose(f); + close(scan_fd); close(fd); return alias; } @@ -353,7 +345,8 @@ char * get_user_friendly_wwid(char *alias, char *file) { char *wwid; - int fd, id; + int fd, scan_fd, id; + FILE *f; if (!alias || *alias == '\0') { condlog(3, "Cannot find binding for empty alias"); @@ -363,12 +356,34 @@ get_user_friendly_wwid(char *alias, char fd = open_bindings_file(file); if (fd < 0) return NULL; - id = rlookup_binding(fd, &wwid, alias); + + scan_fd = dup(fd); + if (scan_fd < 0) { + condlog(0, "Cannot dup bindings file descriptor : %s", + strerror(errno)); + close(fd); + return NULL; + } + + f = fdopen(scan_fd, "r"); + if (!f) { + condlog(0, "cannot fdopen on bindings file descriptor : %s", + strerror(errno)); + close(scan_fd); + close(fd); + return NULL; + } + + id = rlookup_binding(f, &wwid, alias); if (id < 0) { + fclose(f); + close(scan_fd); close(fd); return NULL; } + fclose(f); + close(scan_fd); close(fd); return wwid; } diff --git a/libmultipath/blacklist.c b/libmultipath/blacklist.c index 6a8a681..5f7b2f5 100644 --- a/libmultipath/blacklist.c +++ b/libmultipath/blacklist.c @@ -2,7 +2,6 @@ * Copyright (c) 2004, 2005 Christophe Varoqui */ #include - #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 * ven } ble->product = product; } + ble->origin = origin; return 0; } @@ -105,19 +106,19 @@ setup_default_blist (struct config * con str = STRDUP("^(ram|raw|loop|fd|md|dm-|sr|scd|st)[0-9]*"); if (!str) return 1; - if (store_ble(conf->blist_devnode, str)) + if (store_ble(conf->blist_devnode, str, ORIGIN_DEFAULT)) return 1; str = STRDUP("^hd[a-z]"); if (!str) return 1; - if (store_ble(conf->blist_devnode, str)) + if (store_ble(conf->blist_devnode, str, ORIGIN_DEFAULT)) return 1; str = STRDUP("^cciss!c[0-9]d[0-9]*"); if (!str) return 1; - if (store_ble(conf->blist_devnode, str)) + if (store_ble(conf->blist_devnode, str, ORIGIN_DEFAULT)) return 1; vector_foreach_slot (conf->hwtable, hwe, i) { @@ -128,7 +129,8 @@ setup_default_blist (struct config * con VECTOR_SIZE(conf->blist_device) -1); if (set_ble_device(conf->blist_device, STRDUP(hwe->vendor), - STRDUP(hwe->bl_product))) { + STRDUP(hwe->bl_product), + ORIGIN_DEFAULT)) { FREE(ble); return 1; } @@ -139,52 +141,183 @@ setup_default_blist (struct config * con } int -blacklist (vector blist, char * str) +_blacklist_exceptions (vector elist, char * str) +{ + int i; + struct blentry * ele; + + vector_foreach_slot (elist, ele, i) { + if (!regexec(&ele->regex, str, 0, NULL, 0)) + return 1; + } + return 0; +} + +int +_blacklist (vector blist, char * str) { int i; struct blentry * ble; vector_foreach_slot (blist, ble, i) { - if (!regexec(&ble->regex, str, 0, NULL, 0)) { - condlog(3, "%s: blacklisted", str); + if (!regexec(&ble->regex, str, 0, NULL, 0)) return 1; - } } return 0; } int -blacklist_device (vector blist, char * vendor, char * product) +_blacklist_exceptions_device(vector elist, char * vendor, char * product) +{ + int i; + struct blentry_device * ble; + + vector_foreach_slot (elist, ble, i) { + if (!regexec(&ble->vendor_reg, vendor, 0, NULL, 0) && + !regexec(&ble->product_reg, product, 0, NULL, 0)) + return 1; + } + return 0; +} + +int +_blacklist_device (vector blist, char * vendor, char * product) { int i; struct blentry_device * ble; vector_foreach_slot (blist, ble, i) { if (!regexec(&ble->vendor_reg, vendor, 0, NULL, 0) && - !regexec(&ble->product_reg, product, 0, NULL, 0)) { - condlog(3, "%s:%s: blacklisted", vendor, product); + !regexec(&ble->product_reg, product, 0, NULL, 0)) return 1; - } } return 0; } +#define LOG_BLIST(M) \ + if (vendor && product) \ + condlog(3, "%s: (%s:%s) %s", dev, vendor, product, (M)); \ + else if (wwid) \ + condlog(3, "%s: (%s) %s", dev, wwid, (M)); \ + else \ + condlog(3, "%s: %s", dev, (M)) + +void +log_filter (char *dev, char *vendor, char *product, char *wwid, int r) +{ + /* + * Try to sort from most likely to least. + */ + switch (r) { + case MATCH_NOTHING: + break; + case MATCH_DEVICE_BLIST: + LOG_BLIST("vendor/product blacklisted"); + break; + case MATCH_WWID_BLIST: + LOG_BLIST("wwid blacklisted"); + break; + case MATCH_DEVNODE_BLIST: + LOG_BLIST("device node name blacklisted"); + break; + case MATCH_DEVICE_BLIST_EXCEPT: + LOG_BLIST("vendor/product whitelisted"); + break; + case MATCH_WWID_BLIST_EXCEPT: + LOG_BLIST("wwid whitelisted"); + break; + case MATCH_DEVNODE_BLIST_EXCEPT: + LOG_BLIST("device node name whitelisted"); + break; + } +} + int -blacklist_path (struct config * conf, struct path * pp) +_filter_device (vector blist, vector elist, char * vendor, char * product) { - if (blacklist(conf->blist_devnode, pp->dev)) - return 1; + if (!vendor || !product) + return 0; + if (_blacklist_exceptions_device(elist, vendor, product)) + return MATCH_DEVICE_BLIST_EXCEPT; + if (_blacklist_device(blist, vendor, product)) + return MATCH_DEVICE_BLIST; + return 0; +} - if (blacklist(conf->blist_wwid, pp->wwid)) - return 1; +int +filter_device (vector blist, vector elist, char * vendor, char * product) +{ + int r = _filter_device(blist, elist, vendor, product); + log_filter(NULL, vendor, product, NULL, r); + return r; +} - if (pp->vendor_id && pp->product_id && - blacklist_device(conf->blist_device, pp->vendor_id, pp->product_id)) - return 1; +int +_filter_devnode (vector blist, vector elist, char * dev) +{ + if (!dev) + return 0; + if (_blacklist_exceptions(elist, dev)) + return MATCH_DEVNODE_BLIST_EXCEPT; + if (_blacklist(blist, dev)) + return MATCH_DEVNODE_BLIST; + return 0; +} + +int +filter_devnode (vector blist, vector elist, char * dev) +{ + int r = _filter_devnode(blist, elist, dev); + log_filter(dev, NULL, NULL, NULL, r); + return r; +} + +int +_filter_wwid (vector blist, vector elist, char * wwid) +{ + if (!wwid) + return 0; + if (_blacklist_exceptions(elist, wwid)) + return MATCH_WWID_BLIST_EXCEPT; + if (_blacklist(blist, wwid)) + return MATCH_WWID_BLIST; + return 0; +} +int +filter_wwid (vector blist, vector elist, char * wwid) +{ + int r = _filter_wwid(blist, elist, wwid); + log_filter(NULL, NULL, NULL, wwid, r); + return r; +} + +int +_filter_path (struct config * conf, struct path * pp) +{ + int r; + + r = _filter_devnode(conf->blist_devnode, conf->elist_devnode,pp->dev); + if (r) + return r; + r = _filter_wwid(conf->blist_devnode, conf->elist_devnode, pp->wwid); + if (r) + return r; + r = _filter_device(conf->blist_device, conf->elist_device, + pp->vendor_id, pp->product_id); + if (r) + return r; return 0; } +int +filter_path (struct config * conf, struct path * pp) +{ + int r=_filter_path(conf, pp); + log_filter(pp->dev, pp->vendor_id, pp->product_id, pp->wwid, r); + return r; +} + void free_blacklist (vector blist) { diff --git a/libmultipath/blacklist.h b/libmultipath/blacklist.h index 9e2b63c..cdbebef 100644 --- a/libmultipath/blacklist.h +++ b/libmultipath/blacklist.h @@ -3,9 +3,18 @@ #include "regex.h" +#define MATCH_NOTHING 0 +#define MATCH_WWID_BLIST 1 +#define MATCH_DEVICE_BLIST 2 +#define MATCH_DEVNODE_BLIST 3 +#define MATCH_WWID_BLIST_EXCEPT -MATCH_WWID_BLIST +#define MATCH_DEVICE_BLIST_EXCEPT -MATCH_DEVICE_BLIST +#define MATCH_DEVNODE_BLIST_EXCEPT -MATCH_DEVNODE_BLIST + struct blentry { char * str; regex_t regex; + int origin; }; struct blentry_device { @@ -13,15 +22,17 @@ struct blentry_device { char * product; regex_t vendor_reg; regex_t product_reg; + int origin; }; int setup_default_blist (struct config *); int alloc_ble_device (vector); -int blacklist (vector, char *); -int blacklist_device (vector, char *, char *); -int blacklist_path (struct config *, struct path *); -int store_ble (vector, char *); -int set_ble_device (vector, char *, char *); +int filter_devnode (vector, vector, char *); +int filter_wwid (vector, vector, char *); +int filter_device (vector, vector, char *, char *); +int filter_path (struct config *, struct path *); +int store_ble (vector, char *, int); +int set_ble_device (vector, char *, char *, int); void free_blacklist (vector); void free_blacklist_device (vector); diff --git a/libmultipath/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 hwentr 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..7caa11d 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,6 +63,7 @@ struct config { int rr_weight; int no_path_retry; int user_friendly_names; + int pg_timeout; char * dev; char * udev_dir; @@ -67,17 +74,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..9632fb4 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, v 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, v 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, /* * 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,13 +316,13 @@ 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(); @@ -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, v /* 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, v 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, v 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, v r = domap(mpp); - if (!r) { + if (r == DOMAP_FAIL || r == DOMAP_RETRY) { condlog(3, "%s: domap (%u) failure " "for create/reload map", mpp->alias, r); - remove_map(mpp, vecs, NULL, 0); - continue; - } - else if (r < 0) { - 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, v 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, v } 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, v 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..fa06cbd 100644 --- a/libmultipath/debug.c +++ b/libmultipath/debug.c @@ -29,17 +29,16 @@ void dlog (int sink, int prio, char * fm 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..74ed531 100644 --- a/libmultipath/debug.h +++ b/libmultipath/debug.h @@ -11,11 +11,11 @@ void dlog (int sink, int prio, char * fm 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..8deeb0f 100644 --- a/libmultipath/defaults.h +++ b/libmultipath/defaults.h @@ -9,6 +9,7 @@ #define DEFAULT_FAILBACK -FAILBACK_MANUAL #define DEFAULT_RR_WEIGHT RR_WEIGHT_NONE #define DEFAULT_NO_PATH_RETRY NO_PATH_RETRY_UNDEF +#define DEFAULT_PGTIMEOUT -PGTIMEOUT_NONE #define DEFAULT_USER_FRIENDLY_NAMES 0 #define DEFAULT_CHECKINT 5 diff --git a/libmultipath/devmapper.c b/libmultipath/devmapper.c index 4328036..dece079 100644 --- a/libmultipath/devmapper.c +++ b/libmultipath/devmapper.c @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -23,6 +24,9 @@ #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, ...) { @@ -41,13 +45,35 @@ dm_shut_log (void) dm_log_init(&dm_dummy_log); } -extern int -dm_prereq (char * str, int x, int y, int z) +static int +dm_libprereq (void) +{ + char version[64]; + int v[3]; + int minv[3] = {1, 2, 11}; + + 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; +} + +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 +84,44 @@ dm_prereq (char * str, int x, int y, int 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 +133,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 +150,7 @@ dm_addmap (int task, const char *name, c 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 +161,26 @@ dm_addmap (int task, const char *name, c 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 +254,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 +267,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 +565,16 @@ dm_queue_if_no_path(char *mapname, int e 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 +657,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)) diff --git a/libmultipath/devmapper.h b/libmultipath/devmapper.h index c7879a7..59afd01 100644 --- a/libmultipath/devmapper.h +++ b/libmultipath/devmapper.h @@ -1,6 +1,6 @@ void dm_shut_log(void); void dm_restore_log(void); -int dm_prereq (char *, int, int, int); +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 +13,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..c705cc6 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->blist_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 l } 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 l } 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, } 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..a196583 100644 --- a/libmultipath/discovery.c +++ b/libmultipath/discovery.c @@ -61,7 +61,8 @@ path_discover (vector pathvec, struct co 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, @@ -112,7 +113,7 @@ out: * not multipath(8), ran by udev */ #if DAEMON -#define WAIT_MAX_SECONDS 5 +#define WAIT_MAX_SECONDS 60 #define WAIT_LOOP_PER_SECOND 5 static int @@ -165,7 +166,7 @@ sysfs_get_##fname (char * sysfs_path, ch goto out; \ \ strncpy(buff, attr->value, attr->len - 1); \ - buff[attr->len - 1] = '\0'; \ + strchop(buff); \ sysfs_close_attribute(attr); \ return 0; \ out: \ @@ -237,7 +238,7 @@ devt2devname (char *devname, char *devt) struct dlist * ls; char attr_path[FILE_NAME_SIZE]; char block_path[FILE_NAME_SIZE]; - struct sysfs_attribute * attr; + struct sysfs_attribute * attr = NULL; struct sysfs_class * class; struct sysfs_class_device * dev; @@ -295,7 +296,7 @@ do_inq(int sg_fd, int cmddt, int evpd, u { 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 +314,10 @@ do_inq(int sg_fd, int cmddt, int evpd, u 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,24 +340,26 @@ do_inq(int sg_fd, int cmddt, int evpd, u 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 @@ -440,7 +443,7 @@ 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 @@ -524,7 +527,7 @@ 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 @@ -597,7 +600,7 @@ static int scsi_ioctl_pathinfo (struct path * pp, int mask) { if (mask & DI_SERIAL) { - get_serial(pp->serial, pp->fd); + get_serial(pp->serial, SERIAL_SIZE, pp->fd); condlog(3, "%s: serial = %s", pp->dev, pp->serial); } @@ -609,12 +612,15 @@ get_state (struct path * pp) { struct checker * c = &pp->checker; + if (!pp->mpp) + return 0; + if (!checker_selected(c)) { select_checker(pp); if (!checker_selected(c)) return 1; checker_set_fd(c, pp->fd); - if (checker_init(c)) + if (checker_init(c, &pp->mpp->mpcontext)) return 1; } pp->state = checker_check(c); @@ -636,14 +642,14 @@ get_prio (struct path * pp) pp->getprio_selected = 1; } if (!pp->getprio) { - pp->priority = 1; + pp->priority = PRIO_DEFAULT; } else if (apply_format(pp->getprio, &buff[0], pp)) { condlog(0, "error formatting prio callout command"); - pp->priority = -1; + pp->priority = PRIO_UNDEF; return 1; } else if (execute_program(buff, prio, 16)) { condlog(0, "error calling out %s", buff); - pp->priority = -1; + pp->priority = PRIO_UNDEF; return 1; } else pp->priority = atoi(prio); @@ -699,7 +705,12 @@ pathinfo (struct path *pp, vector hwtabl if (mask & DI_CHECKER && get_state(pp)) goto blank; - if (mask & DI_PRIO && pp->state != PATH_DOWN) + /* + * Retrieve path priority even for not PATH_UP paths if it has never + * been successfully obtained before. + */ + if (mask & DI_PRIO && + (pp->state == PATH_UP || pp->priority == PRIO_UNDEF)) get_prio(pp); if (mask & DI_WWID && !strlen(pp->wwid)) diff --git a/libmultipath/discovery.h b/libmultipath/discovery.h index cdc9627..ab62a59 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 */ @@ -30,7 +33,6 @@ int sysfs_get_size (char * sysfs_path, c 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 * 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 * 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..df6f5aa 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 @@ -54,14 +75,14 @@ static struct hwentry default_hw[] = { .checker_name = HP_SW, }, { - .vendor = "{COMPAQ,HP}", - .product = "{MSA,HSV}1*", + .vendor = "(COMPAQ|HP)", + .product = "(MSA|HSV)1.*", .getuid = DEFAULT_GETUID, .getprio = NULL, .features = DEFAULT_FEATURES, .hwhandler = "1 hp_sw", .selector = DEFAULT_SELECTOR, - .pgpolicy = GROUP_BY_SERIAL, + .pgpolicy = MULTIBUS, .pgfailback = FAILBACK_UNDEF, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_UNDEF, @@ -70,7 +91,7 @@ static struct hwentry default_hw[] = { }, { .vendor = "HP", - .product = "HSV2*", + .product = "A6189A", .getuid = DEFAULT_GETUID, .getprio = NULL, .features = DEFAULT_FEATURES, @@ -85,7 +106,8 @@ static struct hwentry default_hw[] = { }, { .vendor = "HP", - .product = "DF[456]00", + .product = "HSV20.*", + .revision = "[123].*", .getuid = DEFAULT_GETUID, .getprio = NULL, .features = DEFAULT_FEATURES, @@ -96,10 +118,41 @@ 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 = HP_SW, + }, + { + .vendor = "HP", + .product = "HSV20.*", + .revision = "[^123].*", + .getuid = DEFAULT_GETUID, + .getprio = "/sbin/mpath_prio_alua /dev/%n", + .features = DEFAULT_FEATURES, + .hwhandler = DEFAULT_HWHANDLER, + .selector = DEFAULT_SELECTOR, + .pgpolicy = MULTIBUS, + .pgfailback = -FAILBACK_IMMEDIATE, + .rr_weight = RR_WEIGHT_NONE, + .no_path_retry = NO_PATH_RETRY_UNDEF, + .minio = DEFAULT_MINIO, + .checker_name = TUR, + }, + { + .vendor = "HP", + .product = "HSV21.*", + .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_UNDEF, + .rr_weight = RR_WEIGHT_NONE, + .no_path_retry = NO_PATH_RETRY_UNDEF, + .minio = DEFAULT_MINIO, + .checker_name = TUR, }, /* - * DDN controler family + * DDN controller family * * Maintainer : Christophe Varoqui * Mail : christophe.varoqui@free.fr @@ -117,10 +170,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 @@ -142,7 +195,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 +210,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,14 +231,14 @@ 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, @@ -198,10 +251,25 @@ static struct hwentry default_hw[] = { .minio = DEFAULT_MINIO, .checker_name = READSECTOR0, }, + { + .vendor = "HITACHI", + .product = "DF.*", + .getuid = DEFAULT_GETUID, + .getprio = "/sbin/mpath_prio_hds_modular %d", + .features = DEFAULT_FEATURES, + .hwhandler = DEFAULT_HWHANDLER, + .selector = DEFAULT_SELECTOR, + .pgpolicy = GROUP_BY_PRIO, + .pgfailback = -FAILBACK_IMMEDIATE, + .rr_weight = RR_WEIGHT_NONE, + .no_path_retry = NO_PATH_RETRY_UNDEF, + .minio = DEFAULT_MINIO, + .checker_name = READSECTOR0, + }, /* - * IBM controler family + * IBM controller family * - * Maintainer : Hannes Reinecke, Suse + * Maintainer : Hannes Reinecke, SuSE * Mail : hare@suse.de */ { @@ -236,6 +304,22 @@ static struct hwentry default_hw[] = { .checker_name = TUR, }, { + /* IBM Netfinity Fibre Channel RAID Controller Unit */ + .vendor = "IBM", + .product = "3526", + .getuid = DEFAULT_GETUID, + .getprio = "/sbin/mpath_prio_tpc /dev/%n", + .features = DEFAULT_FEATURES, + .hwhandler = DEFAULT_HWHANDLER, + .selector = DEFAULT_SELECTOR, + .pgpolicy = GROUP_BY_PRIO, + .pgfailback = -FAILBACK_IMMEDIATE, + .rr_weight = RR_WEIGHT_NONE, + .no_path_retry = NO_PATH_RETRY_UNDEF, + .minio = DEFAULT_MINIO, + .checker_name = TUR, + }, + { /* IBM DS4200 / FAStT200 */ .vendor = "IBM", .product = "3542", @@ -254,7 +338,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 +352,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 +376,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,10 +384,27 @@ 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/dasd_id /dev/%n", .getprio = NULL, .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, @@ -315,38 +416,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, @@ -422,7 +544,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/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/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 *str 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..e50f37d 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, u static int snprint_size (char * buff, size_t len, unsigned long long size) { - if (size < (1 << 11)) - return snprintf(buff, len, "%llu kB", size >> 1); - else if (size < (1 << 21)) - return snprintf(buff, len, "%llu MB", size >> 11); - else if (size < (1 << 31)) - return snprintf(buff, len, "%llu GB", size >> 21); + float s = (float)(size >> 1); /* start with KB */ + char fmt[6] = {}; + char units[] = {'K','M','G','T','P'}; + char *u = units; + + while (s >= 1024 && *u != 'P') { + s = s / 1024; + u++; + } + if (s < 10) + snprintf(fmt, 6, "%%.1f%c", *u); else - return snprintf(buff, len, "%llu TB", size >> 31); + snprintf(fmt, 6, "%%.0f%c", *u); + + return snprintf(buff, len, fmt, s); } +/* + * multipath info printing functions + */ static int snprint_name (char * buff, size_t len, struct multipath * mpp) { @@ -222,6 +235,9 @@ snprint_action (char * buff, size_t len, } } +/* + * path info printing functions + */ static int snprint_path_uuid (char * buff, size_t len, struct path * pp) { @@ -292,8 +308,8 @@ snprint_dm_path_state (char * buff, size static int snprint_vpr (char * buff, size_t len, struct path * pp) { - return snprintf(buff, len, "%s/%s/%s", - pp->vendor_id, pp->product_id, pp->rev); + return snprintf(buff, len, "%s,%s", + pp->vendor_id, pp->product_id); } static int @@ -496,7 +512,7 @@ snprint_multipath (char * line, int len, char * f = format; /* format string cursor */ int fwd; struct multipath_data * data; - char buff[MAX_FIELD_LEN]; + char buff[MAX_FIELD_LEN] = {}; do { if (!TAIL) @@ -515,6 +531,7 @@ snprint_multipath (char * line, int len, data->snprint(buff, MAX_FIELD_LEN, mpp); PRINT(c, TAIL, buff); PAD(data->width); + buff[0] = '\0'; } while (*f++); line[c - line - 1] = '\n'; @@ -631,7 +648,7 @@ snprint_pathgroup (char * line, int len, extern void print_multipath_topology (struct multipath * mpp, int verbosity) { - char buff[MAX_LINE_LEN * MAX_LINES]; + char buff[MAX_LINE_LEN * MAX_LINES] = {}; snprint_multipath_topology(&buff[0], MAX_LINE_LEN * MAX_LINES, mpp, verbosity); @@ -662,7 +679,10 @@ snprint_multipath_topology (char * buff, c += sprintf(c, "%%n"); if (strncmp(mpp->alias, mpp->wwid, WWID_SIZE)) - c += sprintf(c, " (%%w)"); + c += sprintf(c, " (%%w) "); + + c += sprintf(c, "%%d "); + c += snprint_vpr(c, 24, first_path(mpp)); fwd += snprint_multipath(buff + fwd, len - fwd, style, mpp); if (fwd > len) @@ -833,6 +853,110 @@ snprint_defaults (char * buff, int len) } +static int +snprint_blacklist_group (char *buff, int len, int *fwd, vector *vec) +{ + int threshold = MAX_LINE_LEN; + struct blentry * ble; + int pos; + int i; + + pos = *fwd; + if (!VECTOR_SIZE(*vec)) { + if ((len - pos - threshold) <= 0) + return 0; + pos += snprintf(buff + pos, len - pos, " \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 +1019,126 @@ 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) +{ + struct dlist * ls; + struct sysfs_class * class; + struct sysfs_class_device * dev; + int threshold = MAX_LINE_LEN; + int fwd = 0; + int r; + + struct path * pp; + + if (!(class = sysfs_open_class("block"))) + return 0; + + if (!(ls = sysfs_get_class_devices(class))) { + sysfs_close_class(class); + return 0; + } + + if ((len - fwd - threshold) <= 0) + return len; + fwd += snprintf(buff + fwd, len - fwd, "available block devices:\n"); + + dlist_for_each_data(ls, dev, struct sysfs_class_device) { + if ((len - fwd - threshold) <= 0) + return len; + fwd += snprintf(buff + fwd, len - fwd, " %s", dev->name); + pp = find_path_by_dev(vecs->pathvec, dev->name); + if (!pp) { + r = filter_devnode(conf->blist_devnode, + conf->elist_devnode, dev->name); + 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"); + } + sysfs_close_class(class); + + 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 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 *m } 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, 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 s 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..46dcdee 100644 --- a/libmultipath/structs.h +++ b/libmultipath/structs.h @@ -1,8 +1,8 @@ #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 @@ -18,6 +18,9 @@ #define NO_PATH_RETRY_FAIL -1 #define NO_PATH_RETRY_QUEUE -2 +#define PRIO_UNDEF -1 +#define PRIO_DEFAULT 1 + enum free_path_switch { KEEP_PATHS, FREE_PATHS @@ -55,6 +58,11 @@ enum pgstates { PGSTATE_ACTIVE }; +enum pgtimeouts { + PGTIMEOUT_UNDEF, + PGTIMEOUT_NONE +}; + struct scsi_idlun { int dev_id; int host_unique_id; @@ -127,6 +135,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 +151,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 +160,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 +195,11 @@ struct multipath * find_mp_by_minor (vec 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]; -#endif +#endif /* _STRUCTS_H */ diff --git a/libmultipath/structs_vec.c b/libmultipath/structs_vec.c index 86bf2a5..53b7e17 100644 --- a/libmultipath/structs_vec.c +++ b/libmultipath/structs_vec.c @@ -14,6 +14,7 @@ #include "config.h" #include "propsel.h" #include "discovery.h" +#include "waiter.h" /* @@ -58,8 +59,8 @@ adopt_paths (vector pathvec, struct mult 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 mul 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); } } @@ -277,6 +278,7 @@ retry: select_rr_weight(mpp); select_pgfailback(mpp); set_no_path_retry(mpp); + select_pg_timeout(mpp); return 0; out: @@ -382,3 +384,90 @@ verify_paths(struct multipath * mpp, str return count; } +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; +} + +/* + * 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 start_waiter_thread_func *start_waiter); struct multipath * add_map_with_path (struct vectors * vecs, struct path * pp, int add_vec); +int update_multipath (struct vectors *vecs, char *mapname); +void update_queue_mode_del_path(struct multipath *mpp); +void update_queue_mode_add_path(struct multipath *mpp); #endif /* _STRUCTS_VEC_H */ diff --git a/libmultipath/switchgroup.c b/libmultipath/switchgroup.c index 757543f..9b84bc2 100644 --- a/libmultipath/switchgroup.c +++ b/libmultipath/switchgroup.c @@ -28,7 +28,7 @@ select_path_group (struct multipath * mp priority = 0; vector_foreach_slot (pgp->paths, pp, j) { - if (pp->state != PATH_DOWN) + if (pp->state == PATH_UP) priority += pp->priority; } pgp->priority = priority; diff --git a/libmultipath/util.c b/libmultipath/util.c index 376ca04..911ec55 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); diff --git a/libmultipath/util.h b/libmultipath/util.h index 51f052a..e86bae2 100644 --- a/libmultipath/util.h +++ b/libmultipath/util.h @@ -2,6 +2,7 @@ #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); 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..75ed90c --- /dev/null +++ b/libmultipath/waiter.c @@ -0,0 +1,234 @@ +/* + * 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(); + + /* 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 */ +} + +void *waitevent (void *et) +{ + int r; + struct event_thread *waiter; + + mlockall(MCL_CURRENT | MCL_FUTURE); + + waiter = (struct event_thread *)et; + pthread_cleanup_push(free_waiter, et); + + while (1) { + r = waiteventloop(waiter); + + if (r < 0) + break; + + sleep(r); + } + + pthread_cleanup_pop(1); + return NULL; +} + +int start_waiter_thread (struct multipath *mpp, struct vectors *vecs) +{ + pthread_attr_t attr; + struct event_thread *wp; + + if (!mpp) + return 0; + + if (pthread_attr_init(&attr)) + goto out; + + pthread_attr_setstacksize(&attr, 32 * 1024); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + + wp = alloc_waiter(); + + if (!wp) + goto out; + + mpp->waiter = (void *)wp; + strncpy(wp->mapname, mpp->alias, WWID_SIZE); + wp->vecs = vecs; + wp->mpp = mpp; + + if (pthread_create(&wp->thread, &attr, waitevent, wp)) { + condlog(0, "%s: cannot create event checker", wp->mapname); + goto out1; + } + condlog(2, "%s: event checker started", wp->mapname); + + return 0; +out1: + free_waiter(wp); + mpp->waiter = NULL; +out: + condlog(0, "failed to start waiter thread"); + return 1; +} + diff --git a/libmultipath/waiter.h b/libmultipath/waiter.h new file mode 100644 index 0000000..0223924 --- /dev/null +++ b/libmultipath/waiter.h @@ -0,0 +1,23 @@ +#ifndef _WAITER_H +#define _WAITER_H + +#if DAEMON + +struct event_thread { + struct dm_task *dmt; + pthread_t thread; + int event_nr; + char mapname[WWID_SIZE]; + struct vectors *vecs; + struct multipath *mpp; +}; + +struct event_thread * alloc_waiter (void); +void free_waiter (void *data); +void stop_waiter_thread (struct multipath *mpp, struct vectors *vecs); +int start_waiter_thread (struct multipath *mpp, struct vectors *vecs); +int waiteventloop (struct event_thread *waiter); +void *waitevent (void *et); + +#endif /* DAEMON */ +#endif /* _WAITER_H */ diff --git a/multipath-tools.spec.in b/multipath-tools.spec.in index 494f09e..9d6a7ea 100644 --- a/multipath-tools.spec.in +++ b/multipath-tools.spec.in @@ -50,6 +50,7 @@ rm -rf $RPM_BUILD_ROOT %{prefix}/sbin/mpath_prio_balance_units %{prefix}/sbin/mpath_prio_netapp %{prefix}/sbin/mpath_prio_tpc +%{prefix}/sbin/mpath_prio_hds_modular %{prefix}/usr/share/man/man8/devmap_name.8.gz %{prefix}/usr/share/man/man8/multipath.8.gz %{prefix}/usr/share/man/man8/kpartx.8.gz diff --git a/multipath.conf.annotated b/multipath.conf.annotated index a1f04b7..649ff8c 100644 --- a/multipath.conf.annotated +++ b/multipath.conf.annotated @@ -142,6 +142,22 @@ # product MSA[15]00 # } #} +## +## name : blacklist_exceptions +## scope : multipath & multipathd +## desc : list of device names to be treated as multipath candidates +## even if they are on the blacklist. +## Note: blacklist exceptions are only valid in the same class. +## It is not possible to blacklist devices using the devnode keyword +## and to exclude some devices of them using the wwid keyword. +## default : - +## +#blacklist_exceptions { +# devnode "^dasd[c-d]+[0-9]*" +# wwid "IBM.75000000092461.4d00.34" +# wwid "IBM.75000000092461.4d00.35" +# wwid "IBM.75000000092461.4d00.36" +#} # ## ## name : multipaths @@ -237,7 +253,7 @@ ## ## name : devices ## scope : multipath & multipathd -## desc : list of per storage controler settings +## desc : list of per storage controller settings ## overrides default settings (device_maps block) ## overriden by per multipath settings (multipaths block) ## @@ -245,7 +261,7 @@ # # # # name : device # # scope : multipath & multipathd -# # desc : settings for this specific storage controler +# # desc : settings for this specific storage controller # # # device { # # @@ -260,7 +276,7 @@ # # name : path_grouping_policy # # scope : multipath # # desc : path grouping policy to apply to multipath hosted -# # by this storage controler +# # by this storage controller # # values : failover = 1 path per priority group # # multibus = all valid paths in 1 priority # # group diff --git a/multipath.conf.synthetic b/multipath.conf.synthetic index 4a1f5a4..e5e6cd0 100644 --- a/multipath.conf.synthetic +++ b/multipath.conf.synthetic @@ -16,7 +16,7 @@ # no_path_retry fail # user_friendly_names no #} -#devnode_blacklist { +#blacklist { # wwid 26353900f02796769 # devnode "^(ram|raw|loop|fd|md|dm-|sr|scd|st)[0-9]*" # devnode "^hd[a-z][[0-9]*]" @@ -26,6 +26,10 @@ # product MSA[15]00 # } #} +#blacklist_exceptions { +# devnode "^dasd[c-d]+[0-9]*" +# wwid "IBM.75000000092461.4d00.34" +#} #multipaths { # multipath { # wwid 3600508b4000156d700012000000b0000 diff --git a/multipath/main.c b/multipath/main.c index 98f7207..67076db 100644 --- a/multipath/main.c +++ b/multipath/main.c @@ -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,14 +313,14 @@ 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)) { @@ -322,7 +330,7 @@ main (int argc, char *argv[]) if (load_config(DEFAULT_CONFIGFILE)) exit(1); - while ((arg = getopt(argc, argv, ":qdl::Ffi:M:v:p:b:")) != EOF ) { + 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]); 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 autoco .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 n 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 .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/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 *, c 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, in { 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..4938e84 100644 --- a/multipathd/cli_handlers.c +++ b/multipathd/cli_handlers.c @@ -71,7 +71,7 @@ show_map_topology (char ** r, int * len, 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 +92,8 @@ show_maps_topology (char ** r, int * len char * reply; unsigned int maxlen = INITIAL_REPLY_LEN; int again = 1; - + + get_path_layout(vecs->pathvec); reply = MALLOC(maxlen); while (again) { @@ -142,6 +143,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 +190,7 @@ cli_list_map_topology (void * v, char ** 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 +286,8 @@ cli_add_path (void * v, char ** reply, i 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); @@ -307,7 +315,7 @@ cli_add_map (void * v, char ** reply, in 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); @@ -431,3 +439,79 @@ cli_fail(void * v, char ** reply, int * 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 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..f2f9a96 100644 --- a/multipathd/main.c +++ b/multipathd/main.c @@ -55,136 +55,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 +112,8 @@ coalesce_maps(struct vectors *vecs, vect { 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 +147,12 @@ coalesce_maps(struct vectors *vecs, vect return 0; } -static int -update_multipath (struct vectors *vecs, char *mapname) -{ - struct multipath *mpp; - struct pathgroup *pgp; - struct path *pp; - int i, j; - int r = 1; - - mpp = find_mp_by_alias(vecs->mpvec, mapname); - - if (!mpp) - goto out; - - free_pgvec(mpp->pg, KEEP_PATHS); - mpp->pg = NULL; - - if (setup_multipath(vecs, mpp)) - goto out; /* mpp freed in setup_multipath */ - - /* - * compare checkers states with DM states - */ - vector_foreach_slot (mpp->pg, pgp, i) { - vector_foreach_slot (pgp->paths, pp, j) { - if (pp->dmstate != PSTATE_FAILED) - continue; - - if (pp->state != PATH_DOWN) { - int oldstate = pp->state; - condlog(2, "%s: mark as failed", pp->dev_t); - mpp->stat_path_failures++; - pp->state = PATH_DOWN; - if (oldstate == PATH_UP || - oldstate == PATH_GHOST) - update_queue_mode_del_path(mpp); - - /* - * if opportune, - * schedule the next check earlier - */ - if (pp->tick > conf->checkint) - pp->tick = conf->checkint; - } - } - } - r = 0; -out: - if (r) - condlog(0, "failed to update multipath"); - - return r; -} - -static sigset_t unblock_signals(void) -{ - sigset_t set, old; - - sigemptyset(&set); - sigaddset(&set, SIGHUP); - sigaddset(&set, SIGUSR1); - pthread_sigmask(SIG_UNBLOCK, &set, &old); - return old; -} - -/* - * returns the reschedule delay - * negative means *stop* - */ -static int -waiteventloop (struct event_thread * waiter) -{ - sigset_t set; - int event_nr; - int r; - - if (!waiter->event_nr) - waiter->event_nr = dm_geteventnr(waiter->mapname); - - if (!(waiter->dmt = dm_task_create(DM_DEVICE_WAITEVENT))) { - condlog(0, "%s: devmap event #%i dm_task_create error", - waiter->mapname, waiter->event_nr); - return 1; - } - - if (!dm_task_set_name(waiter->dmt, waiter->mapname)) { - condlog(0, "%s: devmap event #%i dm_task_set_name error", - waiter->mapname, waiter->event_nr); - dm_task_destroy(waiter->dmt); - return 1; - } - - if (waiter->event_nr && !dm_task_set_event_nr(waiter->dmt, - waiter->event_nr)) { - condlog(0, "%s: devmap event #%i dm_task_set_event_nr error", - waiter->mapname, waiter->event_nr); - dm_task_destroy(waiter->dmt); - return 1; - } - - dm_task_no_open_count(waiter->dmt); - - /* accept wait interruption */ - set = unblock_signals(); - - /* interruption spits messages */ - dm_shut_log(); - - /* wait */ - r = dm_task_run(waiter->dmt); - - /* wait is over : event or interrupt */ - pthread_sigmask(SIG_SETMASK, &set, NULL); - //dm_restore_log(); - - if (!r) /* wait interrupted by signal */ - return -1; - - dm_task_destroy(waiter->dmt); - waiter->dmt = NULL; - waiter->event_nr++; - - /* - * upon event ... - */ - while (1) { - condlog(3, "%s: devmap event #%i", - waiter->mapname, waiter->event_nr); - - /* - * event might be : - * - * 1) a table reload, which means our mpp structure is - * obsolete : refresh it through update_multipath() - * 2) a path failed by DM : mark as such through - * update_multipath() - * 3) map has gone away : stop the thread. - * 4) a path reinstate : nothing to do - * 5) a switch group : nothing to do - */ - pthread_cleanup_push(cleanup_lock, waiter->vecs->lock); - lock(waiter->vecs->lock); - r = update_multipath(waiter->vecs, waiter->mapname); - lock_cleanup_pop(waiter->vecs->lock); - - if (r) - return -1; /* stop the thread */ - - event_nr = dm_geteventnr(waiter->mapname); - - if (waiter->event_nr == event_nr) - return 1; /* upon problem reschedule 1s later */ - - waiter->event_nr = event_nr; - } - return -1; /* never reach there */ -} - -static void * -waitevent (void * et) -{ - int r; - struct event_thread *waiter; - - mlockall(MCL_CURRENT | MCL_FUTURE); - - waiter = (struct event_thread *)et; - pthread_cleanup_push(free_waiter, et); - - while (1) { - r = waiteventloop(waiter); - - if (r < 0) - break; - - sleep(r); - } - - pthread_cleanup_pop(1); - return NULL; -} - -static int -start_waiter_thread (struct multipath * mpp, struct vectors * vecs) -{ - pthread_attr_t attr; - struct event_thread * wp; - - if (!mpp) - return 0; - - if (pthread_attr_init(&attr)) - goto out; - - pthread_attr_setstacksize(&attr, 32 * 1024); - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - - wp = alloc_waiter(); - - if (!wp) - goto out; - - mpp->waiter = (void *)wp; - strncpy(wp->mapname, mpp->alias, WWID_SIZE); - wp->vecs = vecs; - wp->mpp = mpp; - - if (pthread_create(&wp->thread, &attr, waitevent, wp)) { - condlog(0, "%s: cannot create event checker", wp->mapname); - goto out1; - } - condlog(2, "%s: event checker started", wp->mapname); - - return 0; -out1: - free_waiter(wp); - mpp->waiter = NULL; -out: - condlog(0, "failed to start waiter thread"); - return 1; -} - static void sync_map_state(struct multipath *mpp) { - int i, j; struct pathgroup *pgp; struct path *pp; + unsigned int i, j; vector_foreach_slot (mpp->pg, pgp, i){ vector_foreach_slot (pgp->paths, pp, j){ @@ -502,7 +174,7 @@ sync_map_state(struct multipath *mpp) static void sync_maps_state(vector mpvec) { - int i; + unsigned int i; struct multipath *mpp; vector_foreach_slot (mpvec, mpp, i) @@ -704,7 +376,7 @@ ev_add_path (char * devname, struct vect 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); @@ -863,7 +535,7 @@ ev_remove_path (char * devname, struct v } 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 +570,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; @@ -995,7 +667,8 @@ uev_trigger (struct uevent * uev, void * /* * path add/remove event */ - if (blacklist(conf->blist_devnode, devname)) + if (filter_devnode(conf->blist_devnode, conf->elist_devnode, + devname) > 0) goto out; if (!strncmp(uev->action, "add", 3)) { @@ -1024,30 +697,29 @@ ueventloop (void * ap) static void * uxlsnrloop (void * ap) { - if (load_keys()) - return NULL; - - if (alloc_handlers()) + if (cli_init()) return NULL; - add_handler(LIST+PATHS, cli_list_paths); - add_handler(LIST+MAPS, cli_list_maps); - add_handler(LIST+MAPS+STATUS, cli_list_maps_status); - add_handler(LIST+MAPS+STATS, cli_list_maps_stats); - add_handler(LIST+MAPS+TOPOLOGY, cli_list_maps_topology); - add_handler(LIST+TOPOLOGY, cli_list_maps_topology); - add_handler(LIST+MAP+TOPOLOGY, cli_list_map_topology); - add_handler(LIST+CONFIG, cli_list_config); - add_handler(ADD+PATH, cli_add_path); - add_handler(DEL+PATH, cli_del_path); - add_handler(ADD+MAP, cli_add_map); - add_handler(DEL+MAP, cli_del_map); - add_handler(SWITCH+MAP+GROUP, cli_switch_group); - add_handler(RECONFIGURE, cli_reconfigure); - add_handler(SUSPEND+MAP, cli_suspend); - add_handler(RESUME+MAP, cli_resume); - add_handler(REINSTATE+PATH, cli_reinstate); - add_handler(FAIL+PATH, cli_fail); + set_handler_callback(LIST+PATHS, cli_list_paths); + set_handler_callback(LIST+MAPS, cli_list_maps); + set_handler_callback(LIST+MAPS+STATUS, cli_list_maps_status); + set_handler_callback(LIST+MAPS+STATS, cli_list_maps_stats); + set_handler_callback(LIST+MAPS+TOPOLOGY, cli_list_maps_topology); + set_handler_callback(LIST+TOPOLOGY, cli_list_maps_topology); + set_handler_callback(LIST+MAP+TOPOLOGY, cli_list_map_topology); + set_handler_callback(LIST+CONFIG, cli_list_config); + set_handler_callback(LIST+BLACKLIST, cli_list_blacklist); + set_handler_callback(LIST+DEVICES, cli_list_devices); + set_handler_callback(ADD+PATH, cli_add_path); + set_handler_callback(DEL+PATH, cli_del_path); + set_handler_callback(ADD+MAP, cli_add_map); + set_handler_callback(DEL+MAP, cli_del_map); + set_handler_callback(SWITCH+MAP+GROUP, cli_switch_group); + set_handler_callback(RECONFIGURE, cli_reconfigure); + set_handler_callback(SUSPEND+MAP, cli_suspend); + set_handler_callback(RESUME+MAP, cli_resume); + set_handler_callback(REINSTATE+PATH, cli_reinstate); + set_handler_callback(FAIL+PATH, cli_fail); uxsock_listen(&uxsock_trigger, ap); @@ -1129,7 +801,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 +816,7 @@ static void defered_failback_tick (vector mpvec) { struct multipath * mpp; - int i; + unsigned int i; vector_foreach_slot (mpvec, mpp, i) { /* @@ -1163,7 +835,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 +854,9 @@ checkerloop (void *ap) { struct vectors *vecs; struct path *pp; - int i, count = 0; + int count = 0; int newstate; + unsigned int i; mlockall(MCL_CURRENT | MCL_FUTURE); vecs = (struct vectors *)ap; @@ -1351,10 +1024,10 @@ configure (struct vectors * vecs, int st vector mpvec; int i; - if (!(vecs->pathvec = vector_alloc())) + if (!vecs->pathvec && !(vecs->pathvec = vector_alloc())) return 1; - if (!(vecs->mpvec = vector_alloc())) + if (!vecs->mpvec && !(vecs->mpvec = vector_alloc())) return 1; if (!(mpvec = vector_alloc())) @@ -1366,7 +1039,7 @@ configure (struct vectors * vecs, int st path_discovery(vecs->pathvec, conf, DI_ALL); vector_foreach_slot (vecs->pathvec, pp, i){ - if (blacklist_path(conf, pp)){ + if (filter_path(conf, pp)){ vector_del_slot(vecs->pathvec, i); free_path(pp); i--; @@ -1394,10 +1067,6 @@ configure (struct vectors * vecs, int st sync_maps_state(mpvec); - if (conf->verbosity > 2) - vector_foreach_slot(mpvec, mpp, i) - print_map(mpp); - /* * purge dm of old maps */ @@ -1406,6 +1075,7 @@ configure (struct vectors * vecs, int st /* * save new set of maps formed by considering current path state */ + vector_free(vecs->mpvec); vecs->mpvec = mpvec; /* @@ -1435,6 +1105,7 @@ reconfigure (struct vectors * vecs) if (VECTOR_SIZE(vecs->pathvec)) free_pathvec(vecs->pathvec, FREE_PATHS); + vecs->pathvec = NULL; conf = NULL; if (load_config(DEFAULT_CONFIGFILE)) @@ -1467,24 +1138,10 @@ init_vecs (void) if (!vecs->lock) goto out; - vecs->pathvec = vector_alloc(); - - if (!vecs->pathvec) - goto out1; - - vecs->mpvec = vector_alloc(); - - if (!vecs->mpvec) - goto out2; - pthread_mutex_init(vecs->lock, NULL); return vecs; -out2: - vector_free(vecs->pathvec); -out1: - FREE(vecs->lock); out: FREE(vecs); condlog(0, "failed to init paths"); @@ -1543,7 +1200,7 @@ signal_init(void) signal_set(SIGUSR1, sigusr1); signal_set(SIGINT, sigend); signal_set(SIGTERM, sigend); - signal_set(SIGKILL, sigend); + signal(SIGPIPE, SIG_IGN); } static void @@ -1551,7 +1208,7 @@ setscheduler (void) { int res; static struct sched_param sched_param = { - sched_priority: 99 + .sched_priority = 99 }; res = sched_setscheduler (0, SCHED_RR, &sched_param); 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/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, unsi } } } - +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/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, u } 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, vecto } 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_hds_modular/Makefile b/path_priority/pp_hds_modular/Makefile new file mode 100644 index 0000000..a0249a5 --- /dev/null +++ b/path_priority/pp_hds_modular/Makefile @@ -0,0 +1,22 @@ +EXEC = mpath_prio_hds_modular +BUILD = glibc +OBJS = pp_hds_modular.o + +TOPDIR = ../.. +include $(TOPDIR)/Makefile.inc + +all: $(BUILD) + +glibc: $(OBJS) + $(CC) -o $(EXEC) $(OBJS) $(LDFLAGS) + +klibc: $(OBJS) + $(CC) -static -o $(EXEC) $(OBJS) + +install: $(EXEC) + install -s -m 755 $(EXEC) $(DESTDIR)$(bindir)/$(EXEC) + +uninstall: + rm $(DESTDIR)$(bindir)/$(EXEC) +clean: + rm -f *.o $(EXEC) diff --git a/path_priority/pp_hds_modular/pp_hds_modular.c b/path_priority/pp_hds_modular/pp_hds_modular.c new file mode 100644 index 0000000..f38ebcf --- /dev/null +++ b/path_priority/pp_hds_modular/pp_hds_modular.c @@ -0,0 +1,252 @@ +/* + * (C) Copyright HDS GmbH 2006. All Rights Reserved. + * + * pp_hds_modular.c + * Version 1.12 + * + * Prioritizer for multipath tools device mapper and HDS Storage + * + * Hitachis Modular Storage contains two controllers for redundancy. The + * Storage internal LUN (LDEV) will normally allocated via two pathes to the + * server (one path per controller). For performance reasons should the server + * access to a LDEV only via one controller. The other path to the other + * controller is stand-by. It is also possible to allocate more as one path + * for a LDEV per controller. Here is active/active access allowed. The other + * pathes via the other controller are stand-by. + * + * This prioritizer checks with inquiry commands the represented LDEV and + * Controller number and gives back a priority followed by this scheme : + * + * CONTROLLER ODD and LDEV ODD: PRIORITY 1 + * CONTROLLER ODD and LDEV EVEN: PRIORITY 0 + * CONTROLLER EVEN and LDEV ODD: PRIORITY 0 + * CONTROLLER EVEN and LDEV EVEN: PRIORITY 1 + * + * In the storage you can define for each LDEV a owner controller. If the + * server makes IOs via the other controller the storage will switch the + * ownership automatically. In this case you can see in the storage that the + * current controller is different from the default controller, but this is + * absolutely no problem. + * + * With this prioritizer it is possible to establish a static load balancing. + * Half of the LUNs are accessed via one HBA/storage controller and the other + * half via the other HBA/storage controller. + * + * In cluster environmemnts (RAC) it also guarantees that all cluster nodes + * have access to the LDEVs via the same controller. + * + * You can run the prioritizer manually in verbose mode : + * # pp_hds_modular -v 8:224 + * VENDOR: HITACHI + * PRODUCT: DF600F-CM + * SERIAL: 0x0105 + * LDEV: 0x00C6 + * CTRL: 1 + * PORT: B + * CTRL ODD, LDEV EVEN, PRIO 0 + * + * The items VENDOR and PRODUCT helps you to make the correct entries in file + * /etc/multipath.conf : + * # cat /etc/multipath.conf + * ... + * devices { + * device { + * vendor "HITACHI" + * product "DF600F" + * path_grouping_policy group_by_prio + * prio_callout "/sbin/pp_hds_modular %d" + * path_checker readsector0 + * getuid_callout "/sbin/scsi_id -g -u -s /block/%n" + * failback immediate + * } + * device { + * vendor "HITACHI" + * product "DF600F-CM" + * path_grouping_policy group_by_prio + * prio_callout "/sbin/pp_hds_modular %d" + * path_checker readsector0 + * getuid_callout "/sbin/scsi_id -g -u -s /block/%n" + * failback immediate + * + * + * Author: Matthias Rudolph + * + * This file is released under the GPL. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* take care: fetches glibc's /usr/include/scsi/sg.h */ + +#define INQ_REPLY_LEN 255 +#define INQ_CMD_CODE 0x12 +#define INQ_CMD_LEN 6 +#define FILE_NAME_SIZE 255 +#define safe_sprintf(var, format, args...) \ + snprintf(var, sizeof(var), format, ##args) >= sizeof(var) +#define safe_snprintf(var, size, format, args...) \ + snprintf(var, size, format, ##args) >= size + +int verbose; + +int hds_modular_prio(char * major_minor) +{ + int sg_fd, k, i; + char vendor[32]; + char product[32]; + char serial[32]; + char ldev[32]; + char ctrl[32]; + char port[32]; + char devpath[FILE_NAME_SIZE]; + unsigned int major; + unsigned int minor; + unsigned char inqCmdBlk[INQ_CMD_LEN] = + {INQ_CMD_CODE, 0, 0, 0, INQ_REPLY_LEN, 0}; + unsigned char inqBuff[INQ_REPLY_LEN]; + unsigned char sense_buffer[32]; + sg_io_hdr_t io_hdr; + + sscanf(major_minor, "%u:%u", &major, &minor); + memset(devpath, 0, FILE_NAME_SIZE); + + if (safe_sprintf(devpath, "/tmp/.pp_balance.%u.%u.devnode", + major, minor)) + exit(1); + + unlink (devpath); + mknod(devpath, S_IFBLK|S_IRUSR|S_IWUSR, makedev(major, minor)); + + if ((sg_fd = open(devpath, O_RDONLY)) < 0) exit(1); + if ((ioctl(sg_fd, SG_GET_VERSION_NUM, &k) < 0) || (k < 30000)) + exit(1); + + memset(&io_hdr, 0, sizeof(sg_io_hdr_t)); + io_hdr.interface_id = 'S'; + io_hdr.cmd_len = sizeof(inqCmdBlk); + io_hdr.mx_sb_len = sizeof(sense_buffer); + io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; + io_hdr.dxfer_len = INQ_REPLY_LEN; + io_hdr.dxferp = inqBuff; + io_hdr.cmdp = inqCmdBlk; + io_hdr.sbp = sense_buffer; + io_hdr.timeout = 2000; /* TimeOut = 2 seconds */ + + if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) exit(1); + if ((io_hdr.info & SG_INFO_OK_MASK) != SG_INFO_OK) exit(1); + + for (i = 0; i < 8 ; i++) vendor[i] = inqBuff[i+8]; + vendor[8] = 0; + for (i = 0; i < 16 ; i++) product[i] = inqBuff[i+16]; + product[16] = 0; + for (i = 0; i < 4 ; i++) serial[i] = inqBuff[i+40]; + serial[4] = 0; + for (i = 0; i < 4 ; i++) ldev[i] = inqBuff[i+44]; + ldev[4] = 0; + ctrl[0] = inqBuff[49]; + ctrl[1] = 0; + port[0] = inqBuff[50]; + port[1] = 0; + + close(sg_fd); + + if (1 == verbose) { + printf("VENDOR: %s\n", vendor); + printf("PRODUCT: %s\n", product); + printf("SERIAL: 0x%s\n", serial); + printf("LDEV: 0x%s\n", ldev); + printf("CTRL: %s\n", ctrl); + printf("PORT: %s\n", port); + } + switch( ctrl[0] ) { + case '0': case '2': case '4': case '6': case '8': + switch( ldev[3] ) { + case '0': case '2': case '4': case '6': + case '8': case 'A': case 'C': case 'E': + if (1 == verbose) + printf("CTRL EVEN, LDEV EVEN, " + "PRIO 1\n"); + return 1; + break; + case '1': case '3': case '5': case '7': + case '9': case 'B': case 'D': case 'F': + if (1 == verbose) + printf("CTRL EVEN, LDEV ODD, " + "PRIO 0\n"); + return 0; + break; + + } + case '1': case '3': case '5': case '7': case '9': + switch( ldev[3] ) { + case '0': case '2': case '4': case '6': + case '8': case 'A': case 'C': case 'E': + if (1 == verbose) + printf("CTRL ODD, LDEV EVEN, " + "PRIO 0\n"); + return 0; + break; + case '1': case '3': case '5': case '7': + case '9': case 'B': case 'D': case 'F': + if (1 == verbose) + printf("CTRL ODD, LDEV ODD, " + "PRIO 1\n"); + return 1; + break; + } + } + exit(1); +} + +void print_help(void) +{ + printf("Usage: " + "pp_hds_modular [-v] \n"); + printf("Option: " + "-v verbose mode\n"); + printf("Description: " + "Prioritizer for Multipath Tools and HDS Storage\n"); + printf("Version: " + "1.12\n"); + printf("Author: " + "Matthias Rudolph \n"); + return; +} + +int main(int argc, char * argv[]) +{ + int prio; + + if (2 == argc) { + if (0 == strcmp(argv[1], "-h")) { + print_help(); + exit(0); + } + else { + verbose = 0; + prio = hds_modular_prio(argv[1]); + printf("%d\n", prio); + exit(0); + } + } + + if ((3 == argc) && (0 == strcmp(argv[1], "-v"))) { + verbose = 1; + prio = hds_modular_prio(argv[2]); + printf("%d\n", prio); + exit(0); + } + print_help(); + exit(1); +} + diff --git a/path_priority/pp_tpc/pp_tpc.c b/path_priority/pp_tpc/pp_tpc.c index 76e7c47..a7ed7ad 100644 --- a/path_priority/pp_tpc/pp_tpc.c +++ b/path_priority/pp_tpc/pp_tpc.c @@ -62,18 +62,13 @@ int sgi_tpc_prio(const char *dev) goto out; } - if ( /* Auto-volume Transfer Enabled */ - (sense_buffer[8] & 0x80) != 0x80 ) { - fprintf(stderr, "Auto-volume Transfer not enabled"); - } - if ( /* Current Volume Path Bit */ ( sense_buffer[8] & 0x01) == 0x01 ) { /* * This volume was owned by the controller receiving * the inquiry command. */ - ret |= 0x02; + ret |= 0x01; } /* Volume Preferred Path Priority */ @@ -83,7 +78,7 @@ int sgi_tpc_prio(const char *dev) * Access to this volume is most preferred through * this path and other paths with this value. */ - ret |= 0x04; + ret |= 0x02; break; case 0x02: /* @@ -91,8 +86,7 @@ int sgi_tpc_prio(const char *dev) * as a secondary path. Typically this path would be used * for fail-over situations. */ - ret |= 0x01; - break; + /* Fallthrough */ default: /* Reserved values */ break;