Makefile.inc | 1 + devmap_name/Makefile | 10 +- kpartx/Makefile | 8 +- kpartx/devmapper.c | 7 +- kpartx/kpartx.c | 16 ++- libcheckers/directio.c | 119 ++++++++---- libcheckers/emc_clariion.c | 8 +- libcheckers/libaio.h | 222 +++++++++++++++++++++ libmultipath/config.c | 5 +- libmultipath/configure.c | 2 - libmultipath/debug.c | 2 +- libmultipath/debug.h | 2 +- libmultipath/devmapper.c | 60 +++++-- libmultipath/devmapper.h | 3 +- libmultipath/dict.c | 2 +- libmultipath/hwtable.c | 81 +++++--- libmultipath/log.c | 5 + libmultipath/log_pthread.c | 2 +- libmultipath/log_pthread.h | 2 +- libmultipath/parser.c | 15 +- libmultipath/parser.h | 1 + libmultipath/print.c | 2 +- libmultipath/structs_vec.c | 32 ++-- libmultipath/waiter.c | 9 +- multipath/Makefile | 7 +- multipath/main.c | 58 ++++++- multipath/multipath.8 | 8 +- multipath/multipath.conf.5 | 384 +++++++++++++++++++++++++++++++++++++ multipathd/Makefile | 5 +- multipathd/cli.c | 2 +- multipathd/main.c | 9 +- path_priority/pp_alua/Makefile | 9 +- path_priority/pp_emc/pp_emc.c | 8 +- path_priority/pp_hp_sw/Makefile | 25 +++ path_priority/pp_hp_sw/pp_hp_sw.c | 119 ++++++++++++ 35 files changed, 1092 insertions(+), 158 deletions(-) diff --git a/Makefile.inc b/Makefile.inc index 4a705aa..fe6cbdf 100644 --- a/Makefile.inc +++ b/Makefile.inc @@ -26,6 +26,7 @@ bindir = $(exec_prefix)/sbin checkersdir = $(TOPDIR)/libcheckers multipathdir = $(TOPDIR)/libmultipath mandir = $(prefix)/usr/share/man/man8 +man5dir = $(prefix)/usr/share/man/man5 rcdir = $(prefix)/etc/init.d GZIP = /bin/gzip -9 -c diff --git a/devmap_name/Makefile b/devmap_name/Makefile index 380c85b..8b0c678 100644 --- a/devmap_name/Makefile +++ b/devmap_name/Makefile @@ -22,21 +22,19 @@ prepare: glibc: prepare $(OBJS) $(CC) $(OBJS) -o $(EXEC) $(LDFLAGS) - $(GZIP) $(EXEC).8 > $(EXEC).8.gz klibc: prepare $(OBJS) $(CC) -static -o $(EXEC) $(OBJS) - $(GZIP) $(EXEC).8 > $(EXEC).8.gz -install: +install: $(EXEC) $(EXEC).8 install -d $(DESTDIR)$(bindir) - install -s -m 755 $(EXEC) $(DESTDIR)$(bindir)/ + install -m 755 $(EXEC) $(DESTDIR)$(bindir)/ install -d $(DESTDIR)$(mandir) - install -m 644 $(EXEC).8.gz $(DESTDIR)$(mandir) + install -m 644 $(EXEC).8 $(DESTDIR)$(mandir) uninstall: rm $(DESTDIR)$(bindir)/$(EXEC) - rm $(DESTDIR)$(mandir)/$(EXEC).8.gz + rm $(DESTDIR)$(mandir)/$(EXEC).8 clean: rm -f core *.o $(EXEC) *.gz diff --git a/kpartx/Makefile b/kpartx/Makefile index bf6e6c1..522a6a0 100644 --- a/kpartx/Makefile +++ b/kpartx/Makefile @@ -27,20 +27,18 @@ prepare: glibc: prepare $(OBJS) $(CC) $(OBJS) -o $(EXEC) $(LDFLAGS) - $(GZIP) $(EXEC).8 > $(EXEC).8.gz - + klibc: prepare $(OBJS) $(CC) -static -o $(EXEC) $(CRT0) $(OBJS) $(KLIBC) $(LIBGCC) - $(GZIP) $(EXEC).8 > $(EXEC).8.gz $(MULTIPATHLIB)-$(BUILD).a: make -C $(multipathdir) BUILD=$(BUILD) -install: +install: $(EXEC) $(EXEC).8 install -d $(DESTDIR)$(bindir) install -s -m 755 $(EXEC) $(DESTDIR)$(bindir) install -d $(DESTDIR)$(mandir) - install -m 644 $(EXEC).8.gz $(DESTDIR)$(mandir) + install -m 644 $(EXEC).8 $(DESTDIR)$(mandir) uninstall: rm -f $(DESTDIR)$(bindir)/$(EXEC) diff --git a/kpartx/devmapper.c b/kpartx/devmapper.c index 5b27487..4b228ed 100644 --- a/kpartx/devmapper.c +++ b/kpartx/devmapper.c @@ -95,19 +95,16 @@ dm_addmap (int task, const char *name, c } sprintf(prefixed_uuid, UUID_PREFIX "%s", part, uuid); if (!dm_task_set_uuid(dmt, prefixed_uuid)) - goto freeout; + goto addout; } 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; } diff --git a/kpartx/kpartx.c b/kpartx/kpartx.c index 0ee0cb3..b406b95 100644 --- a/kpartx/kpartx.c +++ b/kpartx/kpartx.c @@ -193,6 +193,7 @@ main(int argc, char **argv){ char * loopdev = NULL; char * delim = NULL; char *uuid = NULL; + char *mapname = NULL; int loopro = 0; int hotplug = 0; struct stat buf; @@ -310,12 +311,19 @@ main(int argc, char **argv){ off = find_devname_offset(device); - if (!loopdev) + if (!loopdev) { uuid = dm_mapuuid((unsigned int)MAJOR(buf.st_rdev), (unsigned int)MINOR(buf.st_rdev)); + mapname = dm_mapname((unsigned int)MAJOR(buf.st_rdev), + (unsigned int)MINOR(buf.st_rdev)); + } + if (!uuid) uuid = device + off; + if (!mapname) + mapname = device + off; + fd = open(device, O_RDONLY); if (fd == -1) { @@ -365,7 +373,7 @@ main(int argc, char **argv){ continue; printf("%s%s%d : 0 %lu %s %lu\n", - device + off, delim, j+1, + mapname, delim, j+1, (unsigned long) slices[j].size, device, (unsigned long) slices[j].start); } @@ -374,7 +382,7 @@ main(int argc, char **argv){ case DELETE: for (j = 0; j < n; j++) { if (safe_sprintf(partname, "%s%s%d", - device + off , delim, j+1)) { + mapname, delim, j+1)) { fprintf(stderr, "partname too small\n"); exit(1); } @@ -407,7 +415,7 @@ main(int argc, char **argv){ continue; if (safe_sprintf(partname, "%s%s%d", - device + off , delim, j+1)) { + mapname, delim, j+1)) { fprintf(stderr, "partname too small\n"); exit(1); } diff --git a/libcheckers/directio.c b/libcheckers/directio.c index b53c1c3..2251515 100644 --- a/libcheckers/directio.c +++ b/libcheckers/directio.c @@ -12,28 +12,44 @@ #include #include #include +#include +#include +#include "libaio.h" #include "checkers.h" +#include "../libmultipath/debug.h" #define MSG_DIRECTIO_UNKNOWN "directio checker is not available" #define MSG_DIRECTIO_UP "directio checker reports path is up" #define MSG_DIRECTIO_DOWN "directio checker reports path is down" struct directio_context { - int blksize; - unsigned char *buf; - unsigned char *ptr; + int running; + int reset_flags; + int blksize; + unsigned char * buf; + unsigned char * ptr; + io_context_t ioctx; + struct iocb io; }; + int directio_init (struct checker * c) { unsigned long pgsize = getpagesize(); struct directio_context * ct; + long flags; ct = malloc(sizeof(struct directio_context)); if (!ct) return 1; - c->context = (void *)ct; + memset(ct, 0, sizeof(struct directio_context)); + + if (syscall(__NR_io_setup, 1, &ct->ioctx) != 0) { + condlog(1, "io_setup failed"); + free(ct); + return 1; + } if (ioctl(c->fd, BLKBSZGET, &ct->blksize) < 0) { MSG(c, "cannot get blocksize, set default"); @@ -50,11 +66,28 @@ int directio_init (struct checker * c) ct->buf = (unsigned char *)malloc(ct->blksize + pgsize); if (!ct->buf) goto out; - ct->ptr = (unsigned char *)(((unsigned long)ct->buf + pgsize - 1) & - (~(pgsize - 1))); + flags = fcntl(c->fd, F_GETFL); + if (flags < 0) + goto out; + if (!(flags & O_DIRECT)) { + flags |= O_DIRECT; + if (fcntl(c->fd, F_SETFL, flags) < 0) + goto out; + ct->reset_flags = 1; + } + + ct->ptr = (unsigned char *) (((unsigned long)ct->buf + pgsize - 1) & + (~(pgsize - 1))); + + /* Sucessfully initialized, return the context. */ + c->context = (void *) ct; return 0; + out: + if (ct->buf) + free(ct->buf); + syscall(__NR_io_destroy, ct->ioctx); free(ct); return 1; } @@ -62,56 +95,63 @@ out: void directio_free (struct checker * c) { struct directio_context * ct = (struct directio_context *)c->context; + long flags; if (!ct) return; + + if (ct->reset_flags) { + if ((flags = fcntl(c->fd, F_GETFL)) >= 0) { + flags &= ~O_DIRECT; + /* No point in checking for errors */ + fcntl(c->fd, F_SETFL, flags); + } + } + if (ct->buf) free(ct->buf); + syscall(__NR_io_destroy, ct->ioctx); free(ct); } static int -direct_read (int fd, unsigned char * buff, int size) +check_state(int fd, struct directio_context *ct) { - long flags; - int reset_flags = 0; - int res, retval; - - flags = fcntl(fd,F_GETFL); - - if (flags < 0) { - return PATH_UNCHECKED; + struct timespec timeout = { .tv_sec = 2 }; + struct io_event event; + struct stat sb; + int rc = PATH_UNCHECKED; + long r; + + if (fstat(fd, &sb) == 0) { + condlog(4, "directio: called for %x", (unsigned) sb.st_rdev); } - if (!(flags & O_DIRECT)) { - flags |= O_DIRECT; - if (fcntl(fd,F_SETFL,flags) < 0) { + if (!ct->running) { + struct iocb *ios[1] = { &ct->io }; + + condlog(3, "directio: starting new request"); + memset(&ct->io, 0, sizeof(struct iocb)); + io_prep_pread(&ct->io, fd, ct->ptr, ct->blksize, 0); + if (syscall(__NR_io_submit, ct->ioctx, 1, ios) != 1) { + condlog(3, "directio: io_submit error %i", errno); return PATH_UNCHECKED; } - reset_flags = 1; } + ct->running = 1; - while ( (res = read(fd,buff,size)) < 0 && errno == EINTR ); - if (res < 0) { - if (errno == EINVAL) { - /* O_DIRECT is not available */ - retval = PATH_UNCHECKED; - } else if (errno == ENOMEM) { - retval = PATH_UP; - } else { - retval = PATH_DOWN; - } + r = syscall(__NR_io_getevents, ct->ioctx, 1L, 1L, &event, &timeout); + if (r < 1L) { + condlog(3, "directio: timeout r=%li errno=%i", r, errno); + rc = PATH_DOWN; } else { - retval = PATH_UP; - } - - if (reset_flags) { - flags &= ~O_DIRECT; - /* No point in checking for errors */ - fcntl(fd,F_SETFL,flags); + condlog(3, "directio: io finished %lu/%lu", event.res, + event.res2); + ct->running = 0; + rc = (event.res == ct->blksize) ? PATH_UP : PATH_DOWN; } - return retval; + return rc; } int directio (struct checker * c) @@ -119,7 +159,10 @@ int directio (struct checker * c) int ret; struct directio_context * ct = (struct directio_context *)c->context; - ret = direct_read(c->fd, ct->ptr, ct->blksize); + if (!ct) + return PATH_UNCHECKED; + + ret = check_state(c->fd, ct); switch (ret) { diff --git a/libcheckers/emc_clariion.c b/libcheckers/emc_clariion.c index 384a943..d801b42 100644 --- a/libcheckers/emc_clariion.c +++ b/libcheckers/emc_clariion.c @@ -129,8 +129,12 @@ int emc_clariion(struct checker * c) if ( /* Effective initiator type */ sense_buffer[27] != 0x03 - /* Failover mode should be set to 1 */ - || (sense_buffer[28] & 0x07) != 0x04 + /* + * Failover mode should be set to 1 (PNR failover mode) + * or 4 (ALUA failover mode). + */ + || (((sense_buffer[28] & 0x07) != 0x04) && + ((sense_buffer[28] & 0x07) != 0x06)) /* Arraycommpath should be set to 1 */ || (sense_buffer[30] & 0x04) != 0x04) { MSG(c, "emc_clariion_checker: Path not correctly configured " diff --git a/libcheckers/libaio.h b/libcheckers/libaio.h new file mode 100644 index 0000000..6574601 --- /dev/null +++ b/libcheckers/libaio.h @@ -0,0 +1,222 @@ +/* /usr/include/libaio.h + * + * Copyright 2000,2001,2002 Red Hat, Inc. + * + * Written by Benjamin LaHaise + * + * libaio Linux async I/O interface + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __LIBAIO_H +#define __LIBAIO_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +struct timespec; +struct sockaddr; +struct iovec; +struct iocb; + +typedef struct io_context *io_context_t; + +typedef enum io_iocb_cmd { + IO_CMD_PREAD = 0, + IO_CMD_PWRITE = 1, + + IO_CMD_FSYNC = 2, + IO_CMD_FDSYNC = 3, + + IO_CMD_POLL = 5, + IO_CMD_NOOP = 6, +} io_iocb_cmd_t; + +#if defined(__i386__) /* little endian, 32 bits */ +#define PADDED(x, y) x; unsigned y +#define PADDEDptr(x, y) x; unsigned y +#define PADDEDul(x, y) unsigned long x; unsigned y +#elif defined(__ia64__) || defined(__x86_64__) || defined(__alpha__) +#define PADDED(x, y) x, y +#define PADDEDptr(x, y) x +#define PADDEDul(x, y) unsigned long x +#elif defined(__powerpc64__) /* big endian, 64 bits */ +#define PADDED(x, y) unsigned y; x +#define PADDEDptr(x,y) x +#define PADDEDul(x, y) unsigned long x +#elif defined(__PPC__) /* big endian, 32 bits */ +#define PADDED(x, y) unsigned y; x +#define PADDEDptr(x, y) unsigned y; x +#define PADDEDul(x, y) unsigned y; unsigned long x +#elif defined(__s390x__) /* big endian, 64 bits */ +#define PADDED(x, y) unsigned y; x +#define PADDEDptr(x,y) x +#define PADDEDul(x, y) unsigned long x +#elif defined(__s390__) /* big endian, 32 bits */ +#define PADDED(x, y) unsigned y; x +#define PADDEDptr(x, y) unsigned y; x +#define PADDEDul(x, y) unsigned y; unsigned long x +#else +#error endian? +#endif + +struct io_iocb_poll { + PADDED(int events, __pad1); +}; /* result code is the set of result flags or -'ve errno */ + +struct io_iocb_sockaddr { + struct sockaddr *addr; + int len; +}; /* result code is the length of the sockaddr, or -'ve errno */ + +struct io_iocb_common { + PADDEDptr(void *buf, __pad1); + PADDEDul(nbytes, __pad2); + long long offset; + long long __pad3, __pad4; +}; /* result code is the amount read or -'ve errno */ + +struct io_iocb_vector { + const struct iovec *vec; + int nr; + long long offset; +}; /* result code is the amount read or -'ve errno */ + +struct iocb { + PADDEDptr(void *data, __pad1); /* Return in the io completion event */ + PADDED(unsigned key, __pad2); /* For use in identifying io requests */ + + short aio_lio_opcode; + short aio_reqprio; + int aio_fildes; + + union { + struct io_iocb_common c; + struct io_iocb_vector v; + struct io_iocb_poll poll; + struct io_iocb_sockaddr saddr; + } u; +}; + +struct io_event { + PADDEDptr(void *data, __pad1); + PADDEDptr(struct iocb *obj, __pad2); + PADDEDul(res, __pad3); + PADDEDul(res2, __pad4); +}; + +#undef PADDED +#undef PADDEDptr +#undef PADDEDul + +typedef void (*io_callback_t)(io_context_t ctx, struct iocb *iocb, long res, long res2); + +/* library wrappers */ +extern int io_queue_init(int maxevents, io_context_t *ctxp); +/*extern int io_queue_grow(io_context_t ctx, int new_maxevents);*/ +extern int io_queue_release(io_context_t ctx); +/*extern int io_queue_wait(io_context_t ctx, struct timespec *timeout);*/ +extern int io_queue_run(io_context_t ctx); + +/* Actual syscalls */ +extern int io_setup(int maxevents, io_context_t *ctxp); +extern int io_destroy(io_context_t ctx); +extern int io_submit(io_context_t ctx, long nr, struct iocb *ios[]); +extern int io_cancel(io_context_t ctx, struct iocb *iocb, struct io_event *evt); +extern int io_getevents(io_context_t ctx_id, long min_nr, long nr, struct io_event *events, struct timespec *timeout); + + +static inline void io_set_callback(struct iocb *iocb, io_callback_t cb) +{ + iocb->data = (void *)cb; +} + +static inline void io_prep_pread(struct iocb *iocb, int fd, void *buf, size_t count, long long offset) +{ + memset(iocb, 0, sizeof(*iocb)); + iocb->aio_fildes = fd; + iocb->aio_lio_opcode = IO_CMD_PREAD; + iocb->aio_reqprio = 0; + iocb->u.c.buf = buf; + iocb->u.c.nbytes = count; + iocb->u.c.offset = offset; +} + +static inline void io_prep_pwrite(struct iocb *iocb, int fd, void *buf, size_t count, long long offset) +{ + memset(iocb, 0, sizeof(*iocb)); + iocb->aio_fildes = fd; + iocb->aio_lio_opcode = IO_CMD_PWRITE; + iocb->aio_reqprio = 0; + iocb->u.c.buf = buf; + iocb->u.c.nbytes = count; + iocb->u.c.offset = offset; +} + +static inline void io_prep_poll(struct iocb *iocb, int fd, int events) +{ + memset(iocb, 0, sizeof(*iocb)); + iocb->aio_fildes = fd; + iocb->aio_lio_opcode = IO_CMD_POLL; + iocb->aio_reqprio = 0; + iocb->u.poll.events = events; +} + +static inline int io_poll(io_context_t ctx, struct iocb *iocb, io_callback_t cb, int fd, int events) +{ + io_prep_poll(iocb, fd, events); + io_set_callback(iocb, cb); + return io_submit(ctx, 1, &iocb); +} + +static inline void io_prep_fsync(struct iocb *iocb, int fd) +{ + memset(iocb, 0, sizeof(*iocb)); + iocb->aio_fildes = fd; + iocb->aio_lio_opcode = IO_CMD_FSYNC; + iocb->aio_reqprio = 0; +} + +static inline int io_fsync(io_context_t ctx, struct iocb *iocb, io_callback_t cb, int fd) +{ + io_prep_fsync(iocb, fd); + io_set_callback(iocb, cb); + return io_submit(ctx, 1, &iocb); +} + +static inline void io_prep_fdsync(struct iocb *iocb, int fd) +{ + memset(iocb, 0, sizeof(*iocb)); + iocb->aio_fildes = fd; + iocb->aio_lio_opcode = IO_CMD_FDSYNC; + iocb->aio_reqprio = 0; +} + +static inline int io_fdsync(io_context_t ctx, struct iocb *iocb, io_callback_t cb, int fd) +{ + io_prep_fdsync(iocb, fd); + io_set_callback(iocb, cb); + return io_submit(ctx, 1, &iocb); +} + +#ifdef __cplusplus +} +#endif + +#endif /* __LIBAIO_H */ diff --git a/libmultipath/config.c b/libmultipath/config.c index a39af8a..1dfc18c 100644 --- a/libmultipath/config.c +++ b/libmultipath/config.c @@ -366,12 +366,15 @@ load_config (char * file) /* * read the config file */ + set_current_keywords(&conf->keywords); + alloc_keywords(); if (filepresent(file)) { - set_current_keywords(&conf->keywords); if (init_data(file, init_keywords)) { condlog(0, "error parsing config file"); goto out; } + } else { + init_keywords(); } /* diff --git a/libmultipath/configure.c b/libmultipath/configure.c index 9632fb4..a5bad4c 100644 --- a/libmultipath/configure.c +++ b/libmultipath/configure.c @@ -324,7 +324,6 @@ domap (struct multipath * mpp) mpp->alias); return DOMAP_RETRY; } - dm_shut_log(); if (dm_map_present(mpp->alias)) break; @@ -345,7 +344,6 @@ domap (struct multipath * mpp) } lock_multipath(mpp, 0); - dm_restore_log(); break; case ACT_RELOAD: diff --git a/libmultipath/debug.c b/libmultipath/debug.c index fa06cbd..05dfb06 100644 --- a/libmultipath/debug.c +++ b/libmultipath/debug.c @@ -14,7 +14,7 @@ #include "vector.h" #include "config.h" -void dlog (int sink, int prio, char * fmt, ...) +void dlog (int sink, int prio, const char * fmt, ...) { va_list ap; int thres; diff --git a/libmultipath/debug.h b/libmultipath/debug.h index 74ed531..082fff1 100644 --- a/libmultipath/debug.h +++ b/libmultipath/debug.h @@ -1,4 +1,4 @@ -void dlog (int sink, int prio, char * fmt, ...) +void dlog (int sink, int prio, const char * fmt, ...) __attribute__((format(printf, 3, 4))); #if DAEMON diff --git a/libmultipath/devmapper.c b/libmultipath/devmapper.c index dece079..d762cc1 100644 --- a/libmultipath/devmapper.c +++ b/libmultipath/devmapper.c @@ -20,6 +20,13 @@ #include "debug.h" #include "memory.h" #include "devmapper.h" +#include "config.h" + +#if DAEMON +#include "log_pthread.h" +#include +#include +#endif #define MAX_WAIT 5 #define LOOPS_PER_SEC 5 @@ -28,21 +35,50 @@ #define UUID_PREFIX_LEN 6 static void -dm_dummy_log (int level, const char *file, int line, const char *f, ...) +dm_write_log (int level, const char *file, int line, const char *f, ...) { - return; -} + va_list ap; + int thres; + + if (level > 6) + level = 6; + + thres = (conf) ? conf->verbosity : 0; + if (thres <= 3 || level > thres) + return; + + va_start(ap, f); +#if DAEMON + if (!logsink) { + time_t t = time(NULL); + struct tm *tb = localtime(&t); + char buff[16]; + + strftime(buff, sizeof(buff), "%b %d %H:%M:%S", tb); + buff[sizeof(buff)-1] = '\0'; + + fprintf(stdout, "%s | ", buff); + fprintf(stdout, "libdevmapper: %s(%i): ", file, line); + vfprintf(stdout, f, ap); + fprintf(stdout, "\n"); + } else { + condlog(level, "libdevmapper: %s(%i): ", file, line); + log_safe(level + 3, f, ap); + } +#else + fprintf(stdout, "libdevmapper: %s(%i): ", file, line); + vfprintf(stdout, f, ap); + fprintf(stdout, "\n"); +#endif + va_end(ap); -void -dm_restore_log (void) -{ - dm_log_init(NULL); + return; } -void -dm_shut_log (void) -{ - dm_log_init(&dm_dummy_log); +extern void +dm_init(void) { + dm_log_init(&dm_write_log); + dm_log_init_verbose(conf ? conf->verbosity + 3 : 0); } static int @@ -764,9 +800,7 @@ dm_mapname(int major, int minor) * daemon uev_trigger -> uev_add_map */ while (--loop) { - dm_shut_log(); r = dm_task_run(dmt); - dm_restore_log(); if (r) break; diff --git a/libmultipath/devmapper.h b/libmultipath/devmapper.h index 59afd01..8438034 100644 --- a/libmultipath/devmapper.h +++ b/libmultipath/devmapper.h @@ -1,5 +1,4 @@ -void dm_shut_log(void); -void dm_restore_log(void); +void dm_init(void); int dm_prereq (char *); int dm_simplecmd (int, const char *); int dm_addmap (int, const char *, const char *, const char *, diff --git a/libmultipath/dict.c b/libmultipath/dict.c index c705cc6..4572a7d 100644 --- a/libmultipath/dict.c +++ b/libmultipath/dict.c @@ -270,7 +270,7 @@ blacklist_exceptions_handler(vector strv conf->elist_wwid = vector_alloc(); conf->elist_device = vector_alloc(); - if (!conf->elist_devnode || !conf->elist_wwid || !conf->blist_device) + if (!conf->elist_devnode || !conf->elist_wwid || !conf->elist_device) return 1; return 0; diff --git a/libmultipath/hwtable.c b/libmultipath/hwtable.c index df6f5aa..d5b227f 100644 --- a/libmultipath/hwtable.c +++ b/libmultipath/hwtable.c @@ -63,11 +63,11 @@ static struct hwentry default_hw[] = { .vendor = "DEC", .product = "HSG80", .getuid = DEFAULT_GETUID, - .getprio = NULL, - .features = DEFAULT_FEATURES, - .hwhandler = "1 hp_sw", + .getprio = "mpath_prio_hp_sw /dev/%n", + .features = "1 queue_if_no_path", + .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, - .pgpolicy = GROUP_BY_SERIAL, + .pgpolicy = GROUP_BY_PRIO, .pgfailback = FAILBACK_UNDEF, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_UNDEF, @@ -75,61 +75,62 @@ static struct hwentry default_hw[] = { .checker_name = HP_SW, }, { - .vendor = "(COMPAQ|HP)", - .product = "(MSA|HSV)1.*", + .vendor = "HP", + .product = "A6189A", .getuid = DEFAULT_GETUID, .getprio = NULL, .features = DEFAULT_FEATURES, - .hwhandler = "1 hp_sw", + .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 = HP_SW, + .checker_name = READSECTOR0, }, { - .vendor = "HP", - .product = "A6189A", + /* MSA 1000/MSA1500 EVA 3000/5000 with old firmware */ + .vendor = "(COMPAQ|HP)", + .product = "(MSA|HSV)1.0.*", .getuid = DEFAULT_GETUID, - .getprio = NULL, - .features = DEFAULT_FEATURES, + .getprio = "mpath_prio_hp_sw /dev/%n", + .features = "1 queue_if_no_path", .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, - .pgpolicy = MULTIBUS, + .pgpolicy = GROUP_BY_PRIO, .pgfailback = FAILBACK_UNDEF, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = DEFAULT_MINIO, - .checker_name = READSECTOR0, + .checker_name = HP_SW, }, { + /* MSA 1000/1500 with new firmware */ .vendor = "HP", - .product = "HSV20.*", - .revision = "[123].*", + .product = "MSA VOLUME", .getuid = DEFAULT_GETUID, - .getprio = NULL, + .getprio = "mpath_prio_alua /dev/%n", .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, - .pgpolicy = MULTIBUS, - .pgfailback = FAILBACK_UNDEF, + .pgpolicy = GROUP_BY_PRIO, + .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = DEFAULT_MINIO, - .checker_name = HP_SW, + .checker_name = TUR, }, { - .vendor = "HP", - .product = "HSV20.*", - .revision = "[^123].*", + /* EVA 3000/5000 with new firmware */ + .vendor = "(COMPAQ|HP)", + .product = "(MSA|HSV)1.1.*", .getuid = DEFAULT_GETUID, - .getprio = "/sbin/mpath_prio_alua /dev/%n", + .getprio = "mpath_prio_alua /dev/%n", .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, - .pgpolicy = MULTIBUS, + .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_UNDEF, @@ -137,15 +138,16 @@ static struct hwentry default_hw[] = { .checker_name = TUR, }, { + /* EVA 4000/6000/8000 */ .vendor = "HP", - .product = "HSV21.*", + .product = "HSV2.*", .getuid = DEFAULT_GETUID, - .getprio = "/sbin/mpath_prio_alua /dev/%n", + .getprio = "mpath_prio_alua /dev/%n", .features = DEFAULT_FEATURES, .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, @@ -404,9 +406,9 @@ static struct hwentry default_hw[] = { .vendor = "IBM", .product = "S/390 DASD ECKD", .bl_product = "S/390.*", - .getuid = "/sbin/dasd_id /dev/%n", + .getuid = "dasdinfo -u -b %n", .getprio = NULL, - .features = DEFAULT_FEATURES, + .features = "1 queue_if_no_path", .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, .pgpolicy = MULTIBUS, @@ -511,9 +513,24 @@ static struct hwentry default_hw[] = { .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .rr_weight = RR_WEIGHT_NONE, - .no_path_retry = NO_PATH_RETRY_UNDEF, + .no_path_retry = NO_PATH_RETRY_QUEUE, .minio = DEFAULT_MINIO, - .checker_name = TUR, + .checker_name = RDAC, + }, + { + .vendor = "SGI", + .product = "IS.*", + .getuid = DEFAULT_GETUID, + .getprio = "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_QUEUE, + .minio = DEFAULT_MINIO, + .checker_name = RDAC, }, /* * STK arrays diff --git a/libmultipath/log.c b/libmultipath/log.c index 8b339d7..90e4d1f 100644 --- a/libmultipath/log.c +++ b/libmultipath/log.c @@ -118,6 +118,11 @@ int log_enqueue (int prio, const char * /* not enough space on tail : rewind */ if (la->head <= la->tail && len > (la->end - la->tail)) { logdbg(stderr, "enqueue: rewind tail to %p\n", la->tail); + if (la->head == la->start ) { + logdbg(stderr, "enqueue: can not rewind tail, drop msg\n"); + la->tail = lastmsg; + return 1; /* can't reuse */ + } la->tail = la->start; if (la->empty) diff --git a/libmultipath/log_pthread.c b/libmultipath/log_pthread.c index f98cfa4..5a82b6a 100644 --- a/libmultipath/log_pthread.c +++ b/libmultipath/log_pthread.c @@ -12,7 +12,7 @@ #include "log_pthread.h" #include "log.h" -void log_safe (int prio, char * fmt, va_list ap) +void log_safe (int prio, const char * fmt, va_list ap) { pthread_mutex_lock(logq_lock); //va_start(ap, fmt); diff --git a/libmultipath/log_pthread.h b/libmultipath/log_pthread.h index 7c902c7..2b18f59 100644 --- a/libmultipath/log_pthread.h +++ b/libmultipath/log_pthread.h @@ -7,7 +7,7 @@ pthread_mutex_t *logq_lock; pthread_mutex_t *logev_lock; pthread_cond_t *logev_cond; -void log_safe(int prio, char * fmt, va_list ap); +void log_safe(int prio, const char * fmt, va_list ap); void log_thread_start(void); void log_thread_stop(void); diff --git a/libmultipath/parser.c b/libmultipath/parser.c index f9c555e..5302970 100644 --- a/libmultipath/parser.c +++ b/libmultipath/parser.c @@ -455,16 +455,23 @@ process_stream(vector keywords) return r; } +int alloc_keywords(void) +{ + if (!keywords) + keywords = vector_alloc(); + + if (!keywords) + return 1; + + return 0; +} + /* Data initialization */ int init_data(char *conf_file, void (*init_keywords) (void)) { int r; - if (!keywords) - keywords = vector_alloc(); - if (!keywords) - return 1; stream = fopen(conf_file, "r"); if (!stream) { syslog(LOG_WARNING, "Configuration file open problem"); diff --git a/libmultipath/parser.h b/libmultipath/parser.h index 95d4e6f..8496684 100644 --- a/libmultipath/parser.h +++ b/libmultipath/parser.h @@ -74,6 +74,7 @@ extern vector read_value_block(void); extern int alloc_value_block(vector strvec, void (*alloc_func) (vector)); extern void *set_value(vector strvec); extern int process_stream(vector keywords); +extern int alloc_keywords(void); 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); diff --git a/libmultipath/print.c b/libmultipath/print.c index e50f37d..dc8af48 100644 --- a/libmultipath/print.c +++ b/libmultipath/print.c @@ -734,7 +734,7 @@ snprint_hwentry (char * buff, int len, s if (fwd > len) return len; iterate_sub_keywords(rootkw, kw, i) { - fwd += snprint_keyword(buff + fwd, len - fwd, "\t\t%k %v\n", + fwd += snprint_keyword(buff + fwd, len - fwd, "\t\t%k \"%v\"\n", kw, hwe); if (fwd > len) return len; diff --git a/libmultipath/structs_vec.c b/libmultipath/structs_vec.c index 53b7e17..a4a996a 100644 --- a/libmultipath/structs_vec.c +++ b/libmultipath/structs_vec.c @@ -16,7 +16,6 @@ #include "discovery.h" #include "waiter.h" - /* * creates or updates mpp->paths reading mpp->pg */ @@ -118,6 +117,8 @@ remove_map (struct multipath * mpp, stru { int i; + condlog(4, "%s: remove multipath map", mpp->alias); + /* * stop the DM event waiter thread */ @@ -245,8 +246,17 @@ extern int setup_multipath (struct vectors * vecs, struct multipath * mpp) { retry: - if (dm_get_info(mpp->alias, &mpp->dmi)) + if (dm_get_info(mpp->alias, &mpp->dmi)) { + /* Error accessing table */ + condlog(3, "%s: cannot access table", mpp->alias); + goto out; + } + + if (!dm_map_present(mpp->alias)) { + /* Table has been removed */ + condlog(3, "%s: table does not exist", mpp->alias); goto out; + } set_multipath_wwid(mpp); mpp->mpe = find_mpe(mpp->wwid); @@ -270,6 +280,7 @@ retry: #endif goto retry; } + condlog(0, "%s: failed to setup multipath", mpp->alias); goto out; } @@ -282,7 +293,6 @@ retry: return 0; out: - condlog(0, "%s: failed to setup multipath", mpp->alias); remove_map(mpp, vecs, NULL, 1); return 1; } @@ -390,18 +400,19 @@ int update_multipath (struct vectors *ve struct pathgroup *pgp; struct path *pp; int i, j; - int r = 1; mpp = find_mp_by_alias(vecs->mpvec, mapname); - if (!mpp) - goto out; + if (!mpp) { + condlog(3, "%s: multipath map not found\n", mapname); + return 2; + } free_pgvec(mpp->pg, KEEP_PATHS); mpp->pg = NULL; if (setup_multipath(vecs, mpp)) - goto out; /* mpp freed in setup_multipath */ + return 1; /* mpp freed in setup_multipath */ /* * compare checkers states with DM states @@ -429,11 +440,8 @@ int update_multipath (struct vectors *ve } } } - r = 0; -out: - if (r) - condlog(0, "failed to update multipath"); - return r; + + return 0; } /* diff --git a/libmultipath/waiter.c b/libmultipath/waiter.c index 75ed90c..d7af0d1 100644 --- a/libmultipath/waiter.c +++ b/libmultipath/waiter.c @@ -117,15 +117,11 @@ int waiteventloop (struct event_thread * /* 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; @@ -157,8 +153,11 @@ int waiteventloop (struct event_thread * r = update_multipath(waiter->vecs, waiter->mapname); lock_cleanup_pop(waiter->vecs->lock); - if (r) + if (r) { + condlog(2, "%s: event checker exit", + waiter->mapname); return -1; /* stop the thread */ + } event_nr = dm_geteventnr(waiter->mapname); diff --git a/multipath/Makefile b/multipath/Makefile index 646dfc2..947d481 100644 --- a/multipath/Makefile +++ b/multipath/Makefile @@ -25,11 +25,9 @@ prepare: glibc: prepare $(OBJS) $(CC) $(OBJS) -o $(EXEC) $(LDFLAGS) - $(GZIP) $(EXEC).8 > $(EXEC).8.gz klibc: prepare $(OBJS) $(CC) -static -o $(EXEC) $(CRT0) $(OBJS) $(KLIBC) $(LIBGCC) - $(GZIP) $(EXEC).8 > $(EXEC).8.gz $(CHECKERSLIB)-$(BUILD).a: make -C $(checkersdir) BUILD=$(BUILD) $(BUILD) @@ -43,12 +41,13 @@ install: install -d $(DESTDIR)/etc/udev/rules.d install -m 644 multipath.rules $(DESTDIR)/etc/udev/rules.d/ install -d $(DESTDIR)$(mandir) - install -m 644 $(EXEC).8.gz $(DESTDIR)$(mandir) + install -m 644 $(EXEC).8 $(DESTDIR)$(mandir) + install -m 644 multipath.conf.5 $(DESTDIR)$(man5dir) uninstall: rm $(DESTDIR)/etc/udev/rules.d/multipath.rules rm $(DESTDIR)$(bindir)/$(EXEC) - rm $(DESTDIR)$(mandir)/$(EXEC).8.gz + rm $(DESTDIR)$(mandir)/$(EXEC).8 clean: rm -f core *.o $(EXEC) *.gz diff --git a/multipath/main.c b/multipath/main.c index 67076db..c3d0dac 100644 --- a/multipath/main.c +++ b/multipath/main.c @@ -72,7 +72,7 @@ static void usage (char * progname) { fprintf (stderr, VERSION_STRING); - fprintf (stderr, "Usage: %s\t[-v level] [-d] [-h|-l|-ll|-f|-F]\n", + fprintf (stderr, "Usage: %s\t[-v level] [-d] [-h|-l|-ll|-f|-F|-t]\n", progname); fprintf (stderr, "\t\t\t[-p failover|multibus|group_by_serial|group_by_prio]\n" \ @@ -90,6 +90,7 @@ usage (char * progname) "\t-ll\t\tshow multipath topology (maximum info)\n" \ "\t-f\t\tflush a multipath device map\n" \ "\t-F\t\tflush all multipath device maps\n" \ + "\t-t\t\tprint internal hardware table\n" \ "\t-p policy\tforce all maps to specified policy :\n" \ "\t failover\t\t1 path per priority group\n" \ "\t multibus\t\tall paths in 1 priority group\n" \ @@ -307,6 +308,55 @@ out: return r; } +static int +dump_config (void) +{ + char * c; + char * reply; + unsigned int maxlen = 256; + int again = 1; + + reply = MALLOC(maxlen); + + while (again) { + if (!reply) + return 1; + c = reply; + c += snprint_defaults(c, reply + maxlen - c); + again = ((c - reply) == maxlen); + if (again) { + reply = REALLOC(reply, maxlen *= 2); + continue; + } + c += snprint_blacklist(c, reply + maxlen - c); + again = ((c - reply) == maxlen); + if (again) { + 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) { + reply = REALLOC(reply, maxlen *= 2); + continue; + } + c += snprint_mptable(c, reply + maxlen - c, conf->mptable); + again = ((c - reply) == maxlen); + if (again) + reply = REALLOC(reply, maxlen *= 2); + } + + printf("%s", reply); + FREE(reply); + return 0; +} + int main (int argc, char *argv[]) { @@ -330,7 +380,7 @@ main (int argc, char *argv[]) if (load_config(DEFAULT_CONFIGFILE)) exit(1); - while ((arg = getopt(argc, argv, ":dhl::FfM:v:p:b:")) != EOF ) { + while ((arg = getopt(argc, argv, ":dhl::FfM:v:p:b:t")) != EOF ) { switch(arg) { case 1: printf("optarg : %s\n",optarg); break; @@ -373,6 +423,9 @@ main (int argc, char *argv[]) usage(argv[0]); } break; + case 't': + dump_config(); + goto out; case 'h': usage(argv[0]); case ':': @@ -401,6 +454,7 @@ main (int argc, char *argv[]) conf->dev_type = DEV_DEVMAP; } + dm_init(); if (conf->remove == FLUSH_ONE) { if (conf->dev_type == DEV_DEVMAP) diff --git a/multipath/multipath.8 b/multipath/multipath.8 index 693872b..e72cc45 100644 --- a/multipath/multipath.8 +++ b/multipath/multipath.8 @@ -6,7 +6,7 @@ multipath \- Device mapper target autoco .RB [\| \-v\ \c .IR verbosity \|] .RB [\| \-d \|] -.RB [\| \-h | \-l | \-ll | \-f | \-F \|] +.RB [\| \-h | \-l | \-ll | \-f | \-t | \-F \|] .RB [\| \-p\ \c .BR failover | multibus | group_by_serial | group_by_prio | group_by_node_name \|] .RB [\| device \|] @@ -47,6 +47,9 @@ flush a multipath device map specified a .B \-F flush all unused multipath device maps .TP +.B \-t +print internal hardware table to stdout +.TP .BI \-p " policy" force maps to specified policy: .RS 1.2i @@ -76,6 +79,9 @@ is in the /dev/sdb (as shown by udev in .I device may alternatively be a multipath mapname .SH "SEE ALSO" +.BR multipathd (8), +.BR multipath.conf (5), +.BR kpartx (8), .BR udev (8), .BR dmsetup (8) .BR hotplug (8) diff --git a/multipath/multipath.conf.5 b/multipath/multipath.conf.5 new file mode 100644 index 0000000..1c1c0db --- /dev/null +++ b/multipath/multipath.conf.5 @@ -0,0 +1,384 @@ +.TH MULTIPATH.CONF 5 "30 November 2006" +.SH NAME +multipath.conf \- multipath daemon configuration file +.SH DESCRIPTION +.B "multipath.conf" +is the configuration file for the multipath daemon. It is used to +overwrite the built-in configuration table of \fBmultipathd\fP. +Any line whose first non-white-space character is a '#' is considered +a comment line. Empty lines are ignored. +.SH SYNTAX +The configuration file contains entries of the form: +.RS +.nf +.ft B +.sp +
{ +.RS +.ft B + +.I "..." +.ft B + { +.RS +.ft B + +.I "..." +.RE +} +.RE +} +.ft R +.fi +.RE +.LP +Each \fIsection\fP contains one or more attributes or subsections. The +recognized keywords for attributes or subsections depend on the +section in which they occor. +.LP +The following \fIsection\fP keywords are recognized: +.TP 17 +.B defaults +This section defines default values for attributes which are used +whenever no specific setting is given. +.TP +.B blacklist +This section defines which devices should be excluded from the +multipath topology discovery. +.TP +.B blacklist_exceptions +This section defines which devices should be included in the +multipath topology discovery, despite being listed in the +.I blacklist +section. +.TP +.B multipaths +This section defines the multipath topologies. They are indexed by a +\fIWorld Wide Identifier\fR(wwid), which is the result of the +\fIgetuid_callout\fR program. +.TP +.B devices +This section defines the device-specific settings. +.RE +.LP +.SH "defaults section" +The +.B defaults +section recognizes the following keywords: +.TP 17 +.B polling_interval +interval between two path checks in seconds; default is +.I 5 +.TP +.B udev_dir +directory where udev creates its device nodes; default is +.I /dev +.TP +.B selector +The default path selector algorithm to use; they are offered by the +kernel multipath target. The only currently implemented is +.I "round-robin 0" +.TP +.B path_grouping_policy +The default path grouping policy to apply to unspecified +multipaths. Possible values are +.RS +.TP 12 +.B failover +1 path per priority group +.TP +.B multibus +all paths in 1 priority group +.TP +.B group_by_serial +1 priority group per serial number +.TP +.B group_by_prio +1 priority group per priority value. Priorities are determined by +callout programs specified as a global, per-controller or +per-multipath option in the configuration file. +.TP +.B group_by_node_name +1 priority group per target node name. Target node names are fetched +in /sys/class/fc_transport/target*/node_name. +.TP +Default value is \fImultibus\fR. +.RE +.TP +.B getuid_callout +The default program and args to callout to obtain a unique path +identifier. Should be specified with an absolute path. Default value +is +.I /sbin/scsi_id -g -u -s +.TP +.B prio_callout +The default program and args to callout to obtain a path priority +value. The specified program will be executed and should return a +numeric value specifying the relative priority of this path. Higher +number have a higher priority. A '%n' in the command line will be expanded +to the device name, a '%b' will be expanded to the device number in +.I major:minor +format. +.I "none" +is a valid value. Currently the following path priority programs are +implemented: +.RS +.TP 12 +.B mpath_prio_emc /dev/%n +Generate the path priority for EMC arrays +.TP +.B mpath_prio_alua /dev/%n +Generate the path priority based on the SCSI-3 ALUA settings. +.TP +.B mpath_prio_netapp /dev/%n +Generate the path priority for NetApp arrays. +.TP +.B mpath_prio_tpc /dev/%n +Generate the path priority for LSI/Engenio RDAC controller. +.TP +.B mpath_prio_hp_sw /dev/%n +Generate the path priority for Compaq/HP controller in +active/standby mode. +.TP +.B mpath_prio_hds_modular %b +Generate the path priority for Hitachi HDS Modular storage arrays. +.TP +Default value is \fBnone\fR. +.RE +.TP +.B features +Specify any device-mapper features to be used. The most common of +these features is +.I "1 queue_if_no_path" +Note that this can also be set via the +.I no_path_retry +keyword. +.TP +.B path_checker +The default method used to determine the paths' state. Possible values +are +.RS +.TP 12 +.B readsector0 +Read the first sector of the device +.TP +.B tur +Issue a +.I TEST UNIT READY +command to the device. +.TP +.B emc_clariion +Query the EMC Clariion specific EVPD page 0xC0 to determine the path +state. +.TP +.B hp_sw +Check the path state for HP storage arrays with Active/Standby firmware. +.TP +.B rdac +Check the path state for LSI/Engenio RDAC storage controller. +.TP +.B directio +Read the first sector with direct I/O. +.TP +Default value is \fIreadsector0\fR. +.RE +.TP +.B failback +Tell the daemon to manage path group failback, or not to. 0 or +.I immediate +means immediate failback, values >0 means deferred failback (in +seconds). +.I manual +means no failback. Default value is +.I manual +.TP +.B rr_min_io +The number of IO to route to a path before switching to the next in +the same path group. Default is +.I 1000 +.TP +.B rr_weight +If set to \fIpriorities\fR the multipath configurator will assign +path weights as "path prio * rr_min_io". Possible values are +.I priorities +or +.I uniform +. Default is +.I uniform +.TP +.B no_path_retry +Specify the number of retries until disable queueing, or +.I fail +for immediate failure (no queueing), +.I queue +for never stop queueing. Default is 0. +.TP +.B user_friendly_names +If set to +.I yes +, using the bindings file +.I /var/lib/multipath/bindings +to assign a persistent and unique alias to the multipath, in the form of mpath. +If set to +.I no +use the WWID as the alias. In either case this be will +be overriden by any specific aliases in the \fImultipaths\fR section. +Default is +.I no +. +.SH "blacklist section" +The +.I blacklist +section is used to exclude specific device from inclusion in the +multipath topology. It is most commonly used to exclude local disks or +LUNs for the array controller. +.LP +The following keywords are recognized: +.TP 17 +.B wwid +The \fIWorld Wide Identification\fR of a device. +.TP +.B devnode +Regular expression of the device nodes to be excluded. +.TP +.B device +Subsection for the device description. This subsection recognizes the +.I vendor +and +.I product +keywords. For a full description of these keywords please see the +.I devices +section description. +.SH "blacklist_exceptions section" +The +.I blacklist_exceptions +section is used to revert the actions of the +.I blacklist +section, ie to include specific device in the +multipath topology. This allows to selectively include devices which +would normally be excluded via the +.I blacklist +section. +.LP +The following keywords are recognized: +.TP 17 +.B wwid +The \fIWorld Wide Identification\fR of a device. +.TP +.B devnode +Regular expression of the device nodes to be excluded. +.TP +.B device +Subsection for the device description. This subsection recognizes the +.I vendor +and +.I product +keywords. For a full description of these keywords please see the +.I devices +section description. +.SH "multipaths section" +The only recognized attribute for the +.B multipaths +section is the +.I multipath +subsection. +.LP +The +.B multipath +subsection recognizes the following attributes: +.TP 17 +.B wwid +Index of the container. Mandatory for this subsection. +.TP +.B alias +(Optional) symbolic name for the multipath map. +.LP +The following attributes are optional; if not set the default values +are taken from the +.I defaults +section: +.sp 1 +.PD .1v +.RS +.TP 18 +.B path_grouping_policy +.TP +.B path_checker +.TP +.B path_selector +.TP +.B failback +.TP +.B no_path_retry +.TP +.B rr_min_io +.RE +.PD +.LP +.SH "devices section" +The only recognized attribute for the +.B devices +section is the +.I device +subsection. +.LP +The +.I device +subsection recognizes the following attributes: +.TP 17 +.B vendor +(Mandatory) Vendor identifier +.TP +.B product +(Mandatory) Product identifier +.TP +.B product_blacklist +Product strings to blacklist for this vendor +.TP +.B hardware_handler +(Optional) The hardware handler to use for this device type. +The following hardware handler are implemented: +.RS +.TP 12 +.B 1 emc +Hardware handler for EMC storage arrays. +.RE +.LP +The following attributes are optional; if not set the default values +are taken from the +.I defaults +section: +.sp 1 +.PD .1v +.RS +.TP 18 +.B path_grouping_policy +.TP +.B getuid_callout +.TP +.B path_selector +.TP +.B path_checker +.TP +.B features +.TP +.B prio_callout +.TP +.B failback +.TP +.B rr_weight +.TP +.B no_path_retry +.TP +.B rr_min_io +.RE +.PD +.LP +.SH "SEE ALSO" +.BR udev (8), +.BR dmsetup (8) +.BR multipath (8) +.BR multipathd (8) +.SH AUTHORS +.B multipath +was developed by Christophe Varoqui, and others. diff --git a/multipathd/Makefile b/multipathd/Makefile index 8ad25ee..da351dc 100644 --- a/multipathd/Makefile +++ b/multipathd/Makefile @@ -35,7 +35,6 @@ klibc: $(EXEC): clean $(OBJS) $(CC) $(OBJS) -o $(EXEC) $(LDFLAGS) - $(GZIP) $(EXEC).8 > $(EXEC).8.gz $(CHECKERSLIB)-glibc.a: $(MAKE) -C $(checkersdir) BUILD=glibc glibc @@ -48,12 +47,12 @@ install: install -s -m 755 $(EXEC) $(DESTDIR)$(bindir) install -d $(DESTDIR)$(rcdir) install -d $(DESTDIR)$(mandir) - install -m 644 $(EXEC).8.gz $(DESTDIR)$(mandir) + install -m 644 $(EXEC).8 $(DESTDIR)$(mandir) uninstall: rm -f $(DESTDIR)$(bindir)/$(EXEC) rm -f $(DESTDIR)$(rcdir)/$(EXEC) - rm -f $(DESTDIR)$(mandir)/$(EXEC).8.gz + rm -f $(DESTDIR)$(mandir)/$(EXEC).8 clean: $(MAKE) -C $(multipathdir) prepare DAEMON=1 diff --git a/multipathd/cli.c b/multipathd/cli.c index d786eef..587514f 100644 --- a/multipathd/cli.c +++ b/multipathd/cli.c @@ -498,7 +498,7 @@ key_generator (const char * str, int sta /* * Loop through keywords for completion candidates */ - vector_foreach_slot_after (keys, kw, index) { + vector_foreach_slot (keys, kw, index) { if (!strncmp(kw->str, str, len)) { /* * Discard keywords already in the command line diff --git a/multipathd/main.c b/multipathd/main.c index f2f9a96..94b0b95 100644 --- a/multipathd/main.c +++ b/multipathd/main.c @@ -225,6 +225,8 @@ ev_add_map (char * devname, struct vecto int map_present; int r = 1; + /* libsysfs seems to forget to terminate the string... */ + memset(dev_t, 0, BLK_DEV_SIZE); if (sscanf(devname, "dm-%d", &minor) == 1 && !sysfs_get_dev(sysfs_path, devname, dev_t, BLK_DEV_SIZE) && sscanf(dev_t, "%d:%d", &major, &minor) == 2) @@ -646,10 +648,12 @@ uev_trigger (struct uevent * uev, void * lock(vecs->lock); /* - * device map add/remove event + * device map event + * Add events are ignored here as the tables + * are not fully initialised then. */ if (!strncmp(devname, "dm-", 3)) { - if (!strncmp(uev->action, "add", 3)) { + if (!strncmp(uev->action, "change", 6)) { r = uev_add_map(devname, vecs); goto out; } @@ -1397,6 +1401,7 @@ main (int argc, char *argv[]) int err; logsink = 1; + dm_init(); if (getuid() != 0) { fprintf(stderr, "need to be root\n"); diff --git a/path_priority/pp_alua/Makefile b/path_priority/pp_alua/Makefile index 983ffe3..6c58529 100644 --- a/path_priority/pp_alua/Makefile +++ b/path_priority/pp_alua/Makefile @@ -35,20 +35,17 @@ glibc: $(OBJS) klibc: $(OBJS) $(CC) -static -o $(EXEC) $(OBJS) -install: $(BUILD) $(EXEC).8.gz +install: $(EXEC) $(EXEC).8 $(INSTALL) -s -m 755 $(EXEC) $(DESTDIR)$(bindir)/$(EXEC) - $(INSTALL) -m 644 $(EXEC).8.gz $(DESTDIR)$(mandir)/$(EXEC).8.gz + $(INSTALL) -m 644 $(EXEC).8 $(DESTDIR)$(mandir)/$(EXEC).8 uninstall: rm $(DESTDIR)$(bindir)/$(EXEC) - rm $(DESTDIR)$(mandir)/$(EXEC).8.gz + rm $(DESTDIR)$(mandir)/$(EXEC).8 clean: rm -f *.o *.gz $(EXEC) -$(EXEC).8.gz: $(EXEC).8 - $(GZIP) $< >$@ - main.o: main.c rtpg.h spc3.h rtpg.o: rtpg.c rtpg.h spc3.h diff --git a/path_priority/pp_emc/pp_emc.c b/path_priority/pp_emc/pp_emc.c index dd58424..4031720 100644 --- a/path_priority/pp_emc/pp_emc.c +++ b/path_priority/pp_emc/pp_emc.c @@ -60,8 +60,12 @@ int emc_clariion_prio(const char *dev) if ( /* Effective initiator type */ sense_buffer[27] != 0x03 - /* Failover mode should be set to 1 */ - || (sense_buffer[28] & 0x07) != 0x04 + /* + * Failover mode should be set to 1 (PNR failover mode) + * or 4 (ALUA failover mode). + */ + || (((sense_buffer[28] & 0x07) != 0x04) && + ((sense_buffer[28] & 0x07) != 0x06)) /* Arraycommpath should be set to 1 */ || (sense_buffer[30] & 0x04) != 0x04) { fprintf(stderr, "Path not correctly configured for failover"); diff --git a/path_priority/pp_hp_sw/Makefile b/path_priority/pp_hp_sw/Makefile new file mode 100644 index 0000000..e7debf5 --- /dev/null +++ b/path_priority/pp_hp_sw/Makefile @@ -0,0 +1,25 @@ +EXEC = mpath_prio_hp_sw +BUILD = glibc +OBJS = pp_hp_sw.o + +TOPDIR = ../.. +include $(TOPDIR)/Makefile.inc + +all: $(BUILD) + +glibc: $(OBJS) + $(CC) -o $(EXEC) $(OBJS) $(LDFLAGS) + +klibc: $(OBJS) + $(CC) -static -o $(EXEC) $(OBJS) + +install: $(EXEC) + install -m 755 $(EXEC) $(DESTDIR)$(bindir)/$(EXEC) + +uninstall: + rm $(DESTDIR)$(bindir)/$(EXEC) +clean: + rm -f *.o $(EXEC) + +%.o: %.c + $(CC) $(CFLAGS) -c -o $@ $< diff --git a/path_priority/pp_hp_sw/pp_hp_sw.c b/path_priority/pp_hp_sw/pp_hp_sw.c new file mode 100644 index 0000000..e4a18b1 --- /dev/null +++ b/path_priority/pp_hp_sw/pp_hp_sw.c @@ -0,0 +1,119 @@ +/* + * Path priority checker for HP active/standby controller + * + * Check the path state and sort them into groups. + * There is actually a preferred path in the controller; + * we should ask HP on how to retrieve that information. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TUR_CMD_LEN 6 +#define SCSI_CHECK_CONDITION 0x2 +#define SCSI_COMMAND_TERMINATED 0x22 +#define SG_ERR_DRIVER_SENSE 0x08 +#define RECOVERED_ERROR 0x01 +#define NOT_READY 0x02 +#define UNIT_ATTENTION 0x06 + +#define HP_PATH_ACTIVE 0x04 +#define HP_PATH_STANDBY 0x02 +#define HP_PATH_FAILED 0x00 + +#include "../../libmultipath/sg_include.h" + +int hp_sw_prio(const char *dev) +{ + unsigned char turCmdBlk[TUR_CMD_LEN] = { 0x00, 0, 0, 0, 0, 0 }; + unsigned char sb[128]; + struct sg_io_hdr io_hdr; + int ret = HP_PATH_FAILED; + int fd; + + fd = open(dev, O_RDWR|O_NONBLOCK); + + if (fd <= 0) { + fprintf(stderr, "Opening the device failed.\n"); + goto out; + } + + memset(&io_hdr, 0, sizeof (struct sg_io_hdr)); + io_hdr.interface_id = 'S'; + io_hdr.cmd_len = sizeof (turCmdBlk); + io_hdr.mx_sb_len = sizeof (sb); + io_hdr.dxfer_direction = SG_DXFER_NONE; + io_hdr.cmdp = turCmdBlk; + io_hdr.sbp = sb; + io_hdr.timeout = 60000; + io_hdr.pack_id = 0; + retry: + if (ioctl(fd, SG_IO, &io_hdr) < 0) { + fprintf(stderr, "sending tur command failed\n"); + goto out; + } + io_hdr.status &= 0x7e; + if ((0 == io_hdr.status) && (0 == io_hdr.host_status) && + (0 == io_hdr.driver_status)) { + /* Command completed normally, path is active */ + ret = HP_PATH_ACTIVE; + } + + if ((SCSI_CHECK_CONDITION == io_hdr.status) || + (SCSI_COMMAND_TERMINATED == io_hdr.status) || + (SG_ERR_DRIVER_SENSE == (0xf & io_hdr.driver_status))) { + if (io_hdr.sbp && (io_hdr.sb_len_wr > 2)) { + int sense_key, asc, asq; + unsigned char * sense_buffer = io_hdr.sbp; + if (sense_buffer[0] & 0x2) { + sense_key = sense_buffer[1] & 0xf; + asc = sense_buffer[2]; + asq = sense_buffer[3]; + } else { + sense_key = sense_buffer[2] & 0xf; + asc = sense_buffer[12]; + asq = sense_buffer[13]; + } + if(RECOVERED_ERROR == sense_key) + ret = HP_PATH_ACTIVE; + if(NOT_READY == sense_key) { + if (asc == 0x04 && asq == 0x02) { + /* This is a standby path */ + ret = HP_PATH_STANDBY; + } + } + if(UNIT_ATTENTION == sense_key) { + if (asc == 0x29) { + /* Retry for device reset */ + goto retry; + } + } + } + } + + close(fd); + +out: + return(ret); +} + +int +main (int argc, char **argv) +{ + int prio; + if (argc != 2) { + fprintf(stderr, "Arguments wrong!\n"); + prio = 0; + } else + prio = hp_sw_prio(argv[1]); + + printf("%d\n", prio); + exit(0); +} +