From e280ea0f61f4b166ac6b134e36cebef477919d62 Mon Sep 17 00:00:00 2001 From: DongHun Kwak Date: Fri, 14 Jan 2022 13:50:18 +0900 Subject: [PATCH] Imported Upstream version 0.7.9 --- kpartx/devmapper.c | 1 + kpartx/test-kpartx | 24 ++- libmpathpersist/mpath_persist.c | 16 +- libmultipath/checkers.c | 187 +++++++++++------- libmultipath/checkers.h | 69 ++++--- libmultipath/checkers/cciss_tur.c | 13 +- libmultipath/checkers/directio.c | 29 ++- libmultipath/checkers/emc_clariion.c | 114 +++++++++-- libmultipath/checkers/hp_sw.c | 39 ++-- libmultipath/checkers/rdac.c | 92 ++++++--- libmultipath/checkers/readsector0.c | 7 +- libmultipath/checkers/tur.c | 133 ++++++++++--- libmultipath/config.c | 10 +- libmultipath/configure.c | 10 +- libmultipath/defaults.h | 1 + libmultipath/devmapper.c | 8 +- libmultipath/dict.c | 15 +- libmultipath/discovery.c | 45 ++--- libmultipath/foreign.c | 5 +- libmultipath/foreign/nvme.c | 6 +- libmultipath/print.c | 2 +- libmultipath/propsel.c | 19 +- libmultipath/structs.c | 1 + libmultipath/structs_vec.c | 5 +- libmultipath/sysfs.c | 5 +- libmultipath/util.c | 40 ++++ libmultipath/util.h | 10 + libmultipath/version.h | 4 +- multipath/main.c | 12 +- multipath/multipath.8 | 273 +++++++++++++++++---------- multipath/multipath.rules | 4 + multipathd/cli_handlers.c | 14 +- multipathd/main.c | 251 ++++++++++++++---------- multipathd/uxclnt.c | 13 +- multipathd/uxlsnr.c | 14 +- multipathd/uxlsnr.h | 5 +- tests/dmevents.c | 2 +- 37 files changed, 982 insertions(+), 516 deletions(-) diff --git a/kpartx/devmapper.c b/kpartx/devmapper.c index 8db1eb5..3aa4988 100644 --- a/kpartx/devmapper.c +++ b/kpartx/devmapper.c @@ -690,6 +690,7 @@ int dm_find_part(const char *parent, const char *delim, int part, r = dm_rename(tmp, name); if (r == 1) { + free(tmp); *part_uuid = uuid; return 1; } diff --git a/kpartx/test-kpartx b/kpartx/test-kpartx index d2001dc..d3c9aef 100755 --- a/kpartx/test-kpartx +++ b/kpartx/test-kpartx @@ -87,6 +87,7 @@ step preparation FILE1=kpartx1 FILE2=kpartx2 FILE3=kpartx3 +FILE4=kpartx4 SIZE=$((1024*1024*1024)) # use bytes as units here SECTSIZ=512 OFFS=32 # offset of linear mapping into dev, sectors @@ -108,6 +109,7 @@ step "create loop devices" truncate -s $SIZE $FILE1 truncate -s $SIZE $FILE2 truncate -s $SIZE $FILE3 +truncate -s $SIZE $FILE4 LO1=$(losetup -f $FILE1 --show) push_cleanup 'losetup -d $LO1' @@ -115,8 +117,10 @@ LO2=$(losetup -f $FILE2 --show) push_cleanup 'losetup -d $LO2' LO3=$(losetup -f $FILE3 --show) push_cleanup 'losetup -d $LO3' +LO4=$(losetup -f $FILE4 --show) +push_cleanup 'losetup -d $LO4' -[[ $LO1 && $LO2 && $LO3 && -b $LO1 && -b $LO2 && -b $LO3 ]] +[[ $LO1 && $LO2 && $LO3 && $LO4 && -b $LO1 && -b $LO2 && -b $LO3 && -b $LO4 ]] DEV1=$(stat -c "%t:%T" $LO1) DEV2=$(stat -c "%t:%T" $LO2) DEV3=$(stat -c "%t:%T" $LO3) @@ -173,6 +177,7 @@ step "create partitions on loop devices" mk_partitions $LO1 mk_partitions $LO2 +mk_partitions $LO4 # Test invocation of kpartx with regular file here LO2P1=/dev/mapper/$(basename $LO2)-foo1 @@ -310,4 +315,21 @@ $KPARTX $KPARTX_OPTS -f -d $LO3 [[ -b /dev/mapper/$SPAN1 ]] [[ -b /dev/mapper/$SPAN2 ]] +step "test kpartx creation/deletion on an image file with no existing loopdev" +losetup -d $LO4 + +OUTPUT=$($KPARTX $KPARTX_OPTS -v -a $FILE4 2>&1) +read loop dm < \ + <(sed -n 's/^add map \(loop[0-9]*\)p1 ([0-9]*:\([0-9]*\)).*$/\1 dm-\2/p' \ + <<<$OUTPUT) +[[ $dm && $loop ]] +push_cleanup "dmsetup remove -f /dev/$dm" +push_cleanup "losetup -d /dev/$loop" + +[[ -b /dev/mapper/${loop}p1 ]] +$KPARTX -d $KPARTX_OPTS $FILE4 +[[ ! -b /dev/mapper/${loop}p1 ]] +# /dev/$loop is _not_ automatically deleted +[[ -b /dev/${loop} ]] + OK=yes diff --git a/libmpathpersist/mpath_persist.c b/libmpathpersist/mpath_persist.c index 4229a94..2ffe56e 100644 --- a/libmpathpersist/mpath_persist.c +++ b/libmpathpersist/mpath_persist.c @@ -31,7 +31,6 @@ #include #include #include -#include #define __STDC_FORMAT_MACROS 1 @@ -48,15 +47,7 @@ mpath_lib_init (void) return NULL; } - if (conf->max_fds) { - struct rlimit fd_limit; - - fd_limit.rlim_cur = conf->max_fds; - fd_limit.rlim_max = conf->max_fds; - if (setrlimit(RLIMIT_NOFILE, &fd_limit) < 0) - condlog(0, "can't set open fds limit to %d : %s", - conf->max_fds, strerror(errno)); - } + set_max_fds(conf->max_fds); return conf; } @@ -568,11 +559,10 @@ int mpath_prout_reg(struct multipath *mpp,int rq_servact, int rq_scope, } if (rollback && ((rq_servact == MPATH_PROUT_REG_SA) && sa_key != 0 )){ condlog (3, "%s: ERROR: initiating pr out rollback", mpp->wwid); + memcpy(¶mp->key, ¶mp->sa_key, 8); + memset(¶mp->sa_key, 0, 8); for( i=0 ; i < count ; i++){ if(thread[i].param.status == MPATH_PR_SUCCESS) { - memcpy(&thread[i].param.paramp->key, &thread[i].param.paramp->sa_key, 8); - memset(&thread[i].param.paramp->sa_key, 0, 8); - thread[i].param.status = MPATH_PR_SUCCESS; rc = pthread_create(&thread[i].id, &attr, mpath_prout_pthread_fn, (void *)(&thread[i].param)); if (rc){ diff --git a/libmultipath/checkers.c b/libmultipath/checkers.c index 0bacc86..848c4c3 100644 --- a/libmultipath/checkers.c +++ b/libmultipath/checkers.c @@ -8,6 +8,19 @@ #include "checkers.h" #include "vector.h" +struct checker_class { + struct list_head node; + void *handle; + int refcount; + int sync; + char name[CHECKER_NAME_LEN]; + int (*check)(struct checker *); + int (*init)(struct checker *); /* to allocate the context */ + void (*free)(struct checker *); /* to free the context */ + const char **msgtable; + short msgtable_size; +}; + char *checker_state_names[] = { "wild", "unchecked", @@ -23,38 +36,30 @@ char *checker_state_names[] = { static LIST_HEAD(checkers); -char * checker_state_name (int i) +const char *checker_state_name(int i) { return checker_state_names[i]; } -int init_checkers (char *multipath_dir) -{ - if (!add_checker(multipath_dir, DEFAULT_CHECKER)) - return 1; - return 0; -} - -struct checker * alloc_checker (void) +static struct checker_class *alloc_checker_class(void) { - struct checker *c; + struct checker_class *c; - c = MALLOC(sizeof(struct checker)); + c = MALLOC(sizeof(struct checker_class)); if (c) { INIT_LIST_HEAD(&c->node); c->refcount = 1; - c->fd = -1; } return c; } -void free_checker (struct checker * c) +void free_checker_class(struct checker_class *c) { if (!c) return; c->refcount--; if (c->refcount) { - condlog(3, "%s checker refcount %d", + condlog(4, "%s checker refcount %d", c->name, c->refcount); return; } @@ -71,17 +76,17 @@ void free_checker (struct checker * c) void cleanup_checkers (void) { - struct checker * checker_loop; - struct checker * checker_temp; + struct checker_class *checker_loop; + struct checker_class *checker_temp; list_for_each_entry_safe(checker_loop, checker_temp, &checkers, node) { - free_checker(checker_loop); + free_checker_class(checker_loop); } } -struct checker * checker_lookup (char * name) +static struct checker_class *checker_class_lookup(const char *name) { - struct checker * c; + struct checker_class *c; if (!name || !strlen(name)) return NULL; @@ -92,14 +97,15 @@ struct checker * checker_lookup (char * name) return NULL; } -struct checker * add_checker (char *multipath_dir, char * name) +static struct checker_class *add_checker_class(const char *multipath_dir, + const char *name) { char libname[LIB_CHECKER_NAMELEN]; struct stat stbuf; - struct checker * c; + struct checker_class *c; char *errstr; - c = alloc_checker(); + c = alloc_checker_class(); if (!c) return NULL; snprintf(c->name, CHECKER_NAME_LEN, "%s", name); @@ -141,13 +147,28 @@ struct checker * add_checker (char *multipath_dir, char * name) if (!c->free) goto out; + c->msgtable_size = 0; + c->msgtable = dlsym(c->handle, "libcheck_msgtable"); + + if (c->msgtable != NULL) { + const char **p; + + for (p = c->msgtable; + *p && (p - c->msgtable) < CHECKER_MSGTABLE_SIZE; p++) + /* nothing */; + + c->msgtable_size = p - c->msgtable; + } else + c->msgtable_size = 0; + condlog(3, "checker %s: message table size = %d", + c->name, c->msgtable_size); + done: - c->fd = -1; c->sync = 1; list_add(&c->node, &checkers); return c; out: - free_checker(c); + free_checker_class(c); return NULL; } @@ -160,16 +181,16 @@ void checker_set_fd (struct checker * c, int fd) void checker_set_sync (struct checker * c) { - if (!c) + if (!c || !c->cls) return; - c->sync = 1; + c->cls->sync = 1; } void checker_set_async (struct checker * c) { - if (!c) + if (!c || !c->cls) return; - c->sync = 0; + c->cls->sync = 0; } void checker_enable (struct checker * c) @@ -188,11 +209,11 @@ void checker_disable (struct checker * c) int checker_init (struct checker * c, void ** mpctxt_addr) { - if (!c) + if (!c || !c->cls) return 1; c->mpcontext = mpctxt_addr; - if (c->init) - return c->init(c); + if (c->cls->init) + return c->cls->init(c); return 0; } @@ -204,15 +225,16 @@ void checker_clear (struct checker *c) void checker_put (struct checker * dst) { - struct checker * src; + struct checker_class *src; - if (!dst || !strlen(dst->name)) + if (!dst) return; - src = checker_lookup(dst->name); - if (dst->free) - dst->free(dst); + src = dst->cls; + + if (src && src->free) + src->free(dst); checker_clear(dst); - free_checker(src); + free_checker_class(src); } int checker_check (struct checker * c, int path_state) @@ -222,76 +244,103 @@ int checker_check (struct checker * c, int path_state) if (!c) return PATH_WILD; - c->message[0] = '\0'; + c->msgid = CHECKER_MSGID_NONE; if (c->disable) { - MSG(c, "checker disabled"); + c->msgid = CHECKER_MSGID_DISABLED; return PATH_UNCHECKED; } - if (!strncmp(c->name, NONE, 4)) + if (!strncmp(c->cls->name, NONE, 4)) return path_state; if (c->fd < 0) { - MSG(c, "no usable fd"); + c->msgid = CHECKER_MSGID_NO_FD; return PATH_WILD; } - r = c->check(c); + r = c->cls->check(c); return r; } -int checker_selected (struct checker * c) +int checker_selected(const struct checker *c) { if (!c) return 0; - if (!strncmp(c->name, NONE, 4)) - return 1; - return (c->check) ? 1 : 0; + return c->cls != NULL; } -char * checker_name (struct checker * c) +const char *checker_name(const struct checker *c) { - if (!c) + if (!c || !c->cls) return NULL; - return c->name; + return c->cls->name; } -char * checker_message (struct checker * c) +int checker_is_sync(const struct checker *c) { - if (!c) - return NULL; - return c->message; + return c && c->cls && c->cls->sync; +} + +static const char *generic_msg[CHECKER_GENERIC_MSGTABLE_SIZE] = { + [CHECKER_MSGID_NONE] = "", + [CHECKER_MSGID_DISABLED] = " is disabled", + [CHECKER_MSGID_NO_FD] = " has no usable fd", + [CHECKER_MSGID_INVALID] = " provided invalid message id", + [CHECKER_MSGID_UP] = " reports path is up", + [CHECKER_MSGID_DOWN] = " reports path is down", + [CHECKER_MSGID_GHOST] = " reports path is ghost", + [CHECKER_MSGID_UNSUPPORTED] = " doesn't support this device", +}; + +const char *checker_message(const struct checker *c) +{ + int id; + + if (!c || c->msgid < 0 || + (c->msgid >= CHECKER_GENERIC_MSGTABLE_SIZE && + c->msgid < CHECKER_FIRST_MSGID)) + goto bad_id; + + if (c->msgid < CHECKER_GENERIC_MSGTABLE_SIZE) + return generic_msg[c->msgid]; + + id = c->msgid - CHECKER_FIRST_MSGID; + if (id < c->cls->msgtable_size) + return c->cls->msgtable[id]; + +bad_id: + return generic_msg[CHECKER_MSGID_NONE]; } void checker_clear_message (struct checker *c) { if (!c) return; - c->message[0] = '\0'; + c->msgid = CHECKER_MSGID_NONE; } -void checker_get (char *multipath_dir, struct checker * dst, char * name) +void checker_get(const char *multipath_dir, struct checker *dst, + const char *name) { - struct checker * src = NULL; + struct checker_class *src = NULL; if (!dst) return; if (name && strlen(name)) { - src = checker_lookup(name); + src = checker_class_lookup(name); if (!src) - src = add_checker(multipath_dir, name); + src = add_checker_class(multipath_dir, name); } - if (!src) { - dst->check = NULL; + dst->cls = src; + if (!src) return; - } - dst->fd = src->fd; - dst->sync = src->sync; - strncpy(dst->name, src->name, CHECKER_NAME_LEN); - strncpy(dst->message, src->message, CHECKER_MSG_LEN); - dst->check = src->check; - dst->init = src->init; - dst->free = src->free; - dst->handle = NULL; + src->refcount++; } + +int init_checkers(const char *multipath_dir) +{ + if (!add_checker_class(multipath_dir, DEFAULT_CHECKER)) + return 1; + return 0; +} diff --git a/libmultipath/checkers.h b/libmultipath/checkers.h index 7b18a1a..b2e8f9a 100644 --- a/libmultipath/checkers.h +++ b/libmultipath/checkers.h @@ -10,8 +10,10 @@ * 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()) + * - Use: Any checker + * - Description: Corner case where "fd < 0" for path fd (see checker_check()), + * or where a checker detects an unsupported device + * (e.g. wrong checker configured for a given device). * * PATH_UNCHECKED: * - Use: Only in directio checker @@ -97,32 +99,39 @@ enum path_check_state { #define CHECKER_DEV_LEN 256 #define LIB_CHECKER_NAMELEN 256 +/* + * Generic message IDs for use in checkers. + */ +enum { + CHECKER_MSGID_NONE = 0, + CHECKER_MSGID_DISABLED, + CHECKER_MSGID_NO_FD, + CHECKER_MSGID_INVALID, + CHECKER_MSGID_UP, + CHECKER_MSGID_DOWN, + CHECKER_MSGID_GHOST, + CHECKER_MSGID_UNSUPPORTED, + CHECKER_GENERIC_MSGTABLE_SIZE, + CHECKER_FIRST_MSGID = 100, /* lowest msgid for checkers */ + CHECKER_MSGTABLE_SIZE = 100, /* max msg table size for checkers */ +}; + +struct checker_class; struct checker { - struct list_head node; - void *handle; - int refcount; + struct checker_class *cls; int fd; - int sync; unsigned int timeout; int disable; - char name[CHECKER_NAME_LEN]; - char message[CHECKER_MSG_LEN]; /* comm with callers */ + short msgid; /* checker-internal extra status */ void * context; /* store for persistent data */ void ** mpcontext; /* store for persistent data shared multipath-wide. Use MALLOC if you want to stuff data in. */ - int (*check)(struct checker *); - int (*init)(struct checker *); /* to allocate the context */ - void (*free)(struct checker *); /* to free the context */ }; -#define MSG(c, fmt, args...) snprintf((c)->message, CHECKER_MSG_LEN, fmt, ##args); - -char * checker_state_name (int); -int init_checkers (char *); +const char *checker_state_name(int); +int init_checkers(const char *); void cleanup_checkers (void); -struct checker * add_checker (char *, char *); -struct checker * checker_lookup (char *); int checker_init (struct checker *, void **); void checker_clear (struct checker *); void checker_put (struct checker *); @@ -133,15 +142,31 @@ void checker_set_fd (struct checker *, int); void checker_enable (struct checker *); void checker_disable (struct checker *); int checker_check (struct checker *, int); -int checker_selected (struct checker *); -char * checker_name (struct checker *); -char * checker_message (struct checker *); +int checker_selected(const struct checker *); +int checker_is_sync(const struct checker *); +const char *checker_name (const struct checker *); +/* + * This returns a string that's best prepended with "$NAME checker", + * where $NAME is the return value of checker_name(). + */ +const char *checker_message(const struct checker *); void checker_clear_message (struct checker *c); -void checker_get (char *, struct checker *, char *); +void checker_get(const char *, struct checker *, const char *); -/* Functions exported by path checker dynamic libraries (.so) */ +/* Prototypes for symbols exported by path checker dynamic libraries (.so) */ int libcheck_check(struct checker *); int libcheck_init(struct checker *); void libcheck_free(struct checker *); +/* + * msgid => message map. + * + * It only needs to be provided if the checker defines specific + * message IDs. + * Message IDs available to checkers start at CHECKER_FIRST_MSG. + * The msgtable array is 0-based, i.e. msgtable[0] is the message + * for msgid == __CHECKER_FIRST_MSG. + * The table ends with a NULL element. + */ +extern const char *libcheck_msgtable[]; #endif /* _CHECKERS_H */ diff --git a/libmultipath/checkers/cciss_tur.c b/libmultipath/checkers/cciss_tur.c index 1cab201..ea84374 100644 --- a/libmultipath/checkers/cciss_tur.c +++ b/libmultipath/checkers/cciss_tur.c @@ -42,9 +42,6 @@ #define TUR_CMD_LEN 6 #define HEAVY_CHECK_COUNT 10 -#define MSG_CCISS_TUR_UP "cciss_tur checker reports path is up" -#define MSG_CCISS_TUR_DOWN "cciss_tur checker reports path is down" - struct cciss_tur_checker_context { void * dummy; }; @@ -69,7 +66,7 @@ int libcheck_check(struct checker * c) IOCTL_Command_struct cic; // cciss ioctl command if ((c->fd) < 0) { - MSG(c,"no usable fd"); + c->msgid = CHECKER_MSGID_NO_FD; ret = -1; goto out; } @@ -79,7 +76,7 @@ int libcheck_check(struct checker * c) perror("Error: "); fprintf(stderr, "cciss TUR failed in CCISS_GETLUNINFO: %s\n", strerror(errno)); - MSG(c,MSG_CCISS_TUR_DOWN); + c->msgid = CHECKER_MSGID_DOWN; ret = PATH_DOWN; goto out; } else { @@ -106,18 +103,18 @@ int libcheck_check(struct checker * c) if (rc < 0) { fprintf(stderr, "cciss TUR failed: %s\n", strerror(errno)); - MSG(c,MSG_CCISS_TUR_DOWN); + c->msgid = CHECKER_MSGID_DOWN; ret = PATH_DOWN; goto out; } if ((cic.error_info.CommandStatus | cic.error_info.ScsiStatus )) { - MSG(c,MSG_CCISS_TUR_DOWN); + c->msgid = CHECKER_MSGID_DOWN; ret = PATH_DOWN; goto out; } - MSG(c,MSG_CCISS_TUR_UP); + c->msgid = CHECKER_MSGID_UP; ret = PATH_UP; out: diff --git a/libmultipath/checkers/directio.c b/libmultipath/checkers/directio.c index a80848d..1b00b77 100644 --- a/libmultipath/checkers/directio.c +++ b/libmultipath/checkers/directio.c @@ -18,10 +18,19 @@ #include "checkers.h" #include "../libmultipath/debug.h" -#define MSG_DIRECTIO_UNKNOWN "directio checker is not available" -#define MSG_DIRECTIO_UP "directio checker reports path is up" -#define MSG_DIRECTIO_DOWN "directio checker reports path is down" -#define MSG_DIRECTIO_PENDING "directio checker is waiting on aio" +enum { + MSG_DIRECTIO_UNKNOWN = CHECKER_FIRST_MSGID, + MSG_DIRECTIO_PENDING, + MSG_DIRECTIO_BLOCKSIZE, +}; + +#define _IDX(x) (MSG_DIRECTIO_##x - CHECKER_FIRST_MSGID) +const char *libcheck_msgtable[] = { + [_IDX(UNKNOWN)] = " is not available", + [_IDX(PENDING)] = " is waiting on aio", + [_IDX(BLOCKSIZE)] = " cannot get blocksize, set default", + NULL, +}; #define LOG(prio, fmt, args...) condlog(prio, "directio: " fmt, ##args) @@ -54,7 +63,7 @@ int libcheck_init (struct checker * c) } if (ioctl(c->fd, BLKBSZGET, &ct->blksize) < 0) { - MSG(c, "cannot get blocksize, set default"); + c->msgid = MSG_DIRECTIO_BLOCKSIZE; ct->blksize = 512; } if (ct->blksize > 4096) { @@ -193,21 +202,21 @@ int libcheck_check (struct checker * c) if (!ct) return PATH_UNCHECKED; - ret = check_state(c->fd, ct, c->sync, c->timeout); + ret = check_state(c->fd, ct, checker_is_sync(c), c->timeout); switch (ret) { case PATH_UNCHECKED: - MSG(c, MSG_DIRECTIO_UNKNOWN); + c->msgid = MSG_DIRECTIO_UNKNOWN; break; case PATH_DOWN: - MSG(c, MSG_DIRECTIO_DOWN); + c->msgid = CHECKER_MSGID_DOWN; break; case PATH_UP: - MSG(c, MSG_DIRECTIO_UP); + c->msgid = CHECKER_MSGID_UP; break; case PATH_PENDING: - MSG(c, MSG_DIRECTIO_PENDING); + c->msgid = MSG_DIRECTIO_PENDING; break; default: break; diff --git a/libmultipath/checkers/emc_clariion.c b/libmultipath/checkers/emc_clariion.c index 9115b1b..6fc8911 100644 --- a/libmultipath/checkers/emc_clariion.c +++ b/libmultipath/checkers/emc_clariion.c @@ -20,6 +20,11 @@ #define INQUIRY_CMD 0x12 #define INQUIRY_CMDLEN 6 #define HEAVY_CHECK_COUNT 10 +#define SCSI_COMMAND_TERMINATED 0x22 +#define SCSI_CHECK_CONDITION 0x2 +#define RECOVERED_ERROR 0x01 +#define ILLEGAL_REQUEST 0x05 +#define SG_ERR_DRIVER_SENSE 0x08 /* * Mechanism to track CLARiiON inactive snapshot LUs. @@ -41,6 +46,35 @@ ((struct emc_clariion_checker_LU_context *)\ (*c->mpcontext))->inactive_snap = 0 +enum { + MSG_CLARIION_QUERY_FAILED = CHECKER_FIRST_MSGID, + MSG_CLARIION_QUERY_ERROR, + MSG_CLARIION_PATH_CONFIG, + MSG_CLARIION_UNIT_REPORT, + MSG_CLARIION_PATH_NOT_AVAIL, + MSG_CLARIION_LUN_UNBOUND, + MSG_CLARIION_WWN_CHANGED, + MSG_CLARIION_READ_ERROR, + MSG_CLARIION_PASSIVE_GOOD, +}; + +#define _IDX(x) (MSG_CLARIION_ ## x - CHECKER_FIRST_MSGID) +const char *libcheck_msgtable[] = { + [_IDX(QUERY_FAILED)] = ": sending query command failed", + [_IDX(QUERY_ERROR)] = ": query command indicates error", + [_IDX(PATH_CONFIG)] = + ": Path not correctly configured for failover", + [_IDX(UNIT_REPORT)] = + ": Path unit report page in unknown format", + [_IDX(PATH_NOT_AVAIL)] = + ": Path not available for normal operations", + [_IDX(LUN_UNBOUND)] = ": Logical Unit is unbound or LUNZ", + [_IDX(WWN_CHANGED)] = ": Logical Unit WWN has changed", + [_IDX(READ_ERROR)] = ": Read error", + [_IDX(PASSIVE_GOOD)] = ": Active path is healthy", + NULL, +}; + struct emc_clariion_checker_path_context { char wwn[16]; unsigned wwn_set; @@ -101,7 +135,9 @@ int libcheck_check (struct checker * c) (struct emc_clariion_checker_path_context *)c->context; char wwnstr[33]; int ret; + int retry_emc = 5; +retry: memset(&io_hdr, 0, sizeof (struct sg_io_hdr)); memset(sense_buffer, 0, 128); memset(sb, 0, SENSE_BUFF_LEN); @@ -116,17 +152,63 @@ int libcheck_check (struct checker * c) io_hdr.timeout = c->timeout * 1000; io_hdr.pack_id = 0; if (ioctl(c->fd, SG_IO, &io_hdr) < 0) { - MSG(c, "emc_clariion_checker: sending query command failed"); + if (errno == ENOTTY) { + c->msgid = CHECKER_MSGID_UNSUPPORTED; + return PATH_WILD; + } + c->msgid = MSG_CLARIION_QUERY_FAILED; return PATH_DOWN; } + + if (io_hdr.info & SG_INFO_OK_MASK) { + switch (io_hdr.host_status) { + case DID_BUS_BUSY: + case DID_ERROR: + case DID_SOFT_ERROR: + case DID_TRANSPORT_DISRUPTED: + /* Transport error, retry */ + if (--retry_emc) + goto retry; + break; + default: + break; + } + } + + 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)) { + unsigned char *sbp = io_hdr.sbp; + int sense_key; + + if (sbp[0] & 0x2) + sense_key = sbp[1] & 0xf; + else + sense_key = sbp[2] & 0xf; + + if (sense_key == ILLEGAL_REQUEST) { + c->msgid = CHECKER_MSGID_UNSUPPORTED; + return PATH_WILD; + } else if (sense_key != RECOVERED_ERROR) { + condlog(1, "emc_clariion_checker: INQUIRY failed with sense key %02x", + sense_key); + c->msgid = MSG_CLARIION_QUERY_ERROR; + return PATH_DOWN; + } + } + } + if (io_hdr.info & SG_INFO_OK_MASK) { - MSG(c, "emc_clariion_checker: query command indicates error"); + condlog(1, "emc_clariion_checker: INQUIRY failed without sense, status %02x", + io_hdr.status); + c->msgid = MSG_CLARIION_QUERY_ERROR; return PATH_DOWN; } + if (/* Verify the code page - right page & revision */ sense_buffer[1] != 0xc0 || sense_buffer[9] != 0x00) { - MSG(c, "emc_clariion_checker: Path unit report page in " - "unknown format"); + c->msgid = MSG_CLARIION_UNIT_REPORT; return PATH_DOWN; } @@ -140,22 +222,19 @@ int libcheck_check (struct checker * c) ((sense_buffer[28] & 0x07) != 0x06)) /* Arraycommpath should be set to 1 */ || (sense_buffer[30] & 0x04) != 0x04) { - MSG(c, "emc_clariion_checker: Path not correctly configured " - "for failover"); + c->msgid = MSG_CLARIION_PATH_CONFIG; 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"); + c->msgid = MSG_CLARIION_PATH_NOT_AVAIL; return PATH_SHAKY; } if ( /* LUN should at least be bound somewhere and not be LUNZ */ sense_buffer[4] == 0x00) { - MSG(c, "emc_clariion_checker: Logical Unit is unbound " - "or LUNZ"); + c->msgid = MSG_CLARIION_LUN_UNBOUND; return PATH_DOWN; } @@ -166,8 +245,7 @@ int libcheck_check (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!"); + c->msgid = MSG_CLARIION_WWN_CHANGED; return PATH_DOWN; } } else { @@ -202,14 +280,15 @@ int libcheck_check (struct checker * c) condlog(3, "emc_clariion_checker: Active " "path to inactive snapshot WWN %s.", wwnstr); - } else - MSG(c, "emc_clariion_checker: Read " + } else { + condlog(3, "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]); + c->msgid = MSG_CLARIION_READ_ERROR; + } } else { - MSG(c, "emc_clariion_checker: Active path is " - "healthy."); + c->msgid = MSG_CLARIION_PASSIVE_GOOD; /* * Remove the path from the set of paths to inactive * snapshot LUs if it was in this list since the @@ -225,8 +304,7 @@ int libcheck_check (struct checker * c) wwnstr); ret = PATH_DOWN; } else { - MSG(c, - "emc_clariion_checker: Passive path is healthy."); + c->msgid = MSG_CLARIION_PASSIVE_GOOD; ret = PATH_UP; /* not ghost */ } } diff --git a/libmultipath/checkers/hp_sw.c b/libmultipath/checkers/hp_sw.c index 0ad34a6..1a82022 100644 --- a/libmultipath/checkers/hp_sw.c +++ b/libmultipath/checkers/hp_sw.c @@ -24,13 +24,10 @@ #define SCSI_COMMAND_TERMINATED 0x22 #define SG_ERR_DRIVER_SENSE 0x08 #define RECOVERED_ERROR 0x01 +#define ILLEGAL_REQUEST 0x05 #define MX_ALLOC_LEN 255 #define HEAVY_CHECK_COUNT 10 -#define MSG_HP_SW_UP "hp_sw checker reports path is up" -#define MSG_HP_SW_DOWN "hp_sw checker reports path is down" -#define MSG_HP_SW_GHOST "hp_sw checker reports path is ghost" - struct sw_checker_context { void * dummy; }; @@ -72,14 +69,19 @@ do_inq(int sg_fd, int cmddt, int evpd, unsigned int pg_op, io_hdr.sbp = sense_b; io_hdr.timeout = timeout * 1000; - if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) - return 1; + if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) { + if (errno == ENOTTY) + return PATH_WILD; + else + return PATH_DOWN; + } /* 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; + return PATH_UP; + if ((SCSI_CHECK_CONDITION == io_hdr.status) || (SCSI_COMMAND_TERMINATED == io_hdr.status) || (SG_ERR_DRIVER_SENSE == (0xf & io_hdr.driver_status))) { @@ -90,11 +92,13 @@ do_inq(int sg_fd, int cmddt, int evpd, unsigned int pg_op, sense_key = sense_buffer[1] & 0xf; else sense_key = sense_buffer[2] & 0xf; - if(RECOVERED_ERROR == sense_key) - return 0; + if (RECOVERED_ERROR == sense_key) + return PATH_UP; + else if (ILLEGAL_REQUEST == sense_key) + return PATH_WILD; } } - return 1; + return PATH_DOWN; } static int @@ -126,16 +130,21 @@ do_tur (int fd, unsigned int timeout) int libcheck_check(struct checker * c) { char buff[MX_ALLOC_LEN]; + int ret = do_inq(c->fd, 0, 1, 0x80, buff, MX_ALLOC_LEN, 0, c->timeout); - if (0 != do_inq(c->fd, 0, 1, 0x80, buff, MX_ALLOC_LEN, 0, c->timeout)) { - MSG(c, MSG_HP_SW_DOWN); - return PATH_DOWN; + if (ret == PATH_WILD) { + c->msgid = CHECKER_MSGID_UNSUPPORTED; + return ret; } + if (ret != PATH_UP) { + c->msgid = CHECKER_MSGID_DOWN; + return ret; + }; if (do_tur(c->fd, c->timeout)) { - MSG(c, MSG_HP_SW_GHOST); + c->msgid = CHECKER_MSGID_GHOST; return PATH_GHOST; } - MSG(c, MSG_HP_SW_UP); + c->msgid = CHECKER_MSGID_UP; return PATH_UP; } diff --git a/libmultipath/checkers/rdac.c b/libmultipath/checkers/rdac.c index 5104e4e..8a3b73e 100644 --- a/libmultipath/checkers/rdac.c +++ b/libmultipath/checkers/rdac.c @@ -26,14 +26,13 @@ #define SCSI_COMMAND_TERMINATED 0x22 #define SG_ERR_DRIVER_SENSE 0x08 #define RECOVERED_ERROR 0x01 +#define ILLEGAL_REQUEST 0x05 #define CURRENT_PAGE_CODE_VALUES 0 #define CHANGEABLE_PAGE_CODE_VALUES 1 -#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" +#define MSG_RDAC_DOWN " reports path is down" #define MSG_RDAC_DOWN_TYPE(STR) MSG_RDAC_DOWN": "STR #define RTPG_UNAVAILABLE 0x3 @@ -164,14 +163,14 @@ retry: io_hdr.sbp = sense_b; io_hdr.timeout = timeout * 1000; - if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) - return 1; + if (ioctl(sg_fd, SG_IO, &io_hdr) < 0 && errno == ENOTTY) + return PATH_WILD; /* 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; + return PATH_UP; /* check if we need to retry this error */ if (io_hdr.info & SG_INFO_OK_MASK) { @@ -200,10 +199,14 @@ retry: else sense_key = sense_buffer[2] & 0xf; if (RECOVERED_ERROR == sense_key) - return 0; + return PATH_UP; + else if (ILLEGAL_REQUEST == sense_key) + return PATH_WILD; + condlog(1, "rdac checker: INQUIRY failed with sense key %02x", + sense_key); } } - return 1; + return PATH_DOWN; } struct volume_access_inq @@ -219,41 +222,69 @@ struct volume_access_inq char dontcare1[34]; }; -const char -*checker_msg_string(struct volume_access_inq *inq) +enum { + RDAC_MSGID_NOT_CONN = CHECKER_FIRST_MSGID, + RDAC_MSGID_IN_STARTUP, + RDAC_MSGID_NON_RESPONSIVE, + RDAC_MSGID_IN_RESET, + RDAC_MSGID_FW_DOWNLOADING, + RDAC_MSGID_QUIESCED, + RDAC_MSGID_SERVICE_MODE, + RDAC_MSGID_UNAVAILABLE, + RDAC_MSGID_INQUIRY_FAILED, +}; + +#define _IDX(x) (RDAC_MSGID_##x - CHECKER_FIRST_MSGID) +const char *libcheck_msgtable[] = { + [_IDX(NOT_CONN)] = MSG_RDAC_DOWN_TYPE("lun not connected"), + [_IDX(IN_STARTUP)] = MSG_RDAC_DOWN_TYPE("ctlr is in startup sequence"), + [_IDX(NON_RESPONSIVE)] = + MSG_RDAC_DOWN_TYPE("non-responsive to queries"), + [_IDX(IN_RESET)] = MSG_RDAC_DOWN_TYPE("ctlr held in reset"), + [_IDX(FW_DOWNLOADING)] = + MSG_RDAC_DOWN_TYPE("ctlr firmware downloading"), + [_IDX(QUIESCED)] = MSG_RDAC_DOWN_TYPE("ctlr quiesced by admin request"), + [_IDX(SERVICE_MODE)] = MSG_RDAC_DOWN_TYPE("ctlr is in service mode"), + [_IDX(UNAVAILABLE)] = MSG_RDAC_DOWN_TYPE("ctlr is unavailable"), + [_IDX(INQUIRY_FAILED)] = MSG_RDAC_DOWN_TYPE("inquiry failed"), + NULL, +}; + +static int +checker_msg_string(const struct volume_access_inq *inq) { /* lun not connected */ if (((inq->PQ_PDT & 0xE0) == 0x20) || (inq->PQ_PDT & 0x7f)) - return MSG_RDAC_DOWN_TYPE("lun not connected"); + return RDAC_MSGID_NOT_CONN; /* if no tpg data is available, give the generic path down message */ if (!(inq->avtcvp & 0x10)) - return MSG_RDAC_DOWN; + return CHECKER_MSGID_DOWN; /* controller is booting up */ if (((inq->aas_cur & 0x0F) == RTPG_TRANSITIONING) && (inq->aas_alt & 0x0F) != RTPG_TRANSITIONING) - return MSG_RDAC_DOWN_TYPE("ctlr is in startup sequence"); + return RDAC_MSGID_IN_STARTUP; /* if not unavailable, give generic message */ if ((inq->aas_cur & 0x0F) != RTPG_UNAVAILABLE) - return MSG_RDAC_DOWN; + return CHECKER_MSGID_DOWN; /* target port group unavailable */ switch (inq->vendor_specific_cur) { case RTPG_UNAVAIL_NON_RESPONSIVE: - return MSG_RDAC_DOWN_TYPE("non-responsive to queries"); + return RDAC_MSGID_NON_RESPONSIVE; case RTPG_UNAVAIL_IN_RESET: - return MSG_RDAC_DOWN_TYPE("ctlr held in reset"); + return RDAC_MSGID_IN_RESET; case RTPG_UNAVAIL_CFW_DL1: case RTPG_UNAVAIL_CFW_DL2: - return MSG_RDAC_DOWN_TYPE("ctlr firmware downloading"); + return RDAC_MSGID_FW_DOWNLOADING; case RTPG_UNAVAIL_QUIESCED: - return MSG_RDAC_DOWN_TYPE("ctlr quiesced by admin request"); + return RDAC_MSGID_QUIESCED; case RTPG_UNAVAIL_SERVICE_MODE: - return MSG_RDAC_DOWN_TYPE("ctlr is in service mode"); + return RDAC_MSGID_SERVICE_MODE; default: - return MSG_RDAC_DOWN_TYPE("ctlr is unavailable"); + return RDAC_MSGID_UNAVAILABLE; } } @@ -264,12 +295,14 @@ int libcheck_check(struct checker * c) inqfail = 0; memset(&inq, 0, sizeof(struct volume_access_inq)); - if (0 != do_inq(c->fd, 0xC9, &inq, sizeof(struct volume_access_inq), - c->timeout)) { - ret = PATH_DOWN; + ret = do_inq(c->fd, 0xC9, &inq, sizeof(struct volume_access_inq), + c->timeout); + if (ret != PATH_UP) { inqfail = 1; goto done; - } else if (((inq.PQ_PDT & 0xE0) == 0x20) || (inq.PQ_PDT & 0x7f)) { + } + + if (((inq.PQ_PDT & 0xE0) == 0x20) || (inq.PQ_PDT & 0x7f)) { /* LUN not connected*/ ret = PATH_DOWN; goto done; @@ -306,15 +339,18 @@ int libcheck_check(struct checker * c) done: switch (ret) { + case PATH_WILD: + c->msgid = CHECKER_MSGID_UNSUPPORTED; + break; case PATH_DOWN: - MSG(c, "%s", (inqfail) ? MSG_RDAC_DOWN_TYPE("inquiry failed") : - checker_msg_string(&inq)); + c->msgid = (inqfail ? RDAC_MSGID_INQUIRY_FAILED : + checker_msg_string(&inq)); break; case PATH_UP: - MSG(c, MSG_RDAC_UP); + c->msgid = CHECKER_MSGID_UP; break; case PATH_GHOST: - MSG(c, MSG_RDAC_GHOST); + c->msgid = CHECKER_MSGID_GHOST; break; } diff --git a/libmultipath/checkers/readsector0.c b/libmultipath/checkers/readsector0.c index 1c2a868..cf79e06 100644 --- a/libmultipath/checkers/readsector0.c +++ b/libmultipath/checkers/readsector0.c @@ -6,9 +6,6 @@ #include "checkers.h" #include "libsg.h" -#define MSG_READSECTOR0_UP "readsector0 checker reports path is up" -#define MSG_READSECTOR0_DOWN "readsector0 checker reports path is down" - struct readsector0_checker_context { void * dummy; }; @@ -35,10 +32,10 @@ int libcheck_check (struct checker * c) switch (ret) { case PATH_DOWN: - MSG(c, MSG_READSECTOR0_DOWN); + c->msgid = CHECKER_MSGID_DOWN; break; case PATH_UP: - MSG(c, MSG_READSECTOR0_UP); + c->msgid = CHECKER_MSGID_UP; break; default: break; diff --git a/libmultipath/checkers/tur.c b/libmultipath/checkers/tur.c index 86c0cdc..63b1962 100644 --- a/libmultipath/checkers/tur.c +++ b/libmultipath/checkers/tur.c @@ -29,12 +29,19 @@ #define TUR_CMD_LEN 6 #define HEAVY_CHECK_COUNT 10 -#define MSG_TUR_UP "tur checker reports path is up" -#define MSG_TUR_DOWN "tur checker reports path is down" -#define MSG_TUR_GHOST "tur checker reports path is in standby state" -#define MSG_TUR_RUNNING "tur checker still running" -#define MSG_TUR_TIMEOUT "tur checker timed out" -#define MSG_TUR_FAILED "tur checker failed to initialize" +enum { + MSG_TUR_RUNNING = CHECKER_FIRST_MSGID, + MSG_TUR_TIMEOUT, + MSG_TUR_FAILED, +}; + +#define _IDX(x) (MSG_ ## x - CHECKER_FIRST_MSGID) +const char *libcheck_msgtable[] = { + [_IDX(TUR_RUNNING)] = " still running", + [_IDX(TUR_TIMEOUT)] = " timed out", + [_IDX(TUR_FAILED)] = " failed to initialize", + NULL, +}; struct tur_checker_context { dev_t devt; @@ -47,7 +54,7 @@ struct tur_checker_context { pthread_mutex_t lock; pthread_cond_t active; int holders; /* uatomic access only */ - char message[CHECKER_MSG_LEN]; + int msgid; }; int libcheck_init (struct checker * c) @@ -99,7 +106,7 @@ void libcheck_free (struct checker * c) } static int -tur_check(int fd, unsigned int timeout, char *msg) +tur_check(int fd, unsigned int timeout, short *msgid) { struct sg_io_hdr io_hdr; unsigned char turCmdBlk[TUR_CMD_LEN] = { 0x00, 0, 0, 0, 0, 0 }; @@ -118,7 +125,11 @@ retry: io_hdr.timeout = timeout * 1000; io_hdr.pack_id = 0; if (ioctl(fd, SG_IO, &io_hdr) < 0) { - snprintf(msg, CHECKER_MSG_LEN, MSG_TUR_DOWN); + if (errno == ENOTTY) { + *msgid = CHECKER_MSGID_UNSUPPORTED; + return PATH_WILD; + } + *msgid = CHECKER_MSGID_DOWN; return PATH_DOWN; } if ((io_hdr.status & 0x7e) == 0x18) { @@ -126,7 +137,7 @@ retry: * SCSI-3 arrays might return * reservation conflict on TUR */ - snprintf(msg, CHECKER_MSG_LEN, MSG_TUR_UP); + *msgid = CHECKER_MSGID_UP; return PATH_UP; } if (io_hdr.info & SG_INFO_OK_MASK) { @@ -171,14 +182,14 @@ retry: * LOGICAL UNIT NOT ACCESSIBLE, * TARGET PORT IN STANDBY STATE */ - snprintf(msg, CHECKER_MSG_LEN, MSG_TUR_GHOST); + *msgid = CHECKER_MSGID_GHOST; return PATH_GHOST; } } - snprintf(msg, CHECKER_MSG_LEN, MSG_TUR_DOWN); + *msgid = CHECKER_MSGID_DOWN; return PATH_DOWN; } - snprintf(msg, CHECKER_MSG_LEN, MSG_TUR_UP); + *msgid = CHECKER_MSGID_UP; return PATH_UP; } @@ -196,11 +207,55 @@ static void cleanup_func(void *data) rcu_unregister_thread(); } +/* + * Test code for "zombie tur thread" handling. + * Compile e.g. with CFLAGS=-DTUR_TEST_MAJOR=8 + * Additional parameters can be configure with the macros below. + * + * Everty nth started TUR thread will hang in non-cancellable state + * for given number of seconds, for device given by major/minor. + */ +#ifdef TUR_TEST_MAJOR + +#ifndef TUR_TEST_MINOR +#define TUR_TEST_MINOR 0 +#endif +#ifndef TUR_SLEEP_INTERVAL +#define TUR_SLEEP_INTERVAL 3 +#endif +#ifndef TUR_SLEEP_SECS +#define TUR_SLEEP_SECS 60 +#endif + +static void tur_deep_sleep(const struct tur_checker_context *ct) +{ + static int sleep_cnt; + const struct timespec ts = { .tv_sec = TUR_SLEEP_SECS, .tv_nsec = 0 }; + int oldstate; + + if (ct->devt != makedev(TUR_TEST_MAJOR, TUR_TEST_MINOR) || + ++sleep_cnt % TUR_SLEEP_INTERVAL != 0) + return; + + condlog(1, "tur thread going to sleep for %ld seconds", ts.tv_sec); + if (pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate) != 0) + condlog(0, "pthread_setcancelstate: %m"); + if (nanosleep(&ts, NULL) != 0) + condlog(0, "nanosleep: %m"); + condlog(1, "tur zombie thread woke up"); + if (pthread_setcancelstate(oldstate, NULL) != 0) + condlog(0, "pthread_setcancelstate (2): %m"); + pthread_testcancel(); +} +#else +#define tur_deep_sleep(x) do {} while (0) +#endif /* TUR_TEST_MAJOR */ + static void *tur_thread(void *ctx) { struct tur_checker_context *ct = ctx; int state, running; - char msg[CHECKER_MSG_LEN]; + short msgid; /* This thread can be canceled, so setup clean up */ tur_thread_cleanup_push(ct); @@ -209,13 +264,14 @@ static void *tur_thread(void *ctx) condlog(3, "%d:%d : tur checker starting up", major(ct->devt), minor(ct->devt)); - state = tur_check(ct->fd, ct->timeout, msg); + tur_deep_sleep(ct); + state = tur_check(ct->fd, ct->timeout, &msgid); pthread_testcancel(); /* TUR checker done */ pthread_mutex_lock(&ct->lock); ct->state = state; - strlcpy(ct->message, msg, sizeof(ct->message)); + ct->msgid = msgid; pthread_cond_signal(&ct->active); pthread_mutex_unlock(&ct->lock); @@ -267,8 +323,8 @@ int libcheck_check(struct checker * c) if (!ct) return PATH_UNCHECKED; - if (c->sync) - return tur_check(c->fd, c->timeout, c->message); + if (checker_is_sync(c)) + return tur_check(c->fd, c->timeout, &c->msgid); /* * Async mode @@ -280,13 +336,12 @@ int libcheck_check(struct checker * c) pthread_cancel(ct->thread); condlog(3, "%d:%d : tur checker timeout", major(ct->devt), minor(ct->devt)); - MSG(c, MSG_TUR_TIMEOUT); + c->msgid = MSG_TUR_TIMEOUT; tur_status = PATH_TIMEOUT; } else { pthread_mutex_lock(&ct->lock); tur_status = ct->state; - strlcpy(c->message, ct->message, - sizeof(c->message)); + c->msgid = ct->msgid; pthread_mutex_unlock(&ct->lock); } ct->thread = 0; @@ -299,21 +354,39 @@ int libcheck_check(struct checker * c) ct->thread = 0; pthread_mutex_lock(&ct->lock); tur_status = ct->state; - strlcpy(c->message, ct->message, sizeof(c->message)); + c->msgid = ct->msgid; pthread_mutex_unlock(&ct->lock); } } else { if (uatomic_read(&ct->holders) > 1) { - /* The thread has been cancelled but hasn't - * quilt. Fail back to synchronous mode */ - condlog(3, "%d:%d : tur checker failing back to sync", + /* + * The thread has been cancelled but hasn't quit. + * We have to prevent it from interfering with the new + * thread. We create a new context and leave the old + * one with the stale thread, hoping it will clean up + * eventually. + */ + condlog(3, "%d:%d : tur thread not responding", major(ct->devt), minor(ct->devt)); - return tur_check(c->fd, c->timeout, c->message); + + /* + * libcheck_init will replace c->context. + * It fails only in OOM situations. In this case, return + * PATH_UNCHECKED to avoid prematurely failing the path. + */ + if (libcheck_init(c) != 0) + return PATH_UNCHECKED; + + if (!uatomic_sub_return(&ct->holders, 1)) + /* It did terminate, eventually */ + cleanup_context(ct); + + ct = c->context; } /* Start new TUR checker */ pthread_mutex_lock(&ct->lock); tur_status = ct->state = PATH_PENDING; - ct->message[0] = '\0'; + ct->msgid = CHECKER_MSGID_NONE; pthread_mutex_unlock(&ct->lock); ct->fd = c->fd; ct->timeout = c->timeout; @@ -329,16 +402,16 @@ int libcheck_check(struct checker * c) ct->thread = 0; condlog(3, "%d:%d : failed to start tur thread, using" " sync mode", major(ct->devt), minor(ct->devt)); - return tur_check(c->fd, c->timeout, c->message); + return tur_check(c->fd, c->timeout, &c->msgid); } tur_timeout(&tsp); pthread_mutex_lock(&ct->lock); if (ct->state == PATH_PENDING) - r = pthread_cond_timedwait(&ct->active, &ct->lock, + r = pthread_cond_timedwait(&ct->active, &ct->lock, &tsp); if (!r) { tur_status = ct->state; - strlcpy(c->message, ct->message, sizeof(c->message)); + c->msgid = ct->msgid; } pthread_mutex_unlock(&ct->lock); if (tur_status == PATH_PENDING) { diff --git a/libmultipath/config.c b/libmultipath/config.c index 0aef186..5af7af5 100644 --- a/libmultipath/config.c +++ b/libmultipath/config.c @@ -639,17 +639,13 @@ free_config (struct config * conf) FREE(conf); } -static void free_namelist(void *nl) -{ - free(nl); -} - /* if multipath fails to process the config directory, it should continue, * with just a warning message */ static void process_config_dir(struct config *conf, vector keywords, char *dir) { struct dirent **namelist; + struct scandir_result sr; int i, n; char path[LINE_MAX]; int old_hwtable_size; @@ -669,7 +665,9 @@ process_config_dir(struct config *conf, vector keywords, char *dir) return; } else if (n == 0) return; - pthread_cleanup_push(free_namelist, namelist); + sr.di = namelist; + sr.n = n; + pthread_cleanup_push_cast(free_scandir_result, &sr); for (i = 0; i < n; i++) { if (!strstr(namelist[i]->d_name, ".conf")) continue; diff --git a/libmultipath/configure.c b/libmultipath/configure.c index 09c3dcf..ed3e30f 100644 --- a/libmultipath/configure.c +++ b/libmultipath/configure.c @@ -1020,14 +1020,18 @@ int coalesce_paths (struct vectors * vecs, vector newmp, char * refwwid, } } vector_foreach_slot (pathvec, pp1, k) { - int invalid = 0; + int invalid; /* skip this path for some reason */ /* 1. if path has no unique id or wwid blacklisted */ + if (strlen(pp1->wwid) == 0) { + orphan_path(pp1, "no WWID"); + continue; + } + conf = get_multipath_config(); pthread_cleanup_push(put_multipath_config, conf); - if (strlen(pp1->wwid) == 0 || filter_path(conf, pp1) > 0) - invalid = 1; + invalid = (filter_path(conf, pp1) > 0); pthread_cleanup_pop(1); if (invalid) { orphan_path(pp1, "blacklisted"); diff --git a/libmultipath/defaults.h b/libmultipath/defaults.h index 7f3839f..6576939 100644 --- a/libmultipath/defaults.h +++ b/libmultipath/defaults.h @@ -46,6 +46,7 @@ #define DEFAULT_UNKNOWN_FIND_MULTIPATHS_TIMEOUT 1 #define DEFAULT_ALL_TG_PT ALL_TG_PT_OFF +#define CHECKINT_UNDEF (~0U) #define DEFAULT_CHECKINT 5 #define MAX_CHECKINT(a) (a << 2) diff --git a/libmultipath/devmapper.c b/libmultipath/devmapper.c index 8136d15..0433b49 100644 --- a/libmultipath/devmapper.c +++ b/libmultipath/devmapper.c @@ -80,11 +80,11 @@ dm_write_log (int level, const char *file, int line, const char *f, ...) strftime(buff, sizeof(buff), "%b %d %H:%M:%S", tb); buff[sizeof(buff)-1] = '\0'; - fprintf(stdout, "%s | ", buff); + fprintf(stderr, "%s | ", buff); } - fprintf(stdout, "libdevmapper: %s(%i): ", file, line); - vfprintf(stdout, f, ap); - fprintf(stdout, "\n"); + fprintf(stderr, "libdevmapper: %s(%i): ", file, line); + vfprintf(stderr, f, ap); + fprintf(stderr, "\n"); } else { condlog(level, "libdevmapper: %s(%i): ", file, line); log_safe(level + 3, f, ap); diff --git a/libmultipath/dict.c b/libmultipath/dict.c index bf4701e..a81c051 100644 --- a/libmultipath/dict.c +++ b/libmultipath/dict.c @@ -39,6 +39,7 @@ set_int(vector strvec, void *ptr) *int_ptr = atoi(buff); + FREE(buff); return 0; } @@ -263,7 +264,17 @@ snprint_mp_ ## option (struct config *conf, char * buff, int len, \ return function (buff, len, mpe->option); \ } -declare_def_handler(checkint, set_int) +static int checkint_handler(struct config *conf, vector strvec) +{ + int rc = set_int(strvec, &conf->checkint); + + if (rc) + return rc; + if (conf->checkint == CHECKINT_UNDEF) + conf->checkint--; + return 0; +} + declare_def_snprint(checkint, print_int) declare_def_handler(max_checkint, set_int) @@ -1562,7 +1573,7 @@ init_keywords(vector keywords) { install_keyword_root("defaults", NULL); install_keyword("verbosity", &def_verbosity_handler, &snprint_def_verbosity); - install_keyword("polling_interval", &def_checkint_handler, &snprint_def_checkint); + install_keyword("polling_interval", &checkint_handler, &snprint_def_checkint); install_keyword("max_polling_interval", &def_max_checkint_handler, &snprint_def_max_checkint); install_keyword("reassign_maps", &def_reassign_maps_handler, &snprint_def_reassign_maps); install_keyword("multipath_dir", &def_multipath_dir_handler, &snprint_def_multipath_dir); diff --git a/libmultipath/discovery.c b/libmultipath/discovery.c index b267f07..63558ad 100644 --- a/libmultipath/discovery.c +++ b/libmultipath/discovery.c @@ -103,6 +103,7 @@ store_pathinfo (vector pathvec, struct config *conf, err = store_path(pathvec, pp); if (err) goto out; + pp->checkint = conf->checkint; out: if (err) @@ -661,7 +662,7 @@ sysfs_set_session_tmo(struct multipath *mpp, struct path *pp) } else { snprintf(value, 11, "%u", mpp->fast_io_fail); if (sysfs_attr_set_value(session_dev, "recovery_tmo", - value, 11) <= 0) { + value, strlen(value)) <= 0) { condlog(3, "%s: Failed to set recovery_tmo, " " error %d", pp->dev, errno); } @@ -693,7 +694,7 @@ sysfs_set_nexus_loss_tmo(struct multipath *mpp, struct path *pp) if (mpp->dev_loss) { snprintf(value, 11, "%u", mpp->dev_loss); if (sysfs_attr_set_value(sas_dev, "I_T_nexus_loss_timeout", - value, 11) <= 0) + value, strlen(value)) <= 0) condlog(3, "%s: failed to update " "I_T Nexus loss timeout, error %d", pp->dev, errno); @@ -1496,7 +1497,7 @@ sysfs_pathinfo(struct path * pp, vector hwtable) } } -static int +static void scsi_ioctl_pathinfo (struct path * pp, struct config *conf, int mask) { struct udev_device *parent; @@ -1506,7 +1507,7 @@ scsi_ioctl_pathinfo (struct path * pp, struct config *conf, int mask) detect_alua(pp, conf); if (!(mask & DI_SERIAL)) - return 0; + return; parent = pp->udev; while (parent) { @@ -1525,27 +1526,24 @@ scsi_ioctl_pathinfo (struct path * pp, struct config *conf, int mask) parent = udev_device_get_parent(parent); } if (!attr_path || pp->sg_id.host_no == -1) - return 0; + return; if (get_vpd_sysfs(parent, 0x80, pp->serial, SERIAL_SIZE) <= 0) { if (get_serial(pp->serial, SERIAL_SIZE, pp->fd)) { - condlog(2, "%s: fail to get serial", pp->dev); - return 0; + condlog(3, "%s: fail to get serial", pp->dev); + return; } } condlog(3, "%s: serial = %s", pp->dev, pp->serial); - return 0; + return; } -static int -cciss_ioctl_pathinfo (struct path * pp, int mask) +static void +cciss_ioctl_pathinfo(struct path *pp) { - if (mask & DI_SERIAL) { - get_serial(pp->serial, SERIAL_SIZE, pp->fd); - condlog(3, "%s: serial = %s", pp->dev, pp->serial); - } - return 0; + get_serial(pp->serial, SERIAL_SIZE, pp->fd); + condlog(3, "%s: serial = %s", pp->dev, pp->serial); } int @@ -1592,8 +1590,8 @@ get_state (struct path * pp, struct config *conf, int daemon, int oldstate) checker_name(c), checker_state_name(state)); if (state != PATH_UP && state != PATH_GHOST && strlen(checker_message(c))) - condlog(3, "%s: checker msg is \"%s\"", - pp->dev, checker_message(c)); + condlog(3, "%s: %s checker%s", + pp->dev, checker_name(c), checker_message(c)); return state; } @@ -1937,21 +1935,16 @@ int pathinfo(struct path *pp, struct config *conf, int mask) if (mask & DI_SERIAL) get_geometry(pp); - if (path_state == PATH_UP && pp->bus == SYSFS_BUS_SCSI && - scsi_ioctl_pathinfo(pp, conf, mask)) - goto blank; + if (path_state == PATH_UP && pp->bus == SYSFS_BUS_SCSI) + scsi_ioctl_pathinfo(pp, conf, mask); - if (pp->bus == SYSFS_BUS_CCISS && - cciss_ioctl_pathinfo(pp, mask)) - goto blank; + if (pp->bus == SYSFS_BUS_CCISS && mask & DI_SERIAL) + cciss_ioctl_pathinfo(pp); if (mask & DI_CHECKER) { if (path_state == PATH_UP) { pp->chkrstate = pp->state = get_state(pp, conf, 0, path_state); - if (pp->state == PATH_UNCHECKED || - pp->state == PATH_WILD) - goto blank; if (pp->state == PATH_TIMEOUT) pp->state = PATH_DOWN; if (pp->state == PATH_UP && !pp->size) { diff --git a/libmultipath/foreign.c b/libmultipath/foreign.c index 80b399b..48e8d24 100644 --- a/libmultipath/foreign.c +++ b/libmultipath/foreign.c @@ -115,6 +115,7 @@ static int _init_foreign(const char *multipath_dir) { char pathbuf[PATH_MAX]; struct dirent **di; + struct scandir_result sr; int r, i; foreigns = vector_alloc(); @@ -135,7 +136,9 @@ static int _init_foreign(const char *multipath_dir) return -r; } - pthread_cleanup_push(free, di); + sr.di = di; + sr.n = r; + pthread_cleanup_push_cast(free_scandir_result, &sr); for (i = 0; i < r; i++) { const char *msg, *fn, *c; struct foreign *fgn; diff --git a/libmultipath/foreign/nvme.c b/libmultipath/foreign/nvme.c index 8887a75..c753a74 100644 --- a/libmultipath/foreign/nvme.c +++ b/libmultipath/foreign/nvme.c @@ -27,6 +27,7 @@ #include #include #include +#include "util.h" #include "vector.h" #include "generic.h" #include "foreign.h" @@ -534,6 +535,7 @@ static void _find_controllers(struct context *ctx, struct nvme_map *map) { char pathbuf[PATH_MAX], realbuf[PATH_MAX]; struct dirent **di = NULL; + struct scandir_result sr; struct udev_device *subsys; struct nvme_path *path; int r, i, n; @@ -568,7 +570,9 @@ static void _find_controllers(struct context *ctx, struct nvme_map *map) return; } - pthread_cleanup_push(free, di); + sr.di = di; + sr.n = r; + pthread_cleanup_push_cast(free_scandir_result, &sr); for (i = 0; i < r; i++) { char *fn = di[i]->d_name; struct udev_device *ctrl, *udev; diff --git a/libmultipath/print.c b/libmultipath/print.c index 7b610b9..fc40b0f 100644 --- a/libmultipath/print.c +++ b/libmultipath/print.c @@ -615,7 +615,7 @@ static int snprint_path_checker (char * buff, size_t len, const struct path * pp) { const struct checker * c = &pp->checker; - return snprint_str(buff, len, c->name); + return snprint_str(buff, len, checker_name(c)); } static int diff --git a/libmultipath/propsel.c b/libmultipath/propsel.c index fdb5953..970a3b5 100644 --- a/libmultipath/propsel.c +++ b/libmultipath/propsel.c @@ -479,26 +479,27 @@ check_rdac(struct path * pp) int select_checker(struct config *conf, struct path *pp) { const char *origin; - char *checker_name; + char *ckr_name; struct checker * c = &pp->checker; if (pp->detect_checker == DETECT_CHECKER_ON) { origin = autodetect_origin; if (check_rdac(pp)) { - checker_name = RDAC; + ckr_name = RDAC; goto out; } else if (pp->tpgs > 0) { - checker_name = TUR; + ckr_name = TUR; goto out; } } - do_set(checker_name, conf->overrides, checker_name, overrides_origin); - do_set_from_hwe(checker_name, pp, checker_name, hwe_origin); - do_set(checker_name, conf, checker_name, conf_origin); - do_default(checker_name, DEFAULT_CHECKER); + do_set(checker_name, conf->overrides, ckr_name, overrides_origin); + do_set_from_hwe(checker_name, pp, ckr_name, hwe_origin); + do_set(checker_name, conf, ckr_name, conf_origin); + do_default(ckr_name, DEFAULT_CHECKER); out: - checker_get(conf->multipath_dir, c, checker_name); - condlog(3, "%s: path_checker = %s %s", pp->dev, c->name, origin); + checker_get(conf->multipath_dir, c, ckr_name); + condlog(3, "%s: path_checker = %s %s", pp->dev, + checker_name(c), origin); if (conf->checker_timeout) { c->timeout = conf->checker_timeout; condlog(3, "%s: checker timeout = %u s %s", diff --git a/libmultipath/structs.c b/libmultipath/structs.c index caa178a..fee899b 100644 --- a/libmultipath/structs.c +++ b/libmultipath/structs.c @@ -100,6 +100,7 @@ alloc_path (void) pp->fd = -1; pp->tpgs = TPGS_UNDEF; pp->priority = PRIO_UNDEF; + pp->checkint = CHECKINT_UNDEF; checker_clear(&pp->checker); dm_path_to_gen(pp)->ops = &dm_gen_path_ops; pp->hwe = vector_alloc(); diff --git a/libmultipath/structs_vec.c b/libmultipath/structs_vec.c index f87d69d..c85823a 100644 --- a/libmultipath/structs_vec.c +++ b/libmultipath/structs_vec.c @@ -318,8 +318,11 @@ sync_map_state(struct multipath *mpp) else if ((pp->dmstate == PSTATE_ACTIVE || pp->dmstate == PSTATE_UNDEF) && (pp->state == PATH_DOWN || - pp->state == PATH_SHAKY)) + pp->state == PATH_SHAKY)) { + condlog(2, "sync_map_state: failing %s state %d dmstate %d", + pp->dev, pp->state, pp->dmstate); dm_fail_path(mpp->alias, pp->dev_t); + } } } } diff --git a/libmultipath/sysfs.c b/libmultipath/sysfs.c index b7dacaa..558c8d6 100644 --- a/libmultipath/sysfs.c +++ b/libmultipath/sysfs.c @@ -303,6 +303,7 @@ static void close_fd(void *arg) bool sysfs_is_multipathed(const struct path *pp) { char pathbuf[PATH_MAX]; + struct scandir_result sr; struct dirent **di; int n, r, i; bool found = false; @@ -323,7 +324,9 @@ bool sysfs_is_multipathed(const struct path *pp) return false; } - pthread_cleanup_push(free, di); + sr.di = di; + sr.n = r; + pthread_cleanup_push_cast(free_scandir_result, &sr); for (i = 0; i < r && !found; i++) { long fd; int nr; diff --git a/libmultipath/util.c b/libmultipath/util.c index 347af5b..66c4761 100644 --- a/libmultipath/util.c +++ b/libmultipath/util.c @@ -7,6 +7,8 @@ #include #include #include +#include +#include #include #include #include @@ -465,3 +467,41 @@ int safe_write(int fd, const void *buf, size_t count) } return 0; } + +void set_max_fds(int max_fds) +{ + struct rlimit fd_limit; + + if (!max_fds) + return; + + if (getrlimit(RLIMIT_NOFILE, &fd_limit) < 0) { + condlog(0, "can't get open fds limit: %s", + strerror(errno)); + fd_limit.rlim_cur = 0; + fd_limit.rlim_max = 0; + } + if (fd_limit.rlim_cur < max_fds) { + fd_limit.rlim_cur = max_fds; + if (fd_limit.rlim_max < max_fds) + fd_limit.rlim_max = max_fds; + if (setrlimit(RLIMIT_NOFILE, &fd_limit) < 0) { + condlog(0, "can't set open fds limit to " + "%lu/%lu : %s", + fd_limit.rlim_cur, fd_limit.rlim_max, + strerror(errno)); + } else { + condlog(3, "set open fds limit to %lu/%lu", + fd_limit.rlim_cur, fd_limit.rlim_max); + } + } +} + +void free_scandir_result(struct scandir_result *res) +{ + int i; + + for (i = 0; i < res->n; i++) + FREE(res->di[i]); + FREE(res->di); +} diff --git a/libmultipath/util.h b/libmultipath/util.h index 56cec76..a818e29 100644 --- a/libmultipath/util.h +++ b/libmultipath/util.h @@ -21,6 +21,7 @@ int get_linux_version_code(void); int parse_prkey(char *ptr, uint64_t *prkey); int parse_prkey_flags(char *ptr, uint64_t *prkey, uint8_t *flags); int safe_write(int fd, const void *buf, size_t count); +void set_max_fds(int max_fds); #define KERNEL_VERSION(maj, min, ptc) ((((maj) * 256) + (min)) * 256 + (ptc)) @@ -29,4 +30,13 @@ int safe_write(int fd, const void *buf, size_t count); #define safe_snprintf(var, size, format, args...) \ snprintf(var, size, format, ##args) >= size +#define pthread_cleanup_push_cast(f, arg) \ + pthread_cleanup_push(((void (*)(void *))&f), (arg)) + +struct scandir_result { + struct dirent **di; + int n; +}; +void free_scandir_result(struct scandir_result *); + #endif /* _UTIL_H */ diff --git a/libmultipath/version.h b/libmultipath/version.h index 10ebbda..65d0522 100644 --- a/libmultipath/version.h +++ b/libmultipath/version.h @@ -20,8 +20,8 @@ #ifndef _VERSION_H #define _VERSION_H -#define VERSION_CODE 0x000708 -#define DATE_CODE 0x0a0a12 +#define VERSION_CODE 0x000709 +#define DATE_CODE 0x0b0e12 #define PROG "multipath-tools" diff --git a/multipath/main.c b/multipath/main.c index d5aad95..05b7bf0 100644 --- a/multipath/main.c +++ b/multipath/main.c @@ -56,8 +56,6 @@ #include "pgpolicies.h" #include "version.h" #include -#include -#include #include "wwids.h" #include "uxsock.h" #include "mpath_cmd.h" @@ -1002,15 +1000,7 @@ main (int argc, char *argv[]) logsink = 1; } - if (conf->max_fds) { - struct rlimit fd_limit; - - fd_limit.rlim_cur = conf->max_fds; - fd_limit.rlim_max = conf->max_fds; - if (setrlimit(RLIMIT_NOFILE, &fd_limit) < 0) - condlog(0, "can't set open fds limit to %d : %s", - conf->max_fds, strerror(errno)); - } + set_max_fds(conf->max_fds); libmp_udev_set_sync_support(1); diff --git a/multipath/multipath.8 b/multipath/multipath.8 index b5e5292..9cdd05a 100644 --- a/multipath/multipath.8 +++ b/multipath/multipath.8 @@ -5,7 +5,7 @@ .\" .\" ---------------------------------------------------------------------------- . -.TH MULTIPATH 8 2016-10-26 "Linux" +.TH MULTIPATH 8 2018-10-10 "Linux" . . .\" ---------------------------------------------------------------------------- @@ -21,17 +21,68 @@ multipath \- Device mapper target autoconfig. . .B multipath .RB [\| \-v\ \c -.IR verbosity \|] +.IR level \|] +.RB [\| \-B | \-d | \-i | \-q | \-r \|] .RB [\| \-b\ \c -.IR bindings_file \|] -.RB [\| \-d \|] -.RB [\| \-h | \-l | \-ll | \-f | \-t | \-T | \-F | \-B | \-c | \-C | \-q | \-r | \-i | \-a | \-u | \-U | \-w | \-W \|] +.IR file \|] .RB [\| \-p\ \c -.IR failover | multibus | group_by_serial | group_by_prio | group_by_node_name \|] +.IR policy \|] +.RB [\| device \|] +. +.LP +.B multipath +.RB [\| \-v\ \c +.IR level \|] +.RB [\| \-R\ \c +.IR retries \|] +.B \-f device +. +.LP +.B multipath +.RB [\| \-v\ \c +.IR level \|] .RB [\| \-R\ \c .IR retries \|] +.B \-F +. +.LP +.B multipath +.RB [\| \-v\ \c +.IR level \|] +.RB [\| \-l | \-ll \|] .RB [\| device \|] . +.LP +.B multipath +.RB [\| \-v\ \c +.IR level \|] +.RB [\| \-a | \-w \|] +.B device +. +.LP +.B multipath +.RB [\| \-v\ \c +.IR level \|] +.B -W +. +.LP +.B multipath +.RB [\| \-v\ \c +.IR level \|] +.RB [\| \-i \|] +.RB [\| \-c | \-C \|] +.B device +. +.LP +.B multipath +.RB [\| \-v\ \c +.IR level \|] +.RB [\| \-i \|] +.RB [\| \-u | \-U \|] +. +.LP +.B multipath +.RB [\| \-h | \-t | \-T \|] . .\" ---------------------------------------------------------------------------- .SH DESCRIPTION @@ -40,83 +91,66 @@ multipath \- Device mapper target autoconfig. .B multipath is used to detect and coalesce multiple paths to devices, for fail-over or performance reasons. . -. .\" ---------------------------------------------------------------------------- -.SH OPTIONS +.SH ARGUMENTS .\" ---------------------------------------------------------------------------- . -.TP -.BI \-v " level" -Verbosity, print all paths and multipaths: -.RS 1.2i -.TP 1.2i -.I 0 -No output. -.TP -.I 1 -Print the created or updated multipath names only, for use to feed other tools like kpartx. -.TP -.I 2 + -Print all info: detected paths, coalesced paths (ie multipaths) and device maps. -.RE +The \fBdevice\fR argument restricts \fBmultipath\fR's operation to devices matching the given +expression. The argument may refer either to a multipath map or to +its components ("paths"). The expression may be in one of the following formats: . -.TP -.B \-h -Print usage text. +.TP 1.4i +.B device node +file name of a device node, e.g. \fI/dev/dm-10\fR or \fI/dev/sda\fR. If the node refers +to an existing device mapper device representing a multipath map, this selects +the map or its paths, depending on the operation mode. Otherwise, it selects a path device. . .TP -.B \-d -Dry run, do not create or update devmaps. +.B device ID +kernel device number specified by major:minor numbers, e.g. \fI65:16\fR. This +format can only be used for path devices. . .TP -.B \-l -Show the current multipath topology from information fetched in sysfs and the device mapper. +.B WWID +a World Wide Identifier matching a multipath map or its paths. To list WWIDs of devices +present in the system, use e.g. the command "\fImultipath -d -v3 2>/dev/null\fR". . -.TP -.B \-ll -Show the current multipath topology from all available information (sysfs, the device mapper, path checkers ...). +.\" ---------------------------------------------------------------------------- +.SH OPERATION MODES +.\" ---------------------------------------------------------------------------- +. +The default operation mode is to detect and set up multipath maps from the devices found in +the system. . +Other operation modes are chosen by using one of the following command line switches: .TP .B \-f -Flush a multipath device map specified as parameter, if unused. +Flush (remove) a multipath device map specified as parameter, if unused. . .TP .B \-F -Flush all unused multipath device maps. +Flush (remove) all unused multipath device maps. . .TP -.B \-t -Display the currently used multipathd configuration. -. -.TP -.B \-T -Display the currently used multipathd configuration, limiting the output to -those devices actually present in the system. This can be used a template for -creating \fImultipath.conf\fR. +.B \-l +Show ("list") the current multipath topology from information fetched in sysfs and the device mapper. . .TP -.B \-r -Force devmap reload. +.B \-ll +Show ("list") the current multipath topology from all available information (sysfs, the +device mapper, path checkers ...). . .TP -.B \-i -Ignore WWIDs file when processing devices. If -\fIfind_multipaths strict\fR or \fIfind_multipaths no\fR is set in -\fImultipath.conf\fR, multipath only considers devices that are -listed in the WWIDs file. This option overrides that behavior. For other values -of \fIfind_multipaths\fR, this option has no effect. See the description of -\fIfind_multipaths\fR in -.BR multipath.conf (5). -This option should only be used in rare circumstances. +.B \-a +Add the WWID for the specified device to the WWIDs file. . .TP -.B \-B -Treat the bindings file as read only. +.B \-w +Remove the WWID for the specified device from the WWIDs file. . .TP -.BI \-b " bindings_file" -Set user_friendly_names bindings file location. The default is -\fI/etc/multipath/bindings\fR. +.B \-W +Reset the WWIDs file to only include the current multipath devices. . .TP .B \-c @@ -129,14 +163,6 @@ test whether or not I/O on this device is likely to succeed. The command itself doesn't attempt to do I/O on the device. . .TP -.B \-q -Allow device tables with \fIqueue_if_no_path\fR when multipathd is not running. -. -.TP -.B \-a -Add the WWID for the specified device to the WWIDs file. -. -.TP .B \-u Check if the device specified in the program environment should be a path in a multipath device. @@ -147,60 +173,99 @@ Check if the device specified in the program environment is a multipath device with usable paths. See \fB-C\fB. . .TP -.B \-w -Remove the WWID for the specified device from the WWIDs file. +.B \-h +Print usage text. . .TP -.B \-W -Reset the WWIDs file to only include the current multipath devices. +.B \-t +Display the currently used multipathd configuration. . .TP -.BI \-p " policy" -Force new maps to use the specified policy: +.B \-T +Display the currently used multipathd configuration, limiting the output to +those devices actually present in the system. This can be used a template for +creating \fImultipath.conf\fR. +. +.\" ---------------------------------------------------------------------------- +.SH OPTIONS +.\" ---------------------------------------------------------------------------- +. +.TP +.BI \-v " level" +Verbosity of information printed to stdout in default and "list" operation +modes. The default level is \fI-v 2\fR. .RS 1.2i .TP 1.2i -.I failover -One path per priority group. +.I 0 +Nothing is printed. .TP -.I multibus -All paths in one priority group. +.I 1 +In default mode, Names/WWIDs of created or modified multipath maps are +printed. In list mode, WWIDs of all multipath maps are printed. .TP -.I group_by_serial -One priority group per serial number. +.I 2 +In default mode, +Topology of created or modified multipath maps is printed. +In list mode, topology of all multipath maps is printed. .TP -.I group_by_prio -One 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. +.I 3 +All detected paths and the topology of all multipath maps are printed. +. +.LP +. +The verbosity level also controls the level of log and debug messages printed to +\fIstderr\fR. The default level corresponds to \fILOG_NOTICE\fR +(important messages that shouldn't be missed in normal operation). +. +.RE .TP -.I group_by_node_name -One priority group per target node name. Target node names are fetched -in \fI/sys/class/fc_transport/target*/node_name\fR. +.B \-d +Dry run, do not create or update devmaps. +. .TP -.RE -Existing maps are not modified. +.B \-i +Ignore WWIDs file when processing devices. If +\fIfind_multipaths strict\fR or \fIfind_multipaths no\fR is set in +\fImultipath.conf\fR, multipath only considers devices that are +listed in the WWIDs file. This option overrides that behavior. For other values +of \fIfind_multipaths\fR, this option has no effect. See the description of +\fIfind_multipaths\fR in +.BR multipath.conf (5). +This option should only be used in rare circumstances. . .TP -.BI \-R " retries" -Number of times to retry flushing multipath devices that are in-use. The default -is \fI0\fR. +.B \-B +Treat the bindings file as read only. . .TP -.BI device -Update only the devmap specified by -.IR device , -which is either: -.RS 1.2i -.IP \[bu] -A devmap name. -.IP \[bu] -A path associated with the desired devmap; the path may be in one of the following formats: -.RS 1.2i -.IP \[bu] -.B /dev/sdX -.IP \[bu] -.B major:minor +.BI \-b " file" +Set \fIuser_friendly_names\fR bindings file location. The default is +\fI/etc/multipath/bindings\fR. . +.TP +.B \-q +Don't unset the device mapper feature \fIqueue_if_no_path\fR for multipath +maps. Normally, \fBmultipath\fR would do so if \fBmultipathd\fR is not +running, because only a running multipath daemon guarantees that unusable +paths are reinstated when they become usable again. +. +.TP +.BI \-p " policy" +Force new maps to use the specified policy, overriding the configuration in +\fBmultipath.conf(5)\fR. The possible values for +\fIpolicy\fR are the same as the values for \fIpath_grouping_policy\fR in +\fBmultipath.conf(5)\fR. Existing maps are not modified. +. +.TP +.B \-r +Force a reload of all existing multipath maps. This command is delegated to +the multipathd daemon if it's running. In this case, other command line +switches of the \fImultipath\fR command have no effect. +. +.TP +.BI \-R " retries" +Number of times to retry flushing multipath devices that are in use. The default +is \fI0\fR. . .\" ---------------------------------------------------------------------------- .SH "SEE ALSO" diff --git a/multipath/multipath.rules b/multipath/multipath.rules index d658073..9df11a9 100644 --- a/multipath/multipath.rules +++ b/multipath/multipath.rules @@ -45,6 +45,10 @@ ENV{DM_MULTIPATH_DEVICE_PATH}!="2", \ # Code below here is only run in "smart" mode. # multipath -u has indicated this is "maybe" multipath. +# Note that DM_MULTIPATH_DEVICE_PATH has the value 2 at this point. +# This value will never propagate to other rules files, because +# it will be reset to 1 in the "pretend_multipath" section below. + # This shouldn't happen, just in case. ENV{FIND_MULTIPATHS_WAIT_UNTIL}!="?*", GOTO="end_mpath" diff --git a/multipathd/cli_handlers.c b/multipathd/cli_handlers.c index bb16472..a0d57a5 100644 --- a/multipathd/cli_handlers.c +++ b/multipathd/cli_handlers.c @@ -720,6 +720,10 @@ cli_add_path (void * v, char ** reply, int * len, void * data) udevice = udev_device_new_from_subsystem_sysname(udev, "block", param); + if (!udevice) { + condlog(0, "%s: can't find path", param); + return 1; + } conf = get_multipath_config(); pthread_cleanup_push(put_multipath_config, conf); r = store_pathinfo(vecs->pathvec, conf, @@ -732,7 +736,6 @@ cli_add_path (void * v, char ** reply, int * len, void * data) condlog(0, "%s: failed to store path info", param); return 1; } - pp->checkint = conf->checkint; } return ev_add_path(pp, vecs, 1); blacklisted: @@ -1121,12 +1124,17 @@ cli_switch_group(void * v, char ** reply, int * len, void * data) int cli_reconfigure(void * v, char ** reply, int * len, void * data) { + int rc; + condlog(2, "reconfigure (operator)"); - if (set_config_state(DAEMON_CONFIGURE) == ETIMEDOUT) { + rc = set_config_state(DAEMON_CONFIGURE); + if (rc == ETIMEDOUT) { condlog(2, "timeout starting reconfiguration"); return 1; - } + } else if (rc == EINVAL) + /* daemon shutting down */ + return 1; return 0; } diff --git a/multipathd/main.c b/multipathd/main.c index af33239..cc555bb 100644 --- a/multipathd/main.c +++ b/multipathd/main.c @@ -12,8 +12,6 @@ #include #include #include -#include -#include #include #include #include @@ -68,6 +66,7 @@ static int use_watchdog; #include "pgpolicies.h" #include "uevent.h" #include "log.h" +#include "uxsock.h" #include "mpath_cmd.h" #include "mpath_persist.h" @@ -91,12 +90,24 @@ static int use_watchdog; #define FILE_NAME_SIZE 256 #define CMDSIZE 160 -#define LOG_MSG(a, b) \ -do { \ - if (pp->offline) \ - condlog(a, "%s: %s - path offline", pp->mpp->alias, pp->dev); \ - else if (strlen(b)) \ - condlog(a, "%s: %s - %s", pp->mpp->alias, pp->dev, b); \ +#define LOG_MSG(lvl, verb, pp) \ +do { \ + if (lvl <= verb) { \ + if (pp->offline) \ + condlog(lvl, "%s: %s - path offline", \ + pp->mpp->alias, pp->dev); \ + else { \ + const char *__m = \ + checker_message(&pp->checker); \ + \ + if (strlen(__m)) \ + condlog(lvl, "%s: %s - %s checker%s", \ + pp->mpp->alias, \ + pp->dev, \ + checker_name(&pp->checker), \ + __m); \ + } \ + } \ } while(0) struct mpath_event_param @@ -196,10 +207,9 @@ static void config_cleanup(void *arg) pthread_mutex_unlock(&config_lock); } -void post_config_state(enum daemon_status state) +static void __post_config_state(enum daemon_status state) { - pthread_mutex_lock(&config_lock); - if (state != running_state) { + if (state != running_state && running_state != DAEMON_SHUTDOWN) { enum daemon_status old_state = running_state; running_state = state; @@ -208,7 +218,14 @@ void post_config_state(enum daemon_status state) do_sd_notify(old_state); #endif } - pthread_mutex_unlock(&config_lock); +} + +void post_config_state(enum daemon_status state) +{ + pthread_mutex_lock(&config_lock); + pthread_cleanup_push(config_cleanup, NULL); + __post_config_state(state); + pthread_cleanup_pop(1); } int set_config_state(enum daemon_status state) @@ -220,7 +237,9 @@ int set_config_state(enum daemon_status state) if (running_state != state) { enum daemon_status old_state = running_state; - if (running_state != DAEMON_IDLE) { + if (running_state == DAEMON_SHUTDOWN) + rc = EINVAL; + else if (running_state != DAEMON_IDLE) { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); @@ -762,8 +781,8 @@ uev_remove_map (struct uevent * uev, struct vectors * vecs) goto out; } if (strcmp(mpp->alias, alias)) { - condlog(2, "%s: minor number mismatch (map %d, event %d)", - mpp->alias, mpp->dmi->minor, minor); + condlog(2, "%s: map alias mismatch: have \"%s\", got \"%s\")", + uev->kernel, mpp->alias, alias); goto out; } @@ -1486,12 +1505,28 @@ uevqloop (void * ap) static void * uxlsnrloop (void * ap) { + long ux_sock; + + pthread_cleanup_push(rcu_unregister, NULL); + rcu_register_thread(); + + ux_sock = ux_socket_listen(DEFAULT_SOCKET); + if (ux_sock == -1) { + condlog(1, "could not create uxsock: %d", errno); + exit_daemon(); + goto out; + } + pthread_cleanup_push(uxsock_cleanup, (void *)ux_sock); + if (cli_init()) { condlog(1, "Failed to init uxsock listener"); - return NULL; + exit_daemon(); + goto out_sock; } - pthread_cleanup_push(rcu_unregister, NULL); - rcu_register_thread(); + + /* Tell main thread that thread has started */ + post_config_state(DAEMON_CONFIGURE); + set_handler_callback(LIST+PATHS, cli_list_paths); set_handler_callback(LIST+PATHS+FMT, cli_list_paths_fmt); set_handler_callback(LIST+PATHS+RAW+FMT, cli_list_paths_raw); @@ -1546,8 +1581,12 @@ uxlsnrloop (void * ap) set_handler_callback(UNSETPRKEY+MAP, cli_unsetprkey); umask(077); - uxsock_listen(&uxsock_trigger, ap); - pthread_cleanup_pop(1); + uxsock_listen(&uxsock_trigger, ux_sock, ap); + +out_sock: + pthread_cleanup_pop(1); /* uxsock_cleanup */ +out: + pthread_cleanup_pop(1); /* rcu_unregister */ return NULL; } @@ -1813,7 +1852,7 @@ check_path (struct vectors * vecs, struct path * pp, int ticks) int add_active; int disable_reinstate = 0; int oldchkrstate = pp->chkrstate; - int retrigger_tries, checkint; + int retrigger_tries, checkint, max_checkint, verbosity; struct config *conf; int ret; @@ -1829,16 +1868,38 @@ check_path (struct vectors * vecs, struct path * pp, int ticks) conf = get_multipath_config(); retrigger_tries = conf->retrigger_tries; checkint = conf->checkint; + max_checkint = conf->max_checkint; + verbosity = conf->verbosity; put_multipath_config(conf); - if (!pp->mpp && pp->initialized == INIT_MISSING_UDEV && - pp->retriggers < retrigger_tries) { - condlog(2, "%s: triggering change event to reinitialize", - pp->dev); - pp->initialized = INIT_REQUESTED_UDEV; - pp->retriggers++; - sysfs_attr_set_value(pp->udev, "uevent", "change", - strlen("change")); - return 0; + + if (pp->checkint == CHECKINT_UNDEF) { + condlog(0, "%s: BUG: checkint is not set", pp->dev); + pp->checkint = checkint; + }; + + if (!pp->mpp && pp->initialized == INIT_MISSING_UDEV) { + if (pp->retriggers < retrigger_tries) { + condlog(2, "%s: triggering change event to reinitialize", + pp->dev); + pp->initialized = INIT_REQUESTED_UDEV; + pp->retriggers++; + sysfs_attr_set_value(pp->udev, "uevent", "change", + strlen("change")); + return 0; + } else { + condlog(1, "%s: not initialized after %d udev retriggers", + pp->dev, retrigger_tries); + /* + * Make sure that the "add missing path" code path + * below may reinstate the path later, if it ever + * comes up again. + * The WWID needs not be cleared; if it was set, the + * state hadn't been INIT_MISSING_UDEV in the first + * place. + */ + pp->initialized = INIT_FAILED; + return 0; + } } /* @@ -1871,7 +1932,8 @@ check_path (struct vectors * vecs, struct path * pp, int ticks) } if (newstate == PATH_WILD || newstate == PATH_UNCHECKED) { - condlog(2, "%s: unusable path", pp->dev); + condlog(2, "%s: unusable path - checker failed", pp->dev); + LOG_MSG(2, verbosity, pp); conf = get_multipath_config(); pthread_cleanup_push(put_multipath_config, conf); pathinfo(pp, conf, 0); @@ -1879,18 +1941,26 @@ check_path (struct vectors * vecs, struct path * pp, int ticks) return 1; } if (!pp->mpp) { - if (!strlen(pp->wwid) && pp->initialized != INIT_MISSING_UDEV && + if (!strlen(pp->wwid) && pp->initialized == INIT_FAILED && (newstate == PATH_UP || newstate == PATH_GHOST)) { condlog(2, "%s: add missing path", pp->dev); conf = get_multipath_config(); pthread_cleanup_push(put_multipath_config, conf); ret = pathinfo(pp, conf, DI_ALL | DI_BLACKLIST); pthread_cleanup_pop(1); - if (ret == PATHINFO_OK) { + /* INIT_OK implies ret == PATHINFO_OK */ + if (pp->initialized == INIT_OK) { ev_add_path(pp, vecs, 1); pp->tick = 1; - } else if (ret == PATHINFO_SKIPPED) - return -1; + } else { + /* + * We failed multiple times to initialize this + * path properly. Don't re-check too often. + */ + pp->checkint = max_checkint; + if (ret == PATHINFO_SKIPPED) + return -1; + } } return 0; } @@ -1949,7 +2019,7 @@ check_path (struct vectors * vecs, struct path * pp, int ticks) int oldstate = pp->state; pp->state = newstate; - LOG_MSG(1, checker_message(&pp->checker)); + LOG_MSG(1, verbosity, pp); /* * upon state change, reset the checkint @@ -1971,8 +2041,12 @@ check_path (struct vectors * vecs, struct path * pp, int ticks) pp->wait_checks = pp->mpp->delay_wait_checks; pp->watch_checks = 0; } - }else + } else { fail_path(pp, 0); + if (pp->wait_checks > 0) + pp->wait_checks = + pp->mpp->delay_wait_checks; + } /* * cancel scheduled failback @@ -2037,11 +2111,7 @@ check_path (struct vectors * vecs, struct path * pp, int ticks) return 0; } } else { - unsigned int max_checkint; - LOG_MSG(4, checker_message(&pp->checker)); - conf = get_multipath_config(); - max_checkint = conf->max_checkint; - put_multipath_config(conf); + LOG_MSG(4, verbosity, pp); if (pp->checkint != max_checkint) { /* * double the next check delay. @@ -2071,9 +2141,9 @@ check_path (struct vectors * vecs, struct path * pp, int ticks) log_checker_err = conf->log_checker_err; put_multipath_config(conf); if (log_checker_err == LOG_CHKR_ERR_ONCE) - LOG_MSG(3, checker_message(&pp->checker)); + LOG_MSG(3, verbosity, pp); else - LOG_MSG(2, checker_message(&pp->checker)); + LOG_MSG(2, verbosity, pp); } } @@ -2102,19 +2172,6 @@ check_path (struct vectors * vecs, struct path * pp, int ticks) return 1; } -static void init_path_check_interval(struct vectors *vecs) -{ - struct config *conf; - struct path *pp; - unsigned int i; - - vector_foreach_slot (vecs->pathvec, pp, i) { - conf = get_multipath_config(); - pp->checkint = conf->checkint; - put_multipath_config(conf); - } -} - static void * checkerloop (void *ap) { @@ -2161,7 +2218,9 @@ checkerloop (void *ap) if (rc == ETIMEDOUT) { condlog(4, "timeout waiting for DAEMON_IDLE"); continue; - } + } else if (rc == EINVAL) + /* daemon shutdown */ + break; pthread_cleanup_push(cleanup_lock, &vecs->lock); lock(&vecs->lock); @@ -2285,18 +2344,17 @@ configure (struct vectors * vecs) goto fail; } + conf = get_multipath_config(); + pthread_cleanup_push(put_multipath_config, conf); vector_foreach_slot (vecs->pathvec, pp, i){ - conf = get_multipath_config(); - pthread_cleanup_push(put_multipath_config, conf); if (filter_path(conf, pp) > 0){ vector_del_slot(vecs->pathvec, i); free_path(pp); i--; } - else - pp->checkint = conf->checkint; - pthread_cleanup_pop(1); } + pthread_cleanup_pop(1); + if (map_discovery(vecs)) { condlog(0, "configure failed at map discovery"); goto fail; @@ -2663,33 +2721,10 @@ child (void * param) envp = getenv("LimitNOFILE"); - if (envp) { + if (envp) condlog(2,"Using systemd provided open fds limit of %s", envp); - } else if (conf->max_fds) { - struct rlimit fd_limit; - - if (getrlimit(RLIMIT_NOFILE, &fd_limit) < 0) { - condlog(0, "can't get open fds limit: %s", - strerror(errno)); - fd_limit.rlim_cur = 0; - fd_limit.rlim_max = 0; - } - if (fd_limit.rlim_cur < conf->max_fds) { - fd_limit.rlim_cur = conf->max_fds; - if (fd_limit.rlim_max < conf->max_fds) - fd_limit.rlim_max = conf->max_fds; - if (setrlimit(RLIMIT_NOFILE, &fd_limit) < 0) { - condlog(0, "can't set open fds limit to " - "%lu/%lu : %s", - fd_limit.rlim_cur, fd_limit.rlim_max, - strerror(errno)); - } else { - condlog(3, "set open fds limit to %lu/%lu", - fd_limit.rlim_cur, fd_limit.rlim_max); - } - } - - } + else + set_max_fds(conf->max_fds); vecs = gvecs = init_vecs(); if (!vecs) @@ -2718,12 +2753,26 @@ child (void * param) */ conf = NULL; - /* - * Signal start of configuration - */ - post_config_state(DAEMON_CONFIGURE); + pthread_cleanup_push(config_cleanup, NULL); + pthread_mutex_lock(&config_lock); - init_path_check_interval(vecs); + __post_config_state(DAEMON_IDLE); + rc = pthread_create(&uxlsnr_thr, &misc_attr, uxlsnrloop, vecs); + if (!rc) { + /* Wait for uxlsnr startup */ + while (running_state == DAEMON_IDLE) + pthread_cond_wait(&config_cond, &config_lock); + } + pthread_cleanup_pop(1); + + if (rc) { + condlog(0, "failed to create cli listener: %d", rc); + goto failed; + } + else if (running_state != DAEMON_CONFIGURE) { + condlog(0, "cli listener failed to start"); + goto failed; + } if (poll_dmevents) { if (init_dmevent_waiter(vecs)) { @@ -2746,10 +2795,6 @@ child (void * param) goto failed; } pthread_attr_destroy(&uevent_attr); - if ((rc = pthread_create(&uxlsnr_thr, &misc_attr, uxlsnrloop, vecs))) { - condlog(0, "failed to create cli listener: %d", rc); - goto failed; - } /* * start threads @@ -2984,15 +3029,16 @@ main (int argc, char *argv[]) logsink = -1; break; case 'k': + logsink = 0; conf = load_config(DEFAULT_CONFIGFILE); if (!conf) exit(1); if (verbosity) conf->verbosity = verbosity; uxsock_timeout = conf->uxsock_timeout; - uxclnt(optarg, uxsock_timeout + 100); + err = uxclnt(optarg, uxsock_timeout + 100); free_config(conf); - exit(0); + return err; case 'B': bindings_read_only = 1; break; @@ -3013,6 +3059,7 @@ main (int argc, char *argv[]) char * s = cmd; char * c = s; + logsink = 0; conf = load_config(DEFAULT_CONFIGFILE); if (!conf) exit(1); @@ -3028,9 +3075,9 @@ main (int argc, char *argv[]) optind++; } c += snprintf(c, s + CMDSIZE - c, "\n"); - uxclnt(s, uxsock_timeout + 100); + err = uxclnt(s, uxsock_timeout + 100); free_config(conf); - exit(0); + return err; } if (foreground) { diff --git a/multipathd/uxclnt.c b/multipathd/uxclnt.c index 08db0e8..a76f8e2 100644 --- a/multipathd/uxclnt.c +++ b/multipathd/uxclnt.c @@ -103,14 +103,14 @@ static void process(int fd, unsigned int timeout) } } -static void process_req(int fd, char * inbuf, unsigned int timeout) +static int process_req(int fd, char * inbuf, unsigned int timeout) { char *reply; int ret; if (send_packet(fd, inbuf) != 0) { printf("cannot send packet\n"); - return; + return 1; } ret = recv_packet(fd, &reply, timeout); if (ret < 0) { @@ -118,9 +118,12 @@ static void process_req(int fd, char * inbuf, unsigned int timeout) printf("timeout receiving packet\n"); else printf("error %d receiving packet\n", ret); + return 1; } else { printf("%s", reply); + ret = (strcmp(reply, "fail\n") == 0); FREE(reply); + return ret; } } @@ -129,16 +132,16 @@ static void process_req(int fd, char * inbuf, unsigned int timeout) */ int uxclnt(char * inbuf, unsigned int timeout) { - int fd; + int fd, ret = 0; fd = mpath_connect(); if (fd == -1) exit(1); if (inbuf) - process_req(fd, inbuf, timeout); + ret = process_req(fd, inbuf, timeout); else process(fd, timeout); mpath_disconnect(fd); - return 0; + return ret; } diff --git a/multipathd/uxlsnr.c b/multipathd/uxlsnr.c index 6f66666..773bc87 100644 --- a/multipathd/uxlsnr.c +++ b/multipathd/uxlsnr.c @@ -165,24 +165,15 @@ void uxsock_cleanup(void *arg) /* * entry point */ -void * uxsock_listen(uxsock_trigger_fn uxsock_trigger, void * trigger_data) +void * uxsock_listen(uxsock_trigger_fn uxsock_trigger, long ux_sock, + void * trigger_data) { - long ux_sock; int rlen; char *inbuf; char *reply; sigset_t mask; int old_clients = MIN_POLLS; - ux_sock = ux_socket_listen(DEFAULT_SOCKET); - - if (ux_sock == -1) { - condlog(1, "could not create uxsock: %d", errno); - exit_daemon(); - } - - pthread_cleanup_push(uxsock_cleanup, (void *)ux_sock); - condlog(3, "uxsock: startup listener"); polls = (struct pollfd *)MALLOC((MIN_POLLS + 1) * sizeof(struct pollfd)); if (!polls) { @@ -322,6 +313,5 @@ void * uxsock_listen(uxsock_trigger_fn uxsock_trigger, void * trigger_data) } } - pthread_cleanup_pop(1); return NULL; } diff --git a/multipathd/uxlsnr.h b/multipathd/uxlsnr.h index d51a8f9..18f008d 100644 --- a/multipathd/uxlsnr.h +++ b/multipathd/uxlsnr.h @@ -5,7 +5,8 @@ typedef int (uxsock_trigger_fn)(char *, char **, int *, bool, void *); -void * uxsock_listen(uxsock_trigger_fn uxsock_trigger, - void * trigger_data); +void uxsock_cleanup(void *arg); +void *uxsock_listen(uxsock_trigger_fn uxsock_trigger, long ux_sock, + void * trigger_data); #endif diff --git a/tests/dmevents.c b/tests/dmevents.c index 3399c81..bee117a 100644 --- a/tests/dmevents.c +++ b/tests/dmevents.c @@ -465,7 +465,7 @@ static void test_watch_dmevents_good1(void **state) skip(); remove_all_dm_device_events(); - assert_int_equal(add_dm_device_event("foo", 1, 5), 0); + assert_int_equal(add_dm_device_event("foo", 1, 5), 0); will_return(__wrap_dm_geteventnr, 0); assert_int_equal(watch_dmevents("foo"), 0); dev_evt = find_dmevents("foo"); -- 2.34.1