Imported Upstream version 0.7.9 upstream/0.7.9
authorDongHun Kwak <dh0128.kwak@samsung.com>
Fri, 14 Jan 2022 04:50:18 +0000 (13:50 +0900)
committerDongHun Kwak <dh0128.kwak@samsung.com>
Fri, 14 Jan 2022 04:50:18 +0000 (13:50 +0900)
37 files changed:
kpartx/devmapper.c
kpartx/test-kpartx
libmpathpersist/mpath_persist.c
libmultipath/checkers.c
libmultipath/checkers.h
libmultipath/checkers/cciss_tur.c
libmultipath/checkers/directio.c
libmultipath/checkers/emc_clariion.c
libmultipath/checkers/hp_sw.c
libmultipath/checkers/rdac.c
libmultipath/checkers/readsector0.c
libmultipath/checkers/tur.c
libmultipath/config.c
libmultipath/configure.c
libmultipath/defaults.h
libmultipath/devmapper.c
libmultipath/dict.c
libmultipath/discovery.c
libmultipath/foreign.c
libmultipath/foreign/nvme.c
libmultipath/print.c
libmultipath/propsel.c
libmultipath/structs.c
libmultipath/structs_vec.c
libmultipath/sysfs.c
libmultipath/util.c
libmultipath/util.h
libmultipath/version.h
multipath/main.c
multipath/multipath.8
multipath/multipath.rules
multipathd/cli_handlers.c
multipathd/main.c
multipathd/uxclnt.c
multipathd/uxlsnr.c
multipathd/uxlsnr.h
tests/dmevents.c

index 8db1eb5..3aa4988 100644 (file)
@@ -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;
        }
index d2001dc..d3c9aef 100755 (executable)
@@ -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
index 4229a94..2ffe56e 100644 (file)
@@ -31,7 +31,6 @@
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
-#include <sys/resource.h>
 
 #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(&paramp->key, &paramp->sa_key, 8);
+               memset(&paramp->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){
index 0bacc86..848c4c3 100644 (file)
@@ -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;
+}
index 7b18a1a..b2e8f9a 100644 (file)
  * 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 */
index 1cab201..ea84374 100644 (file)
@@ -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:
index a80848d..1b00b77 100644 (file)
 #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;
index 9115b1b..6fc8911 100644 (file)
 #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.
                                ((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 */
                }
        }
index 0ad34a6..1a82022 100644 (file)
 #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;
 }
index 5104e4e..8a3b73e 100644 (file)
 #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;
        }
 
index 1c2a868..cf79e06 100644 (file)
@@ -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;
index 86c0cdc..63b1962 100644 (file)
 #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) {
index 0aef186..5af7af5 100644 (file)
@@ -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;
index 09c3dcf..ed3e30f 100644 (file)
@@ -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");
index 7f3839f..6576939 100644 (file)
@@ -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)
 
index 8136d15..0433b49 100644 (file)
@@ -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);
index bf4701e..a81c051 100644 (file)
@@ -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);
index b267f07..63558ad 100644 (file)
@@ -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) {
index 80b399b..48e8d24 100644 (file)
@@ -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;
index 8887a75..c753a74 100644 (file)
@@ -27,6 +27,7 @@
 #include <dirent.h>
 #include <errno.h>
 #include <ctype.h>
+#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;
index 7b610b9..fc40b0f 100644 (file)
@@ -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
index fdb5953..970a3b5 100644 (file)
@@ -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",
index caa178a..fee899b 100644 (file)
@@ -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();
index f87d69d..c85823a 100644 (file)
@@ -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);
+                       }
                }
        }
 }
index b7dacaa..558c8d6 100644 (file)
@@ -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;
index 347af5b..66c4761 100644 (file)
@@ -7,6 +7,8 @@
 #include <sys/sysmacros.h>
 #include <sys/types.h>
 #include <sys/utsname.h>
+#include <sys/time.h>
+#include <sys/resource.h>
 #include <dirent.h>
 #include <unistd.h>
 #include <errno.h>
@@ -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);
+}
index 56cec76..a818e29 100644 (file)
@@ -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 */
index 10ebbda..65d0522 100644 (file)
@@ -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"
 
index d5aad95..05b7bf0 100644 (file)
@@ -56,8 +56,6 @@
 #include "pgpolicies.h"
 #include "version.h"
 #include <errno.h>
-#include <sys/time.h>
-#include <sys/resource.h>
 #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);
 
index b5e5292..9cdd05a 100644 (file)
@@ -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"
index d658073..9df11a9 100644 (file)
@@ -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"
 
index bb16472..a0d57a5 100644 (file)
@@ -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;
 }
 
index af33239..cc555bb 100644 (file)
@@ -12,8 +12,6 @@
 #include <sys/types.h>
 #include <fcntl.h>
 #include <errno.h>
-#include <sys/time.h>
-#include <sys/resource.h>
 #include <limits.h>
 #include <linux/oom.h>
 #include <libudev.h>
@@ -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) {
index 08db0e8..a76f8e2 100644 (file)
@@ -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;
 }
index 6f66666..773bc87 100644 (file)
@@ -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;
 }
index d51a8f9..18f008d 100644 (file)
@@ -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
index 3399c81..bee117a 100644 (file)
@@ -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");