Imported Upstream version 0.8.4 upstream/0.8.4
authorDongHun Kwak <dh0128.kwak@samsung.com>
Fri, 14 Jan 2022 04:50:19 +0000 (13:50 +0900)
committerDongHun Kwak <dh0128.kwak@samsung.com>
Fri, 14 Jan 2022 04:50:19 +0000 (13:50 +0900)
107 files changed:
Makefile
Makefile.inc
kpartx/bsd.c
kpartx/dasd.c
kpartx/devmapper.c
kpartx/devmapper.h
kpartx/dos.c
kpartx/gpt.c
kpartx/gpt.h
kpartx/kpartx.h
kpartx/mac.c
kpartx/ps3.c
kpartx/solaris.c
kpartx/sun.c
kpartx/unixware.c
libdmmp/libdmmp_private.h
libmpathcmd/mpath_cmd.c
libmpathpersist/mpath_persist.c
libmpathpersist/mpath_pr_ioctl.c
libmultipath/Makefile
libmultipath/alias.c
libmultipath/alias.h
libmultipath/byteorder.h
libmultipath/checkers.c
libmultipath/checkers.h
libmultipath/checkers/cciss_tur.c
libmultipath/checkers/directio.c
libmultipath/checkers/hp_sw.c
libmultipath/checkers/rdac.c
libmultipath/checkers/readsector0.c
libmultipath/config.c
libmultipath/config.h
libmultipath/configure.c
libmultipath/defaults.h
libmultipath/devmapper.c
libmultipath/dict.c
libmultipath/discovery.c
libmultipath/discovery.h
libmultipath/dm-generic.c
libmultipath/file.c
libmultipath/foreign.c
libmultipath/foreign/nvme.c
libmultipath/generic.c
libmultipath/hwtable.c
libmultipath/io_err_stat.c
libmultipath/log.h
libmultipath/log_pthread.c
libmultipath/log_pthread.h
libmultipath/nvme/linux/nvme.h
libmultipath/nvme/nvme-ioctl.c
libmultipath/nvme/nvme-ioctl.h
libmultipath/nvme/nvme.h
libmultipath/parser.c
libmultipath/pgpolicies.c
libmultipath/print.c
libmultipath/print.h
libmultipath/prio.c
libmultipath/prioritizers/alua_rtpg.c
libmultipath/prioritizers/ana.c
libmultipath/prioritizers/const.c
libmultipath/prioritizers/datacore.c
libmultipath/prioritizers/emc.c
libmultipath/prioritizers/hds.c
libmultipath/prioritizers/hp_sw.c
libmultipath/prioritizers/iet.c
libmultipath/prioritizers/ontap.c
libmultipath/prioritizers/random.c
libmultipath/prioritizers/rdac.c
libmultipath/prioritizers/sysfs.c
libmultipath/prioritizers/weightedpath.c
libmultipath/propsel.c
libmultipath/propsel.h
libmultipath/structs.c
libmultipath/structs.h
libmultipath/structs_vec.c
libmultipath/structs_vec.h
libmultipath/sysfs.c
libmultipath/time-util.c
libmultipath/uevent.c
libmultipath/unaligned.h
libmultipath/util.c
libmultipath/util.h
libmultipath/uxsock.c
libmultipath/vector.h
libmultipath/version.h
libmultipath/wwids.c
mpathpersist/main.c
multipath/main.c
multipath/multipath.conf.5
multipathd/Makefile
multipathd/cli_handlers.c
multipathd/dmevents.c
multipathd/main.c
multipathd/pidfile.c
multipathd/uxlsnr.c
multipathd/waiter.c
multipathd/waiter.h
tests/Makefile
tests/README.md [new file with mode: 0644]
tests/alias.c [new file with mode: 0644]
tests/blacklist.c
tests/directio.c [new file with mode: 0644]
tests/hwtable.c
tests/test-log.c [new file with mode: 0644]
tests/test-log.h [new file with mode: 0644]
tests/util.c
tests/vpd.c

index 4b145c5..1dee368 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -2,7 +2,7 @@
 # Copyright (C) 2003 Christophe Varoqui, <christophe.varoqui@opensvc.com>
 #
 
-BUILDDIRS = \
+BUILDDIRS := \
        libmpathcmd \
        libmultipath \
        libmultipath/prioritizers \
@@ -19,32 +19,30 @@ BUILDDIRS += \
        libdmmp
 endif
 
-all: recurse
+BUILDDIRS.clean := $(BUILDDIRS:=.clean) tests.clean
 
-recurse:
-       @for dir in $(BUILDDIRS); do $(MAKE) -C $$dir || exit $?; done
+.PHONY:        $(BUILDDIRS) $(BUILDDIRS:=.uninstall) $(BUILDDIRS:=.install) $(BUILDDIRS.clean)
 
-recurse_clean:
-       @for dir in $(BUILDDIRS); do \
-       $(MAKE) -C $$dir clean || exit $?; \
-       done
-       $(MAKE) -C tests clean
+all:   $(BUILDDIRS)
 
-recurse_install:
-       @for dir in $(BUILDDIRS); do \
-       $(MAKE) -C $$dir install || exit $?; \
-       done
+$(BUILDDIRS):
+       $(MAKE) -C $@
 
-recurse_uninstall:
-       @for dir in $(BUILDDIRS); do \
-       $(MAKE) -C $$dir uninstall || exit $?; \
-       done
+multipath multipathd mpathpersist: libmultipath
+mpathpersist:  libmpathpersist
 
-clean: recurse_clean
+$(BUILDDIRS.clean):
+       $(MAKE) -C ${@:.clean=} clean
 
-install: recurse_install
+$(BUILDDIRS:=.install):
+       $(MAKE) -C ${@:.install=} install
 
-uninstall: recurse_uninstall
+$(BUILDDIRS:=.uninstall):
+       $(MAKE) -C ${@:.uninstall=} uninstall
+
+clean: $(BUILDDIRS.clean)
+install: $(BUILDDIRS:=.install)
+uninstall: $(BUILDDIRS:=.uninstall)
 
 test:  all
        $(MAKE) -C tests
index 56c3eda..d4d1e0d 100644 (file)
@@ -81,7 +81,8 @@ INSTALL_PROGRAM       = install
 # Test if the C compiler supports the option.
 # Evaluates to "option" if yes, and "fallback" otherwise.
 TEST_CC_OPTION = $(shell \
-       if echo 'int main(void){return 0;}' | $(CC) -o /dev/null -c "$(1)" -xc - >/dev/null 2>&1; \
+       if echo 'int main(void){return 0;}' | \
+               $(CC) -o /dev/null -c -Werror "$(1)" -xc - >/dev/null 2>&1; \
        then \
                echo "$(1)"; \
        else \
@@ -90,14 +91,14 @@ TEST_CC_OPTION = $(shell \
 
 STACKPROT := $(call TEST_CC_OPTION,-fstack-protector-strong,-fstack-protector)
 ERROR_DISCARDED_QUALIFIERS := $(call TEST_CC_OPTION,-Werror=discarded-qualifiers,)
+WNOCLOBBERED := $(call TEST_CC_OPTION,-Wno-clobbered,)
 
-OPTFLAGS       = -O2 -g -pipe -Wall -Wextra -Wformat=2 -Werror=implicit-int \
+OPTFLAGS       = -O2 -g -pipe -Werror -Wall -Wextra -Wformat=2 -Werror=implicit-int \
                  -Werror=implicit-function-declaration -Werror=format-security \
-                 -Wno-sign-compare -Wno-unused-parameter -Wno-clobbered \
+                 $(WNOCLOBBERED) \
                  -Werror=cast-qual $(ERROR_DISCARDED_QUALIFIERS) \
-                 -Wp,-D_FORTIFY_SOURCE=2 $(STACKPROT) \
-                 --param=ssp-buffer-size=4
-
+                 $(STACKPROT) --param=ssp-buffer-size=4
+CPPFLAGS       := -Wp,-D_FORTIFY_SOURCE=2 
 CFLAGS         := $(OPTFLAGS) -DBIN_DIR=\"$(bindir)\" -DLIB_STRING=\"${LIB}\" -DRUN_DIR=\"${RUN}\" \
                   -MMD -MP $(CFLAGS)
 BIN_CFLAGS     = -fPIE -DPIE
@@ -134,4 +135,4 @@ check_file = $(shell \
 
 %.o:   %.c
        @echo building $@ because of $?
-       $(CC) $(CFLAGS) -c -o $@ $<
+       $(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $<
index f87175e..0e661fb 100644 (file)
@@ -47,13 +47,13 @@ struct bsd_disklabel {
 };
 
 int
-read_bsd_pt(int fd, struct slice all, struct slice *sp, int ns) {
+read_bsd_pt(int fd, struct slice all, struct slice *sp, unsigned int ns) {
        struct bsd_disklabel *l;
        struct bsd_partition *p;
        unsigned int offset = all.start, end;
        int max_partitions;
        char *bp;
-       int n = 0, i, j;
+       unsigned int n = 0, i, j;
 
        bp = getblock(fd, offset+1);    /* 1 sector suffices */
        if (bp == NULL)
index 4e7e474..14b9d3a 100644 (file)
@@ -65,7 +65,8 @@ typedef unsigned int __attribute__((__may_alias__)) label_ints_t;
 /*
  */
 int
-read_dasd_pt(int fd, struct slice all, struct slice *sp, int ns)
+read_dasd_pt(int fd, __attribute__((unused)) struct slice all,
+            struct slice *sp, __attribute__((unused)) unsigned int ns)
 {
        int retval = -1;
        int blocksize;
@@ -185,7 +186,7 @@ read_dasd_pt(int fd, struct slice all, struct slice *sp, int ns)
                goto out;
        }
 
-       if ((!info.FBA_layout) && (!strcmp(info.type, "ECKD")))
+       if ((!info.FBA_layout) && (!memcmp(info.type, "ECKD", 4)))
                memcpy (&vlabel, data, sizeof(vlabel));
        else {
                bzero(&vlabel,4);
@@ -215,7 +216,7 @@ read_dasd_pt(int fd, struct slice all, struct slice *sp, int ns)
                sp[0].size  = size - sp[0].start;
                retval = 1;
        } else if ((strncmp(type, "VOL1", 4) == 0) &&
-               (!info.FBA_layout) && (!strcmp(info.type, "ECKD"))) {
+               (!info.FBA_layout) && (!memcmp(info.type, "ECKD",4))) {
                /*
                 * New style VOL1 labeled disk
                 */
@@ -264,7 +265,7 @@ read_dasd_pt(int fd, struct slice all, struct slice *sp, int ns)
                        if (vlabel.ldl_version == 0xf2) {
                                fmt_size = sectors512(vlabel.formatted_blocks,
                                                      blocksize);
-                       } else if (!strcmp(info.type, "ECKD")) {
+                       } else if (!memcmp(info.type, "ECKD",4)) {
                                /* formatted w/o large volume support */
                                fmt_size = geo.cylinders * geo.heads
                                        * geo.sectors * (blocksize >> 9);
index 3aa4988..86731ea 100644 (file)
@@ -10,6 +10,7 @@
 #include <errno.h>
 #include <sys/sysmacros.h>
 #include "devmapper.h"
+#include "kpartx.h"
 
 #define _UUID_PREFIX "part"
 #define UUID_PREFIX _UUID_PREFIX "%d-"
@@ -17,7 +18,7 @@
 #define MAX_PREFIX_LEN (_UUID_PREFIX_LEN + 4)
 #define PARAMS_SIZE 1024
 
-int dm_prereq(char * str, int x, int y, int z)
+int dm_prereq(char * str, uint32_t x, uint32_t y, uint32_t z)
 {
        int r = 1;
        struct dm_task *dmt;
@@ -107,7 +108,7 @@ strip_slash (char * device)
 static int format_partname(char *buf, size_t bufsiz,
                           const char *mapname, const char *delim, int part)
 {
-       if (snprintf(buf, bufsiz, "%s%s%d", mapname, delim, part) >= bufsiz)
+       if (safe_snprintf(buf, bufsiz, "%s%s%d", mapname, delim, part))
                return 0;
        strip_slash(buf);
        return 1;
@@ -358,7 +359,7 @@ out:
 }
 
 int
-dm_devn (const char * mapname, int *major, int *minor)
+dm_devn (const char * mapname, unsigned int *major, unsigned int *minor)
 {
        int r = 1;
        struct dm_task *dmt;
@@ -527,7 +528,7 @@ do_foreach_partmaps (const char * mapname, const char *uuid,
        struct remove_data *rd = data;
        unsigned next = 0;
        char params[PARAMS_SIZE];
-       int major, minor;
+       unsigned int major, minor;
        char dev_t[32];
        int r = 1;
        int is_dmdev = 1;
@@ -644,7 +645,7 @@ int dm_find_part(const char *parent, const char *delim, int part,
        char params[PARAMS_SIZE];
        char *tmp;
        char *uuid;
-       int major, minor;
+       unsigned int major, minor;
        char dev_t[32];
 
        if (!format_partname(name, namesiz, parent, delim, part)) {
@@ -714,7 +715,7 @@ char *nondm_create_uuid(dev_t devt)
        return uuid_buf;
 }
 
-int nondm_parse_uuid(const char *uuid, int *major, int *minor)
+int nondm_parse_uuid(const char *uuid, unsigned int *major, unsigned int *minor)
 {
        const char *p;
        char *e;
index 73b80f2..701bdf6 100644 (file)
@@ -9,14 +9,14 @@
 
 extern int udev_sync;
 
-int dm_prereq (char *, int, int, int);
+int dm_prereq (char *, uint32_t, uint32_t, uint32_t);
 int dm_simplecmd (int, const char *, int, uint16_t);
 int dm_addmap (int, const char *, const char *, const char *, uint64_t,
               int, const char *, int, mode_t, uid_t, gid_t);
 char * dm_mapname(int major, int minor);
 dev_t dm_get_first_dep(char *devname);
 char * dm_mapuuid(const char *mapname);
-int dm_devn (const char * mapname, int *major, int *minor);
+int dm_devn (const char * mapname, unsigned int *major, unsigned int *minor);
 int dm_remove_partmaps (char * mapname, char *uuid, dev_t devt, int verbose);
 int dm_find_part(const char *parent, const char *delim, int part,
                 const char *parent_uuid,
@@ -34,5 +34,6 @@ int dm_find_part(const char *parent, const char *delim, int part,
 #define NONDM_UUID_PREFIX "devnode"
 #define NONDM_UUID_SUFFIX "Wh5pYvM"
 char *nondm_create_uuid(dev_t devt);
-int nondm_parse_uuid(const char *uuid, int *major, int *minor);
+int nondm_parse_uuid(const char *uuid,
+                    unsigned int *major, unsigned int *minor);
 #endif /* _KPARTX_DEVMAPPER_H */
index 4985152..0c70669 100644 (file)
@@ -74,10 +74,10 @@ is_gpt(int type) {
 }
 
 int
-read_dos_pt(int fd, struct slice all, struct slice *sp, int ns) {
+read_dos_pt(int fd, struct slice all, struct slice *sp, unsigned int ns) {
        struct partition p;
        unsigned long offset = all.start;
-       int i, n=4;
+       unsigned int i, n=4;
        unsigned char *bp;
        uint64_t  sector_size_mul = get_sector_size(fd)/512;
 
index e31611a..785b34e 100644 (file)
@@ -40,9 +40,9 @@
 #include "kpartx.h"
 
 #if BYTE_ORDER == LITTLE_ENDIAN
-#  define __le16_to_cpu(x) (x)
-#  define __le32_to_cpu(x) (x)
-#  define __le64_to_cpu(x) (x)
+#  define __le16_to_cpu(x) (uint16_t)(x)
+#  define __le32_to_cpu(x) (uint32_t)(x)
+#  define __le64_to_cpu(x) (uint64_t)(x)
 #  define __cpu_to_le32(x) (x)
 #elif BYTE_ORDER == BIG_ENDIAN
 #  define __le16_to_cpu(x) bswap_16(x)
@@ -182,7 +182,7 @@ last_lba(int filedes)
 
 
 static ssize_t
-read_lastoddsector(int fd, uint64_t lba, void *buffer, size_t count)
+read_lastoddsector(int fd, void *buffer, size_t count)
 {
        int rc;
        struct blkdev_ioctl_param ioctl_param;
@@ -221,7 +221,7 @@ read_lba(int fd, uint64_t lba, void *buffer, size_t bytes)
           one sector, so we don't have to be fancy.
        */
        if (!bytesread && !(lastlba & 1) && lba == lastlba) {
-               bytesread = read_lastoddsector(fd, lba, buffer, bytes);
+               bytesread = read_lastoddsector(fd, buffer, bytes);
        }
        return bytesread;
 }
@@ -601,11 +601,12 @@ fail:
  *
  */
 int
-read_gpt_pt (int fd, struct slice all, struct slice *sp, int ns)
+read_gpt_pt (int fd, __attribute__((unused)) struct slice all,
+            struct slice *sp, unsigned int ns)
 {
        gpt_header *gpt = NULL;
        gpt_entry *ptes = NULL;
-       uint32_t i;
+       unsigned int i;
        int n = 0;
        int last_used_index=-1;
        int sector_size_mul = get_sector_size(fd)/512;
index 7bb54b7..4e1b49a 100644 (file)
@@ -105,7 +105,7 @@ typedef struct _legacy_mbr {
 #define EFI_GPT_PRIMARY_PARTITION_TABLE_LBA 1
 
 /* Functions */
-int read_gpt_pt (int fd, struct slice all, struct slice *sp, int ns);
+int read_gpt_pt (int fd, struct slice all, struct slice *sp, unsigned int ns);
 
 
 #endif
index 52920e4..67edeb8 100644 (file)
 #define likely(x)       __builtin_expect(!!(x), 1)
 #define unlikely(x)     __builtin_expect(!!(x), 0)
 
+#define safe_snprintf(var, size, format, args...)                      \
+       ({                                                              \
+               size_t __size = size;                                   \
+               int __ret;                                              \
+                                                                       \
+               __ret = snprintf(var, __size, format, ##args);          \
+               __ret < 0 || (size_t)__ret >= __size;                   \
+       })
+
 #define safe_sprintf(var, format, args...)     \
-       snprintf(var, sizeof(var), format, ##args) >= sizeof(var)
+               safe_snprintf(var, sizeof(var), format, ##args)
 
 #ifndef BLKSSZGET
 #define BLKSSZGET  _IO(0x12,104)       /* get block device sector size */
@@ -33,11 +42,12 @@ struct slice {
        uint64_t start;
        uint64_t size;
        int container;
-       int major;
-       int minor;
+       unsigned int major;
+       unsigned int minor;
 };
 
-typedef int (ptreader)(int fd, struct slice all, struct slice *sp, int ns);
+typedef int (ptreader)(int fd, struct slice all, struct slice *sp,
+                      unsigned int ns);
 
 extern int force_gpt;
 
@@ -53,7 +63,7 @@ extern ptreader read_ps3_pt;
 
 char *getblock(int fd, unsigned int secnr);
 
-static inline int
+static inline unsigned int
 four2int(unsigned char *p) {
        return p[0] + (p[1]<<8) + (p[2]<<16) + (p[3]<<24);
 }
index 6e82c95..c21ac70 100644 (file)
@@ -5,12 +5,13 @@
 #include "mac.h"
 
 int
-read_mac_pt(int fd, struct slice all, struct slice *sp, int ns) {
+read_mac_pt(int fd, __attribute__((unused)) struct slice all,
+           struct slice *sp, unsigned int ns) {
        struct mac_driver_desc *md;
        struct mac_partition *part;
        unsigned secsize;
        char *data;
-       int blk, blocks_in_map;
+       unsigned int blk, blocks_in_map;
        int n = 0;
 
        md = (struct mac_driver_desc *) getblock(fd, 0);
index 8455097..42551bc 100644 (file)
@@ -34,7 +34,7 @@ struct disklabel {
 static int
 read_disklabel(int fd, struct disklabel *label) {
        unsigned char *data;
-       int i;
+       unsigned int i;
 
        for (i = 0; i < sizeof(struct disklabel) / SECTOR_SIZE; i++) {
                data = (unsigned char *) getblock(fd, i);
@@ -48,7 +48,8 @@ read_disklabel(int fd, struct disklabel *label) {
 }
 
 int
-read_ps3_pt(int fd, struct slice all, struct slice *sp, int ns) {
+read_ps3_pt(int fd, __attribute__((unused)) struct slice all,
+           struct slice *sp, __attribute__((unused)) unsigned int ns) {
        struct disklabel label;
        int n = 0;
        int i;
index e7826c6..c2480b5 100644 (file)
@@ -28,11 +28,11 @@ struct solaris_x86_vtoc {
 };
 
 int
-read_solaris_pt(int fd, struct slice all, struct slice *sp, int ns) {
+read_solaris_pt(int fd, struct slice all, struct slice *sp, unsigned int ns) {
        struct solaris_x86_vtoc *v;
        struct solaris_x86_slice *s;
        unsigned int offset = all.start;
-       int i, n;
+       unsigned int i, n;
        char *bp;
 
        bp = getblock(fd, offset+1);    /* 1 sector suffices */
index 276066d..df630a7 100644 (file)
@@ -59,11 +59,11 @@ sun_verify_checksum (struct sun_disk_label *label)
 }
 
 int
-read_sun_pt(int fd, struct slice all, struct slice *sp, int ns) {
+read_sun_pt(int fd, struct slice all, struct slice *sp, unsigned int ns) {
        struct sun_disk_label *l;
        struct sun_raw_part *s;
        unsigned int offset = all.start, end;
-       int i, j, n;
+       unsigned int i, j, n;
        char *bp;
 
        bp = getblock(fd, offset);
index c7b9786..2f663af 100644 (file)
@@ -48,12 +48,12 @@ struct unixware_disklabel {
 };  /* 408 */
 
 int
-read_unixware_pt(int fd, struct slice all, struct slice *sp, int ns) {
+read_unixware_pt(int fd, struct slice all, struct slice *sp, unsigned int ns) {
        struct unixware_disklabel *l;
        struct unixware_slice *p;
        unsigned int offset = all.start;
        char *bp;
-       int n = 0;
+       unsigned int n = 0;
 
        bp = getblock(fd, offset+29);   /* 1 sector suffices */
        if (bp == NULL)
index 3e813cb..ac85b63 100644 (file)
@@ -131,13 +131,15 @@ DMMP_DLL_LOCAL void _dmmp_path_free(struct dmmp_path *dmmp_p);
 DMMP_DLL_LOCAL void _dmmp_log(struct dmmp_context *ctx, int priority,
                              const char *file, int line,
                              const char *func_name,
-                             const char *format, ...);
+                             const char *format, ...)
+       __attribute__((format(printf, 6, 7)));
 DMMP_DLL_LOCAL void _dmmp_log_err_str(struct dmmp_context *ctx, int rc);
 
 DMMP_DLL_LOCAL void _dmmp_log_stderr(struct dmmp_context *ctx, int priority,
                                     const char *file, int line,
                                     const char *func_name, const char *format,
-                                    va_list args);
+                                    va_list args)
+       __attribute__((format(printf, 6, 0)));
 
 
 #define _dmmp_log_cond(ctx, prio, arg...) \
@@ -164,7 +166,7 @@ DMMP_DLL_LOCAL void _dmmp_log_stderr(struct dmmp_context *ctx, int priority,
        do { \
                if (ptr == NULL) { \
                        rc = DMMP_ERR_NO_MEMORY; \
-                       _error(ctx, dmmp_strerror(rc)); \
+                       _error(ctx, "%s", dmmp_strerror(rc));   \
                        goto goto_out; \
                } \
        } while(0)
index f00bf7e..60b2d96 100644 (file)
@@ -96,7 +96,8 @@ static size_t write_all(int fd, const void *buf, size_t len)
  */
 int __mpath_connect(int nonblocking)
 {
-       int fd, len;
+       int fd;
+       size_t len;
        struct sockaddr_un addr;
        int flags = 0;
 
@@ -172,7 +173,7 @@ int mpath_recv_reply_data(int fd, char *reply, size_t len,
        ret = read_all(fd, reply, len, timeout);
        if (ret < 0)
                return ret;
-       if (ret != len) {
+       if ((size_t)ret != len) {
                errno = EIO;
                return -1;
        }
index 603cfc3..3da7a6c 100644 (file)
@@ -47,7 +47,7 @@ mpath_lib_init (void)
                condlog(0, "Failed to initialize multipath config.");
                return NULL;
        }
-
+       conf->force_sync = 1;
        set_max_fds(conf->max_fds);
 
        return conf;
@@ -854,7 +854,8 @@ int update_map_pr(struct multipath *mpp)
 {
        int noisy=0;
        struct prin_resp *resp;
-       int i, ret, isFound;
+       unsigned int i;
+       int ret, isFound;
 
        if (!get_be64(mpp->reservation_key))
        {
index cf528fe..74b26b0 100644 (file)
 #define MAXRETRY 5
 
 int prin_do_scsi_ioctl(char * dev, int rq_servact, struct prin_resp *resp, int noisy);
-void mpath_format_readkeys(struct prin_resp *pr_buff, int len , int noisy);
-void mpath_format_readfullstatus(struct prin_resp *pr_buff, int len, int noisy);
 int mpath_translate_response (char * dev, struct sg_io_hdr io_hdr,
-                             SenseData_t *Sensedata, int noisy);
+                             SenseData_t *Sensedata);
 void dumpHex(const char* str, int len, int no_ascii);
 int prout_do_scsi_ioctl( char * dev, int rq_servact, int rq_scope,
                unsigned int rq_type, struct prout_param_descriptor *paramp, int noisy);
@@ -115,7 +113,7 @@ retry :
 
        condlog(4, "%s: Duration=%u (ms)", dev, io_hdr.duration);
 
-       status = mpath_translate_response(dev, io_hdr, &Sensedata, noisy);
+       status = mpath_translate_response(dev, io_hdr, &Sensedata);
        condlog(3, "%s: status = %d", dev, status);
 
        if (status == MPATH_PR_SENSE_UNIT_ATTENTION && (retry > 0))
@@ -142,7 +140,7 @@ retry :
 
 uint32_t  format_transportids(struct prout_param_descriptor *paramp)
 {
-       int i = 0, len;
+       unsigned int i = 0, len;
        uint32_t buff_offset = 4;
        memset(paramp->private_buffer, 0, MPATH_MAX_PARAM_LEN);
        for (i=0; i < paramp->num_transportid; i++ )
@@ -181,22 +179,22 @@ uint32_t  format_transportids(struct prout_param_descriptor *paramp)
        return buff_offset;
 }
 
-void mpath_format_readkeys( struct prin_resp *pr_buff, int len, int noisy)
+static void mpath_format_readkeys(struct prin_resp *pr_buff)
 {
        convert_be32_to_cpu(&pr_buff->prin_descriptor.prin_readkeys.prgeneration);
        convert_be32_to_cpu(&pr_buff->prin_descriptor.prin_readkeys.additional_length);
 }
 
-void mpath_format_readresv(struct prin_resp *pr_buff, int len, int noisy)
+static void mpath_format_readresv(struct prin_resp *pr_buff)
 {
 
-       convert_be32_to_cpu(&pr_buff->prin_descriptor.prin_readkeys.prgeneration);
-       convert_be32_to_cpu(&pr_buff->prin_descriptor.prin_readkeys.additional_length);
+       convert_be32_to_cpu(&pr_buff->prin_descriptor.prin_readresv.prgeneration);
+       convert_be32_to_cpu(&pr_buff->prin_descriptor.prin_readresv.additional_length);
 
        return;
 }
 
-void mpath_format_reportcapabilities(struct prin_resp *pr_buff, int len, int noisy)
+static void mpath_format_reportcapabilities(struct prin_resp *pr_buff)
 {
        convert_be16_to_cpu(&pr_buff->prin_descriptor.prin_readcap.length);
        convert_be16_to_cpu(&pr_buff->prin_descriptor.prin_readcap.pr_type_mask);
@@ -204,13 +202,13 @@ void mpath_format_reportcapabilities(struct prin_resp *pr_buff, int len, int noi
        return;
 }
 
-void mpath_format_readfullstatus(struct prin_resp *pr_buff, int len, int noisy)
+static void mpath_format_readfullstatus(struct prin_resp *pr_buff)
 {
-       int num, k, tid_len_len=0;
+       int num;
        uint32_t fdesc_count=0;
        unsigned char *p;
        char  *ppbuff;
-       uint32_t additional_length;
+       uint32_t additional_length, k, tid_len_len = 0;
        char tempbuff[MPATH_MAX_PARAM_LEN];
        struct prin_fulldescr fdesc;
 
@@ -271,8 +269,8 @@ void mpath_format_readfullstatus(struct prin_resp *pr_buff, int len, int noisy)
 void
 decode_transport_id(struct prin_fulldescr *fdesc, unsigned char * p, int length)
 {
-       int num, k;
-       int jump;
+       unsigned int num;
+       int jump, k;
        for (k = 0, jump = 24; k < length; k += jump, p += jump) {
                fdesc->trnptid.format_code = ((p[0] >> 6) & 0x3);
                fdesc->trnptid.protocol_id = (p[0] & 0xf);
@@ -361,7 +359,7 @@ retry :
        condlog(3, "%s: duration = %u (ms)", dev, io_hdr.duration);
        condlog(4, "%s: persistent reservation in: requested %d bytes but got %d bytes)", dev, mx_resp_len, got);
 
-       status = mpath_translate_response(dev, io_hdr, &Sensedata, noisy);
+       status = mpath_translate_response(dev, io_hdr, &Sensedata);
 
        if (status == MPATH_PR_SENSE_UNIT_ATTENTION && (retry > 0))
        {
@@ -389,16 +387,16 @@ retry :
        switch (rq_servact)
        {
                case MPATH_PRIN_RKEY_SA :
-                       mpath_format_readkeys(resp, got, noisy);
+                       mpath_format_readkeys(resp);
                        break;
                case MPATH_PRIN_RRES_SA :
-                       mpath_format_readresv(resp, got, noisy);
+                       mpath_format_readresv(resp);
                        break;
                case MPATH_PRIN_RCAP_SA :
-                       mpath_format_reportcapabilities(resp, got, noisy);
+                       mpath_format_reportcapabilities(resp);
                        break;
                case MPATH_PRIN_RFSTAT_SA :
-                       mpath_format_readfullstatus(resp, got, noisy);
+                       mpath_format_readfullstatus(resp);
        }
 
 out:
@@ -407,7 +405,7 @@ out:
 }
 
 int mpath_translate_response (char * dev, struct sg_io_hdr io_hdr,
-                             SenseData_t *Sensedata, int noisy)
+                             SenseData_t *Sensedata)
 {
        condlog(3, "%s: status driver:%02x host:%02x scsi:%02x", dev,
                        io_hdr.driver_status, io_hdr.host_status ,io_hdr.status);
index a2be42e..e5651e4 100644 (file)
@@ -50,6 +50,11 @@ all: $(LIBS)
 nvme-lib.o: nvme-lib.c nvme-ioctl.c nvme-ioctl.h
        $(CC) $(CFLAGS) -Wno-unused-function -c -o $@ $<
 
+# there are lots of "unused parameters" in dict.c
+# because not all handler / snprint methods nees all parameters
+dict.o:        dict.c
+       $(CC) $(CFLAGS) -Wno-unused-parameter -c -o $@ $<
+
 make_static = $(shell sed '/^static/!s/^\([a-z]\{1,\} \)/static \1/' <$1 >$2)
 
 nvme-ioctl.c: nvme/nvme-ioctl.c
index fd6b7f9..14401ca 100644 (file)
@@ -10,6 +10,7 @@
 #include <stdio.h>
 
 #include "debug.h"
+#include "util.h"
 #include "uxsock.h"
 #include "alias.h"
 #include "file.h"
@@ -37,7 +38,7 @@
  */
 
 int
-valid_alias(char *alias)
+valid_alias(const char *alias)
 {
        if (strchr(alias, '/') != NULL)
                return 0;
@@ -46,30 +47,37 @@ valid_alias(char *alias)
 
 
 static int
-format_devname(char *name, int id, int len, char *prefix)
+format_devname(char *name, int id, int len, const char *prefix)
 {
        int pos;
        int prefix_len = strlen(prefix);
 
-       memset(name,0, len);
+       if (len <= prefix_len + 1 || id <= 0)
+               return -1;
+
+       memset(name, 0, len);
        strcpy(name, prefix);
-       for (pos = len - 1; pos >= prefix_len; pos--) {
+       name[len - 1] = '\0';
+       for (pos = len - 2; pos >= prefix_len; pos--) {
                id--;
                name[pos] = 'a' + id % 26;
                if (id < 26)
                        break;
                id /= 26;
        }
+       if (pos < prefix_len)
+               return -1;
+
        memmove(name + prefix_len, name + pos, len - pos);
-       name[prefix_len + len - pos] = '\0';
-       return (prefix_len + len - pos);
+       return (prefix_len + len - pos - 1);
 }
 
 static int
-scan_devname(char *alias, char *prefix)
+scan_devname(const char *alias, const char *prefix)
 {
-       char *c;
+       const char *c;
        int i, n = 0;
+       static const int last_26 = INT_MAX / 26;
 
        if (!prefix || strncmp(alias, prefix, strlen(prefix)))
                return -1;
@@ -86,9 +94,9 @@ scan_devname(char *alias, char *prefix)
                if (*c < 'a' || *c > 'z')
                        return -1;
                i = *c - 'a';
-               n = ( n * 26 ) + i;
-               if (n < 0)
+               if (n > last_26 || (n == last_26 && i >= INT_MAX % 26))
                        return -1;
+               n = n * 26 + i;
                c++;
                n++;
        }
@@ -96,8 +104,16 @@ scan_devname(char *alias, char *prefix)
        return n;
 }
 
+/*
+ * Returns: 0   if matching entry in WWIDs file found
+ *         -1   if an error occurs
+ *         >0   a free ID that could be used for the WWID at hand
+ * *map_alias is set to a freshly allocated string with the matching alias if
+ * the function returns 0, or to NULL otherwise.
+ */
 static int
-lookup_binding(FILE *f, char *map_wwid, char **map_alias, char *prefix)
+lookup_binding(FILE *f, const char *map_wwid, char **map_alias,
+              const char *prefix)
 {
        char buf[LINE_MAX];
        unsigned int line_nr = 0;
@@ -109,7 +125,8 @@ lookup_binding(FILE *f, char *map_wwid, char **map_alias, char *prefix)
 
        rewind(f);
        while (fgets(buf, LINE_MAX, f)) {
-               char *c, *alias, *wwid;
+               const char *alias, *wwid;
+               char *c;
                int curr_id;
 
                line_nr++;
@@ -120,8 +137,14 @@ lookup_binding(FILE *f, char *map_wwid, char **map_alias, char *prefix)
                if (!alias) /* blank line */
                        continue;
                curr_id = scan_devname(alias, prefix);
-               if (curr_id == id)
-                       id++;
+               if (curr_id == id) {
+                       if (id < INT_MAX)
+                               id++;
+                       else {
+                               id = -1;
+                               break;
+                       }
+               }
                if (curr_id > biggest_id)
                        biggest_id = curr_id;
                if (curr_id > id && curr_id < smallest_bigger_id)
@@ -137,24 +160,30 @@ lookup_binding(FILE *f, char *map_wwid, char **map_alias, char *prefix)
                        condlog(3, "Found matching wwid [%s] in bindings file."
                                " Setting alias to %s", wwid, alias);
                        *map_alias = strdup(alias);
-                       if (*map_alias == NULL)
+                       if (*map_alias == NULL) {
                                condlog(0, "Cannot copy alias from bindings "
-                                       "file : %s", strerror(errno));
+                                       "file: out of memory");
+                               return -1;
+                       }
                        return 0;
                }
        }
-       condlog(3, "No matching wwid [%s] in bindings file.", map_wwid);
+       if (id >= smallest_bigger_id) {
+               if (biggest_id < INT_MAX)
+                       id = biggest_id + 1;
+               else
+                       id = -1;
+       }
        if (id < 0) {
                condlog(0, "no more available user_friendly_names");
-               return 0;
-       }
-       if (id < smallest_bigger_id)
-               return id;
-       return biggest_id + 1;
+               return -1;
+       } else
+               condlog(3, "No matching wwid [%s] in bindings file.", map_wwid);
+       return id;
 }
 
 static int
-rlookup_binding(FILE *f, char *buff, char *map_alias, char *prefix)
+rlookup_binding(FILE *f, char *buff, const char *map_alias)
 {
        char line[LINE_MAX];
        unsigned int line_nr = 0;
@@ -162,7 +191,8 @@ rlookup_binding(FILE *f, char *buff, char *map_alias, char *prefix)
        buff[0] = '\0';
 
        while (fgets(line, LINE_MAX, f)) {
-               char *c, *alias, *wwid;
+               char *c;
+               const char *alias, *wwid;
 
                line_nr++;
                c = strpbrk(line, "#\n\r");
@@ -186,8 +216,7 @@ rlookup_binding(FILE *f, char *buff, char *map_alias, char *prefix)
                if (strcmp(alias, map_alias) == 0){
                        condlog(3, "Found matching alias [%s] in bindings file."
                                "\nSetting wwid to %s", alias, wwid);
-                       strncpy(buff, wwid, WWID_SIZE);
-                       buff[WWID_SIZE - 1] = '\0';
+                       strlcpy(buff, wwid, WWID_SIZE);
                        return 0;
                }
        }
@@ -197,21 +226,28 @@ rlookup_binding(FILE *f, char *buff, char *map_alias, char *prefix)
 }
 
 static char *
-allocate_binding(int fd, char *wwid, int id, char *prefix)
+allocate_binding(int fd, const char *wwid, int id, const char *prefix)
 {
        char buf[LINE_MAX];
        off_t offset;
        char *alias, *c;
        int i;
 
-       if (id < 0) {
-               condlog(0, "Bindings file full. Cannot allocate new binding");
+       if (id <= 0) {
+               condlog(0, "%s: cannot allocate new binding for id %d",
+                       __func__, id);
                return NULL;
        }
 
        i = format_devname(buf, id, LINE_MAX, prefix);
+       if (i == -1)
+               return NULL;
+
        c = buf + i;
-       snprintf(c,LINE_MAX - i, " %s\n", wwid);
+       if (snprintf(c, LINE_MAX - i, " %s\n", wwid) >= LINE_MAX - i) {
+               condlog(1, "%s: line too long for %s\n", __func__, wwid);
+               return NULL;
+       }
        buf[LINE_MAX - 1] = '\0';
 
        offset = lseek(fd, 0, SEEK_END);
@@ -220,7 +256,7 @@ allocate_binding(int fd, char *wwid, int id, char *prefix)
                        strerror(errno));
                return NULL;
        }
-       if (write(fd, buf, strlen(buf)) != strlen(buf)){
+       if (write(fd, buf, strlen(buf)) != (ssize_t)strlen(buf)){
                condlog(0, "Cannot write binding to bindings file : %s",
                        strerror(errno));
                /* clear partial write */
@@ -232,19 +268,18 @@ allocate_binding(int fd, char *wwid, int id, char *prefix)
        c = strchr(buf, ' ');
        if (c)
                *c = '\0';
+
+       condlog(3, "Created new binding [%s] for WWID [%s]", buf, wwid);
        alias = strdup(buf);
        if (alias == NULL)
-               condlog(0, "cannot copy new alias from bindings file : %s",
-                       strerror(errno));
-       else
-               condlog(3, "Created new binding [%s] for WWID [%s]", alias,
-                       wwid);
+               condlog(0, "cannot copy new alias from bindings file: out of memory");
+
        return alias;
 }
 
 char *
-use_existing_alias (char *wwid, char *file, char *alias_old,
-               char *prefix, int bindings_read_only)
+use_existing_alias (const char *wwid, const char *file, const char *alias_old,
+                   const char *prefix, int bindings_read_only)
 {
        char *alias = NULL;
        int id = 0;
@@ -262,10 +297,10 @@ use_existing_alias (char *wwid, char *file, char *alias_old,
                close(fd);
                return NULL;
        }
-       /* lookup the binding. if it exsists, the wwid will be in buff
+       /* lookup the binding. if it exists, the wwid will be in buff
         * either way, id contains the id for the alias
         */
-       rlookup_binding(f, buff, alias_old, prefix);
+       rlookup_binding(f, buff, alias_old);
 
        if (strlen(buff) > 0) {
                /* if buff is our wwid, it's already
@@ -306,12 +341,14 @@ use_existing_alias (char *wwid, char *file, char *alias_old,
        }
 
 out:
+       pthread_cleanup_push(free, alias);
        fclose(f);
+       pthread_cleanup_pop(0);
        return alias;
 }
 
 char *
-get_user_friendly_alias(char *wwid, char *file, char *prefix,
+get_user_friendly_alias(const char *wwid, const char *file, const char *prefix,
                        int bindings_read_only)
 {
        char *alias;
@@ -342,23 +379,24 @@ get_user_friendly_alias(char *wwid, char *file, char *prefix,
                return NULL;
        }
 
+       pthread_cleanup_push(free, alias);
+
        if (fflush(f) != 0) {
                condlog(0, "cannot fflush bindings file stream : %s",
                        strerror(errno));
                free(alias);
-               fclose(f);
-               return NULL;
-       }
-
-       if (!alias && can_write && !bindings_read_only && id)
+               alias = NULL;
+       } else if (can_write && !bindings_read_only && !alias)
                alias = allocate_binding(fd, wwid, id, prefix);
 
        fclose(f);
+
+       pthread_cleanup_pop(0);
        return alias;
 }
 
 int
-get_user_friendly_wwid(char *alias, char *buff, char *file)
+get_user_friendly_wwid(const char *alias, char *buff, const char *file)
 {
        int fd, unused;
        FILE *f;
@@ -380,7 +418,7 @@ get_user_friendly_wwid(char *alias, char *buff, char *file)
                return -1;
        }
 
-       rlookup_binding(f, buff, alias, NULL);
+       rlookup_binding(f, buff, alias);
        if (!strlen(buff)) {
                fclose(f);
                return -1;
index 95473ff..7c4b302 100644 (file)
@@ -7,9 +7,11 @@
 "# alias wwid\n" \
 "#\n"
 
-int valid_alias(char *alias);
-char *get_user_friendly_alias(char *wwid, char *file, char *prefix,
+int valid_alias(const char *alias);
+char *get_user_friendly_alias(const char *wwid, const char *file,
+                             const char *prefix,
                              int bindings_readonly);
-int get_user_friendly_wwid(char *alias, char *buff, char *file);
-char *use_existing_alias (char *wwid, char *file, char *alias_old,
-               char *prefix, int bindings_read_only);
+int get_user_friendly_wwid(const char *alias, char *buff, const char *file);
+char *use_existing_alias (const char *wwid, const char *file,
+                         const char *alias_old,
+                         const char *prefix, int bindings_read_only);
index 5c77146..0a86244 100644 (file)
@@ -9,19 +9,19 @@
 #endif
 
 #if BYTE_ORDER == LITTLE_ENDIAN
-#  define le16_to_cpu(x) (x)
+#  define le16_to_cpu(x) (uint16_t)(x)
 #  define be16_to_cpu(x) bswap_16(x)
-#  define le32_to_cpu(x) (x)
-#  define le64_to_cpu(x) (x)
+#  define le32_to_cpu(x) (uint32_t)(x)
+#  define le64_to_cpu(x) (uint64_t)(x)
 #  define be32_to_cpu(x) bswap_32(x)
 #  define be64_to_cpu(x) bswap_64(x)
 #elif BYTE_ORDER == BIG_ENDIAN
 #  define le16_to_cpu(x) bswap_16(x)
-#  define be16_to_cpu(x) (x)
+#  define be16_to_cpu(x) (uint16_t)(x)
 #  define le32_to_cpu(x) bswap_32(x)
 #  define le64_to_cpu(x) bswap_64(x)
-#  define be32_to_cpu(x) (x)
-#  define be64_to_cpu(x) (x)
+#  define be32_to_cpu(x) (uint32_t)(x)
+#  define be64_to_cpu(x) (uint64_t)(x)
 #else
 #  error unsupported
 #endif
index a08bf41..8d2be8a 100644 (file)
@@ -18,6 +18,7 @@ struct checker_class {
        int (*init)(struct checker *);       /* to allocate the context */
        int (*mp_init)(struct checker *);    /* to allocate the mpcontext */
        void (*free)(struct checker *);      /* to free the context */
+       void (*reset)(void);                 /* to reset the global variables */
        const char **msgtable;
        short msgtable_size;
 };
@@ -66,6 +67,8 @@ void free_checker_class(struct checker_class *c)
        }
        condlog(3, "unloading %s checker", c->name);
        list_del(&c->node);
+       if (c->reset)
+               c->reset();
        if (c->handle) {
                if (dlclose(c->handle) != 0) {
                        condlog(0, "Cannot unload checker %s: %s",
@@ -98,6 +101,16 @@ static struct checker_class *checker_class_lookup(const char *name)
        return NULL;
 }
 
+void reset_checker_classes(void)
+{
+       struct checker_class *c;
+
+       list_for_each_entry(c, &checkers, node) {
+               if (c->reset)
+                       c->reset();
+       }
+}
+
 static struct checker_class *add_checker_class(const char *multipath_dir,
                                               const char *name)
 {
@@ -142,7 +155,9 @@ static struct checker_class *add_checker_class(const char *multipath_dir,
                goto out;
 
        c->mp_init = (int (*)(struct checker *)) dlsym(c->handle, "libcheck_mp_init");
-       /* NULL mp_init is o.k. call dlerror() to clear out any error string */
+       c->reset = (void (*)(void)) dlsym(c->handle, "libcheck_reset");
+       /* These 2 functions can be NULL. call dlerror() to clear out any
+        * error string */
        dlerror();
 
        c->free = (void (*)(struct checker *)) dlsym(c->handle, "libcheck_free");
index 5237e7e..b458118 100644 (file)
@@ -150,6 +150,7 @@ void checker_disable (struct checker *);
 int checker_check (struct checker *, int);
 int checker_is_sync(const struct checker *);
 const char *checker_name (const struct checker *);
+void reset_checker_classes(void);
 /*
  * This returns a string that's best prepended with "$NAME checker",
  * where $NAME is the return value of checker_name().
index ea84374..eaf67b3 100644 (file)
@@ -46,12 +46,12 @@ struct cciss_tur_checker_context {
        void * dummy;
 };
 
-int libcheck_init (struct checker * c)
+int libcheck_init (__attribute__((unused)) struct checker * c)
 {
        return 0;
 }
 
-void libcheck_free (struct checker * c)
+void libcheck_free (__attribute__((unused)) struct checker * c)
 {
        return;
 }
index 1b00b77..503519e 100644 (file)
 
 #include "checkers.h"
 #include "../libmultipath/debug.h"
+#include "../libmultipath/time-util.h"
+
+#define AIO_GROUP_SIZE 1024
+
+/* Note: This checker type relies on the fact that only one checker can be run
+ * at a time, since multiple checkers share the same aio_group, and must be
+ * able to modify other checker's async_reqs. If multple checkers become able
+ * to be run at the same time, this checker will need to add locking, and
+ * probably polling on event fds, to deal with that */
+
+struct aio_group {
+       struct list_head node;
+       int holders;
+       io_context_t ioctx;
+       struct list_head orphans;
+};
+
+struct async_req {
+       struct iocb io;
+       unsigned int blksize;
+       unsigned char * buf;
+       struct list_head node;
+       int state; /* PATH_REMOVED means this is an orphan */
+};
+
+static LIST_HEAD(aio_grp_list);
 
 enum {
        MSG_DIRECTIO_UNKNOWN = CHECKER_FIRST_MSGID,
@@ -37,18 +63,97 @@ const char *libcheck_msgtable[] = {
 struct directio_context {
        int             running;
        int             reset_flags;
-       int             blksize;
-       unsigned char * buf;
-       unsigned char * ptr;
-       io_context_t    ioctx;
-       struct iocb     io;
+       struct aio_group *aio_grp;
+       struct async_req *req;
 };
 
+static struct aio_group *
+add_aio_group(void)
+{
+       struct aio_group *aio_grp;
+
+       aio_grp = malloc(sizeof(struct aio_group));
+       if (!aio_grp)
+               return NULL;
+       memset(aio_grp, 0, sizeof(struct aio_group));
+       INIT_LIST_HEAD(&aio_grp->orphans);
+
+       if (io_setup(AIO_GROUP_SIZE, &aio_grp->ioctx) != 0) {
+               LOG(1, "io_setup failed");
+               if (errno == EAGAIN)
+                       LOG(1, "global number of io events too small. Increase fs.aio-max-nr with sysctl");
+               free(aio_grp);
+               return NULL;
+       }
+       list_add(&aio_grp->node, &aio_grp_list);
+       return aio_grp;
+}
+
+static int
+set_aio_group(struct directio_context *ct)
+{
+       struct aio_group *aio_grp = NULL;
+
+       list_for_each_entry(aio_grp, &aio_grp_list, node)
+               if (aio_grp->holders < AIO_GROUP_SIZE)
+                       goto found;
+       aio_grp = add_aio_group();
+       if (!aio_grp) {
+               ct->aio_grp = NULL;
+               return -1;
+       }
+found:
+       aio_grp->holders++;
+       ct->aio_grp = aio_grp;
+       return 0;
+}
+
+static void
+remove_aio_group(struct aio_group *aio_grp)
+{
+       struct async_req *req, *tmp;
+
+       io_destroy(aio_grp->ioctx);
+       list_for_each_entry_safe(req, tmp, &aio_grp->orphans, node) {
+               list_del(&req->node);
+               free(req->buf);
+               free(req);
+       }
+       list_del(&aio_grp->node);
+       free(aio_grp);
+}
+
+/* If an aio_group is completely full of orphans, then no checkers can
+ * use it, which means that no checkers can clear out the orphans. To
+ * avoid keeping the useless group around, simply remove remove the
+ * group */
+static void
+check_orphaned_group(struct aio_group *aio_grp)
+{
+       int count = 0;
+       struct list_head *item;
+
+       if (aio_grp->holders < AIO_GROUP_SIZE)
+               return;
+       list_for_each(item, &aio_grp->orphans)
+               count++;
+       if (count >= AIO_GROUP_SIZE)
+               remove_aio_group(aio_grp);
+}
+
+void libcheck_reset (void)
+{
+       struct aio_group *aio_grp, *tmp;
+
+       list_for_each_entry_safe(aio_grp, tmp, &aio_grp_list, node)
+               remove_aio_group(aio_grp);
+}
 
 int libcheck_init (struct checker * c)
 {
        unsigned long pgsize = getpagesize();
        struct directio_context * ct;
+       struct async_req *req = NULL;
        long flags;
 
        ct = malloc(sizeof(struct directio_context));
@@ -56,26 +161,30 @@ int libcheck_init (struct checker * c)
                return 1;
        memset(ct, 0, sizeof(struct directio_context));
 
-       if (io_setup(1, &ct->ioctx) != 0) {
-               condlog(1, "io_setup failed");
-               free(ct);
-               return 1;
+       if (set_aio_group(ct) < 0)
+               goto out;
+
+       req = malloc(sizeof(struct async_req));
+       if (!req) {
+               goto out;
        }
+       memset(req, 0, sizeof(struct async_req));
+       INIT_LIST_HEAD(&req->node);
 
-       if (ioctl(c->fd, BLKBSZGET, &ct->blksize) < 0) {
+       if (ioctl(c->fd, BLKBSZGET, &req->blksize) < 0) {
                c->msgid = MSG_DIRECTIO_BLOCKSIZE;
-               ct->blksize = 512;
+               req->blksize = 4096;
        }
-       if (ct->blksize > 4096) {
+       if (req->blksize > 4096) {
                /*
                 * Sanity check for DASD; BSZGET is broken
                 */
-               ct->blksize = 4096;
+               req->blksize = 4096;
        }
-       if (!ct->blksize)
+       if (!req->blksize)
                goto out;
-       ct->buf = (unsigned char *)malloc(ct->blksize + pgsize);
-       if (!ct->buf)
+
+       if (posix_memalign((void **)&req->buf, pgsize, req->blksize) != 0)
                goto out;
 
        flags = fcntl(c->fd, F_GETFL);
@@ -88,17 +197,19 @@ int libcheck_init (struct checker * c)
                ct->reset_flags = 1;
        }
 
-       ct->ptr = (unsigned char *) (((unsigned long)ct->buf + pgsize - 1) &
-                 (~(pgsize - 1)));
-
        /* Successfully initialized, return the context. */
+       ct->req = req;
        c->context = (void *) ct;
        return 0;
 
 out:
-       if (ct->buf)
-               free(ct->buf);
-       io_destroy(ct->ioctx);
+       if (req) {
+               if (req->buf)
+                       free(req->buf);
+               free(req);
+       }
+       if (ct->aio_grp)
+               ct->aio_grp->holders--;
        free(ct);
        return 1;
 }
@@ -106,6 +217,7 @@ out:
 void libcheck_free (struct checker * c)
 {
        struct directio_context * ct = (struct directio_context *)c->context;
+       struct io_event event;
        long flags;
 
        if (!ct)
@@ -121,20 +233,71 @@ void libcheck_free (struct checker * c)
                }
        }
 
-       if (ct->buf)
-               free(ct->buf);
-       io_destroy(ct->ioctx);
+       if (ct->running &&
+           (ct->req->state != PATH_PENDING ||
+            io_cancel(ct->aio_grp->ioctx, &ct->req->io, &event) == 0))
+               ct->running = 0;
+       if (!ct->running) {
+               free(ct->req->buf);
+               free(ct->req);
+               ct->aio_grp->holders--;
+       } else {
+               ct->req->state = PATH_REMOVED;
+               list_add(&ct->req->node, &ct->aio_grp->orphans);
+               check_orphaned_group(ct->aio_grp);
+       }
+
        free(ct);
+       c->context = NULL;
+}
+
+static int
+get_events(struct aio_group *aio_grp, struct timespec *timeout)
+{
+       struct io_event events[128];
+       int i, nr, got_events = 0;
+       struct timespec zero_timeout = {0};
+       struct timespec *timep = timeout;
+
+       do {
+               errno = 0;
+               nr = io_getevents(aio_grp->ioctx, 1, 128, events, timep);
+               got_events |= (nr > 0);
+
+               for (i = 0; i < nr; i++) {
+                       struct async_req *req = container_of(events[i].obj, struct async_req, io);
+
+                       LOG(3, "io finished %lu/%lu", events[i].res,
+                           events[i].res2);
+
+                       /* got an orphaned request */
+                       if (req->state == PATH_REMOVED) {
+                               list_del(&req->node);
+                               free(req->buf);
+                               free(req);
+                               aio_grp->holders--;
+                       } else
+                               req->state = (events[i].res == req->blksize) ?
+                                             PATH_UP : PATH_DOWN;
+               }
+               timep = &zero_timeout;
+       } while (nr == 128); /* assume there are more events and try again */
+
+       if (nr < 0)
+               LOG(3, "async io getevents returned %i (errno=%s)",
+                   nr, strerror(errno));
+
+       return got_events;
 }
 
 static int
 check_state(int fd, struct directio_context *ct, int sync, int timeout_secs)
 {
-       struct timespec timeout = { .tv_nsec = 5 };
-       struct io_event event;
+       struct timespec timeout = { .tv_nsec = 1000 };
        struct stat     sb;
-       int             rc = PATH_UNCHECKED;
+       int             rc;
        long            r;
+       struct timespec currtime, endtime;
 
        if (fstat(fd, &sb) == 0) {
                LOG(4, "called for %x", (unsigned) sb.st_rdev);
@@ -145,50 +308,61 @@ check_state(int fd, struct directio_context *ct, int sync, int timeout_secs)
                timeout.tv_nsec = 0;
        }
 
-       if (!ct->running) {
-               struct iocb *ios[1] = { &ct->io };
+       if (ct->running) {
+               if (ct->req->state != PATH_PENDING) {
+                       ct->running = 0;
+                       return ct->req->state;
+               }
+       } else {
+               struct iocb *ios[1] = { &ct->req->io };
 
                LOG(3, "starting new request");
-               memset(&ct->io, 0, sizeof(struct iocb));
-               io_prep_pread(&ct->io, fd, ct->ptr, ct->blksize, 0);
-               if (io_submit(ct->ioctx, 1, ios) != 1) {
+               memset(&ct->req->io, 0, sizeof(struct iocb));
+               io_prep_pread(&ct->req->io, fd, ct->req->buf,
+                             ct->req->blksize, 0);
+               ct->req->state = PATH_PENDING;
+               if (io_submit(ct->aio_grp->ioctx, 1, ios) != 1) {
                        LOG(3, "io_submit error %i", errno);
                        return PATH_UNCHECKED;
                }
        }
        ct->running++;
 
-       errno = 0;
-       r = io_getevents(ct->ioctx, 1L, 1L, &event, &timeout);
+       get_monotonic_time(&endtime);
+       endtime.tv_sec += timeout.tv_sec;
+       endtime.tv_nsec += timeout.tv_nsec;
+       normalize_timespec(&endtime);
+       while(1) {
+               r = get_events(ct->aio_grp, &timeout);
 
-       if (r < 0 ) {
-               LOG(3, "async io getevents returned %li (errno=%s)", r,
-                   strerror(errno));
-               ct->running = 0;
-               rc = PATH_UNCHECKED;
-       } else if (r < 1L) {
-               if (ct->running > timeout_secs || sync) {
-                       struct iocb *ios[1] = { &ct->io };
-
-                       LOG(3, "abort check on timeout");
-                       r = io_cancel(ct->ioctx, ios[0], &event);
-                       /*
-                        * Only reset ct->running if we really
-                        * could abort the pending I/O
-                        */
-                       if (r)
-                               LOG(3, "io_cancel error %i", errno);
-                       else
-                               ct->running = 0;
-                       rc = PATH_DOWN;
-               } else {
-                       LOG(3, "async io pending");
-                       rc = PATH_PENDING;
-               }
+               if (ct->req->state != PATH_PENDING) {
+                       ct->running = 0;
+                       return ct->req->state;
+               } else if (r == 0 ||
+                          (timeout.tv_sec == 0 && timeout.tv_nsec == 0))
+                       break;
+
+               get_monotonic_time(&currtime);
+               timespecsub(&endtime, &currtime, &timeout);
+               if (timeout.tv_sec < 0)
+                       timeout.tv_sec = timeout.tv_nsec = 0;
+       }
+       if (ct->running > timeout_secs || sync) {
+               struct io_event event;
+
+               LOG(3, "abort check on timeout");
+
+               r = io_cancel(ct->aio_grp->ioctx, &ct->req->io, &event);
+               /*
+                * Only reset ct->running if we really
+                * could abort the pending I/O
+                */
+               if (!r)
+                       ct->running = 0;
+               rc = PATH_DOWN;
        } else {
-               LOG(3, "io finished %lu/%lu", event.res, event.res2);
-               ct->running = 0;
-               rc = (event.res == ct->blksize) ? PATH_UP : PATH_DOWN;
+               LOG(3, "async io pending");
+               rc = PATH_PENDING;
        }
 
        return rc;
index 1a82022..915918c 100644 (file)
@@ -32,19 +32,19 @@ struct sw_checker_context {
        void * dummy;
 };
 
-int libcheck_init (struct checker * c)
+int libcheck_init (__attribute__((unused)) struct checker * c)
 {
        return 0;
 }
 
-void libcheck_free (struct checker * c)
+void libcheck_free (__attribute__((unused)) struct checker * c)
 {
        return;
 }
 
 static int
 do_inq(int sg_fd, int cmddt, int evpd, unsigned int pg_op,
-       void *resp, int mx_resp_len, int noisy, unsigned int timeout)
+       void *resp, int mx_resp_len, unsigned int timeout)
 {
        unsigned char inqCmdBlk[INQUIRY_CMDLEN] =
                { INQUIRY_CMD, 0, 0, 0, 0, 0 };
@@ -130,7 +130,7 @@ 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);
+       int ret = do_inq(c->fd, 0, 1, 0x80, buff, MX_ALLOC_LEN, c->timeout);
 
        if (ret == PATH_WILD) {
                c->msgid = CHECKER_MSGID_UNSUPPORTED;
index 8a3b73e..d924a9f 100644 (file)
@@ -133,7 +133,7 @@ out:
        return 0;
 }
 
-void libcheck_free (struct checker * c)
+void libcheck_free(__attribute__((unused)) struct checker *c)
 {
        return;
 }
index cf79e06..b041f11 100644 (file)
@@ -10,12 +10,12 @@ struct readsector0_checker_context {
        void * dummy;
 };
 
-int libcheck_init (struct checker * c)
+int libcheck_init (__attribute__((unused)) struct checker * c)
 {
        return 0;
 }
 
-void libcheck_free (struct checker * c)
+void libcheck_free (__attribute__((unused)) struct checker * c)
 {
        return;
 }
index 20e3b8b..b4d8768 100644 (file)
@@ -369,9 +369,14 @@ merge_hwe (struct hwentry * dst, struct hwentry * src)
        merge_num(max_sectors_kb);
        merge_num(ghost_delay);
        merge_num(all_tg_pt);
+       merge_num(vpd_vendor_id);
        merge_num(san_path_err_threshold);
        merge_num(san_path_err_forget_rate);
        merge_num(san_path_err_recovery_time);
+       merge_num(marginal_path_err_sample_time);
+       merge_num(marginal_path_err_rate_threshold);
+       merge_num(marginal_path_err_recheck_gap_time);
+       merge_num(marginal_path_double_failed_time);
 
        snprintf(id, sizeof(id), "%s/%s", dst->vendor, dst->product);
        reconcile_features_with_options(id, &dst->features,
@@ -397,6 +402,7 @@ merge_mpe(struct mpentry *dst, struct mpentry *src)
        if (dst->prkey_source == PRKEY_SOURCE_NONE &&
            src->prkey_source != PRKEY_SOURCE_NONE) {
                dst->prkey_source = src->prkey_source;
+               dst->sa_flags = src->sa_flags;
                memcpy(&dst->reservation_key, &src->reservation_key,
                       sizeof(dst->reservation_key));
        }
@@ -413,6 +419,9 @@ merge_mpe(struct mpentry *dst, struct mpentry *src)
        merge_num(deferred_remove);
        merge_num(delay_watch_checks);
        merge_num(delay_wait_checks);
+       merge_num(san_path_err_threshold);
+       merge_num(san_path_err_forget_rate);
+       merge_num(san_path_err_recovery_time);
        merge_num(marginal_path_err_sample_time);
        merge_num(marginal_path_err_rate_threshold);
        merge_num(marginal_path_err_recheck_gap_time);
@@ -509,6 +518,7 @@ store_hwe (vector hwtable, struct hwentry * dhwe)
        hwe->detect_prio = dhwe->detect_prio;
        hwe->detect_checker = dhwe->detect_checker;
        hwe->ghost_delay = dhwe->ghost_delay;
+       hwe->vpd_vendor_id = dhwe->vpd_vendor_id;
 
        if (dhwe->bl_product && !(hwe->bl_product = set_param_str(dhwe->bl_product)))
                goto out;
@@ -644,7 +654,7 @@ free_config (struct config * conf)
 /* 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)
+process_config_dir(struct config *conf, char *dir)
 {
        struct dirent **namelist;
        struct scandir_result sr;
@@ -671,8 +681,11 @@ process_config_dir(struct config *conf, vector keywords, char *dir)
        sr.n = n;
        pthread_cleanup_push_cast(free_scandir_result, &sr);
        for (i = 0; i < n; i++) {
-               if (!strstr(namelist[i]->d_name, ".conf"))
+               char *ext = strrchr(namelist[i]->d_name, '.');
+
+               if (!ext || strcmp(ext, ".conf"))
                        continue;
+
                old_hwtable_size = VECTOR_SIZE(conf->hwtable);
                snprintf(path, LINE_MAX, "%s/%s", dir, namelist[i]->d_name);
                path[LINE_MAX-1] = '\0';
@@ -683,6 +696,27 @@ process_config_dir(struct config *conf, vector keywords, char *dir)
        pthread_cleanup_pop(1);
 }
 
+static void set_max_checkint_from_watchdog(struct config *conf)
+{
+#ifdef USE_SYSTEMD
+       char *envp = getenv("WATCHDOG_USEC");
+       unsigned long checkint;
+
+       if (envp && sscanf(envp, "%lu", &checkint) == 1) {
+               /* Value is in microseconds */
+               checkint /= 1000000;
+               if (checkint < 1 || checkint > UINT_MAX) {
+                       condlog(1, "invalid value for WatchdogSec: \"%s\"", envp);
+                       return;
+               }
+               if (conf->max_checkint == 0 || conf->max_checkint > checkint)
+                       conf->max_checkint = checkint;
+               condlog(3, "enabling watchdog, interval %ld", checkint);
+               conf->use_watchdog = true;
+       }
+#endif
+}
+
 struct config *
 load_config (char * file)
 {
@@ -703,7 +737,8 @@ load_config (char * file)
        conf->multipath_dir = set_default(DEFAULT_MULTIPATHDIR);
        conf->attribute_flags = 0;
        conf->reassign_maps = DEFAULT_REASSIGN_MAPS;
-       conf->checkint = DEFAULT_CHECKINT;
+       conf->checkint = CHECKINT_UNDEF;
+       conf->use_watchdog = false;
        conf->max_checkint = 0;
        conf->force_sync = DEFAULT_FORCE_SYNC;
        conf->partition_delim = (default_partition_delim != NULL ?
@@ -749,13 +784,25 @@ load_config (char * file)
        if (conf->config_dir == NULL)
                conf->config_dir = set_default(DEFAULT_CONFIG_DIR);
        if (conf->config_dir && conf->config_dir[0] != '\0')
-               process_config_dir(conf, conf->keywords, conf->config_dir);
+               process_config_dir(conf, conf->config_dir);
 
        /*
         * fill the voids left in the config file
         */
-       if (conf->max_checkint == 0)
-               conf->max_checkint = MAX_CHECKINT(conf->checkint);
+       set_max_checkint_from_watchdog(conf);
+       if (conf->max_checkint == 0) {
+               if (conf->checkint == CHECKINT_UNDEF)
+                       conf->checkint = DEFAULT_CHECKINT;
+               conf->max_checkint = (conf->checkint < UINT_MAX / 4 ?
+                                     conf->checkint * 4 : UINT_MAX);
+       } else if (conf->checkint == CHECKINT_UNDEF)
+               conf->checkint = (conf->max_checkint >= 4 ?
+                                 conf->max_checkint / 4 : 1);
+       else if (conf->checkint > conf->max_checkint)
+               conf->checkint = conf->max_checkint;
+       condlog(3, "polling interval: %d, max: %d",
+               conf->checkint, conf->max_checkint);
+
        if (conf->blist_devnode == NULL) {
                conf->blist_devnode = vector_alloc();
 
index ffec310..ceecff2 100644 (file)
@@ -87,6 +87,7 @@ struct hwentry {
        int max_sectors_kb;
        int ghost_delay;
        int all_tg_pt;
+       int vpd_vendor_id;
        char * bl_product;
 };
 
@@ -137,8 +138,9 @@ struct config {
        int pgpolicy;
        int minio;
        int minio_rq;
-       int checkint;
-       int max_checkint;
+       unsigned int checkint;
+       unsigned int max_checkint;
+       bool use_watchdog;
        int pgfailback;
        int remove;
        int rr_weight;
@@ -188,6 +190,7 @@ struct config {
        int find_multipaths_timeout;
        int marginal_pathgroups;
        unsigned int version[3];
+       unsigned int sequence_nr;
 
        char * multipath_dir;
        char * selector;
index 5ac7d90..c95848a 100644 (file)
@@ -401,7 +401,6 @@ int setup_map(struct multipath *mpp, char *params, int params_size,
                        condlog(2, "%s: setting up map with %d/%d path checkers pending",
                                mpp->alias, n_pending, n_paths);
        }
-       mpp->nr_active = pathcount(mpp, PATH_UP) + pathcount(mpp, PATH_GHOST);
 
        /*
         * ponders each path group and determine highest prio pg
@@ -934,8 +933,8 @@ int domap(struct multipath *mpp, char *params, int is_daemon)
                }
 
                sysfs_set_max_sectors_kb(mpp, 0);
-               if (is_daemon && mpp->ghost_delay > 0 && mpp->nr_active &&
-                   pathcount(mpp, PATH_GHOST) == mpp->nr_active)
+               if (is_daemon && mpp->ghost_delay > 0 && count_active_paths(mpp) &&
+                   pathcount(mpp, PATH_UP) == 0)
                        mpp->ghost_delay_tick = mpp->ghost_delay;
                r = dm_addmap_create(mpp, params);
 
index 4dfe007..e5ee6af 100644 (file)
@@ -1,5 +1,7 @@
 #ifndef _DEFAULTS_H
 #define _DEFAULTS_H
+#include <limits.h>
+
 /*
  * If you add or modify a value also update multipath/multipath.conf.5
  * and the TEMPLATE in libmultipath/hwtable.c
 /* Enable all foreign libraries by default */
 #define DEFAULT_ENABLE_FOREIGN ""
 
-#define CHECKINT_UNDEF         (~0U)
+#define CHECKINT_UNDEF         UINT_MAX
 #define DEFAULT_CHECKINT       5
-#define MAX_CHECKINT(a)                (a << 2)
 
-#define MAX_DEV_LOSS_TMO       0x7FFFFFFF
+#define MAX_DEV_LOSS_TMO       UINT_MAX
 #define DEFAULT_PIDFILE                "/" RUN_DIR "/multipathd.pid"
 #define DEFAULT_SOCKET         "/org/kernel/linux/storage/multipathd"
 #define DEFAULT_CONFIGFILE     "/etc/multipath.conf"
index 0f0c3a3..bed8ddc 100644 (file)
@@ -59,7 +59,7 @@ void dm_udev_set_sync_support(int c)
 
 #endif
 
-static void
+__attribute__((format(printf, 4, 5))) static void
 dm_write_log (int level, const char *file, int line, const char *f, ...)
 {
        va_list ap;
@@ -403,7 +403,7 @@ static uint16_t build_udev_flags(const struct multipath *mpp, int reload)
        /* DM_UDEV_DISABLE_LIBRARY_FALLBACK is added in dm_addmap */
        return  (mpp->skip_kpartx == SKIP_KPARTX_ON ?
                 MPATH_UDEV_NO_KPARTX_FLAG : 0) |
-               ((mpp->nr_active == 0 || mpp->ghost_delay_tick > 0)?
+               ((count_active_paths(mpp) == 0 || mpp->ghost_delay_tick > 0) ?
                 MPATH_UDEV_NO_PATHS_FLAG : 0) |
                (reload && !mpp->force_udev_reload ?
                 MPATH_UDEV_RELOAD_FLAG : 0);
@@ -812,7 +812,8 @@ dm_get_major_minor(const char *name, int *major, int *minor)
 }
 
 static int
-has_partmap(const char *name, void *data)
+has_partmap(const char *name __attribute__((unused)),
+           void *data __attribute__((unused)))
 {
        return 1;
 }
@@ -1308,7 +1309,7 @@ dm_remove_partmaps (const char * mapname, int need_sync, int deferred_remove)
 #ifdef LIBDM_API_DEFERRED
 
 static int
-cancel_remove_partmap (const char *name, void *unused)
+cancel_remove_partmap (const char *name, void *unused __attribute__((unused)))
 {
        if (dm_get_opencount(name))
                dm_cancel_remove_partmaps(name);
@@ -1560,7 +1561,8 @@ int dm_reassign(const char *mapname)
        struct dm_task *dmt;
        struct dm_info info;
        char dev_t[32], dm_dep[32];
-       int r = 0, i;
+       int r = 0;
+       unsigned int i;
 
        if (dm_dev_t(mapname, &dev_t[0], 32)) {
                condlog(3, "%s: failed to get device number", mapname);
index 2b046e1..3e25e74 100644 (file)
@@ -31,16 +31,58 @@ static int
 set_int(vector strvec, void *ptr)
 {
        int *int_ptr = (int *)ptr;
-       char * buff;
+       char *buff, *eptr;
+       long res;
+       int rc;
 
        buff = set_value(strvec);
        if (!buff)
                return 1;
 
-       *int_ptr = atoi(buff);
+       res = strtol(buff, &eptr, 10);
+       if (eptr > buff)
+               while (isspace(*eptr))
+                       eptr++;
+       if (*buff == '\0' || *eptr != '\0' || res > INT_MAX || res < INT_MIN) {
+               condlog(1, "%s: invalid value for %s: \"%s\"",
+                       __func__, (char*)VECTOR_SLOT(strvec, 0), buff);
+               rc = 1;
+       } else {
+               rc = 0;
+               *int_ptr = res;
+       }
 
        FREE(buff);
-       return 0;
+       return rc;
+}
+
+static int
+set_uint(vector strvec, void *ptr)
+{
+       unsigned int *uint_ptr = (unsigned int *)ptr;
+       char *buff, *eptr;
+       long res;
+       int rc;
+
+       buff = set_value(strvec);
+       if (!buff)
+               return 1;
+
+       res = strtol(buff, &eptr, 10);
+       if (eptr > buff)
+               while (isspace(*eptr))
+                       eptr++;
+       if (*buff == '\0' || *eptr != '\0' || res < 0 || res > UINT_MAX) {
+               condlog(1, "%s: invalid value for %s: \"%s\"",
+                       __func__, (char*)VECTOR_SLOT(strvec, 0), buff);
+               rc = 1;
+       } else {
+               rc = 0;
+               *uint_ptr = res;
+       }
+
+       FREE(buff);
+       return rc;
 }
 
 static int
@@ -272,7 +314,7 @@ snprint_mp_ ## option (struct config *conf, char * buff, int len,   \
 
 static int checkint_handler(struct config *conf, vector strvec)
 {
-       int rc = set_int(strvec, &conf->checkint);
+       int rc = set_uint(strvec, &conf->checkint);
 
        if (rc)
                return rc;
@@ -283,7 +325,7 @@ static int checkint_handler(struct config *conf, vector strvec)
 
 declare_def_snprint(checkint, print_int)
 
-declare_def_handler(max_checkint, set_int)
+declare_def_handler(max_checkint, set_uint)
 declare_def_snprint(max_checkint, print_int)
 
 declare_def_handler(verbosity, set_int)
@@ -1057,7 +1099,7 @@ declare_mp_handler(pgfailback, set_pgfailback)
 declare_mp_snprint(pgfailback, print_pgfailback)
 
 static int
-set_no_path_retry(vector strvec, void *ptr)
+no_path_retry_helper(vector strvec, void *ptr)
 {
        int *int_ptr = (int *)ptr;
        char * buff;
@@ -1092,13 +1134,13 @@ print_no_path_retry(char * buff, int len, long v)
        }
 }
 
-declare_def_handler(no_path_retry, set_no_path_retry)
+declare_def_handler(no_path_retry, no_path_retry_helper)
 declare_def_snprint(no_path_retry, print_no_path_retry)
-declare_ovr_handler(no_path_retry, set_no_path_retry)
+declare_ovr_handler(no_path_retry, no_path_retry_helper)
 declare_ovr_snprint(no_path_retry, print_no_path_retry)
-declare_hw_handler(no_path_retry, set_no_path_retry)
+declare_hw_handler(no_path_retry, no_path_retry_helper)
 declare_hw_snprint(no_path_retry, print_no_path_retry)
-declare_mp_handler(no_path_retry, set_no_path_retry)
+declare_mp_handler(no_path_retry, no_path_retry_helper)
 declare_mp_snprint(no_path_retry, print_no_path_retry)
 
 static int
@@ -1366,6 +1408,43 @@ def_uxsock_timeout_handler(struct config *conf, vector strvec)
        return 0;
 }
 
+static int
+hw_vpd_vendor_handler(struct config *conf, vector strvec)
+{
+       int i;
+       char *buff;
+
+       struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable);
+       if (!hwe)
+               return 1;
+
+       buff = set_value(strvec);
+       if (!buff)
+               return 1;
+       for (i = 0; i < VPD_VP_ARRAY_SIZE; i++) {
+               if (strcmp(buff, vpd_vendor_pages[i].name) == 0) {
+                       hwe->vpd_vendor_id = i;
+                       goto out;
+               }
+       }
+       hwe->vpd_vendor_id = 0;
+out:
+       FREE(buff);
+       return 0;
+}
+
+static int
+snprint_hw_vpd_vendor(struct config *conf, char * buff, int len,
+                     const void * data)
+{
+       const struct hwentry * hwe = (const struct hwentry *)data;
+
+       if (hwe->vpd_vendor_id > 0 && hwe->vpd_vendor_id < VPD_VP_ARRAY_SIZE)
+               return snprintf(buff, len, "%s",
+                               vpd_vendor_pages[hwe->vpd_vendor_id].name);
+       return 0;
+}
+
 /*
  * blacklist block handlers
  */
@@ -1806,6 +1885,7 @@ init_keywords(vector keywords)
        install_keyword("max_sectors_kb", &hw_max_sectors_kb_handler, &snprint_hw_max_sectors_kb);
        install_keyword("ghost_delay", &hw_ghost_delay_handler, &snprint_hw_ghost_delay);
        install_keyword("all_tg_pt", &hw_all_tg_pt_handler, &snprint_hw_all_tg_pt);
+       install_keyword("vpd_vendor", &hw_vpd_vendor_handler, &snprint_hw_vpd_vendor);
        install_sublevel_end();
 
        install_keyword_root("overrides", &overrides_handler);
index 72f455e..ee3290c 100644 (file)
 #include "prioritizers/alua_rtpg.h"
 #include "foreign.h"
 
+struct vpd_vendor_page vpd_vendor_pages[VPD_VP_ARRAY_SIZE] = {
+       [VPD_VP_UNDEF]  = { 0x00, "undef" },
+       [VPD_VP_HP3PAR] = { 0xc0, "hp3par" },
+};
+
 int
 alloc_path_with_pathinfo (struct config *conf, struct udev_device *udevice,
                          const char *wwid, int flag, struct path **pp_ptr)
@@ -140,27 +145,61 @@ path_discover (vector pathvec, struct config * conf,
        return pathinfo(pp, conf, flag);
 }
 
+static void cleanup_udev_enumerate_ptr(void *arg)
+{
+       struct udev_enumerate *ue;
+
+       if (!arg)
+               return;
+       ue = *((struct udev_enumerate**) arg);
+       if (ue)
+               (void)udev_enumerate_unref(ue);
+}
+
+static void cleanup_udev_device_ptr(void *arg)
+{
+       struct udev_device *ud;
+
+       if (!arg)
+               return;
+       ud = *((struct udev_device**) arg);
+       if (ud)
+               (void)udev_device_unref(ud);
+}
+
 int
 path_discovery (vector pathvec, int flag)
 {
-       struct udev_enumerate *udev_iter;
+       struct udev_enumerate *udev_iter = NULL;
        struct udev_list_entry *entry;
-       struct udev_device *udevice;
+       struct udev_device *udevice = NULL;
        struct config *conf;
-       const char *devpath;
-       int num_paths = 0, total_paths = 0;
+       int num_paths = 0, total_paths = 0, ret;
+
+       pthread_cleanup_push(cleanup_udev_enumerate_ptr, &udev_iter);
+       pthread_cleanup_push(cleanup_udev_device_ptr, &udevice);
+       conf = get_multipath_config();
+       pthread_cleanup_push(put_multipath_config, conf);
 
        udev_iter = udev_enumerate_new(udev);
-       if (!udev_iter)
-               return -ENOMEM;
+       if (!udev_iter) {
+               ret = -ENOMEM;
+               goto out;
+       }
 
-       udev_enumerate_add_match_subsystem(udev_iter, "block");
-       udev_enumerate_add_match_is_initialized(udev_iter);
-       udev_enumerate_scan_devices(udev_iter);
+       if (udev_enumerate_add_match_subsystem(udev_iter, "block") < 0 ||
+           udev_enumerate_add_match_is_initialized(udev_iter) < 0 ||
+           udev_enumerate_scan_devices(udev_iter) < 0) {
+               condlog(1, "%s: error setting up udev_enumerate: %m", __func__);
+               ret = -1;
+               goto out;
+       }
 
        udev_list_entry_foreach(entry,
                                udev_enumerate_get_list_entry(udev_iter)) {
                const char *devtype;
+               const char *devpath;
+
                devpath = udev_list_entry_get_name(entry);
                condlog(4, "Discover device %s", devpath);
                udevice = udev_device_new_from_syspath(udev, devpath);
@@ -171,25 +210,26 @@ path_discovery (vector pathvec, int flag)
                devtype = udev_device_get_devtype(udevice);
                if(devtype && !strncmp(devtype, "disk", 4)) {
                        total_paths++;
-                       conf = get_multipath_config();
-                       pthread_cleanup_push(put_multipath_config, conf);
                        if (path_discover(pathvec, conf,
                                          udevice, flag) == PATHINFO_OK)
                                num_paths++;
-                       pthread_cleanup_pop(1);
                }
-               udev_device_unref(udevice);
+               udevice = udev_device_unref(udevice);
        }
-       udev_enumerate_unref(udev_iter);
+       ret = total_paths - num_paths;
        condlog(4, "Discovered %d/%d paths", num_paths, total_paths);
-       return (total_paths - num_paths);
+out:
+       pthread_cleanup_pop(1);
+       pthread_cleanup_pop(1);
+       pthread_cleanup_pop(1);
+       return ret;
 }
 
 #define declare_sysfs_get_str(fname)                                   \
 ssize_t                                                                        \
 sysfs_get_##fname (struct udev_device * udev, char * buff, size_t len) \
 {                                                                      \
-       int l;                                                  \
+       size_t l;                                                       \
        const char * attr;                                              \
        const char * devname;                                           \
                                                                        \
@@ -589,9 +629,10 @@ sysfs_set_rport_tmo(struct multipath *mpp, struct path *pp)
            mpp->fast_io_fail != MP_FAST_IO_FAIL_ZERO &&
            mpp->fast_io_fail != MP_FAST_IO_FAIL_OFF) {
                /* Check if we need to temporarily increase dev_loss_tmo */
-               if (mpp->fast_io_fail >= tmo) {
+               if ((unsigned int)mpp->fast_io_fail >= tmo) {
                        /* Increase dev_loss_tmo temporarily */
-                       snprintf(value, 16, "%u", mpp->fast_io_fail + 1);
+                       snprintf(value, sizeof(value), "%u",
+                                (unsigned int)mpp->fast_io_fail + 1);
                        ret = sysfs_attr_set_value(rport_dev, "dev_loss_tmo",
                                                   value, strlen(value));
                        if (ret <= 0) {
@@ -718,14 +759,15 @@ sysfs_set_nexus_loss_tmo(struct multipath *mpp, struct path *pp)
 }
 
 int
-sysfs_set_scsi_tmo (struct multipath *mpp, int checkint)
+sysfs_set_scsi_tmo (struct multipath *mpp, unsigned int checkint)
 {
        struct path *pp;
        int i;
-       int dev_loss_tmo = mpp->dev_loss;
+       unsigned int dev_loss_tmo = mpp->dev_loss;
 
        if (mpp->no_path_retry > 0) {
-               uint64_t no_path_retry_tmo = (uint64_t)mpp->no_path_retry * checkint;
+               uint64_t no_path_retry_tmo =
+                       (uint64_t)mpp->no_path_retry * checkint;
 
                if (no_path_retry_tmo > MAX_DEV_LOSS_TMO)
                        no_path_retry_tmo = MAX_DEV_LOSS_TMO;
@@ -739,7 +781,8 @@ sysfs_set_scsi_tmo (struct multipath *mpp, int checkint)
                        mpp->alias, dev_loss_tmo);
        }
        mpp->dev_loss = dev_loss_tmo;
-       if (mpp->dev_loss && mpp->fast_io_fail >= (int)mpp->dev_loss) {
+       if (mpp->dev_loss && mpp->fast_io_fail > 0 &&
+           (unsigned int)mpp->fast_io_fail >= mpp->dev_loss) {
                condlog(3, "%s: turning off fast_io_fail (%d is not smaller than dev_loss_tmo)",
                        mpp->alias, mpp->fast_io_fail);
                mpp->fast_io_fail = MP_FAST_IO_FAIL_OFF;
@@ -833,6 +876,10 @@ get_serial (char * str, int maxlen, int fd)
        return 1;
 }
 
+/*
+ * Side effect: sets pp->tpgs if it could be determined.
+ * If ALUA calls fail because paths are unreachable, pp->tpgs remains unchanged.
+ */
 static void
 detect_alua(struct path * pp)
 {
@@ -843,12 +890,28 @@ detect_alua(struct path * pp)
        if (sysfs_get_timeout(pp, &timeout) <= 0)
                timeout = DEF_TIMEOUT;
 
-       if ((tpgs = get_target_port_group_support(pp, timeout)) <= 0) {
+       tpgs = get_target_port_group_support(pp, timeout);
+       if (tpgs == -RTPG_INQUIRY_FAILED)
+               return;
+       else if (tpgs <= 0) {
                pp->tpgs = TPGS_NONE;
                return;
        }
+
+       if (pp->fd == -1 || pp->offline)
+               return;
+
        ret = get_target_port_group(pp, timeout);
        if (ret < 0 || get_asymmetric_access_state(pp, ret, timeout) < 0) {
+               int state;
+
+               if (ret == -RTPG_INQUIRY_FAILED)
+                       return;
+
+               state = path_offline(pp);
+               if (state == PATH_DOWN || state == PATH_PENDING)
+                       return;
+
                pp->tpgs = TPGS_NONE;
                return;
        }
@@ -870,6 +933,7 @@ static int
 sgio_get_vpd (unsigned char * buff, int maxlen, int fd, int pg)
 {
        int len = DEFAULT_SGIO_LEN;
+       int rlen;
 
        if (fd < 0) {
                errno = EBADF;
@@ -877,12 +941,11 @@ sgio_get_vpd (unsigned char * buff, int maxlen, int fd, int pg)
        }
 retry:
        if (0 == do_inq(fd, 0, 1, pg, buff, len)) {
-               len = get_unaligned_be16(&buff[2]) + 4;
-               if (len >= maxlen)
-                       return len;
-               if (len > DEFAULT_SGIO_LEN)
-                       goto retry;
-               return len;
+               rlen = get_unaligned_be16(&buff[2]) + 4;
+               if (rlen <= len || len >= maxlen)
+                       return rlen;
+               len = (rlen < maxlen)? rlen : maxlen;
+               goto retry;
        }
        return -1;
 }
@@ -907,7 +970,10 @@ get_geometry(struct path *pp)
 static int
 parse_vpd_pg80(const unsigned char *in, char *out, size_t out_len)
 {
-       int len = get_unaligned_be16(&in[2]);
+       size_t len = get_unaligned_be16(&in[2]);
+
+       if (out_len == 0)
+               return 0;
 
        /*
         * Strip leading and trailing whitespace
@@ -920,8 +986,8 @@ parse_vpd_pg80(const unsigned char *in, char *out, size_t out_len)
        }
 
        if (len >= out_len) {
-               condlog(2, "vpd pg80 overflow, %d/%d bytes required",
-                       len + 1, (int)out_len);
+               condlog(2, "vpd pg80 overflow, %lu/%lu bytes required",
+                       len + 1, out_len);
                len = out_len - 1;
        }
        if (len > 0) {
@@ -937,7 +1003,8 @@ parse_vpd_pg83(const unsigned char *in, size_t in_len,
 {
        const unsigned char *d;
        const unsigned char *vpd = NULL;
-       int len = -ENODATA, vpd_type, vpd_len, prio = -1, i, naa_prio;
+       size_t len, vpd_len, i;
+       int vpd_type, prio = -1, naa_prio;
 
        d = in + 4;
        while (d < in + in_len) {
@@ -1004,106 +1071,138 @@ parse_vpd_pg83(const unsigned char *in, size_t in_len,
                }
                d += d[3] + 4;
        }
-       if (prio > 0) {
-               vpd_type = vpd[1] & 0xf;
-               vpd_len = vpd[3];
+
+       if (prio <= 0)
+               return -ENODATA;
+       /* Need space at least for one digit */
+       else if (out_len <= 1)
+               return 0;
+
+       len = 0;
+       vpd_type = vpd[1] & 0xf;
+       vpd_len = vpd[3];
+       vpd += 4;
+       if (vpd_type == 0x2 || vpd_type == 0x3) {
+               size_t i;
+
+               len = sprintf(out, "%d", vpd_type);
+               if (2 * vpd_len >= out_len - len) {
+                       condlog(1, "%s: WWID overflow, type %d, %lu/%lu bytes required",
+                               __func__, vpd_type,
+                               2 * vpd_len + len + 1, out_len);
+                       vpd_len = (out_len - len - 1) / 2;
+               }
+               for (i = 0; i < vpd_len; i++)
+                       len += sprintf(out + len,
+                                      "%02x", vpd[i]);
+       } else if (vpd_type == 0x8 && vpd_len < 4) {
+               condlog(1, "%s: VPD length %lu too small for designator type 8",
+                       __func__, vpd_len);
+               return -EINVAL;
+       } else if (vpd_type == 0x8) {
+               if (!memcmp("eui.", vpd, 4))
+                       out[0] =  '2';
+               else if (!memcmp("naa.", vpd, 4))
+                       out[0] = '3';
+               else
+                       out[0] = '8';
+
                vpd += 4;
-               if (vpd_type == 0x2 || vpd_type == 0x3) {
-                       int i;
-
-                       assert(out_len >= 2);
-                       len = sprintf(out, "%d", vpd_type);
-                       if (2 * vpd_len >= out_len - len) {
-                               condlog(1, "%s: WWID overflow, type %d, %d/%lu bytes required",
-                                       __func__, vpd_type,
-                                       2 * vpd_len + len + 1, out_len);
-                               vpd_len = (out_len - len - 1) / 2;
-                       }
-                       for (i = 0; i < vpd_len; i++)
-                               len += sprintf(out + len,
-                                              "%02x", vpd[i]);
-               } else if (vpd_type == 0x8) {
-                       if (!memcmp("eui.", vpd, 4))
-                               out[0] =  '2';
-                       else if (!memcmp("naa.", vpd, 4))
-                               out[0] = '3';
-                       else
-                               out[0] = '8';
-
-                       vpd += 4;
-                       len = vpd_len - 4;
-                       while (len > 2 && vpd[len - 2] == '\0')
-                               --len;
-                       if (len > out_len - 1) {
-                               condlog(1, "%s: WWID overflow, type 8/%c, %d/%lu bytes required",
-                                       __func__, out[0], len + 1, out_len);
-                               len = out_len - 1;
-                       }
+               len = vpd_len - 4;
+               while (len > 2 && vpd[len - 2] == '\0')
+                       --len;
+               if (len > out_len - 1) {
+                       condlog(1, "%s: WWID overflow, type 8/%c, %lu/%lu bytes required",
+                               __func__, out[0], len + 1, out_len);
+                       len = out_len - 1;
+               }
 
-                       if (out[0] == '8')
-                               for (i = 0; i < len; ++i)
-                                       out[1 + i] = vpd[i];
-                       else
-                               for (i = 0; i < len; ++i)
-                                       out[1 + i] = tolower(vpd[i]);
+               if (out[0] == '8')
+                       for (i = 0; i < len; ++i)
+                               out[1 + i] = vpd[i];
+               else
+                       for (i = 0; i < len; ++i)
+                               out[1 + i] = tolower(vpd[i]);
 
-                       /* designator should be 0-terminated, but let's make sure */
-                       out[len] = '\0';
+               /* designator should be 0-terminated, but let's make sure */
+               out[len] = '\0';
 
-               } else if (vpd_type == 0x1) {
-                       const unsigned char *p;
-                       int p_len;
-
-                       out[0] = '1';
-                       len = 1;
-                       p = vpd;
-                       while ((p = memchr(vpd, ' ', vpd_len))) {
-                               p_len = p - vpd;
-                               if (len + p_len > out_len - 1) {
-                                       condlog(1, "%s: WWID overflow, type 1, %d/%lu bytes required",
-                                               __func__, len + p_len, out_len);
-                                       p_len = out_len - len - 1;
-                               }
-                               memcpy(out + len, vpd, p_len);
-                               len += p_len;
-                               if (len >= out_len - 1) {
-                                       out[len] = '\0';
-                                       break;
-                               }
-                               out[len] = '_';
-                               len ++;
-                               if (len >= out_len - 1) {
-                                       out[len] = '\0';
-                                       break;
-                               }
-                               vpd = p;
-                               vpd_len -= p_len;
-                               while (vpd && *vpd == ' ') {
-                                       vpd++;
-                                       vpd_len --;
-                               }
+       } else if (vpd_type == 0x1) {
+               const unsigned char *p;
+               size_t p_len;
+
+               out[0] = '1';
+               len = 1;
+               while ((p = memchr(vpd, ' ', vpd_len))) {
+                       p_len = p - vpd;
+                       if (len + p_len > out_len - 1) {
+                               condlog(1, "%s: WWID overflow, type 1, %lu/%lu bytes required",
+                                       __func__, len + p_len, out_len);
+                               p_len = out_len - len - 1;
+                       }
+                       memcpy(out + len, vpd, p_len);
+                       len += p_len;
+                       if (len >= out_len - 1) {
+                               out[len] = '\0';
+                               break;
                        }
-                       p_len = vpd_len;
-                       if (p_len > 0 && len < out_len - 1) {
-                               if (len + p_len > out_len - 1) {
-                                       condlog(1, "%s: WWID overflow, type 1, %d/%lu bytes required",
-                                               __func__, len + p_len + 1, out_len);
-                                       p_len = out_len - len - 1;
-                               }
-                               memcpy(out + len, vpd, p_len);
-                               len += p_len;
+                       out[len] = '_';
+                       len ++;
+                       if (len >= out_len - 1) {
                                out[len] = '\0';
+                               break;
                        }
-                       if (len > 1 && out[len - 1] == '_') {
-                               out[len - 1] = '\0';
-                               len--;
+                       vpd = p;
+                       vpd_len -= p_len;
+                       while (vpd && *vpd == ' ') {
+                               vpd++;
+                               vpd_len --;
+                       }
+               }
+               p_len = vpd_len;
+               if (p_len > 0 && len < out_len - 1) {
+                       if (len + p_len > out_len - 1) {
+                               condlog(1, "%s: WWID overflow, type 1, %lu/%lu bytes required",
+                                       __func__, len + p_len + 1, out_len);
+                               p_len = out_len - len - 1;
                        }
+                       memcpy(out + len, vpd, p_len);
+                       len += p_len;
+                       out[len] = '\0';
+               }
+               if (len > 1 && out[len - 1] == '_') {
+                       out[len - 1] = '\0';
+                       len--;
                }
        }
        return len;
 }
 
 static int
+parse_vpd_c0_hp3par(const unsigned char *in, size_t in_len,
+                   char *out, size_t out_len)
+{
+       size_t len;
+
+       memset(out, 0x0, out_len);
+       if (in_len <= 4 || (in[4] > 3 && in_len < 44)) {
+               condlog(3, "HP/3PAR vendor specific VPD page length too short: %lu", in_len);
+               return -EINVAL;
+       }
+       if (in[4] <= 3) /* revision must be > 3 to have Vomlume Name */
+               return -ENODATA;
+       len = get_unaligned_be32(&in[40]);
+       if (len > out_len || len + 44 > in_len) {
+               condlog(3, "HP/3PAR vendor specific Volume name too long: %lu",
+                       len);
+               return -EINVAL;
+       }
+       memcpy(out, &in[44], len);
+       out[out_len - 1] = '\0';
+       return len;
+}
+
+static int
 get_vpd_sysfs (struct udev_device *parent, int pg, char * str, int maxlen)
 {
        int len, buff_len;
@@ -1135,7 +1234,7 @@ get_vpd_sysfs (struct udev_device *parent, int pg, char * str, int maxlen)
 }
 
 int
-get_vpd_sgio (int fd, int pg, char * str, int maxlen)
+get_vpd_sgio (int fd, int pg, int vend_id, char * str, int maxlen)
 {
        int len, buff_len;
        unsigned char buff[4096];
@@ -1170,7 +1269,9 @@ get_vpd_sgio (int fd, int pg, char * str, int maxlen)
                        len = (buff_len <= maxlen)? buff_len : maxlen;
                        memcpy (str, buff, len);
                }
-       } else
+       } else if (pg == 0xc0 && vend_id == VPD_VP_HP3PAR)
+               len = parse_vpd_c0_hp3par(buff, buff_len, str, maxlen);
+       else
                len = -ENOSYS;
 
        return len;
@@ -1536,14 +1637,34 @@ sysfs_pathinfo(struct path * pp, vector hwtable)
 }
 
 static void
-scsi_ioctl_pathinfo (struct path * pp, struct config *conf, int mask)
+scsi_ioctl_pathinfo (struct path * pp, int mask)
 {
        struct udev_device *parent;
        const char *attr_path = NULL;
+       int vpd_id;
 
        if (!(mask & DI_SERIAL))
                return;
 
+       select_vpd_vendor_id(pp);
+       vpd_id = pp->vpd_vendor_id;
+
+       if (vpd_id != VPD_VP_UNDEF) {
+               char vpd_data[VPD_DATA_SIZE] = {0};
+
+               if (get_vpd_sgio(pp->fd, vpd_vendor_pages[vpd_id].pg, vpd_id,
+                   vpd_data, sizeof(vpd_data)) < 0)
+                       condlog(3, "%s: failed to get extra vpd data", pp->dev);
+               else {
+                       vpd_data[VPD_DATA_SIZE - 1] = '\0';
+                       if (pp->vpd_data)
+                               free(pp->vpd_data);
+                       pp->vpd_data = strdup(vpd_data);
+                       if (!pp->vpd_data)
+                               condlog(0, "%s: failed to allocate space for vpd data", pp->dev);
+               }
+       }
+
        parent = pp->udev;
        while (parent) {
                const char *subsys = udev_device_get_subsystem(parent);
@@ -1611,12 +1732,10 @@ get_state (struct path * pp, struct config *conf, int daemon, int oldstate)
        if (pp->mpp && !c->mpcontext)
                checker_mp_init(c, &pp->mpp->mpcontext);
        checker_clear_message(c);
-       if (daemon) {
-               if (conf->force_sync == 0)
-                       checker_set_async(c);
-               else
-                       checker_set_sync(c);
-       }
+       if (conf->force_sync == 0)
+               checker_set_async(c);
+       else
+               checker_set_sync(c);
        if (!conf->checker_timeout &&
            sysfs_get_timeout(pp, &(c->timeout)) <= 0)
                c->timeout = DEF_TIMEOUT;
@@ -1631,11 +1750,10 @@ get_state (struct path * pp, struct config *conf, int daemon, int oldstate)
 }
 
 static int
-get_prio (struct path * pp)
+get_prio (struct path * pp, int timeout)
 {
        struct prio * p;
        struct config *conf;
-       int checker_timeout;
        int old_prio;
 
        if (!pp)
@@ -1654,11 +1772,8 @@ get_prio (struct path * pp)
                        return 1;
                }
        }
-       conf = get_multipath_config();
-       checker_timeout = conf->checker_timeout;
-       put_multipath_config(conf);
        old_prio = pp->priority;
-       pp->priority = prio_getprio(p, pp, checker_timeout);
+       pp->priority = prio_getprio(p, pp, timeout);
        if (pp->priority < 0) {
                /* this changes pp->offline, but why not */
                int state = path_offline(pp);
@@ -1687,7 +1802,7 @@ get_prio (struct path * pp)
  * Returns a pointer to the position where "end" was moved to.
  */
 static char
-*skip_zeroes_backward(char* start, int *len, char *end)
+*skip_zeroes_backward(char* start, size_t *len, char *end)
 {
        char *p = end;
 
@@ -1713,10 +1828,10 @@ static char
  * Otherwise, returns 0.
  */
 static int
-fix_broken_nvme_wwid(struct path *pp, const char *value, int size)
+fix_broken_nvme_wwid(struct path *pp, const char *value, size_t size)
 {
        static const char _nvme[] = "nvme.";
-       int len, i;
+       size_t len, i;
        char mangled[256];
        char *p;
 
@@ -1810,7 +1925,7 @@ static ssize_t uid_fallback(struct path *pp, int path_state,
                if (len < 0 && path_state == PATH_UP) {
                        condlog(1, "%s: failed to get sysfs uid: %s",
                                pp->dev, strerror(-len));
-                       len = get_vpd_sgio(pp->fd, 0x83, pp->wwid,
+                       len = get_vpd_sgio(pp->fd, 0x83, 0, pp->wwid,
                                           WWID_SIZE);
                        *origin = "sgio";
                }
@@ -2015,7 +2130,7 @@ int pathinfo(struct path *pp, struct config *conf, int mask)
                get_geometry(pp);
 
        if (path_state == PATH_UP && pp->bus == SYSFS_BUS_SCSI)
-               scsi_ioctl_pathinfo(pp, conf, mask);
+               scsi_ioctl_pathinfo(pp, mask);
 
        if (pp->bus == SYSFS_BUS_CCISS && mask & DI_SERIAL)
                cciss_ioctl_pathinfo(pp);
@@ -2065,11 +2180,13 @@ int pathinfo(struct path *pp, struct config *conf, int mask)
 
         /*
          * Retrieve path priority, even for PATH_DOWN paths if it has never
-         * been successfully obtained before.
+         * been successfully obtained before. If path is down don't try
+         * for too long.
          */
        if ((mask & DI_PRIO) && path_state == PATH_UP && strlen(pp->wwid)) {
                if (pp->state != PATH_DOWN || pp->priority == PRIO_UNDEF) {
-                       get_prio(pp);
+                       get_prio(pp, (pp->state != PATH_DOWN)?
+                                    (conf->checker_timeout * 1000) : 10);
                }
        }
 
index 8d04c2a..6444887 100644 (file)
@@ -35,14 +35,14 @@ int path_get_tpgs(struct path *pp); /* This function never returns TPGS_UNDEF */
 int do_tur (char *);
 int path_offline (struct path *);
 int get_state (struct path * pp, struct config * conf, int daemon, int state);
-int get_vpd_sgio (int fd, int pg, char * str, int maxlen);
+int get_vpd_sgio (int fd, int pg, int vend_id, char * str, int maxlen);
 int pathinfo (struct path * pp, struct config * conf, int mask);
 int alloc_path_with_pathinfo (struct config *conf, struct udev_device *udevice,
                              const char *wwid, int flag, struct path **pp_ptr);
 int store_pathinfo (vector pathvec, struct config *conf,
                    struct udev_device *udevice, int flag,
                    struct path **pp_ptr);
-int sysfs_set_scsi_tmo (struct multipath *mpp, int checkint);
+int sysfs_set_scsi_tmo (struct multipath *mpp, unsigned int checkint);
 int sysfs_get_timeout(const struct path *pp, unsigned int *timeout);
 int sysfs_get_host_pci_name(const struct path *pp, char *pci_name);
 int sysfs_get_iscsi_ip_address(const struct path *pp, char *ip_address);
index d752991..1b42fa0 100644 (file)
@@ -31,7 +31,8 @@ dm_mp_get_pgs(const struct gen_multipath *gmp)
                              struct pathgroup, dm_pathgroup_to_gen);
 }
 
-static void dm_mp_rel_pgs(const struct gen_multipath *gmp,
+static void dm_mp_rel_pgs(__attribute__((unused))
+                         const struct gen_multipath *gmp,
                          const struct _vector* v)
 {
        vector_free_const(v);
@@ -44,7 +45,8 @@ dm_pg_get_paths(const struct gen_pathgroup *gpg)
                              struct path, dm_path_to_gen);
 }
 
-static void dm_mp_rel_paths(const struct gen_pathgroup *gpg,
+static void dm_mp_rel_paths(__attribute__((unused))
+                           const struct gen_pathgroup *gpg,
                            const struct _vector* v)
 {
        vector_free_const(v);
index 8727f16..72f1d24 100644 (file)
@@ -74,7 +74,7 @@ ensure_directories_exist(const char *str, mode_t dir_mode)
 }
 
 static void
-sigalrm(int sig)
+sigalrm(__attribute__((unused)) int sig)
 {
        /* do nothing */
 }
@@ -157,7 +157,8 @@ open_file(const char *file, int *can_write, const char *header)
                if (*can_write == 0)
                        goto fail;
                /* If file is empty, write the header */
-               size_t len = strlen(header);
+               int len = strlen(header);
+
                if (write(fd, header, len) != len) {
                        condlog(0,
                                "Cannot write header to file %s : %s", file,
index 4b34e14..0159a83 100644 (file)
@@ -50,7 +50,7 @@ static void wrlock_foreigns(void)
        pthread_rwlock_wrlock(&foreign_lock);
 }
 
-static void unlock_foreigns(void *unused)
+static void unlock_foreigns(__attribute__((unused)) void *unused)
 {
        pthread_rwlock_unlock(&foreign_lock);
 }
@@ -148,7 +148,7 @@ static int _init_foreign(const char *multipath_dir, const char *enable)
                        (void)regerror(r, enable_re, errbuf, sizeof(errbuf));
                        condlog (2, "%s: error compiling enable_foreign = \"%s\": \"%s\"",
                                 __func__, str, errbuf);
-                       free_pre(&enable_re);
+                       goto out_free_pre;
                }
        }
 
@@ -157,13 +157,13 @@ static int _init_foreign(const char *multipath_dir, const char *enable)
        if (r == 0) {
                condlog(3, "%s: no foreign multipath libraries found",
                        __func__);
-               return 0;
+               goto out_free_pre;
        } else if (r < 0) {
-               r = errno;
-               condlog(1, "%s: error %d scanning foreign multipath libraries",
-                       __func__, r);
+               r = -errno;
+               condlog(1, "%s: error scanning foreign multipath libraries: %m",
+                       __func__);
                _cleanup_foreign();
-               return -r;
+               goto out_free_pre;
        }
 
        sr.di = di;
@@ -172,7 +172,7 @@ static int _init_foreign(const char *multipath_dir, const char *enable)
        for (i = 0; i < r; i++) {
                const char *msg, *fn, *c;
                struct foreign *fgn;
-               int len, namesz;
+               size_t len, namesz;
 
                fn = di[i]->d_name;
 
@@ -249,9 +249,11 @@ static int _init_foreign(const char *multipath_dir, const char *enable)
        dl_err:
                free_foreign(fgn);
        }
+       r = 0;
        pthread_cleanup_pop(1); /* free_scandir_result */
+out_free_pre:
        pthread_cleanup_pop(1); /* free_pre */
-       return 0;
+       return r;
 }
 
 int init_foreign(const char *multipath_dir, const char *enable)
index 7e654ec..09cdddf 100644 (file)
@@ -123,7 +123,8 @@ nvme_mp_get_pgs(const struct gen_multipath *gmp) {
 }
 
 static void
-nvme_mp_rel_pgs(const struct gen_multipath *gmp, const struct _vector *v)
+nvme_mp_rel_pgs(__attribute__((unused)) const struct gen_multipath *gmp,
+               __attribute__((unused)) const struct _vector *v)
 {
        /* empty */
 }
@@ -207,7 +208,8 @@ nvme_pg_get_paths(const struct gen_pathgroup *gpg) {
 }
 
 static void
-nvme_pg_rel_paths(const struct gen_pathgroup *gpg, const struct _vector *v)
+nvme_pg_rel_paths(__attribute__((unused)) const struct gen_pathgroup *gpg,
+                 __attribute__((unused)) const struct _vector *v)
 {
        /* empty */
 }
@@ -331,8 +333,9 @@ static int snprint_nvme_pg(const struct gen_pathgroup *gmp,
        }
 }
 
-static int nvme_style(const struct gen_multipath* gm,
-                     char *buf, int len, int verbosity)
+static int nvme_style(__attribute__((unused)) const struct gen_multipath* gm,
+                     char *buf, int len,
+                     __attribute__((unused)) int verbosity)
 {
        int n = snprintf(buf, len, "%%w [%%G]:%%d %%s");
 
@@ -588,8 +591,7 @@ static void test_ana_support(struct nvme_map *map, struct udev_device *ctl)
                return;
 
        dev_t = udev_device_get_sysattr_value(ctl, "dev");
-       if (snprintf(sys_path, sizeof(sys_path), "/dev/char/%s", dev_t)
-           >= sizeof(sys_path))
+       if (safe_sprintf(sys_path, "/dev/char/%s", dev_t))
                return;
 
        fd = open(sys_path, O_RDONLY);
@@ -660,8 +662,7 @@ static void _find_controllers(struct context *ctx, struct nvme_map *map)
                char *fn = di[i]->d_name;
                struct udev_device *ctrl, *udev;
 
-               if (snprintf(pathbuf + n, sizeof(pathbuf) - n, "/%s", fn)
-                   >= sizeof(pathbuf) - n)
+               if (safe_snprintf(pathbuf + n, sizeof(pathbuf) - n, "/%s", fn))
                        continue;
                if (realpath(pathbuf, realbuf) == NULL) {
                        condlog(3, "%s: %s: realpath: %s", __func__, THIS,
@@ -821,7 +822,8 @@ int add(struct context *ctx, struct udev_device *ud)
        return rc;
 }
 
-int change(struct context *ctx, struct udev_device *ud)
+int change(__attribute__((unused)) struct context *ctx,
+          __attribute__((unused)) struct udev_device *ud)
 {
        condlog(5, "%s called for \"%s\"", __func__, THIS);
        return FOREIGN_IGNORED;
@@ -903,7 +905,8 @@ const struct _vector *get_multipaths(const struct context *ctx)
        return ctx->mpvec;
 }
 
-void release_multipaths(const struct context *ctx, const struct _vector *mpvec)
+void release_multipaths(__attribute__((unused)) const struct context *ctx,
+                       __attribute__((unused)) const struct _vector *mpvec)
 {
        condlog(5, "%s called for \"%s\"", __func__, THIS);
        /* NOP */
@@ -927,7 +930,8 @@ const struct _vector * get_paths(const struct context *ctx)
        return paths;
 }
 
-void release_paths(const struct context *ctx, const struct _vector *mpvec)
+void release_paths(__attribute__((unused)) const struct context *ctx,
+                  const struct _vector *mpvec)
 {
        condlog(5, "%s called for \"%s\"", __func__, THIS);
        vector_free_const(mpvec);
index 0d1e632..5f03b9e 100644 (file)
@@ -21,7 +21,7 @@
 #include "structs.h"
 
 int generic_style(const struct gen_multipath* gm,
-                 char *buf, int len, int verbosity)
+                 char *buf, int len, __attribute__((unused)) int verbosity)
 {
        char alias_buf[WWID_SIZE];
        char wwid_buf[WWID_SIZE];
index 16627ec..d1fcfdb 100644 (file)
@@ -117,6 +117,7 @@ static struct hwentry default_hw[] = {
                .no_path_retry = 18,
                .fast_io_fail  = 10,
                .dev_loss      = MAX_DEV_LOSS_TMO,
+               .vpd_vendor_id = VPD_VP_HP3PAR,
        },
        {
                /* RA8000 / ESA12000 */
@@ -1039,7 +1040,12 @@ static struct hwentry default_hw[] = {
                /* FlashArray */
                .vendor        = "PURE",
                .product       = "FlashArray",
-               .pgpolicy      = MULTIBUS,
+               .pgpolicy      = GROUP_BY_PRIO,
+               .pgfailback    = -FAILBACK_IMMEDIATE,
+               .hwhandler     = "1 alua",
+               .prio_name     = PRIO_ALUA,
+               .fast_io_fail  = 10,
+               .max_sectors_kb = 4096,
        },
        /*
         * Huawei
index 554b777..1b9cd6c 100644 (file)
@@ -54,7 +54,7 @@ struct io_err_stat_pathvec {
 
 struct dio_ctx {
        struct timespec io_starttime;
-       int             blksize;
+       unsigned int    blksize;
        void            *buf;
        struct iocb     io;
 };
@@ -84,7 +84,7 @@ io_context_t  ioctx;
 
 static void cancel_inflight_io(struct io_err_stat_path *pp);
 
-static void rcu_unregister(void *param)
+static void rcu_unregister(__attribute__((unused)) void *param)
 {
        rcu_unregister_thread();
 }
@@ -128,7 +128,7 @@ static int setup_directio_ctx(struct io_err_stat_path *p)
 {
        unsigned long pgsize = getpagesize();
        char fpath[PATH_MAX];
-       int blksize = 0;
+       unsigned int blksize = 0;
        int i;
 
        if (snprintf(fpath, PATH_MAX, "/dev/%s", p->devname) >= PATH_MAX)
@@ -357,7 +357,7 @@ int io_err_stat_handle_pathfail(struct path *path)
                if (path->state != PATH_DOWN) {
                        struct config *conf;
                        int oldstate = path->state;
-                       int checkint;
+                       unsigned int checkint;
 
                        conf = get_multipath_config();
                        checkint = conf->checkint;
@@ -383,7 +383,7 @@ int need_io_err_check(struct path *pp)
 
        if (uatomic_read(&io_err_thread_running) == 0)
                return 0;
-       if (pp->mpp->nr_active <= 0) {
+       if (count_active_paths(pp->mpp) <= 0) {
                io_err_stat_log(2, "%s: recover path early", pp->dev);
                goto recover;
        }
@@ -481,7 +481,7 @@ static int poll_io_err_stat(struct vectors *vecs, struct io_err_stat_path *pp)
                 */
                path->tick = 1;
 
-       } else if (path->mpp && path->mpp->nr_active > 0) {
+       } else if (path->mpp && count_active_paths(path->mpp) > 0) {
                io_err_stat_log(3, "%s: keep failing the dm path %s",
                                path->mpp->alias, path->dev);
                path->io_err_pathfail_cnt = PATH_IO_ERR_WAITING_TO_CHECK;
@@ -689,7 +689,7 @@ static void cleanup_unlock(void *arg)
        pthread_mutex_unlock((pthread_mutex_t*) arg);
 }
 
-static void cleanup_exited(void *arg)
+static void cleanup_exited(__attribute__((unused)) void *arg)
 {
        uatomic_set(&io_err_thread_running, 0);
 }
index 6551b5c..d2448f6 100644 (file)
@@ -34,7 +34,8 @@ extern struct logarea* la;
 int log_init (char * progname, int size);
 void log_close (void);
 void log_reset (char * progname);
-int log_enqueue (int prio, const char * fmt, va_list ap);
+int log_enqueue (int prio, const char * fmt, va_list ap)
+       __attribute__((format(printf, 2, 0)));
 int log_dequeue (void *);
 void log_syslog (void *);
 void dump_logmsg (void *);
index be57bb1..15baef8 100644 (file)
@@ -56,7 +56,7 @@ static void flush_logqueue (void)
        } while (empty == 0);
 }
 
-static void * log_thread (void * et)
+static void * log_thread (__attribute__((unused)) void * et)
 {
        int running;
 
index 7e138a0..810ac92 100644 (file)
@@ -3,7 +3,8 @@
 
 #include <pthread.h>
 
-void log_safe(int prio, const char * fmt, va_list ap);
+void log_safe(int prio, const char * fmt, va_list ap)
+       __attribute__((format(printf, 2, 0)));
 void log_thread_start(pthread_attr_t *attr);
 void log_thread_reset (void);
 void log_thread_stop(void);
index 68000eb..a697554 100644 (file)
@@ -124,6 +124,9 @@ enum {
        NVME_REG_BPINFO = 0x0040,       /* Boot Partition Information */
        NVME_REG_BPRSEL = 0x0044,       /* Boot Partition Read Select */
        NVME_REG_BPMBL  = 0x0048,       /* Boot Partition Memory Buffer Location */
+       NVME_REG_PMRCAP = 0x0e00,       /* Persistent Memory Capabilities */
+       NVME_REG_PMRCTL = 0x0e04,       /* Persistent Memory Region Control */
+       NVME_REG_PMRSTS = 0x0e08,       /* Persistent Memory Region Status */
        NVME_REG_DBS    = 0x1000,       /* SQ 0 Tail Doorbell */
 };
 
@@ -221,7 +224,11 @@ struct nvme_id_ctrl {
        __le32                  oaes;
        __le32                  ctratt;
        __le16                  rrls;
-       __u8                    rsvd102[154];
+       __u8                    rsvd102[26];
+       __le16                  crdt1;
+       __le16                  crdt2;
+       __le16                  crdt3;
+       __u8                    rsvd134[122];
        __le16                  oacs;
        __u8                    acl;
        __u8                    aerl;
@@ -302,6 +309,8 @@ enum {
        NVME_CTRL_CTRATT_READ_RECV_LVLS         = 1 << 3,
        NVME_CTRL_CTRATT_ENDURANCE_GROUPS       = 1 << 4,
        NVME_CTRL_CTRATT_PREDICTABLE_LAT        = 1 << 5,
+       NVME_CTRL_CTRATT_NAMESPACE_GRANULARITY  = 1 << 7,
+       NVME_CTRL_CTRATT_UUID_LIST              = 1 << 9,
 };
 
 struct nvme_lbaf {
@@ -332,7 +341,12 @@ struct nvme_id_ns {
        __le16                  nabspf;
        __le16                  noiob;
        __u8                    nvmcap[16];
-       __u8                    rsvd64[28];
+       __le16                  npwg;
+       __le16                  npwa;
+       __le16                  npdg;
+       __le16                  npda;
+       __le16                  nows;
+       __u8                    rsvd74[18];
        __le32                  anagrpid;
        __u8                    rsvd96[3];
        __u8                    nsattr;
@@ -355,6 +369,9 @@ enum {
        NVME_ID_CNS_NS_PRESENT          = 0x11,
        NVME_ID_CNS_CTRL_NS_LIST        = 0x12,
        NVME_ID_CNS_CTRL_LIST           = 0x13,
+       NVME_ID_CNS_SCNDRY_CTRL_LIST    = 0x15,
+       NVME_ID_CNS_NS_GRANULARITY      = 0x16,
+       NVME_ID_CNS_UUID_LIST           = 0x17,
 };
 
 enum {
@@ -425,26 +442,56 @@ struct nvme_id_nvmset {
        struct nvme_nvmset_attr_entry   ent[NVME_MAX_NVMSET];
 };
 
-/* Derived from 1.3a Figure 101: Get Log Page â€“ Telemetry Host
- * -Initiated Log (Log Identifier 07h)
+struct nvme_id_ns_granularity_list_entry {
+       __le64                  namespace_size_granularity;
+       __le64                  namespace_capacity_granularity;
+};
+
+struct nvme_id_ns_granularity_list {
+       __le32                  attributes;
+       __u8                    num_descriptors;
+       __u8                    rsvd[27];
+       struct nvme_id_ns_granularity_list_entry entry[16];
+};
+
+#define NVME_MAX_UUID_ENTRIES  128
+struct nvme_id_uuid_list_entry {
+       __u8                    header;
+       __u8                    rsvd1[15];
+       __u8                    uuid[16];
+};
+
+struct nvme_id_uuid_list {
+       struct nvme_id_uuid_list_entry  entry[NVME_MAX_UUID_ENTRIES];
+};
+
+/**
+ * struct nvme_telemetry_log_page_hdr - structure for telemetry log page
+ * @lpi: Log page identifier
+ * @iee_oui: IEEE OUI Identifier
+ * @dalb1: Data area 1 last block
+ * @dalb2: Data area 2 last block
+ * @dalb3: Data area 3 last block
+ * @ctrlavail: Controller initiated data available
+ * @ctrldgn: Controller initiated telemetry Data Generation Number
+ * @rsnident: Reason Identifier
+ * @telemetry_dataarea: Contains telemetry data block
+ *
+ * This structure can be used for both telemetry host-initiated log page
+ * and controller-initiated log page.
  */
 struct nvme_telemetry_log_page_hdr {
-       __u8    lpi; /* Log page identifier */
-       __u8    rsvd[4];
-       __u8    iee_oui[3];
-       __u16   dalb1; /* Data area 1 last block */
-       __u16   dalb2; /* Data area 2 last block */
-       __u16   dalb3; /* Data area 3 last block */
-       __u8    rsvd1[368]; /* TODO verify */
-       __u8    ctrlavail; /* Controller initiated data avail?*/
-       __u8    ctrldgn; /* Controller initiated telemetry Data Gen # */
-       __u8    rsnident[128];
-       /* We'll have to double fetch so we can get the header,
-        * parse dalb1->3 determine how much size we need for the
-        * log then alloc below. Or just do a secondary non-struct
-        * allocation.
-        */
-       __u8    telemetry_dataarea[0];
+       __u8    lpi;
+       __u8    rsvd[4];
+       __u8    iee_oui[3];
+       __le16  dalb1;
+       __le16  dalb2;
+       __le16  dalb3;
+       __u8    rsvd1[368];
+       __u8    ctrlavail;
+       __u8    ctrldgn;
+       __u8    rsnident[128];
+       __u8    telemetry_dataarea[0];
 };
 
 struct nvme_endurance_group_log {
@@ -513,6 +560,21 @@ struct nvme_fw_slot_info_log {
        __u8                    rsvd64[448];
 };
 
+struct nvme_lba_status_desc {
+       __u64 dslba;
+       __u32 nlb;
+       __u8 rsvd_12;
+       __u8 status;
+       __u8 rsvd_15_14[2];
+};
+
+struct nvme_lba_status {
+       __u32 nlsd;
+       __u8 cmpc;
+       __u8 rsvd_7_5[3];
+       struct nvme_lba_status_desc descs[0];
+};
+
 /* NVMe Namespace Write Protect State */
 enum {
        NVME_NS_NO_WRITE_PROTECT = 0,
@@ -534,6 +596,7 @@ enum {
        NVME_CMD_EFFECTS_NIC            = 1 << 3,
        NVME_CMD_EFFECTS_CCC            = 1 << 4,
        NVME_CMD_EFFECTS_CSE_MASK       = 3 << 16,
+       NVME_CMD_EFFECTS_UUID_SEL       = 1 << 19,
 };
 
 struct nvme_effects_log {
@@ -581,9 +644,6 @@ enum {
        NVME_AER_SMART                  = 1,
        NVME_AER_CSS                    = 6,
        NVME_AER_VS                     = 7,
-       NVME_AER_NOTICE_NS_CHANGED      = 0x0002,
-       NVME_AER_NOTICE_ANA             = 0x0003,
-       NVME_AER_NOTICE_FW_ACT_STARTING = 0x0102,
 };
 
 struct nvme_lba_range_type {
@@ -606,12 +666,13 @@ enum {
        NVME_LBART_ATTRIB_HIDE  = 1 << 1,
 };
 
+/* Predictable Latency Mode - Deterministic Threshold Configuration Data */
 struct nvme_plm_config {
-       __u16   enable_event;
+       __le16  enable_event;
        __u8    rsvd2[30];
-       __u64   dtwin_reads_thresh;
-       __u64   dtwin_writes_thresh;
-       __u64   dtwin_time_thresh;
+       __le64  dtwin_reads_thresh;
+       __le64  dtwin_writes_thresh;
+       __le64  dtwin_time_thresh;
        __u8    rsvd56[456];
 };
 
@@ -665,6 +726,7 @@ enum nvme_opcode {
        nvme_cmd_compare        = 0x05,
        nvme_cmd_write_zeroes   = 0x08,
        nvme_cmd_dsm            = 0x09,
+       nvme_cmd_verify         = 0x0c,
        nvme_cmd_resv_register  = 0x0d,
        nvme_cmd_resv_report    = 0x0e,
        nvme_cmd_resv_acquire   = 0x11,
@@ -892,6 +954,7 @@ enum nvme_admin_opcode {
        nvme_admin_security_send        = 0x81,
        nvme_admin_security_recv        = 0x82,
        nvme_admin_sanitize_nvm         = 0x84,
+       nvme_admin_get_lba_status       = 0x86,
 };
 
 enum {
@@ -921,6 +984,8 @@ enum {
        NVME_FEAT_RRL           = 0x12,
        NVME_FEAT_PLM_CONFIG    = 0x13,
        NVME_FEAT_PLM_WINDOW    = 0x14,
+       NVME_FEAT_HOST_BEHAVIOR = 0x16,
+       NVME_FEAT_SANITIZE      = 0x17,
        NVME_FEAT_SW_PROGRESS   = 0x80,
        NVME_FEAT_HOST_ID       = 0x81,
        NVME_FEAT_RESV_MASK     = 0x82,
@@ -972,6 +1037,7 @@ enum {
        NVME_SANITIZE_LOG_COMPLETED_SUCCESS     = 0x0001,
        NVME_SANITIZE_LOG_IN_PROGESS            = 0x0002,
        NVME_SANITIZE_LOG_COMPLETED_FAILED      = 0x0003,
+       NVME_SANITIZE_LOG_ND_COMPLETED_SUCCESS  = 0x0004,
 };
 
 enum {
@@ -1131,6 +1197,9 @@ struct nvme_sanitize_log_page {
        __le32                  est_ovrwrt_time;
        __le32                  est_blk_erase_time;
        __le32                  est_crypto_erase_time;
+       __le32                  est_ovrwrt_time_with_no_deallocate;
+       __le32                  est_blk_erase_time_with_no_deallocate;
+       __le32                  est_crypto_erase_time_with_no_deallocate;
 };
 
 /*
@@ -1315,6 +1384,12 @@ static inline bool nvme_is_write(struct nvme_command *cmd)
 }
 
 enum {
+       NVME_SCT_GENERIC                = 0x0,
+       NVME_SCT_CMD_SPECIFIC           = 0x1,
+       NVME_SCT_MEDIA                  = 0x2,
+};
+
+enum {
        /*
         * Generic Command Status:
         */
@@ -1344,6 +1419,7 @@ enum {
        NVME_SC_SANITIZE_IN_PROGRESS    = 0x1D,
 
        NVME_SC_NS_WRITE_PROTECTED      = 0x20,
+       NVME_SC_CMD_INTERRUPTED         = 0x21,
 
        NVME_SC_LBA_RANGE               = 0x80,
        NVME_SC_CAP_EXCEEDED            = 0x81,
@@ -1372,9 +1448,9 @@ enum {
        NVME_SC_FW_NEEDS_SUBSYS_RESET   = 0x110,
        NVME_SC_FW_NEEDS_RESET          = 0x111,
        NVME_SC_FW_NEEDS_MAX_TIME       = 0x112,
-       NVME_SC_FW_ACIVATE_PROHIBITED   = 0x113,
+       NVME_SC_FW_ACTIVATE_PROHIBITED  = 0x113,
        NVME_SC_OVERLAPPING_RANGE       = 0x114,
-       NVME_SC_NS_INSUFFICENT_CAP      = 0x115,
+       NVME_SC_NS_INSUFFICIENT_CAP     = 0x115,
        NVME_SC_NS_ID_UNAVAILABLE       = 0x116,
        NVME_SC_NS_ALREADY_ATTACHED     = 0x118,
        NVME_SC_NS_IS_PRIVATE           = 0x119,
@@ -1382,6 +1458,7 @@ enum {
        NVME_SC_THIN_PROV_NOT_SUPP      = 0x11b,
        NVME_SC_CTRL_LIST_INVALID       = 0x11c,
        NVME_SC_BP_WRITE_PROHIBITED     = 0x11e,
+       NVME_SC_PMR_SAN_PROHIBITED      = 0x123,
 
        /*
         * I/O Command Set Specific - NVM commands:
@@ -1422,6 +1499,7 @@ enum {
        NVME_SC_ANA_INACCESSIBLE        = 0x302,
        NVME_SC_ANA_TRANSITION          = 0x303,
 
+       NVME_SC_CRD                     = 0x1800,
        NVME_SC_DNR                     = 0x4000,
 };
 
index 70a16ce..6959976 100644 (file)
@@ -1,3 +1,4 @@
+#include <assert.h>
 #include <sys/ioctl.h>
 #include <sys/stat.h>
 #include <string.h>
@@ -177,6 +178,22 @@ int nvme_compare(int fd, __u64 slba, __u16 nblocks, __u16 control, __u32 dsmgmt,
                       reftag, apptag, appmask, data, metadata);
 }
 
+int nvme_verify(int fd, __u32 nsid, __u64 slba, __u16 nblocks,
+               __u16 control, __u32 reftag, __u16 apptag, __u16 appmask)
+{
+       struct nvme_passthru_cmd cmd = {
+               .opcode         = nvme_cmd_verify,
+               .nsid           = nsid,
+               .cdw10          = slba & 0xffffffff,
+               .cdw11          = slba >> 32,
+               .cdw12          = nblocks | (control << 16),
+               .cdw14          = reftag,
+               .cdw15          = apptag | (appmask << 16),
+       };
+
+       return nvme_submit_io_passthru(fd, &cmd);
+}
+
 int nvme_passthru_io(int fd, __u8 opcode, __u8 flags, __u16 rsvd,
                     __u32 nsid, __u32 cdw2, __u32 cdw3, __u32 cdw10,
                     __u32 cdw11, __u32 cdw12, __u32 cdw13, __u32 cdw14,
@@ -370,6 +387,11 @@ int nvme_identify_ctrl_list(int fd, __u32 nsid, __u16 cntid, void *data)
        return nvme_identify(fd, nsid, (cntid << 16) | cns, data);
 }
 
+int nvme_identify_secondary_ctrl_list(int fd, __u32 nsid, __u16 cntid, void *data)
+{
+       return nvme_identify(fd, nsid, (cntid << 16) | NVME_ID_CNS_SCNDRY_CTRL_LIST, data);
+}
+
 int nvme_identify_ns_descs(int fd, __u32 nsid, void *data)
 {
 
@@ -381,8 +403,18 @@ int nvme_identify_nvmset(int fd, __u16 nvmset_id, void *data)
        return nvme_identify13(fd, 0, NVME_ID_CNS_NVMSET_LIST, nvmset_id, data);
 }
 
-int nvme_get_log13(int fd, __u32 nsid, __u8 log_id, __u8 lsp, __u64 lpo,
-                 __u16 lsi, bool rae, __u32 data_len, void *data)
+int nvme_identify_ns_granularity(int fd, void *data)
+{
+       return nvme_identify13(fd, 0, NVME_ID_CNS_NS_GRANULARITY, 0, data);
+}
+
+int nvme_identify_uuid(int fd, void *data)
+{
+       return nvme_identify(fd, 0, NVME_ID_CNS_UUID_LIST, data);
+}
+
+int nvme_get_log14(int fd, __u32 nsid, __u8 log_id, __u8 lsp, __u64 lpo,
+                 __u16 lsi, bool rae, __u8 uuid_ix, __u32 data_len, void *data)
 {
        struct nvme_admin_cmd cmd = {
                .opcode         = nvme_admin_get_log_page,
@@ -400,6 +432,7 @@ int nvme_get_log13(int fd, __u32 nsid, __u8 log_id, __u8 lsp, __u64 lpo,
        cmd.cdw11 = numdu | (lsi << 16);
        cmd.cdw12 = lpo;
        cmd.cdw13 = (lpo >> 32);
+       cmd.cdw14 = uuid_ix;
 
        return nvme_submit_admin_passthru(fd, &cmd);
 
@@ -498,7 +531,7 @@ int nvme_self_test_log(int fd, struct nvme_self_test_log *self_test_log)
 
 int nvme_effects_log(int fd, struct nvme_effects_log_page *effects_log)
 {
-       return nvme_get_log(fd, 0, NVME_LOG_CMD_EFFECTS, false,
+       return nvme_get_log(fd, NVME_NSID_ALL, NVME_LOG_CMD_EFFECTS, false,
                        sizeof(*effects_log), effects_log);
 }
 
@@ -542,77 +575,61 @@ int nvme_set_feature(int fd, __u32 nsid, __u8 fid, __u32 value, __u32 cdw12,
                            cdw12, data_len, data, result);
 }
 
-static int nvme_property(int fd, __u8 fctype, __le32 off, __le64 *value, __u8 attrib)
-{
-       int err;
-       struct nvme_admin_cmd cmd = {
-               .opcode         = nvme_fabrics_command,
-               .cdw10          = attrib,
-               .cdw11          = off,
-       };
-
-       if (!value) {
-               errno = EINVAL;
-               return -errno;
-       }
-
-       if (fctype == nvme_fabrics_type_property_get){
-               cmd.nsid = nvme_fabrics_type_property_get;
-       } else if(fctype == nvme_fabrics_type_property_set) {
-               cmd.nsid = nvme_fabrics_type_property_set;
-               cmd.cdw12 = *value;
-       } else {
-               errno = EINVAL;
-               return -errno;
-       }
 
-       err = nvme_submit_admin_passthru(fd, &cmd);
-       if (!err && fctype == nvme_fabrics_type_property_get)
-               *value = cpu_to_le64(cmd.result);
-       return err;
+/*
+ * Perform the opposite operation of the byte-swapping code at the start of the
+ * kernel function nvme_user_cmd().
+ */
+static void nvme_to_passthru_cmd(struct nvme_passthru_cmd *pcmd,
+                                const struct nvme_command *ncmd)
+{
+       assert(sizeof(*ncmd) < sizeof(*pcmd));
+       memset(pcmd, 0, sizeof(*pcmd));
+       pcmd->opcode = ncmd->common.opcode;
+       pcmd->flags = ncmd->common.flags;
+       pcmd->rsvd1 = ncmd->common.command_id;
+       pcmd->nsid = le32_to_cpu(ncmd->common.nsid);
+       pcmd->cdw2 = le32_to_cpu(ncmd->common.cdw2[0]);
+       pcmd->cdw3 = le32_to_cpu(ncmd->common.cdw2[1]);
+       /* Skip metadata and addr */
+       pcmd->cdw10 = le32_to_cpu(ncmd->common.cdw10[0]);
+       pcmd->cdw11 = le32_to_cpu(ncmd->common.cdw10[1]);
+       pcmd->cdw12 = le32_to_cpu(ncmd->common.cdw10[2]);
+       pcmd->cdw13 = le32_to_cpu(ncmd->common.cdw10[3]);
+       pcmd->cdw14 = le32_to_cpu(ncmd->common.cdw10[4]);
+       pcmd->cdw15 = le32_to_cpu(ncmd->common.cdw10[5]);
 }
 
-static int get_property_helper(int fd, int offset, void *value, int *advance)
+int nvme_get_property(int fd, int offset, uint64_t *value)
 {
-       __le64 value64;
-       int err = -EINVAL;
-
-       switch (offset) {
-       case NVME_REG_CAP:
-       case NVME_REG_ASQ:
-       case NVME_REG_ACQ:
-               *advance = 8;
-               break;
-       default:
-               *advance = 4;
-       }
-
-       if (!value)
-               return err;
-
-       err = nvme_property(fd, nvme_fabrics_type_property_get,
-                       cpu_to_le32(offset), &value64, (*advance == 8));
+       struct nvme_passthru_cmd pcmd;
+       struct nvmf_property_get_command pg = {
+               .opcode = nvme_fabrics_command,
+               .fctype = nvme_fabrics_type_property_get,
+               .offset = cpu_to_le32(offset),
+               .attrib = is_64bit_reg(offset),
+       };
+       struct nvme_command gcmd;
+       int err;
 
+       gcmd.prop_get = pg;
+       nvme_to_passthru_cmd(&pcmd, &gcmd);
+       err = nvme_submit_admin_passthru(fd, &pcmd);
        if (!err) {
-               if (*advance == 8)
-                       *((uint64_t *)value) = le64_to_cpu(value64);
-               else
-                       *((uint32_t *)value) = le32_to_cpu(value64);
+               /*
+                * nvme_submit_admin_passthru() stores the lower 32 bits
+                * of the property value in pcmd.result using CPU endianness.
+                */
+               *value = pcmd.result;
        }
-
        return err;
 }
 
-int nvme_get_property(int fd, int offset, uint64_t *value)
-{
-       int advance;
-       return get_property_helper(fd, offset, value, &advance);
-}
-
 int nvme_get_properties(int fd, void **pbar)
 {
-       int offset, advance;
-       int err, ret = -EINVAL;
+       int offset;
+       uint64_t value;
+       int err;
        int size = getpagesize();
 
        *pbar = malloc(size);
@@ -622,33 +639,42 @@ int nvme_get_properties(int fd, void **pbar)
        }
 
        memset(*pbar, 0xff, size);
-       for (offset = NVME_REG_CAP; offset <= NVME_REG_CMBSZ; offset += advance) {
-               err = get_property_helper(fd, offset, *pbar + offset, &advance);
-               if (!err)
-                       ret = 0;
+       for (offset = NVME_REG_CAP; offset <= NVME_REG_CMBSZ;) {
+               err = nvme_get_property(fd, offset, &value);
+               if (err > 0 && (err & 0xff) == NVME_SC_INVALID_FIELD) {
+                       err = 0;
+                       value = -1;
+               } else if (err) {
+                       free(*pbar);
+                       break;
+               }
+               if (is_64bit_reg(offset)) {
+                       *(uint64_t *)(*pbar + offset) = value;
+                       offset += 8;
+               } else {
+                       *(uint32_t *)(*pbar + offset) = value;
+                       offset += 4;
+               }
        }
 
-       return ret;
+       return err;
 }
 
-int nvme_set_property(int fd, int offset, int value)
+int nvme_set_property(int fd, int offset, uint64_t value)
 {
-       __le64 val = cpu_to_le64(value);
-       __le32 off = cpu_to_le32(offset);
-       bool is64bit;
-
-       switch (off) {
-       case NVME_REG_CAP:
-       case NVME_REG_ASQ:
-       case NVME_REG_ACQ:
-               is64bit = true;
-               break;
-       default:
-               is64bit = false;
-       }
+       struct nvmf_property_set_command ps = {
+               .opcode = nvme_fabrics_command,
+               .fctype = nvme_fabrics_type_property_set,
+               .offset = cpu_to_le32(offset),
+               .value = cpu_to_le64(value),
+               .attrib = is_64bit_reg(offset),
+       };
+       struct nvme_command scmd;
+       struct nvme_passthru_cmd pcmd;
 
-       return nvme_property(fd, nvme_fabrics_type_property_set,
-                       off, &val, is64bit ? 1: 0);
+       scmd.prop_set = ps;
+       nvme_to_passthru_cmd(&pcmd, &scmd);
+       return nvme_submit_admin_passthru(fd, &pcmd);
 }
 
 int nvme_get_feature(int fd, __u32 nsid, __u8 fid, __u8 sel, __u32 cdw11,
@@ -675,7 +701,7 @@ int nvme_format(int fd, __u32 nsid, __u8 lbaf, __u8 ses, __u8 pi,
 }
 
 int nvme_ns_create(int fd, __u64 nsze, __u64 ncap, __u8 flbas,
-                  __u8 dps, __u8 nmic, __u32 *result)
+                  __u8 dps, __u8 nmic, __u32 timeout, __u32 *result)
 {
        struct nvme_id_ns ns = {
                .nsze           = cpu_to_le64(nsze),
@@ -689,6 +715,7 @@ int nvme_ns_create(int fd, __u64 nsze, __u64 ncap, __u8 flbas,
                .addr           = (__u64)(uintptr_t) ((void *)&ns),
                .cdw10          = 0,
                .data_len       = 0x1000,
+               .timeout_ms     = timeout,
        };
        int err;
 
@@ -698,12 +725,13 @@ int nvme_ns_create(int fd, __u64 nsze, __u64 ncap, __u8 flbas,
        return err;
 }
 
-int nvme_ns_delete(int fd, __u32 nsid)
+int nvme_ns_delete(int fd, __u32 nsid, __u32 timeout)
 {
        struct nvme_admin_cmd cmd = {
                .opcode         = nvme_admin_ns_mgmt,
                .nsid           = nsid,
                .cdw10          = 1,
+               .timeout_ms     = timeout,
        };
 
        return nvme_submit_admin_passthru(fd, &cmd);
@@ -803,6 +831,21 @@ int nvme_sec_recv(int fd, __u32 nsid, __u8 nssf, __u16 spsp,
        return err;
 }
 
+int nvme_get_lba_status(int fd, __u64 slba, __u32 mndw, __u8 atype, __u16 rl,
+               void *data)
+{
+       struct nvme_admin_cmd cmd = {
+               .opcode =  nvme_admin_get_lba_status,
+               .addr = (__u64)(uintptr_t) data,
+               .cdw10 = slba & 0xffffffff,
+               .cdw11 = slba >> 32,
+               .cdw12 = mndw,
+               .cdw13 = (atype << 24) | rl,
+       };
+
+       return nvme_submit_admin_passthru(fd, &cmd);
+}
+
 int nvme_dir_send(int fd, __u32 nsid, __u16 dspec, __u8 dtype, __u8 doper,
                   __u32 data_len, __u32 dw12, void *data, __u32 *result)
 {
@@ -867,3 +910,19 @@ int nvme_self_test_start(int fd, __u32 nsid, __u32 cdw10)
 
        return nvme_submit_admin_passthru(fd, &cmd);
 }
+
+int nvme_virtual_mgmt(int fd, __u32 cdw10, __u32 cdw11, __u32 *result)
+{
+       struct nvme_admin_cmd cmd = {
+               .opcode = nvme_admin_virtual_mgmt,
+               .cdw10  = cdw10,
+               .cdw11  = cdw11,
+       };
+       int err;
+
+       err = nvme_submit_admin_passthru(fd, &cmd);
+       if (!err && result)
+               *result = cmd.result;
+
+       return err;
+}
index 3fb740c..565f764 100644 (file)
@@ -6,6 +6,8 @@
 #include "linux/nvme_ioctl.h"
 #include "nvme.h"
 
+#define NVME_IOCTL_TIMEOUT 120000 /* in milliseconds */
+
 int nvme_get_nsid(int fd);
 
 /* Generic passthrough */
@@ -36,6 +38,9 @@ int nvme_compare(int fd, __u64 slba, __u16 nblocks, __u16 control,
                 __u32 dsmgmt, __u32 reftag, __u16 apptag,
                 __u16 appmask, void *data, void *metadata);
 
+int nvme_verify(int fd, __u32 nsid, __u64 slba, __u16 nblocks,
+               __u16 control, __u32 reftag, __u16 apptag, __u16 appmask);
+
 /* NVME_IO_CMD */
 int nvme_passthru_io(int fd, __u8 opcode, __u8 flags, __u16 rsvd,
                     __u32 nsid, __u32 cdw2, __u32 cdw3,
@@ -73,11 +78,22 @@ int nvme_identify_ns_list(int fd, __u32 nsid, bool all, void *data);
 int nvme_identify_ctrl_list(int fd, __u32 nsid, __u16 cntid, void *data);
 int nvme_identify_ns_descs(int fd, __u32 nsid, void *data);
 int nvme_identify_nvmset(int fd, __u16 nvmset_id, void *data);
-int nvme_get_log13(int fd, __u32 nsid, __u8 log_id, __u8 lsp, __u64 lpo,
-                  __u16 group_id, bool rae, __u32 data_len, void *data);
+int nvme_identify_uuid(int fd, void *data);
+int nvme_identify_secondary_ctrl_list(int fd, __u32 nsid, __u16 cntid, void *data);
+int nvme_identify_ns_granularity(int fd, void *data);
 int nvme_get_log(int fd, __u32 nsid, __u8 log_id, bool rae,
                 __u32 data_len, void *data);
-
+int nvme_get_log14(int fd, __u32 nsid, __u8 log_id, __u8 lsp, __u64 lpo,
+                  __u16 group_id, bool rae, __u8 uuid_ix,
+                  __u32 data_len, void *data);
+
+static inline int nvme_get_log13(int fd, __u32 nsid, __u8 log_id, __u8 lsp,
+                                __u64 lpo, __u16 lsi, bool rae, __u32 data_len,
+                                void *data)
+{
+       return nvme_get_log14(fd, nsid, log_id, lsp, lpo, lsi, rae, 0,
+                             data_len, data);
+}
 
 int nvme_get_telemetry_log(int fd, void *lp, int generate_report,
                           int ctrl_gen, size_t log_page_size, __u64 offset);
@@ -105,8 +121,8 @@ int nvme_format(int fd, __u32 nsid, __u8 lbaf, __u8 ses, __u8 pi,
                __u8 pil, __u8 ms, __u32 timeout);
 
 int nvme_ns_create(int fd, __u64 nsze, __u64 ncap, __u8 flbas,
-                  __u8 dps, __u8 nmic, __u32 *result);
-int nvme_ns_delete(int fd, __u32 nsid);
+                  __u8 dps, __u8 nmic, __u32 timeout, __u32 *result);
+int nvme_ns_delete(int fd, __u32 nsid, __u32 timeout);
 
 int nvme_ns_attachment(int fd, __u32 nsid, __u16 num_ctrls,
                       __u16 *ctrlist, bool attach);
@@ -125,15 +141,18 @@ int nvme_subsystem_reset(int fd);
 int nvme_reset_controller(int fd);
 int nvme_ns_rescan(int fd);
 
+int nvme_get_lba_status(int fd, __u64 slba, __u32 mndw, __u8 atype, __u16 rl,
+               void *data);
 int nvme_dir_send(int fd, __u32 nsid, __u16 dspec, __u8 dtype, __u8 doper,
                  __u32 data_len, __u32 dw12, void *data, __u32 *result);
 int nvme_dir_recv(int fd, __u32 nsid, __u16 dspec, __u8 dtype, __u8 doper,
                  __u32 data_len, __u32 dw12, void *data, __u32 *result);
 int nvme_get_properties(int fd, void **pbar);
-int nvme_set_property(int fd, int offset, int value);
+int nvme_set_property(int fd, int offset, uint64_t value);
 int nvme_get_property(int fd, int offset, uint64_t *value);
 int nvme_sanitize(int fd, __u8 sanact, __u8 ause, __u8 owpass, __u8 oipbp,
                  __u8 no_dealloc, __u32 ovrpat);
 int nvme_self_test_start(int fd, __u32 nsid, __u32 cdw10);
 int nvme_self_test_log(int fd, struct nvme_self_test_log *self_test_log);
+int nvme_virtual_mgmt(int fd, __u32 cdw10, __u32 cdw11, __u32 *result);
 #endif                         /* _NVME_LIB_H */
index 685d179..7e0278b 100644 (file)
@@ -40,16 +40,16 @@ struct nvme_effects_log_page {
 };
 
 struct nvme_error_log_page {
-       __u64   error_count;
-       __u16   sqid;
-       __u16   cmdid;
-       __u16   status_field;
-       __u16   parm_error_location;
-       __u64   lba;
-       __u32   nsid;
+       __le64  error_count;
+       __le16  sqid;
+       __le16  cmdid;
+       __le16  status_field;
+       __le16  parm_error_location;
+       __le64  lba;
+       __le32  nsid;
        __u8    vs;
        __u8    resv[3];
-       __u64   cs;
+       __le64  cs;
        __u8    resv2[24];
 };
 
@@ -87,13 +87,30 @@ struct nvme_controller_list {
        __le16 identifier[];
 };
 
+struct nvme_secondary_controller_entry {
+       __le16 scid;    /* Secondary Controller Identifier */
+       __le16 pcid;    /* Primary Controller Identifier */
+       __u8   scs;     /* Secondary Controller State */
+       __u8   rsvd5[3];
+       __le16 vfn;     /* Virtual Function Number */
+       __le16 nvq;     /* Number of VQ Flexible Resources Assigned */
+       __le16 nvi;     /* Number of VI Flexible Resources Assigned */
+       __u8   rsvd14[18];
+};
+
+struct nvme_secondary_controllers_list {
+       __u8   num;
+       __u8   rsvd[31];
+       struct nvme_secondary_controller_entry sc_entry[127];
+};
+
 struct nvme_bar_cap {
        __u16   mqes;
        __u8    ams_cqr;
        __u8    to;
        __u16   bps_css_nssrs_dstrd;
        __u8    mpsmax_mpsmin;
-       __u8    reserved;
+       __u8    rsvd_pmrs;
 };
 
 #ifdef __CHECKER__
@@ -102,19 +119,31 @@ struct nvme_bar_cap {
 #define __force
 #endif
 
-#define cpu_to_le16(x) \
-       ((__force __le16)htole16(x))
-#define cpu_to_le32(x) \
-       ((__force __le32)htole32(x))
-#define cpu_to_le64(x) \
-       ((__force __le64)htole64(x))
-
-#define le16_to_cpu(x) \
-       le16toh((__force __u16)(x))
-#define le32_to_cpu(x) \
-       le32toh((__force __u32)(x))
-#define le64_to_cpu(x) \
-       le64toh((__force __u64)(x))
+static inline __le16 cpu_to_le16(uint16_t x)
+{
+       return (__force __le16)htole16(x);
+}
+static inline __le32 cpu_to_le32(uint32_t x)
+{
+       return (__force __le32)htole32(x);
+}
+static inline __le64 cpu_to_le64(uint64_t x)
+{
+       return (__force __le64)htole64(x);
+}
+
+static inline uint16_t le16_to_cpu(__le16 x)
+{
+       return le16toh((__force __u16)x);
+}
+static inline uint32_t le32_to_cpu(__le32 x)
+{
+       return le32toh((__force __u32)x);
+}
+static inline uint64_t le64_to_cpu(__le64 x)
+{
+       return le64toh((__force __u64)x);
+}
 
 #define MAX_LIST_ITEMS 256
 struct list_item {
@@ -131,6 +160,10 @@ struct ctrl_list_item {
        char *transport;
        char *state;
        char *ana_state;
+       char *subsysnqn;
+       char *traddr;
+       char *trsvcid;
+       char *host_traddr;
 };
 
 struct subsys_list_item {
@@ -146,6 +179,26 @@ enum {
        BINARY,
 };
 
+struct connect_args {
+       char *subsysnqn;
+       char *transport;
+       char *traddr;
+       char *trsvcid;
+       char *host_traddr;
+};
+
+#define SYS_NVME               "/sys/class/nvme"
+
+bool ctrl_matches_connectargs(char *name, struct connect_args *args);
+char *find_ctrl_with_connectargs(struct connect_args *args);
+char *__parse_connect_arg(char *conargs, const char delim, const char *fieldnm);
+
+extern const char *conarg_nqn;
+extern const char *conarg_transport;
+extern const char *conarg_traddr;
+extern const char *conarg_trsvcid;
+extern const char *conarg_host_traddr;
+
 void register_extension(struct plugin *plugin);
 
 #include "argconfig.h"
@@ -160,4 +213,28 @@ int        validate_output_format(char *format);
 struct subsys_list_item *get_subsys_list(int *subcnt, char *subsysnqn, __u32 nsid);
 void free_subsys_list(struct subsys_list_item *slist, int n);
 char *nvme_char_from_block(char *block);
+
+/*
+ * is_64bit_reg - It checks whether given offset of the controller register is
+ *                64bit or not.
+ * @offset: offset of controller register field in bytes
+ *
+ * It gives true if given offset is 64bit register, otherwise it returns false.
+ *
+ * Notes:  This function does not care about transport so that the offset is
+ * not going to be checked inside of this function for the unsupported fields
+ * in a specific transport.  For example, BPMBL(Boot Partition Memory Buffer
+ * Location) register is not supported by fabrics, but it can be chcked here.
+ */
+static inline bool is_64bit_reg(__u32 offset)
+{
+       if (offset == NVME_REG_CAP ||
+                       offset == NVME_REG_ASQ ||
+                       offset == NVME_REG_ACQ ||
+                       offset == NVME_REG_BPMBL)
+               return true;
+
+       return false;
+}
+
 #endif /* _NVME_H */
index e00c5ff..d478b17 100644 (file)
@@ -124,7 +124,7 @@ find_keyword(vector keywords, vector v, char * name)
 {
        struct keyword *keyword;
        int i;
-       int len;
+       size_t len;
 
        if (!name || !keywords)
                return NULL;
index 8f7c6b1..02cafdc 100644 (file)
@@ -54,7 +54,7 @@ int get_pgpolicy_name(char * buff, int len, int id)
                s = "undefined";
                break;
        }
-       return snprintf(buff, POLICY_NAME_SIZE, "%s", s);
+       return snprintf(buff, len, "%s", s);
 }
 
 
index 907469a..b944ef3 100644 (file)
@@ -29,6 +29,7 @@
 #include "uevent.h"
 #include "debug.h"
 #include "discovery.h"
+#include "util.h"
 
 #define MAX(x,y) (((x) > (y)) ? (x) : (y))
 #define MIN(x,y) (((x) > (y)) ? (y) : (x))
@@ -36,7 +37,7 @@
 #define NOPAD    s = c
 #define PAD(x) \
 do { \
-       while ((int)(c - s) < (x) && (c < (line + len - 1))) \
+       while (c < (s + x) && (c < (line + len - 1))) \
                *c++ = ' '; \
        s = c; \
 } while (0)
@@ -181,9 +182,10 @@ snprint_queueing (char * buff, size_t len, const struct multipath * mpp)
                return snprintf(buff, len, "-");
        else if (mpp->no_path_retry > 0) {
                if (mpp->retry_tick > 0)
+
                        return snprintf(buff, len, "%i sec",
                                        mpp->retry_tick);
-               else if (mpp->retry_tick == 0 && mpp->nr_active > 0)
+               else if (mpp->retry_tick == 0 && count_active_paths(mpp) > 0)
                        return snprintf(buff, len, "%i chk",
                                        mpp->no_path_retry);
                else
@@ -195,7 +197,7 @@ snprint_queueing (char * buff, size_t len, const struct multipath * mpp)
 static int
 snprint_nb_paths (char * buff, size_t len, const struct multipath * mpp)
 {
-       return snprint_int(buff, len, mpp->nr_active);
+       return snprint_int(buff, len, count_active_paths(mpp));
 }
 
 static int
@@ -334,7 +336,8 @@ snprint_multipath_rev (char * buff, size_t len, const struct multipath * mpp)
 }
 
 static int
-snprint_multipath_foreign (char * buff, size_t len, const struct multipath * pp)
+snprint_multipath_foreign (char * buff, size_t len,
+                          __attribute__((unused)) const struct multipath * pp)
 {
        return snprintf(buff, len, "%s", "--");
 }
@@ -358,6 +361,21 @@ snprint_action (char * buff, size_t len, const struct multipath * mpp)
        }
 }
 
+static int
+snprint_multipath_vpd_data(char * buff, size_t len,
+                          const struct multipath * mpp)
+{
+       struct pathgroup * pgp;
+       struct path * pp;
+       int i, j;
+
+       vector_foreach_slot(mpp->pg, pgp, i)
+               vector_foreach_slot(pgp->paths, pp, j)
+                       if (pp->vpd_data)
+                               return snprintf(buff, len, "%s", pp->vpd_data);
+       return snprintf(buff, len, "[undef]");
+}
+
 /*
  * path info printing functions
  */
@@ -627,7 +645,8 @@ snprint_path_checker (char * buff, size_t len, const struct path * pp)
 }
 
 static int
-snprint_path_foreign (char * buff, size_t len, const struct path * pp)
+snprint_path_foreign (char * buff, size_t len,
+                     __attribute__((unused)) const struct path * pp)
 {
        return snprintf(buff, len, "%s", "--");
 }
@@ -688,6 +707,14 @@ snprint_path_marginal(char * buff, size_t len, const struct path * pp)
        return snprintf(buff, len, "normal");
 }
 
+static int
+snprint_path_vpd_data(char * buff, size_t len, const struct path * pp)
+{
+       if (pp->vpd_data)
+               return snprintf(buff, len, "%s", pp->vpd_data);
+       return snprintf(buff, len, "[undef]");
+}
+
 struct multipath_data mpd[] = {
        {'n', "name",          0, snprint_name},
        {'w', "uuid",          0, snprint_multipath_uuid},
@@ -712,6 +739,7 @@ struct multipath_data mpd[] = {
        {'p', "prod",          0, snprint_multipath_prod},
        {'e', "rev",           0, snprint_multipath_rev},
        {'G', "foreign",       0, snprint_multipath_foreign},
+       {'g', "vpd page data", 0, snprint_multipath_vpd_data},
        {0, NULL, 0 , NULL}
 };
 
@@ -737,6 +765,7 @@ struct path_data pd[] = {
        {'r', "target WWPN",   0, snprint_tgt_wwpn},
        {'a', "host adapter",  0, snprint_host_adapter},
        {'G', "foreign",       0, snprint_path_foreign},
+       {'g', "vpd page data", 0, snprint_path_vpd_data},
        {'0', "failures",      0, snprint_path_failures},
        {'P', "protocol",      0, snprint_path_protocol},
        {0, NULL, 0 , NULL}
@@ -781,7 +810,7 @@ get_path_layout(vector pathvec, int header)
 }
 
 static void
-reset_width(int *width, enum layout_reset reset, const char *header)
+reset_width(unsigned int *width, enum layout_reset reset, const char *header)
 {
        switch (reset) {
        case LAYOUT_RESET_HEADER:
@@ -1337,8 +1366,8 @@ snprint_multipath_fields_json (char * buff, int len,
 }
 
 int
-snprint_multipath_map_json (char * buff, int len,
-               const struct multipath * mpp, int last){
+snprint_multipath_map_json (char * buff, int len, const struct multipath * mpp)
+{
        int fwd = 0;
 
        fwd +=  snprint_json_header(buff, len);
@@ -2004,7 +2033,6 @@ int snprint_devices(struct config *conf, char * buff, int len,
        struct dirent *blkdev;
        struct stat statbuf;
        char devpath[PATH_MAX];
-       char *devptr;
        int threshold = MAX_LINE_LEN;
        int fwd = 0;
        int r;
@@ -2020,15 +2048,14 @@ int snprint_devices(struct config *conf, char * buff, int len,
        }
        fwd += snprintf(buff + fwd, len - fwd, "available block devices:\n");
 
-       strcpy(devpath,"/sys/block/");
        while ((blkdev = readdir(blkdir)) != NULL) {
                if ((strcmp(blkdev->d_name,".") == 0) ||
                    (strcmp(blkdev->d_name,"..") == 0))
                        continue;
 
-               devptr = devpath + 11;
-               *devptr = '\0';
-               strncat(devptr, blkdev->d_name, PATH_MAX-12);
+               if (safe_sprintf(devpath, "/sys/block/%s", blkdev->d_name))
+                       continue;
+
                if (stat(devpath, &statbuf) < 0)
                        continue;
 
@@ -2040,11 +2067,12 @@ int snprint_devices(struct config *conf, char * buff, int len,
                        return len;
                }
 
-               fwd += snprintf(buff + fwd, len - fwd, "    %s", devptr);
-               pp = find_path_by_dev(vecs->pathvec, devptr);
+               fwd += snprintf(buff + fwd, len - fwd, "    %s",
+                               blkdev->d_name);
+               pp = find_path_by_dev(vecs->pathvec, blkdev->d_name);
                if (!pp) {
                        r = filter_devnode(conf->blist_devnode,
-                                          conf->elist_devnode, devptr);
+                                          conf->elist_devnode, blkdev->d_name);
                        if (r > 0)
                                fwd += snprintf(buff + fwd, len - fwd,
                                                " devnode blacklisted, unmonitored");
index 7e36ec6..e8260d0 100644 (file)
 struct path_data {
        char wildcard;
        char * header;
-       int width;
+       unsigned int width;
        int (*snprint)(char * buff, size_t len, const struct path * pp);
 };
 
 struct multipath_data {
        char wildcard;
        char * header;
-       int width;
+       unsigned int width;
        int (*snprint)(char * buff, size_t len, const struct multipath * mpp);
 };
 
 struct pathgroup_data {
        char wildcard;
        char * header;
-       int width;
+       unsigned int width;
        int (*snprint)(char * buff, size_t len, const struct pathgroup * pgp);
 };
 
@@ -125,7 +125,7 @@ char *snprint_config(const struct config *conf, int *len,
                     const struct _vector *hwtable,
                     const struct _vector *mpvec);
 int snprint_multipath_map_json (char * buff, int len,
-                               const struct multipath * mpp, int last);
+                               const struct multipath * mpp);
 int snprint_blacklist_report (struct config *, char *, int);
 int snprint_wildcards (char *, int);
 int snprint_status (char *, int, const struct vectors *);
index 87de1f9..194563c 100644 (file)
 
 static LIST_HEAD(prioritizers);
 
-unsigned int get_prio_timeout(unsigned int checker_timeout,
+unsigned int get_prio_timeout(unsigned int timeout_ms,
                              unsigned int default_timeout)
 {
-       if (checker_timeout)
-               return checker_timeout * 1000;
+       if (timeout_ms)
+               return timeout_ms;
        return default_timeout;
 }
 
index 271a019..bbf5aac 100644 (file)
@@ -371,7 +371,7 @@ get_asymmetric_access_state(const struct path *pp, unsigned int tpg,
        struct rtpg_data *      tpgd;
        struct rtpg_tpg_dscr *  dscr;
        int                     rc;
-       int                     buflen;
+       unsigned int            buflen;
        uint64_t                scsi_buflen;
        int fd = pp->fd;
 
index 2673d9d..b5c7873 100644 (file)
@@ -62,7 +62,7 @@ static const char *anas_string[] = {
 static const char *aas_print_string(int rc)
 {
        rc &= 0xff;
-       if (rc >= 0 && rc < ARRAY_SIZE(anas_string) &&
+       if (rc >= 0 && rc < (int)ARRAY_SIZE(anas_string) &&
            anas_string[rc] != NULL)
                return anas_string[rc];
 
@@ -78,7 +78,8 @@ static int get_ana_state(__u32 nsid, __u32 anagrpid, void *ana_log,
        size_t offset = sizeof(struct nvme_ana_rsp_hdr);
        __u32 nr_nsids;
        size_t nsid_buf_size;
-       int i, j;
+       int i;
+       unsigned int j;
 
        for (i = 0; i < le16_to_cpu(hdr->ngrps); i++) {
                ana_desc = base + offset;
@@ -106,7 +107,7 @@ static int get_ana_state(__u32 nsid, __u32 anagrpid, void *ana_log,
        return -ANA_ERR_GETANAS_NOTFOUND;
 }
 
-int get_ana_info(struct path * pp, unsigned int timeout)
+static int get_ana_info(struct path * pp)
 {
        int     rc;
        __u32 nsid;
@@ -202,14 +203,15 @@ int get_ana_info(struct path * pp, unsigned int timeout)
  * - ALUA's LBA-dependent state has no ANA equivalent.
  */
 
-int getprio(struct path *pp, char *args, unsigned int timeout)
+int getprio(struct path *pp, __attribute__((unused)) char *args,
+           __attribute__((unused)) unsigned int timeout)
 {
        int rc;
 
        if (pp->fd < 0)
                rc = -ANA_ERR_NO_INFORMATION;
        else
-               rc = get_ana_info(pp, timeout);
+               rc = get_ana_info(pp);
 
        switch (rc) {
        case NVME_ANA_OPTIMIZED:
@@ -224,7 +226,7 @@ int getprio(struct path *pp, char *args, unsigned int timeout)
        default:
                break;
        }
-       if (rc < 0 && -rc < ARRAY_SIZE(ana_errmsg))
+       if (rc < 0 && -rc < (int)ARRAY_SIZE(ana_errmsg))
                condlog(2, "%s: ANA error: %s", pp->dev, ana_errmsg[-rc]);
        else
                condlog(1, "%s: invalid ANA rc code %d", pp->dev, rc);
index aad6927..059d859 100644 (file)
@@ -2,7 +2,9 @@
 
 #include "prio.h"
 
-int getprio(struct path * pp, char * args, unsigned int timeout)
+int getprio(__attribute__((unused)) struct path * pp,
+           __attribute__((unused)) char * args,
+           __attribute__((unused)) unsigned int timeout)
 {
        return 1;
 }
index 59c9816..02dc2e2 100644 (file)
@@ -98,7 +98,8 @@ int datacore_prio (const char *dev, int sg_fd, char * args)
        return 0;
 }
 
-int getprio(struct path * pp, char * args, unsigned int timeout)
+int getprio(struct path * pp, char * args,
+           __attribute__((unused)) unsigned int timeout)
 {
        return datacore_prio(pp->dev, pp->fd, args);
 }
index a2f7487..3b63cca 100644 (file)
@@ -81,7 +81,8 @@ out:
        return(ret);
 }
 
-int getprio (struct path * pp, char * args, unsigned int timeout)
+int getprio (struct path *pp, __attribute__((unused)) char *args,
+            unsigned int timeout)
 {
        return emc_clariion_prio(pp->dev, pp->fd, timeout);
 }
index 70fb5d1..88cac5f 100644 (file)
@@ -168,7 +168,8 @@ int hds_modular_prio (const char *dev, int fd, unsigned int timeout)
        return -1;
 }
 
-int getprio (struct path * pp, char * args, unsigned int timeout)
+int getprio (struct path * pp, __attribute__((unused)) char *args,
+            unsigned int timeout)
 {
        return hds_modular_prio(pp->dev, pp->fd, timeout);
 }
index 6b0ed39..5b85ad2 100644 (file)
@@ -95,7 +95,8 @@ out:
        return(ret);
 }
 
-int getprio (struct path * pp, char * args, unsigned int timeout)
+int getprio (struct path *pp, __attribute__((unused)) char *args,
+            unsigned int timeout)
 {
        return hp_sw_prio(pp->dev, pp->fd, timeout);
 }
index a4ea61e..e98773c 100644 (file)
@@ -138,7 +138,8 @@ int iet_prio(const char *dev, char * args)
        return 10;
 }
 
-int getprio(struct path * pp, char * args, unsigned int timeout)
+int getprio(struct path * pp, char * args,
+           __attribute__((unused)) unsigned int timeout)
 {
        return iet_prio(pp->dev, args);
 }
index 6505033..262e69d 100644 (file)
@@ -241,7 +241,8 @@ prio_select:
        }
 }
 
-int getprio (struct path * pp, char * args, unsigned int timeout)
+int getprio (struct path *pp, __attribute__((unused)) char *args,
+            unsigned int timeout)
 {
        return ontap_prio(pp->dev, pp->fd, timeout);
 }
index 4a27123..b742ac2 100644 (file)
@@ -5,7 +5,9 @@
 
 #include "prio.h"
 
-int getprio(struct path * pp, char * args, unsigned int timeout)
+int getprio(__attribute__((unused)) struct path *pp,
+           __attribute__((unused)) char *args,
+           __attribute__((unused)) unsigned int timeout)
 {
        struct timeval tv;
 
index f5df032..92a2fb8 100644 (file)
@@ -91,7 +91,8 @@ out:
        return(ret);
 }
 
-int getprio (struct path * pp, char * args, unsigned int timeout)
+int getprio (struct path *pp, __attribute__((unused)) char *args,
+            unsigned int timeout)
 {
        return rdac_prio(pp->dev, pp->fd, timeout);
 }
index ff567df..a6feb42 100644 (file)
@@ -36,7 +36,8 @@ int get_exclusive_pref_arg(char *args)
        return 1;
 }
 
-int getprio (struct path * pp, char * args, unsigned int timeout)
+int getprio (struct path * pp, char *args,
+            __attribute__((unused)) unsigned int timeout)
 {
        int prio = 0, rc, i;
        char buff[512];
index e0f3efb..916970d 100644 (file)
@@ -143,7 +143,8 @@ int prio_path_weight(struct path *pp, char *prio_args)
        return priority;
 }
 
-int getprio(struct path *pp, char *args, unsigned int timeout)
+int getprio(struct path *pp, char *args,
+           __attribute__((unused)) unsigned int timeout)
 {
        return prio_path_weight(pp, args);
 }
index 27e8d68..897e48c 100644 (file)
@@ -432,12 +432,26 @@ int select_hwhandler(struct config *conf, struct multipath *mp)
        static const char tpgs_origin[]= "(setting: autodetected from TPGS)";
        char *dh_state;
        int i;
-       bool all_tpgs = true;
+       bool all_tpgs = true, one_tpgs = false;
 
        dh_state = &handler[2];
 
-       vector_foreach_slot(mp->paths, pp, i)
-               all_tpgs = all_tpgs && (path_get_tpgs(pp) > 0);
+       /*
+        * TPGS_UNDEF means that ALUA support couldn't determined either way
+        * yet, probably because the path was always down.
+        * If at least one path does have TPGS support, and no path has
+        * TPGS_NONE, assume that TPGS would be supported by all paths if
+        * all were up.
+        */
+       vector_foreach_slot(mp->paths, pp, i) {
+               int tpgs = path_get_tpgs(pp);
+
+               all_tpgs = all_tpgs && tpgs != TPGS_NONE;
+               one_tpgs = one_tpgs ||
+                       (tpgs != TPGS_NONE && tpgs != TPGS_UNDEF);
+       }
+       all_tpgs = all_tpgs && one_tpgs;
+
        if (mp->retain_hwhandler != RETAIN_HWHANDLER_OFF) {
                vector_foreach_slot(mp->paths, pp, i) {
                        if (get_dh_state(pp, dh_state, sizeof(handler) - 2) > 0
@@ -490,7 +504,7 @@ check_rdac(struct path * pp)
        if (__do_set_from_hwe(checker_name, pp, checker_name) &&
            strcmp(checker_name, RDAC))
                return 0;
-       len = get_vpd_sgio(pp->fd, 0xC9, buff, 44);
+       len = get_vpd_sgio(pp->fd, 0xC9, 0, buff, 44);
        if (len <= 0)
                return 0;
        return !(memcmp(buff + 4, "vac1", 4));
@@ -1203,3 +1217,21 @@ out:
                origin);
        return 0;
 }
+
+int select_vpd_vendor_id (struct path *pp)
+{
+       const char *origin;
+
+       pp_set_hwe(vpd_vendor_id);
+       pp_set_default(vpd_vendor_id, 0);
+out:
+       if (pp->vpd_vendor_id < 0 || pp->vpd_vendor_id >= VPD_VP_ARRAY_SIZE) {
+               condlog(3, "%s: vpd_vendor_id = %d (invalid, setting to 0)",
+                       pp->dev, pp->vpd_vendor_id);
+               pp->vpd_vendor_id = 0;
+       }
+       condlog(3, "%s: vpd_vendor_id = %d \"%s\" %s", pp->dev,
+               pp->vpd_vendor_id, vpd_vendor_pages[pp->vpd_vendor_id].name,
+               origin);
+       return 0;
+}
index ddfd626..3d6edd8 100644 (file)
@@ -37,3 +37,4 @@ void reconcile_features_with_options(const char *id, char **features,
                                     int* no_path_retry,
                                     int *retain_hwhandler);
 int select_all_tg_pt (struct config *conf, struct multipath * mp);
+int select_vpd_vendor_id (struct path *pp);
index bf7fdd7..2dd378c 100644 (file)
@@ -131,6 +131,9 @@ free_path (struct path * pp)
                udev_device_unref(pp->udev);
                pp->udev = NULL;
        }
+       if (pp->vpd_data)
+               free(pp->vpd_data);
+
        vector_free(pp->hwe);
 
        FREE(pp);
@@ -350,7 +353,7 @@ store_adaptergroup(vector adapters, struct adapter_group * agp)
 }
 
 struct multipath *
-find_mp_by_minor (const struct _vector *mpvec, int minor)
+find_mp_by_minor (const struct _vector *mpvec, unsigned int minor)
 {
        int i;
        struct multipath * mpp;
@@ -388,7 +391,7 @@ struct multipath *
 find_mp_by_alias (const struct _vector *mpvec, const char * alias)
 {
        int i;
-       int len;
+       size_t len;
        struct multipath * mpp;
 
        if (!mpvec)
@@ -478,6 +481,25 @@ int pathcount(const struct multipath *mpp, int state)
        return count;
 }
 
+int count_active_paths(const struct multipath *mpp)
+{
+       struct pathgroup *pgp;
+       struct path *pp;
+       int count = 0;
+       int i, j;
+
+       if (!mpp->pg)
+               return 0;
+
+       vector_foreach_slot (mpp->pg, pgp, i) {
+               vector_foreach_slot (pgp->paths, pp, j) {
+                       if (pp->state == PATH_UP || pp->state == PATH_GHOST)
+                               count++;
+               }
+       }
+       return count;
+}
+
 int pathcmp(const struct pathgroup *pgp, const struct pathgroup *cpgp)
 {
        int i, j;
index a3adf90..9bd39eb 100644 (file)
@@ -3,6 +3,7 @@
 
 #include <sys/types.h>
 #include <inttypes.h>
+#include <stdbool.h>
 
 #include "prio.h"
 #include "byteorder.h"
@@ -21,6 +22,7 @@
 #define HOST_NAME_LEN          16
 #define SLOT_NAME_SIZE         40
 #define PRKEY_SIZE             19
+#define VPD_DATA_SIZE          128
 
 #define SCSI_VENDOR_SIZE       9
 #define SCSI_PRODUCT_SIZE      17
@@ -106,7 +108,7 @@ enum yes_no_undef_states {
  * _FIND_MULTIPATHS_F must have the same value as YNU_YES.
  * Generate a compile time error if that isn't the case.
  */
-char ___error1___[-(_FIND_MULTIPATHS_F != YNU_YES)];
+extern char ___error1___[-(_FIND_MULTIPATHS_F != YNU_YES)];
 
 #define find_multipaths_on(conf) \
        (!!((conf)->find_multipaths & _FIND_MULTIPATHS_F))
@@ -221,6 +223,18 @@ enum all_tg_pt_states {
        ALL_TG_PT_ON = YNU_YES,
 };
 
+enum vpd_vendor_ids {
+       VPD_VP_UNDEF,
+       VPD_VP_HP3PAR,
+       VPD_VP_ARRAY_SIZE, /* This must remain the last entry */
+};
+
+struct vpd_vendor_page {
+       int pg;
+       const char *name;
+};
+extern struct vpd_vendor_page vpd_vendor_pages[VPD_VP_ARRAY_SIZE];
+
 struct sg_id {
        int host_no;
        int channel;
@@ -255,6 +269,7 @@ struct path {
        char rev[PATH_REV_SIZE];
        char serial[SERIAL_SIZE];
        char tgt_node_name[NODE_NAME_SIZE];
+       char *vpd_data;
        unsigned long long size;
        unsigned int checkint;
        unsigned int tick;
@@ -272,7 +287,6 @@ struct path {
        char * uid_attribute;
        char * getuid;
        struct prio prio;
-       char * prio_args;
        struct checker checker;
        struct multipath * mpp;
        int fd;
@@ -288,6 +302,7 @@ struct path {
        int io_err_pathfail_starttime;
        int find_multipaths_timeout;
        int marginal;
+       int vpd_vendor_id;
        /* configlet pointers */
        vector hwe;
        struct gen_path generic_path;
@@ -309,7 +324,6 @@ struct multipath {
        int pgfailback;
        int failback_tick;
        int rr_weight;
-       int nr_active;     /* current available(= not known as failed) paths */
        int no_path_retry; /* number of retries after all paths are down */
        int retry_tick;    /* remaining times for retries */
        int disable_queueing;
@@ -319,6 +333,7 @@ struct multipath {
        int fast_io_fail;
        int retain_hwhandler;
        int deferred_remove;
+       bool in_recovery;
        int san_path_err_threshold;
        int san_path_err_forget_rate;
        int san_path_err_recovery_time;
@@ -440,7 +455,8 @@ int add_pathgroup(struct multipath*, struct pathgroup *);
 struct multipath * find_mp_by_alias (const struct _vector *mp, const char *alias);
 struct multipath * find_mp_by_wwid (const struct _vector *mp, const char *wwid);
 struct multipath * find_mp_by_str (const struct _vector *mp, const char *wwid);
-struct multipath * find_mp_by_minor (const struct _vector *mp, int minor);
+struct multipath * find_mp_by_minor (const struct _vector *mp,
+                                    unsigned int minor);
 
 struct path * find_path_by_devt (const struct _vector *pathvec, const char *devt);
 struct path * find_path_by_dev (const struct _vector *pathvec, const char *dev);
@@ -448,6 +464,7 @@ struct path * first_path (const struct multipath *mpp);
 
 int pathcountgr (const struct pathgroup *, int);
 int pathcount (const struct multipath *, int);
+int count_active_paths(const struct multipath *);
 int pathcmp (const struct pathgroup *, const struct pathgroup *);
 int add_feature (char **, const char *);
 int remove_feature (char **, const char *);
index c43b58f..3dbbaa0 100644 (file)
@@ -290,10 +290,15 @@ update_multipath_strings(struct multipath *mpp, vector pathvec, int is_daemon)
        return 0;
 }
 
-void enter_recovery_mode(struct multipath *mpp)
+static void enter_recovery_mode(struct multipath *mpp)
 {
-       int checkint;
-       struct config *conf = get_multipath_config();
+       unsigned int checkint;
+       struct config *conf;
+
+       if (mpp->in_recovery || mpp->no_path_retry <= 0)
+               return;
+
+       conf = get_multipath_config();
        checkint = conf->checkint;
        put_multipath_config(conf);
 
@@ -302,12 +307,68 @@ void enter_recovery_mode(struct multipath *mpp)
         * meaning of +1: retry_tick may be decremented in checkerloop before
         * starting retry.
         */
+       mpp->in_recovery = true;
        mpp->stat_queueing_timeouts++;
        mpp->retry_tick = mpp->no_path_retry * checkint + 1;
        condlog(1, "%s: Entering recovery mode: max_retries=%d",
                mpp->alias, mpp->no_path_retry);
 }
 
+static void leave_recovery_mode(struct multipath *mpp)
+{
+       bool recovery = mpp->in_recovery;
+
+       mpp->in_recovery = false;
+       mpp->retry_tick = 0;
+
+       /*
+        * in_recovery is only ever set if mpp->no_path_retry > 0
+        * (see enter_recovery_mode()). But no_path_retry may have been
+        * changed while the map was recovering, so test it here again.
+        */
+       if (recovery && (mpp->no_path_retry == NO_PATH_RETRY_QUEUE ||
+                        mpp->no_path_retry > 0)) {
+               dm_queue_if_no_path(mpp->alias, 1);
+               condlog(2, "%s: queue_if_no_path enabled", mpp->alias);
+               condlog(1, "%s: Recovered to normal mode", mpp->alias);
+       }
+}
+
+void __set_no_path_retry(struct multipath *mpp, bool check_features)
+{
+       bool is_queueing;
+
+       check_features = check_features && mpp->features != NULL;
+       if (check_features)
+               is_queueing = strstr(mpp->features, "queue_if_no_path");
+
+       switch (mpp->no_path_retry) {
+       case NO_PATH_RETRY_UNDEF:
+               break;
+       case NO_PATH_RETRY_FAIL:
+               if (!check_features || is_queueing)
+                       dm_queue_if_no_path(mpp->alias, 0);
+               break;
+       case NO_PATH_RETRY_QUEUE:
+               if (!check_features || !is_queueing)
+                       dm_queue_if_no_path(mpp->alias, 1);
+               break;
+       default:
+               if (count_active_paths(mpp) > 0) {
+                       /*
+                        * If in_recovery is set, leave_recovery_mode() takes
+                        * care of dm_queue_if_no_path. Otherwise, do it here.
+                        */
+                       if ((!check_features || !is_queueing) &&
+                           !mpp->in_recovery)
+                               dm_queue_if_no_path(mpp->alias, 1);
+                       leave_recovery_mode(mpp);
+               } else
+                       enter_recovery_mode(mpp);
+               break;
+       }
+}
+
 void
 sync_map_state(struct multipath *mpp)
 {
@@ -450,25 +511,23 @@ int verify_paths(struct multipath *mpp, struct vectors *vecs)
  */
 void update_queue_mode_del_path(struct multipath *mpp)
 {
-       if (--mpp->nr_active == 0) {
-               if (mpp->no_path_retry > 0)
-                       enter_recovery_mode(mpp);
-               else if (mpp->no_path_retry != NO_PATH_RETRY_QUEUE)
+       int active = count_active_paths(mpp);
+
+       if (active == 0) {
+               enter_recovery_mode(mpp);
+               if (mpp->no_path_retry != NO_PATH_RETRY_QUEUE)
                        mpp->stat_map_failures++;
        }
-       condlog(2, "%s: remaining active paths: %d", mpp->alias, mpp->nr_active);
+       condlog(2, "%s: remaining active paths: %d", mpp->alias, active);
 }
 
 void update_queue_mode_add_path(struct multipath *mpp)
 {
-       if (mpp->nr_active++ == 0 && mpp->no_path_retry > 0) {
-               /* come back to normal mode from retry mode */
-               mpp->retry_tick = 0;
-               dm_queue_if_no_path(mpp->alias, 1);
-               condlog(2, "%s: queue_if_no_path enabled", mpp->alias);
-               condlog(1, "%s: Recovered to normal mode", mpp->alias);
-       }
-       condlog(2, "%s: remaining active paths: %d", mpp->alias, mpp->nr_active);
+       int active = count_active_paths(mpp);
+
+       if (active > 0)
+               leave_recovery_mode(mpp);
+       condlog(2, "%s: remaining active paths: %d", mpp->alias, active);
 }
 
 vector get_used_hwes(const struct _vector *pathvec)
index f8b9f63..2a5e3d6 100644 (file)
@@ -11,7 +11,8 @@ struct vectors {
        vector mpvec;
 };
 
-void enter_recovery_mode(struct multipath *mpp);
+void __set_no_path_retry(struct multipath *mpp, bool check_features);
+#define set_no_path_retry(mpp) __set_no_path_retry(mpp, true)
 
 int adopt_paths (vector pathvec, struct multipath * mpp);
 void orphan_paths(vector pathvec, struct multipath *mpp,
index 65904d7..62ec2ed 100644 (file)
@@ -88,7 +88,7 @@ ssize_t sysfs_attr_get_value(struct udev_device *dev, const char *attr_name,
                condlog(4, "read from %s failed: %s", devpath, strerror(errno));
                size = -errno;
                value[0] = '\0';
-       } else if (size == value_len) {
+       } else if (size == (ssize_t)value_len) {
                value[size - 1] = '\0';
                condlog(4, "overflow while reading from %s", devpath);
                size = 0;
@@ -146,7 +146,7 @@ ssize_t sysfs_bin_attr_get_value(struct udev_device *dev, const char *attr_name,
        if (size < 0) {
                condlog(4, "read from %s failed: %s", devpath, strerror(errno));
                size = -errno;
-       } else if (size == value_len) {
+       } else if (size == (ssize_t)value_len) {
                condlog(4, "overflow while reading from %s", devpath);
                size = 0;
        }
@@ -200,7 +200,7 @@ ssize_t sysfs_attr_set_value(struct udev_device *dev, const char *attr_name,
        if (size < 0) {
                condlog(4, "write to %s failed: %s", devpath, strerror(errno));
                size = -errno;
-       } else if (size < value_len) {
+       } else if (size < (ssize_t)value_len) {
                condlog(4, "tried to write %ld to %s. Wrote %ld",
                        (long)value_len, devpath, (long)size);
                size = 0;
@@ -306,7 +306,7 @@ bool sysfs_is_multipathed(const struct path *pp)
        n = snprintf(pathbuf, sizeof(pathbuf), "/sys/block/%s/holders",
                     pp->dev);
 
-       if (n >= sizeof(pathbuf)) {
+       if (n < 0 || (size_t)n >= sizeof(pathbuf)) {
                condlog(1, "%s: pathname overflow", __func__);
                return false;
        }
@@ -327,9 +327,8 @@ bool sysfs_is_multipathed(const struct path *pp)
                int nr;
                char uuid[6];
 
-               if (snprintf(pathbuf + n, sizeof(pathbuf) - n,
-                            "/%s/dm/uuid", di[i]->d_name)
-                   >= sizeof(pathbuf) - n)
+               if (safe_snprintf(pathbuf + n, sizeof(pathbuf) - n,
+                                 "/%s/dm/uuid", di[i]->d_name))
                        continue;
 
                fd = open(pathbuf, O_RDONLY);
index a3739a2..55f366c 100644 (file)
@@ -32,11 +32,11 @@ void pthread_cond_init_mono(pthread_cond_t *cond)
 void normalize_timespec(struct timespec *ts)
 {
        while (ts->tv_nsec < 0) {
-               ts->tv_nsec += 1000UL * 1000 * 1000;
+               ts->tv_nsec += 1000L * 1000 * 1000;
                ts->tv_sec--;
        }
-       while (ts->tv_nsec >= 1000UL * 1000 * 1000) {
-               ts->tv_nsec -= 1000UL * 1000 * 1000;
+       while (ts->tv_nsec >= 1000L * 1000 * 1000) {
+               ts->tv_nsec -= 1000L * 1000 * 1000;
                ts->tv_sec++;
        }
 }
index 8f7b2ef..d38e8a7 100644 (file)
@@ -108,7 +108,8 @@ uevq_cleanup(struct list_head *tmpq)
 static const char* uevent_get_env_var(const struct uevent *uev,
                                      const char *attr)
 {
-       int i, len;
+       int i;
+       size_t len;
        const char *p = NULL;
 
        if (attr == NULL)
@@ -853,7 +854,7 @@ int uevent_listen(struct udev *udev)
                poll_timeout = timeout * 1000;
                errno = 0;
                fdcount = poll(&ev_poll, 1, poll_timeout);
-               if (fdcount && ev_poll.revents & POLLIN) {
+               if (fdcount > 0 && ev_poll.revents & POLLIN) {
                        timeout = uevent_burst(&start_time, events + 1) ? 1 : 0;
                        dev = udev_monitor_receive_device(monitor);
                        if (!dev) {
index 68c0774..b9eaa7c 100644 (file)
@@ -10,14 +10,14 @@ static inline uint16_t get_unaligned_be16(const void *ptr)
        return p[0] << 8 | p[1];
 }
 
-static inline uint32_t get_unaligned_be32(void *ptr)
+static inline uint32_t get_unaligned_be32(const void *ptr)
 {
        const uint8_t *p = ptr;
 
        return p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3];
 }
 
-static inline uint64_t get_unaligned_be64(void *ptr)
+static inline uint64_t get_unaligned_be64(const void *ptr)
 {
        uint32_t low = get_unaligned_be32(ptr + 4);
        uint64_t high = get_unaligned_be32(ptr);
index 28cbf4b..51c38c8 100644 (file)
@@ -44,7 +44,7 @@ basenamecpy (const char *src, char *dst, size_t size)
        p = basename(src);
 
        for (e = p + strlen(p) - 1; e >= p && isspace(*e); --e) ;
-       if (e < p || e - p > size - 2)
+       if (e < p || (size_t)(e - p) > size - 2)
                return 0;
 
        strlcpy(dst, p, e - p + 2);
@@ -212,8 +212,7 @@ int devt2devname(char *devname, int devname_len, char *devt)
                        continue;
 
                if ((major == tmpmaj) && (minor == tmpmin)) {
-                       if (snprintf(block_path, sizeof(block_path),
-                                    "/sys/block/%s", dev) >= sizeof(block_path)) {
+                       if (safe_sprintf(block_path, "/sys/block/%s", dev)) {
                                condlog(0, "device name %s is too long", dev);
                                fclose(fd);
                                return 1;
@@ -428,7 +427,7 @@ int safe_write(int fd, const void *buf, size_t count)
        return 0;
 }
 
-void set_max_fds(int max_fds)
+void set_max_fds(rlim_t max_fds)
 {
        struct rlimit fd_limit;
 
index 693991c..56bd78c 100644 (file)
@@ -2,6 +2,8 @@
 #define _UTIL_H
 
 #include <sys/types.h>
+/* for rlim_t */
+#include <sys/resource.h>
 #include <inttypes.h>
 #include <stdbool.h>
 
@@ -21,15 +23,22 @@ 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);
+void set_max_fds(rlim_t max_fds);
 
 #define KERNEL_VERSION(maj, min, ptc) ((((maj) * 256) + (min)) * 256 + (ptc))
 #define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0]))
 
 #define safe_sprintf(var, format, args...)     \
-       snprintf(var, sizeof(var), format, ##args) >= sizeof(var)
+       safe_snprintf(var, sizeof(var), format, ##args)
+
 #define safe_snprintf(var, size, format, args...)      \
-       snprintf(var, size, format, ##args) >= size
+       ({                                                              \
+               size_t __size = size;                                   \
+               int __ret;                                              \
+                                                                       \
+               __ret = snprintf(var, __size, format, ##args);          \
+               __ret < 0 || (size_t)__ret >= __size;                   \
+       })
 
 #define pthread_cleanup_push_cast(f, arg)              \
        pthread_cleanup_push(((void (*)(void *))&f), (arg))
index 9b4e978..6adeedf 100644 (file)
@@ -40,7 +40,8 @@ static int _recv_packet(int fd, char **buf, unsigned int timeout,
  */
 int ux_socket_listen(const char *name)
 {
-       int fd, len;
+       int fd;
+       size_t len;
 #ifdef USE_SYSTEMD
        int num;
 #endif
index 344dffd..e16ec46 100644 (file)
@@ -36,9 +36,9 @@ typedef struct _vector *vector;
 #define VECTOR_LAST_SLOT(V)   (((V) && VECTOR_SIZE(V) > 0) ? (V)->slot[(VECTOR_SIZE(V) - 1)] : NULL)
 
 #define vector_foreach_slot(v,p,i) \
-       for (i = 0; (v) && i < VECTOR_SIZE(v) && ((p) = (v)->slot[i]); i++)
+       for (i = 0; (v) && (int)i < VECTOR_SIZE(v) && ((p) = (v)->slot[i]); i++)
 #define vector_foreach_slot_after(v,p,i) \
-       for (; (v) && i < VECTOR_SIZE(v) && ((p) = (v)->slot[i]); i++)
+       for (; (v) && (int)i < VECTOR_SIZE(v) && ((p) = (v)->slot[i]); i++)
 #define vector_foreach_slot_backwards(v,p,i) \
        for (i = VECTOR_SIZE(v) - 1; (int)i >= 0 && ((p) = (v)->slot[i]); i--)
 
index 5e4c328..7ddb4e8 100644 (file)
@@ -20,8 +20,8 @@
 #ifndef _VERSION_H
 #define _VERSION_H
 
-#define VERSION_CODE 0x000803
-#define DATE_CODE    0x0a0213
+#define VERSION_CODE 0x000804
+#define DATE_CODE    0x050414
 
 #define PROG    "multipath-tools"
 
index ef74812..28a2150 100644 (file)
@@ -7,6 +7,7 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 
+#include "util.h"
 #include "checkers.h"
 #include "vector.h"
 #include "structs.h"
@@ -73,7 +74,7 @@ write_out_wwid(int fd, char *wwid) {
                        strerror(errno));
                return -1;
        }
-       if (write(fd, buf, strlen(buf)) != strlen(buf)) {
+       if (write(fd, buf, strlen(buf)) != (ssize_t)strlen(buf)) {
                condlog(0, "cannot write wwid to wwids file : %s",
                        strerror(errno));
                if (ftruncate(fd, offset))
@@ -87,7 +88,8 @@ write_out_wwid(int fd, char *wwid) {
 int
 replace_wwids(vector mp)
 {
-       int i, fd, can_write;
+       int i, can_write;
+       long fd;
        struct multipath * mpp;
        size_t len;
        int ret = -1;
@@ -99,6 +101,8 @@ replace_wwids(vector mp)
        pthread_cleanup_pop(1);
        if (fd < 0)
                goto out;
+
+       pthread_cleanup_push(close_fd, (void*)fd);
        if (!can_write) {
                condlog(0, "cannot replace wwids. wwids file is read-only");
                goto out_file;
@@ -113,7 +117,7 @@ replace_wwids(vector mp)
                goto out_file;
        }
        len = strlen(WWIDS_FILE_HEADER);
-       if (write(fd, WWIDS_FILE_HEADER, len) != len) {
+       if (write(fd, WWIDS_FILE_HEADER, len) != (ssize_t)len) {
                condlog(0, "Can't write wwid file header : %s",
                        strerror(errno));
                /* cleanup partially written header */
@@ -132,7 +136,7 @@ replace_wwids(vector mp)
        }
        ret = 0;
 out_file:
-       close(fd);
+       pthread_cleanup_pop(1);
 out:
        return ret;
 }
@@ -191,7 +195,8 @@ do_remove_wwid(int fd, char *str) {
 
 int
 remove_wwid(char *wwid) {
-       int fd, len, can_write;
+       long fd;
+       int len, can_write;
        char *str;
        int ret = -1;
        struct config *conf;
@@ -203,8 +208,10 @@ remove_wwid(char *wwid) {
                        strerror(errno));
                return -1;
        }
+       pthread_cleanup_push(free, str);
        if (snprintf(str, len, "/%s/\n", wwid) >= len) {
                condlog(0, "string overflow trying to remove wwid");
+               ret = -1;
                goto out;
        }
        condlog(3, "removing line '%s' from wwids file", str);
@@ -212,18 +219,22 @@ remove_wwid(char *wwid) {
        pthread_cleanup_push(put_multipath_config, conf);
        fd = open_file(conf->wwids_file, &can_write, WWIDS_FILE_HEADER);
        pthread_cleanup_pop(1);
-       if (fd < 0)
+
+       if (fd < 0) {
+               ret = -1;
                goto out;
-       if (!can_write) {
-               condlog(0, "cannot remove wwid. wwids file is read-only");
-               goto out_file;
        }
-       ret = do_remove_wwid(fd, str);
 
-out_file:
-       close(fd);
+       pthread_cleanup_push(close_fd, (void*)fd);
+       if (!can_write) {
+               ret = -1;
+               condlog(0, "cannot remove wwid. wwids file is read-only");
+       } else
+               ret = do_remove_wwid(fd, str);
+       pthread_cleanup_pop(1);
 out:
-       free(str);
+       /* free(str) */
+       pthread_cleanup_pop(1);
        return ret;
 }
 
@@ -382,8 +393,7 @@ static int _failed_wwid_op(const char *wwid, bool rw,
        long lockfd;
        int r = -1;
 
-       if (snprintf(path, sizeof(path), "%s/%s", shm_dir, wwid)
-           >= sizeof(path)) {
+       if (safe_sprintf(path, "%s/%s", shm_dir, wwid)) {
                condlog(1, "%s: path name overflow", __func__);
                return -1;
        }
index 278b8d5..28bfe41 100644 (file)
@@ -50,7 +50,7 @@ struct config *get_multipath_config(void)
        return multipath_conf;
 }
 
-void put_multipath_config(void * arg)
+void put_multipath_config(__attribute__((unused)) void * arg)
 {
        /* Noop for now */
 }
@@ -499,6 +499,7 @@ static int handle_args(int argc, char * argv[], int nline)
                if (ret != MPATH_PR_SUCCESS )
                {
                        fprintf (stderr, "Persistent Reserve IN command failed\n");
+                       free(resp);
                        goto out_fd;
                }
 
index 4f4d8e8..cf9d2a2 100644 (file)
@@ -86,7 +86,7 @@ struct config *get_multipath_config(void)
        return multipath_conf;
 }
 
-void put_multipath_config(void *arg)
+void put_multipath_config(__attribute__((unused)) void *arg)
 {
        /* Noop for now */
 }
@@ -423,8 +423,7 @@ static int find_multipaths_check_timeout(const struct path *pp, long tmo,
 
        clock_gettime(CLOCK_REALTIME, &now);
 
-       if (snprintf(path, sizeof(path), "%s/%s", shm_find_mp_dir, pp->dev_t)
-           >= sizeof(path)) {
+       if (safe_sprintf(path, "%s/%s", shm_find_mp_dir, pp->dev_t)) {
                condlog(1, "%s: path name overflow", __func__);
                return FIND_MULTIPATHS_ERROR;
        }
@@ -811,8 +810,10 @@ enum {
        NOT_DELEGATED = 1,
 };
 
-int delegate_to_multipathd(enum mpath_cmds cmd, const char *dev,
-                          enum devtypes dev_type, const struct config *conf)
+int delegate_to_multipathd(enum mpath_cmds cmd,
+                          __attribute__((unused)) const char *dev,
+                          __attribute__((unused)) enum devtypes dev_type,
+                          const struct config *conf)
 {
        int fd;
        char command[1024], *p, *reply = NULL;
@@ -905,6 +906,7 @@ main (int argc, char *argv[])
                exit(RTVL_FAIL);
        multipath_conf = conf;
        conf->retrigger_tries = 0;
+       conf->force_sync = 1;
        while ((arg = getopt(argc, argv, ":adcChl::FfM:v:p:b:BrR:itTquUwW")) != EOF ) {
                switch(arg) {
                case 1: printf("optarg : %s\n",optarg);
@@ -1023,7 +1025,7 @@ main (int argc, char *argv[])
                if (!dev)
                        goto out;
 
-               strncpy(dev, argv[optind], FILE_NAME_SIZE);
+               strlcpy(dev, argv[optind], FILE_NAME_SIZE);
                if (dev_type != DEV_UEVENT)
                        dev_type = get_dev_type(dev);
                if (dev_type == DEV_NONE) {
index e866da2..05a5e8f 100644 (file)
@@ -494,9 +494,10 @@ Check the path state for LSI/Engenio/NetApp RDAC class as NetApp SANtricity E/EF
 Series, and OEM arrays from IBM DELL SGI STK and SUN.
 .TP
 .I directio
-(Deprecated) Read the first sector with direct I/O. This checker is being
-deprecated, it could cause spurious path failures under high load.
-Please use \fItur\fR instead.
+(Deprecated) Read the first sector with direct I/O. If you have a large number
+of paths, or many AIO users on a system, you may need to use sysctl to
+increase fs.aio-max-nr. This checker is being deprecated, it could cause
+spurious path failures under high load. Please use \fItur\fR instead.
 .TP
 .I cciss_tur
 (Hardware-dependent)
@@ -1472,6 +1473,14 @@ the \fIproduct\fR attribute set to the value of \fIproduct_blacklist\fR.
 The user_friendly_names prefix to use for this
 device type, instead of the default "mpath".
 .TP
+.B vpd_vendor
+The vendor specific vpd page information, using the vpd page abbreviation.
+The vpd page abbreviation can be found by running \fIsg_vpd -e\fR. multipathd
+will use this information to gather device specific information that can be
+displayed with the \fI%g\fR wilcard for the \fImultipathd show maps format\fR
+and \fImultipathd show paths format\fR commands. Currently only the
+\fBhp3par\fR vpd page is supported.
+.TP
 .B hardware_handler
 The hardware handler to use for this device type.
 The following hardware handler are implemented:
index d1a9863..8d90117 100644 (file)
@@ -36,6 +36,9 @@ $(EXEC): $(OBJS) $(multipathdir)/libmultipath.so $(mpathcmddir)/libmpathcmd.so
        $(CC) $(CFLAGS) $(OBJS) $(LDFLAGS) -o $(EXEC) $(LIBDEPS)
        $(GZIP) $(EXEC).8 > $(EXEC).8.gz
 
+cli_handlers.o:        cli_handlers.c
+       $(CC) $(CFLAGS) -Wno-unused-parameter -c -o $@ $<
+
 install:
        $(INSTALL_PROGRAM) -d $(DESTDIR)$(bindir)
        $(INSTALL_PROGRAM) -m 755 $(EXEC) $(DESTDIR)$(bindir)
index 8a89904..7d878c8 100644 (file)
@@ -237,7 +237,7 @@ show_map_json (char ** r, int * len, struct multipath * mpp,
 
                c = reply;
 
-               c += snprint_multipath_map_json(c, maxlen, mpp, 1);
+               c += snprint_multipath_map_json(c, maxlen, mpp);
                again = ((c - reply) == maxlen);
 
                REALLOC_REPLY(reply, again, maxlen);
@@ -1024,16 +1024,17 @@ cli_restore_queueing(void *v, char **reply, int *len, void *data)
        select_no_path_retry(conf, mpp);
        pthread_cleanup_pop(1);
 
+       /*
+        * Don't call set_no_path_retry() for the NO_PATH_RETRY_FAIL case.
+        * That would disable queueing when "restorequeueing" is called,
+        * and the code never behaved that way. Users might not expect it.
+        * In almost all cases, queueing will be disabled anyway when we
+        * are here.
+        */
        if (mpp->no_path_retry != NO_PATH_RETRY_UNDEF &&
-                       mpp->no_path_retry != NO_PATH_RETRY_FAIL) {
-               dm_queue_if_no_path(mpp->alias, 1);
-               if (mpp->no_path_retry > 0) {
-                       if (mpp->nr_active > 0)
-                               mpp->retry_tick = 0;
-                       else
-                               enter_recovery_mode(mpp);
-               }
-       }
+           mpp->no_path_retry != NO_PATH_RETRY_FAIL)
+               set_no_path_retry(mpp);
+
        return 0;
 }
 
@@ -1051,16 +1052,10 @@ cli_restore_all_queueing(void *v, char **reply, int *len, void *data)
                pthread_cleanup_push(put_multipath_config, conf);
                select_no_path_retry(conf, mpp);
                pthread_cleanup_pop(1);
+               /* See comment in cli_restore_queueing() */
                if (mpp->no_path_retry != NO_PATH_RETRY_UNDEF &&
-                   mpp->no_path_retry != NO_PATH_RETRY_FAIL) {
-                       dm_queue_if_no_path(mpp->alias, 1);
-                       if (mpp->no_path_retry > 0) {
-                               if (mpp->nr_active > 0)
-                                       mpp->retry_tick = 0;
-                               else
-                                       enter_recovery_mode(mpp);
-                       }
-               }
+                   mpp->no_path_retry != NO_PATH_RETRY_FAIL)
+                       set_no_path_retry(mpp);
        }
        return 0;
 }
@@ -1085,12 +1080,12 @@ cli_disable_queueing(void *v, char **reply, int *len, void *data)
                return 1;
        }
 
-       if (mpp->nr_active == 0)
+       if (count_active_paths(mpp) == 0)
                mpp->stat_map_failures++;
        mpp->retry_tick = 0;
        mpp->no_path_retry = NO_PATH_RETRY_FAIL;
        mpp->disable_queueing = 1;
-       dm_queue_if_no_path(mpp->alias, 0);
+       set_no_path_retry(mpp);
        return 0;
 }
 
@@ -1103,12 +1098,12 @@ cli_disable_all_queueing(void *v, char **reply, int *len, void *data)
 
        condlog(2, "disable queueing (operator)");
        vector_foreach_slot(vecs->mpvec, mpp, i) {
-               if (mpp->nr_active == 0)
+               if (count_active_paths(mpp) == 0)
                        mpp->stat_map_failures++;
                mpp->retry_tick = 0;
                mpp->no_path_retry = NO_PATH_RETRY_FAIL;
                mpp->disable_queueing = 1;
-               dm_queue_if_no_path(mpp->alias, 0);
+               set_no_path_retry(mpp);
        }
        return 0;
 }
index 0034892..b22b47d 100644 (file)
@@ -370,12 +370,12 @@ static int dmevent_loop (void)
        return -1; /* never reach there */
 }
 
-static void rcu_unregister(void *param)
+static void rcu_unregister(__attribute__((unused)) void *param)
 {
        rcu_unregister_thread();
 }
 
-void *wait_dmevents (void *unused)
+void *wait_dmevents (__attribute__((unused)) void *unused)
 {
        int r;
 
index 34a5768..8baf9ab 100644 (file)
  */
 #include "checkers.h"
 
-#ifdef USE_SYSTEMD
-static int use_watchdog;
-#endif
-
 /*
  * libmultipath
  */
@@ -215,7 +211,7 @@ static void do_sd_notify(enum daemon_status old_state,
 }
 #endif
 
-static void config_cleanup(void *arg)
+static void config_cleanup(__attribute__((unused)) void *arg)
 {
        pthread_mutex_unlock(&config_lock);
 }
@@ -306,7 +302,7 @@ struct config *get_multipath_config(void)
        return rcu_dereference(multipath_conf);
 }
 
-void put_multipath_config(void *arg)
+void put_multipath_config(__attribute__((unused)) void *arg)
 {
        rcu_read_unlock();
 }
@@ -377,7 +373,7 @@ remove_map_and_stop_waiter(struct multipath *mpp, struct vectors *vecs)
         * so they don't need to be manually removed here */
        condlog(3, "%s: removing map from internal tables", mpp->alias);
        if (!poll_dmevents)
-               stop_waiter_thread(mpp, vecs);
+               stop_waiter_thread(mpp);
        remove_map(mpp, vecs, PURGE_VEC);
 }
 
@@ -392,7 +388,7 @@ remove_maps_and_stop_waiters(struct vectors *vecs)
 
        if (!poll_dmevents) {
                vector_foreach_slot(vecs->mpvec, mpp, i)
-                       stop_waiter_thread(mpp, vecs);
+                       stop_waiter_thread(mpp);
        }
        else
                unwatch_all_dmevents();
@@ -409,36 +405,6 @@ set_multipath_wwid (struct multipath * mpp)
        dm_get_uuid(mpp->alias, mpp->wwid, WWID_SIZE);
 }
 
-static void set_no_path_retry(struct multipath *mpp)
-{
-       char is_queueing = 0;
-
-       mpp->nr_active = pathcount(mpp, PATH_UP) + pathcount(mpp, PATH_GHOST);
-       if (mpp->features && strstr(mpp->features, "queue_if_no_path"))
-               is_queueing = 1;
-
-       switch (mpp->no_path_retry) {
-       case NO_PATH_RETRY_UNDEF:
-               break;
-       case NO_PATH_RETRY_FAIL:
-               if (is_queueing)
-                       dm_queue_if_no_path(mpp->alias, 0);
-               break;
-       case NO_PATH_RETRY_QUEUE:
-               if (!is_queueing)
-                       dm_queue_if_no_path(mpp->alias, 1);
-               break;
-       default:
-               if (mpp->nr_active > 0) {
-                       mpp->retry_tick = 0;
-                       if (!is_queueing)
-                               dm_queue_if_no_path(mpp->alias, 1);
-               } else if (is_queueing && mpp->retry_tick == 0)
-                       enter_recovery_mode(mpp);
-               break;
-       }
-}
-
 int __setup_multipath(struct vectors *vecs, struct multipath *mpp,
                      int reset)
 {
@@ -493,7 +459,7 @@ int update_multipath (struct vectors *vecs, char *mapname, int reset)
                        if (pp->state != PATH_DOWN) {
                                struct config *conf;
                                int oldstate = pp->state;
-                               int checkint;
+                               unsigned int checkint;
 
                                conf = get_multipath_config();
                                checkint = conf->checkint;
@@ -906,9 +872,8 @@ uev_add_path (struct uevent *uev, struct vectors * vecs, int need_do_map)
                        }
                }
        }
-       lock_cleanup_pop(vecs->lock);
        if (pp)
-               return ret;
+               goto out;
 
        /*
         * get path vital state
@@ -920,13 +885,13 @@ uev_add_path (struct uevent *uev, struct vectors * vecs, int need_do_map)
        pthread_cleanup_pop(1);
        if (!pp) {
                if (ret == PATHINFO_SKIPPED)
-                       return 0;
-               condlog(3, "%s: failed to get path info", uev->kernel);
-               return 1;
+                       ret = 0;
+               else {
+                       condlog(3, "%s: failed to get path info", uev->kernel);
+                       ret = 1;
+               }
+               goto out;
        }
-       pthread_cleanup_push(cleanup_lock, &vecs->lock);
-       lock(&vecs->lock);
-       pthread_testcancel();
        ret = store_path(vecs->pathvec, pp);
        if (!ret) {
                conf = get_multipath_config();
@@ -940,6 +905,7 @@ uev_add_path (struct uevent *uev, struct vectors * vecs, int need_do_map)
                free_path(pp);
                ret = 1;
        }
+out:
        lock_cleanup_pop(vecs->lock);
        return ret;
 }
@@ -1503,7 +1469,7 @@ out:
        return r;
 }
 
-static void rcu_unregister(void *param)
+static void rcu_unregister(__attribute__((unused)) void *param)
 {
        rcu_unregister_thread();
 }
@@ -1646,7 +1612,7 @@ fail_path (struct path * pp, int del_active)
  * caller must have locked the path list before calling that function
  */
 static int
-reinstate_path (struct path * pp, int add_active)
+reinstate_path (struct path * pp)
 {
        int ret = 0;
 
@@ -1658,8 +1624,7 @@ reinstate_path (struct path * pp, int add_active)
                ret = 1;
        } else {
                condlog(2, "%s: reinstated", pp->dev_t);
-               if (add_active)
-                       update_queue_mode_add_path(pp->mpp);
+               update_queue_mode_add_path(pp->mpp);
        }
        return ret;
 }
@@ -1891,7 +1856,7 @@ static int check_path_reinstate_state(struct path * pp) {
 
        if (pp->disable_reinstate) {
                /* If there are no other usable paths, reinstate the path */
-               if (pp->mpp->nr_active == 0) {
+               if (count_active_paths(pp->mpp) == 0) {
                        condlog(2, "%s : reinstating path early", pp->dev);
                        goto reinstate_path;
                }
@@ -1949,8 +1914,9 @@ static int check_path_reinstate_state(struct path * pp) {
         * so that the cutomer can rectify the issue within this time. After
         * the completion of san_path_err_recovery_time it should
         * automatically reinstate the path
+        * (note: we know that san_path_err_threshold > 0 here).
         */
-       if (pp->path_failures > pp->mpp->san_path_err_threshold) {
+       if (pp->path_failures > (unsigned int)pp->mpp->san_path_err_threshold) {
                condlog(2, "%s : hit error threshold. Delaying path reinstatement", pp->dev);
                pp->dis_reinstate_time = curr_time.tv_sec;
                pp->disable_reinstate = 1;
@@ -1984,15 +1950,15 @@ should_skip_path(struct path *pp){
  * and '0' otherwise
  */
 int
-check_path (struct vectors * vecs, struct path * pp, int ticks)
+check_path (struct vectors * vecs, struct path * pp, unsigned int ticks)
 {
        int newstate;
        int new_path_up = 0;
        int chkr_new_path_up = 0;
-       int add_active;
        int disable_reinstate = 0;
        int oldchkrstate = pp->chkrstate;
-       int retrigger_tries, checkint, max_checkint, verbosity;
+       int retrigger_tries, verbosity;
+       unsigned int checkint, max_checkint;
        struct config *conf;
        int marginal_pathgroups, marginal_changed = 0;
        int ret;
@@ -2162,7 +2128,7 @@ check_path (struct vectors * vecs, struct path * pp, int ticks)
         * paths if there are no other active paths in map.
         */
        disable_reinstate = (newstate == PATH_GHOST &&
-                            pp->mpp->nr_active == 0 &&
+                            count_active_paths(pp->mpp) == 0 &&
                             path_get_tpgs(pp) == TPGS_IMPLICIT) ? 1 : 0;
 
        pp->chkrstate = newstate;
@@ -2213,12 +2179,7 @@ check_path (struct vectors * vecs, struct path * pp, int ticks)
                /*
                 * reinstate this path
                 */
-               if (oldstate != PATH_UP &&
-                   oldstate != PATH_GHOST)
-                       add_active = 1;
-               else
-                       add_active = 0;
-               if (!disable_reinstate && reinstate_path(pp, add_active)) {
+               if (!disable_reinstate && reinstate_path(pp)) {
                        condlog(3, "%s: reload map", pp->dev);
                        ev_add_path(pp, vecs, 1);
                        pp->tick = 1;
@@ -2241,7 +2202,7 @@ check_path (struct vectors * vecs, struct path * pp, int ticks)
                    pp->dmstate == PSTATE_UNDEF) &&
                    !disable_reinstate) {
                        /* Clear IO errors */
-                       if (reinstate_path(pp, 0)) {
+                       if (reinstate_path(pp)) {
                                condlog(3, "%s: reload map", pp->dev);
                                ev_add_path(pp, vecs, 1);
                                pp->tick = 1;
@@ -2319,6 +2280,7 @@ checkerloop (void *ap)
        struct timespec last_time;
        struct config *conf;
        int foreign_tick = 0;
+       bool use_watchdog;
 
        pthread_cleanup_push(rcu_unregister, NULL);
        rcu_register_thread();
@@ -2330,9 +2292,15 @@ checkerloop (void *ap)
        get_monotonic_time(&last_time);
        last_time.tv_sec -= 1;
 
+       /* use_watchdog is set from process environment and never changes */
+       conf = get_multipath_config();
+       use_watchdog = conf->use_watchdog;
+       put_multipath_config(conf);
+
        while (1) {
                struct timespec diff_time, start_time, end_time;
-               int num_paths = 0, ticks = 0, strict_timing, rc = 0;
+               int num_paths = 0, strict_timing, rc = 0;
+               unsigned int ticks = 0;
 
                get_monotonic_time(&start_time);
                if (start_time.tv_sec && last_time.tv_sec) {
@@ -2607,6 +2575,7 @@ reconfigure (struct vectors * vecs)
        vecs->pathvec = NULL;
        delete_all_foreign();
 
+       reset_checker_classes();
        /* Re-read any timezone changes */
        tzset();
 
@@ -2618,6 +2587,7 @@ reconfigure (struct vectors * vecs)
        uxsock_timeout = conf->uxsock_timeout;
 
        old = rcu_dereference(multipath_conf);
+       conf->sequence_nr = old->sequence_nr + 1;
        rcu_assign_pointer(multipath_conf, conf);
        call_rcu(&old->rcu, rcu_free_config);
 
@@ -2685,25 +2655,25 @@ handle_signals(bool nonfatal)
 }
 
 static void
-sighup (int sig)
+sighup(__attribute__((unused)) int sig)
 {
        reconfig_sig = 1;
 }
 
 static void
-sigend (int sig)
+sigend(__attribute__((unused)) int sig)
 {
        exit_sig = 1;
 }
 
 static void
-sigusr1 (int sig)
+sigusr1(__attribute__((unused)) int sig)
 {
        log_reset_sig = 1;
 }
 
 static void
-sigusr2 (int sig)
+sigusr2(__attribute__((unused)) int sig)
 {
        condlog(3, "SIGUSR2 received");
 }
@@ -2792,7 +2762,7 @@ set_oom_adj (void)
 }
 
 static int
-child (void * param)
+child (__attribute__((unused)) void *param)
 {
        pthread_t check_thr, uevent_thr, uxlsnr_thr, uevq_thr, dmevent_thr;
        pthread_attr_t log_attr, misc_attr, uevent_attr;
@@ -2800,7 +2770,6 @@ child (void * param)
        struct multipath * mpp;
        int i;
 #ifdef USE_SYSTEMD
-       unsigned long checkint;
        int startup_done = 0;
 #endif
        int rc;
@@ -2877,21 +2846,6 @@ child (void * param)
        setscheduler();
        set_oom_adj();
 
-#ifdef USE_SYSTEMD
-       envp = getenv("WATCHDOG_USEC");
-       if (envp && sscanf(envp, "%lu", &checkint) == 1) {
-               /* Value is in microseconds */
-               conf->max_checkint = checkint / 1000000;
-               /* Rescale checkint */
-               if (conf->checkint > conf->max_checkint)
-                       conf->checkint = conf->max_checkint;
-               else
-                       conf->checkint = conf->max_checkint / 4;
-               condlog(3, "enabling watchdog, interval %d max %d",
-                       conf->checkint, conf->max_checkint);
-               use_watchdog = conf->checkint;
-       }
-#endif
        /*
         * Startup done, invalidate configuration
         */
@@ -3247,7 +3201,8 @@ main (int argc, char *argv[])
 void *  mpath_pr_event_handler_fn (void * pathp )
 {
        struct multipath * mpp;
-       int i, ret, isFound;
+       unsigned int i;
+       int ret, isFound;
        struct path * pp = (struct path *)pathp;
        struct prout_param_descriptor *param;
        struct prin_resp *resp;
index 47d18e2..cc0fbec 100644 (file)
@@ -42,7 +42,7 @@ int pidfile_create(const char *pidFile, pid_t pid)
        }
        memset(buf, 0, sizeof(buf));
        snprintf(buf, sizeof(buf)-1, "%u", pid);
-       if (write(fd, buf, strlen(buf)) != strlen(buf)) {
+       if (write(fd, buf, strlen(buf)) != (ssize_t)strlen(buf)) {
                condlog(0, "Cannot write pid to pidfile [%s], error was [%s]",
                        pidFile, strerror(errno));
                goto fail;
index bc71679..1c5ce9d 100644 (file)
@@ -23,6 +23,7 @@
 #include <sys/time.h>
 #include <signal.h>
 #include <stdbool.h>
+#include <sys/inotify.h>
 #include "checkers.h"
 #include "memory.h"
 #include "debug.h"
@@ -39,7 +40,7 @@
 #include "cli.h"
 #include "uxlsnr.h"
 
-struct timespec sleep_time = {5, 0};
+static struct timespec sleep_time = {5, 0};
 
 struct client {
        struct list_head node;
@@ -48,9 +49,11 @@ struct client {
 
 #define MIN_POLLS 1023
 
-LIST_HEAD(clients);
-pthread_mutex_t client_lock = PTHREAD_MUTEX_INITIALIZER;
-struct pollfd *polls;
+static LIST_HEAD(clients);
+static pthread_mutex_t client_lock = PTHREAD_MUTEX_INITIALIZER;
+static struct pollfd *polls;
+static int notify_fd = -1;
+static char *watch_config_dir;
 
 static bool _socket_client_is_root(int fd);
 
@@ -119,13 +122,13 @@ static void dead_client(struct client *c)
        pthread_cleanup_pop(1);
 }
 
-void free_polls (void)
+static void free_polls (void)
 {
        if (polls)
                FREE(polls);
 }
 
-void check_timeout(struct timespec start_time, char *inbuf,
+static void check_timeout(struct timespec start_time, char *inbuf,
                   unsigned int timeout)
 {
        struct timespec diff_time, end_time;
@@ -151,6 +154,8 @@ void uxsock_cleanup(void *arg)
        long ux_sock = (long)arg;
 
        close(ux_sock);
+       close(notify_fd);
+       free(watch_config_dir);
 
        pthread_mutex_lock(&client_lock);
        list_for_each_entry_safe(client_loop, client_tmp, &clients, node) {
@@ -162,6 +167,110 @@ void uxsock_cleanup(void *arg)
        free_polls();
 }
 
+struct watch_descriptors {
+       int conf_wd;
+       int dir_wd;
+};
+
+/* failing to set the watch descriptor is o.k. we just miss a warning
+ * message */
+static void reset_watch(int notify_fd, struct watch_descriptors *wds,
+                       unsigned int *sequence_nr)
+{
+       struct config *conf;
+       int dir_reset = 0;
+       int conf_reset = 0;
+
+       if (notify_fd == -1)
+               return;
+
+       conf = get_multipath_config();
+       /* instead of repeatedly try to reset the inotify watch if
+        * the config directory or multipath.conf isn't there, just
+        * do it once per reconfigure */
+       if (*sequence_nr != conf->sequence_nr) {
+               *sequence_nr = conf->sequence_nr;
+               if (wds->conf_wd == -1)
+                       conf_reset = 1;
+               if (!watch_config_dir || !conf->config_dir ||
+                   strcmp(watch_config_dir, conf->config_dir)) {
+                       dir_reset = 1;
+                       if (watch_config_dir)
+                               free(watch_config_dir);
+                       if (conf->config_dir)
+                               watch_config_dir = strdup(conf->config_dir);
+                       else
+                               watch_config_dir = NULL;
+               } else if (wds->dir_wd == -1)
+                       dir_reset = 1;
+       }
+       put_multipath_config(conf);
+
+       if (dir_reset) {
+               if (wds->dir_wd != -1) {
+                       inotify_rm_watch(notify_fd, wds->dir_wd);
+                       wds->dir_wd = -1;
+               }
+               if (watch_config_dir) {
+                       wds->dir_wd = inotify_add_watch(notify_fd,
+                                                       watch_config_dir,
+                                                       IN_CLOSE_WRITE |
+                                                       IN_DELETE | IN_ONLYDIR);
+                       if (wds->dir_wd == -1)
+                               condlog(3, "didn't set up notifications on %s: %m", watch_config_dir);
+               }
+       }
+       if (conf_reset) {
+               wds->conf_wd = inotify_add_watch(notify_fd, DEFAULT_CONFIGFILE,
+                                                IN_CLOSE_WRITE);
+               if (wds->conf_wd == -1)
+                       condlog(3, "didn't set up notifications on /etc/multipath.conf: %m");
+       }
+       return;
+}
+
+static void handle_inotify(int fd, struct watch_descriptors *wds)
+{
+       char buff[1024]
+               __attribute__ ((aligned(__alignof__(struct inotify_event))));
+       const struct inotify_event *event;
+       ssize_t len;
+       char *ptr;
+       int got_notify = 0;
+
+       for (;;) {
+               len = read(fd, buff, sizeof(buff));
+               if (len <= 0) {
+                       if (len < 0 && errno != EAGAIN) {
+                               condlog(3, "error reading from inotify_fd");
+                               if (wds->conf_wd != -1)
+                                       inotify_rm_watch(fd, wds->conf_wd);
+                               if (wds->dir_wd != -1)
+                                       inotify_rm_watch(fd, wds->dir_wd);
+                               wds->conf_wd = wds->dir_wd = -1;
+                       }
+                       break;
+               }
+
+               got_notify = 1;
+               for (ptr = buff; ptr < buff + len;
+                    ptr += sizeof(struct inotify_event) + event->len) {
+                       event = (const struct inotify_event *) ptr;
+
+                       if (event->mask & IN_IGNORED) {
+                               /* multipathd.conf may have been overwritten.
+                                * Try once to reset the notification */
+                               if (wds->conf_wd == event->wd)
+                                       wds->conf_wd = inotify_add_watch(notify_fd, DEFAULT_CONFIGFILE, IN_CLOSE_WRITE);
+                               else if (wds->dir_wd == event->wd)
+                                       wds->dir_wd = -1;
+                       }
+               }
+       }
+       if (got_notify)
+               condlog(1, "Multipath configuration updated.\nReload multipathd for changes to take effect");
+}
+
 /*
  * entry point
  */
@@ -173,13 +282,19 @@ void * uxsock_listen(uxsock_trigger_fn uxsock_trigger, long ux_sock,
        char *reply;
        sigset_t mask;
        int old_clients = MIN_POLLS;
+       /* conf->sequence_nr will be 1 when uxsock_listen is first called */
+       unsigned int sequence_nr = 0;
+       struct watch_descriptors wds = { .conf_wd = -1, .dir_wd = -1 };
 
        condlog(3, "uxsock: startup listener");
-       polls = (struct pollfd *)MALLOC((MIN_POLLS + 1) * sizeof(struct pollfd));
+       polls = (struct pollfd *)MALLOC((MIN_POLLS + 2) * sizeof(struct pollfd));
        if (!polls) {
                condlog(0, "uxsock: failed to allocate poll fds");
                exit_daemon();
        }
+       notify_fd = inotify_init1(IN_NONBLOCK);
+       if (notify_fd == -1) /* it's fine if notifications fail */
+               condlog(3, "failed to start up configuration notifications");
        sigfillset(&mask);
        sigdelset(&mask, SIGINT);
        sigdelset(&mask, SIGTERM);
@@ -198,18 +313,18 @@ void * uxsock_listen(uxsock_trigger_fn uxsock_trigger, long ux_sock,
                if (num_clients != old_clients) {
                        struct pollfd *new;
                        if (num_clients <= MIN_POLLS && old_clients > MIN_POLLS) {
-                               new = REALLOC(polls, (1 + MIN_POLLS) *
+                               new = REALLOC(polls, (2 + MIN_POLLS) *
                                                sizeof(struct pollfd));
                        } else if (num_clients <= MIN_POLLS && old_clients <= MIN_POLLS) {
                                new = polls;
                        } else {
-                               new = REALLOC(polls, (1+num_clients) *
+                               new = REALLOC(polls, (2 + num_clients) *
                                                sizeof(struct pollfd));
                        }
                        if (!new) {
                                pthread_mutex_unlock(&client_lock);
                                condlog(0, "%s: failed to realloc %d poll fds",
-                                       "uxsock", 1 + num_clients);
+                                       "uxsock", 2 + num_clients);
                                sched_yield();
                                continue;
                        }
@@ -219,8 +334,15 @@ void * uxsock_listen(uxsock_trigger_fn uxsock_trigger, long ux_sock,
                polls[0].fd = ux_sock;
                polls[0].events = POLLIN;
 
+               reset_watch(notify_fd, &wds, &sequence_nr);
+               if (notify_fd == -1 || (wds.conf_wd == -1 && wds.dir_wd == -1))
+                       polls[1].fd = -1;
+               else
+                       polls[1].fd = notify_fd;
+               polls[1].events = POLLIN;
+
                /* setup the clients */
-               i = 1;
+               i = 2;
                list_for_each_entry(c, &clients, node) {
                        polls[i].fd = c->fd;
                        polls[i].events = POLLIN;
@@ -262,7 +384,7 @@ void * uxsock_listen(uxsock_trigger_fn uxsock_trigger, long ux_sock,
                }
 
                /* see if a client wants to speak to us */
-               for (i = 1; i < num_clients + 1; i++) {
+               for (i = 2; i < num_clients + 2; i++) {
                        if (polls[i].revents & POLLIN) {
                                struct timespec start_time;
 
@@ -321,6 +443,10 @@ void * uxsock_listen(uxsock_trigger_fn uxsock_trigger, long ux_sock,
                if (polls[0].revents & POLLIN) {
                        new_client(ux_sock);
                }
+
+               /* handle inotify events on config files */
+               if (polls[1].revents & POLLIN)
+                       handle_inotify(notify_fd, &wds);
        }
 
        return NULL;
index eb8d699..e645766 100644 (file)
@@ -49,7 +49,7 @@ static void free_waiter (void *data)
        FREE(wp);
 }
 
-void stop_waiter_thread (struct multipath *mpp, struct vectors *vecs)
+void stop_waiter_thread (struct multipath *mpp)
 {
        pthread_t thread;
 
index 0cfae46..28e0f6d 100644 (file)
@@ -11,7 +11,7 @@ struct event_thread {
        struct vectors *vecs;
 };
 
-void stop_waiter_thread (struct multipath *mpp, struct vectors *vecs);
+void stop_waiter_thread (struct multipath *mpp);
 int start_waiter_thread (struct multipath *mpp, struct vectors *vecs);
 
 #endif /* _WAITER_H */
index a5cdf39..77ff324 100644 (file)
@@ -1,17 +1,37 @@
 include ../Makefile.inc
 
-CFLAGS += $(BIN_CFLAGS) -I$(multipathdir) -I$(mpathcmddir)
+# Test special behavior of gcc 4.8 with nested initializers
+# gcc 4.8 compiles blacklist.c only with -Wno-missing-field-initializers
+TEST_MISSING_INITIALIZERS = $(shell \
+       echo 'struct A {int a, b;}; struct B {struct A a; int b;} b = {.a.a=1};' | \
+               $(CC) -c -Werror -Wmissing-field-initializers -o /dev/null -xc - >/dev/null 2>&1 \
+       || echo -Wno-missing-field-initializers)
+W_MISSING_INITIALIZERS := $(call TEST_MISSING_INITIALIZERS)
+
+CFLAGS += $(BIN_CFLAGS) -I$(multipathdir) -I$(mpathcmddir) \
+       -Wno-unused-parameter $(W_MISSING_INITIALIZERS)
 LIBDEPS += -L$(multipathdir) -lmultipath -lcmocka
 
-TESTS := uevent parser util dmevents hwtable blacklist unaligned vpd pgpolicy
+TESTS := uevent parser util dmevents hwtable blacklist unaligned vpd pgpolicy \
+        alias directio
 
 .SILENT: $(TESTS:%=%.o)
 .PRECIOUS: $(TESTS:%=%-test)
 
 all:   $(TESTS:%=%.out)
 
+# test-specific compiler flags
+# XYZ-test_FLAGS: Additional compiler flags for this test
+
+ifneq ($(wildcard directio_test_dev),)
+DIO_TEST_DEV = $(shell sed -n -e 's/^[[:space:]]*DIO_TEST_DEV[[:space:]]*=[[:space:]]*\([^[:space:]\#]\+\).*/\1/p' < directio_test_dev)
+endif
+ifneq ($(DIO_TEST_DEV),)
+directio-test_FLAGS := -DDIO_TEST_DEV=\"$(DIO_TEST_DEV)\"
+endif
+
 # test-specific linker flags
-# XYZ-test-TESTDEPS: test libraries containing __wrap_xyz functions
+# XYZ-test_TESTDEPS: test libraries containing __wrap_xyz functions
 # XYZ-test_OBJDEPS: object files from libraries to link in explicitly
 #    That may be necessary if functions called from the object file are wrapped
 #    (wrapping works only for symbols which are undefined after processing a
@@ -23,10 +43,19 @@ hwtable-test_TESTDEPS := test-lib.o
 hwtable-test_OBJDEPS := ../libmultipath/discovery.o ../libmultipath/blacklist.o \
        ../libmultipath/prio.o ../libmultipath/callout.o ../libmultipath/structs.o
 hwtable-test_LIBDEPS := -ludev -lpthread -ldl
+blacklist-test_TESTDEPS := test-log.o
 blacklist-test_OBJDEPS := ../libmultipath/blacklist.o
 blacklist-test_LIBDEPS := -ludev
 vpd-test_OBJDEPS :=  ../libmultipath/discovery.o
 vpd-test_LIBDEPS := -ludev -lpthread -ldl
+alias-test_TESTDEPS := test-log.o
+alias-test_LIBDEPS := -lpthread -ldl
+ifneq ($(DIO_TEST_DEV),)
+directio-test_LIBDEPS := -laio
+endif
+
+%.o: %.c
+       $(CC) $(CFLAGS) $($*-test_FLAGS) -c -o $@ $<
 
 lib/libchecktur.so:
        mkdir lib
@@ -38,8 +67,11 @@ lib/libchecktur.so:
 
 OBJS = $(TESTS:%=%.o) test-lib.o
 
-clean: dep_clean
-       $(RM) $(TESTS:%=%-test) $(TESTS:%=%.out) $(OBJS)
+test_clean:
+       $(RM) $(TESTS:%=%.out)
+
+clean: test_clean dep_clean
+       $(RM) $(TESTS:%=%-test) $(OBJS) *.o.wrap
        $(RM) -rf lib
 
 .SECONDARY: $(OBJS)
@@ -53,6 +85,7 @@ dep_clean:
        @sed -n 's/^.*__wrap_\([a-zA-Z0-9_]*\).*$$/-Wl,--wrap=\1/p' $< | \
                sort -u | tr '\n' ' ' >$@
 
+
 # COLON will get expanded during second expansion below
 COLON:=:
 .SECONDEXPANSION:
diff --git a/tests/README.md b/tests/README.md
new file mode 100644 (file)
index 0000000..6438a82
--- /dev/null
@@ -0,0 +1,72 @@
+# multipath-tools unit tests
+
+Unit tests are built and run by running `make test` in the top directory,
+or simply `make` in the `tests` subdirectory. The test output is saved as
+`<testname>.out`. The test programs are called `<testname>-test`, and can
+be run standalone e.g. for debugging purposes.
+
+## Notes on individual tests
+
+### Tests that require root permissions
+
+The following tests must be run as root, otherwise some test items will be
+skipped because of missing permissions, or the test will fail outright:
+
+ * `dmevents`
+ * `directio` (if `DIO_TEST_DEV` is set, see below)
+
+To run these tests, after building the tests as non-root user, change to the
+`tests` directory and run `make test-clean`; then run `make` again as root.
+
+### directio test
+
+This test includes test items that require a access to a block device. The
+device will be opened in read-only mode; you don't need to worry about data
+loss. However, the user needs to specify a device to be used. Set the
+environment variable `DIO_TEST_DEV` to the path of the device.
+Alternatively, create a file `directio_test_dev` under
+the `tests` directory containting a single line that sets this environment
+variable in Bourne Shell syntax, like this:
+
+    DIO_TEST_DEV=/dev/sdc3
+
+After that, run `make directio.out` as root in the `tests` directory to
+perform the test.
+
+## Adding tests
+
+The unit tests are based on the [cmocka test framework](https://cmocka.org/),
+and make use of cmocka's "mock objects" feature to simulate how the code behaves
+for different input values. cmocka achieves this by modifying the symbol
+lookup at link time, substituting "wrapper functions" for the originally
+called function. The Makefile contains code to make sure that `__wrap_xyz()`
+wrapper functions are automatically passed to the linker with matching
+`-Wl,--wrap` command line arguments, so that tests are correctly rebuilt if
+wrapper functions are added or removed.
+
+### Making sure symbol wrapping works: OBJDEPS
+
+Special care must be taken to wrap function calls inside a library. Suppose you want
+to wrap a function which is both defined in libmultipath and called from other
+functions in libmultipath, such as `checker_check()`. When `libmultipath.so` is
+created, the linker resolves calls to `checker_check()` inside the `.so`
+file. When later the test executable is built by linking the test object file with
+`libmultipath.so`, these calls can't be wrapped any more, because they've
+already been resolved, and wrapping works only for *unresolved* symbols.
+Therefore, object files from libraries that contain calls to functions
+which need to be wrapped must be explicitly listed on the linker command line
+in order to make the wrapping work. To enforce this, add these object files to
+the `xyz-test_OBJDEPS` variable in the Makefile.
+
+### Using wrapper function libraries: TESTDEPS
+
+Some wrapper functions are useful in multiple tests. These are maintained in
+separate input files, such as `test-lib.c` or `test-log.c`. List these files
+in the `xyz-test_TESTDEPS` variable for your test program if you need these
+wrappers.
+
+### Specifying library dependencies: LIBDEPS
+
+In order to keep the tests lean, not all libraries that libmultipath
+normally pulls in are used for every test. Add libraries you need (such as
+`-lpthread`) to the `xyz-test_LIBDEPS` variable.
diff --git a/tests/alias.c b/tests/alias.c
new file mode 100644 (file)
index 0000000..30414db
--- /dev/null
@@ -0,0 +1,744 @@
+#include <stdint.h>
+#include <setjmp.h>
+#include <stdio.h>
+#include <cmocka.h>
+#include "util.h"
+#include "alias.h"
+#include "test-log.h"
+#include <errno.h>
+
+#include "globals.c"
+#include "../libmultipath/alias.c"
+
+#if INT_MAX == 0x7fffffff
+/* user_friendly_name for map #INT_MAX */
+#define MPATH_ID_INT_MAX "fxshrxw"
+/* ... and one less */
+#define MPATH_ID_INT_MAX_m1 "fxshrxv"
+/* ... and one more */
+#define MPATH_ID_INT_MAX_p1 "fxshrxx"
+#endif
+
+void __wrap_rewind(FILE *stream)
+{}
+
+char *__wrap_fgets(char *buf, int n, FILE *stream)
+{
+       char *val = mock_ptr_type(char *);
+       if (!val)
+               return NULL;
+       strlcpy(buf, val, n);
+       return buf;
+}
+
+static int __set_errno(int err)
+{
+       if (err >= 0) {
+               errno = 0;
+               return err;
+       } else {
+               errno = -err;
+               return -1;
+       }
+}
+
+off_t __wrap_lseek(int fd, off_t offset, int whence)
+{
+       return __set_errno(mock_type(int));
+
+}
+
+ssize_t __wrap_write(int fd, const void *buf, size_t count)
+{
+       check_expected(count);
+       check_expected(buf);
+       return __set_errno(mock_type(int));
+}
+
+int __wrap_ftruncate(int fd, off_t length)
+{
+       check_expected(length);
+       return __set_errno(mock_type(int));
+}
+
+static void fd_mpatha(void **state)
+{
+       char buf[32];
+       int rc;
+
+       rc = format_devname(buf, 1, sizeof(buf), "FOO");
+       assert_int_equal(rc, 4);
+       assert_string_equal(buf, "FOOa");
+}
+
+static void fd_mpathz(void **state)
+{
+       /* This also tests a "short" buffer, see fd_mpath_short1 */
+       char buf[5];
+       int rc;
+
+       rc = format_devname(buf, 26, sizeof(buf), "FOO");
+       assert_int_equal(rc, 4);
+       assert_string_equal(buf, "FOOz");
+}
+
+static void fd_mpathaa(void **state)
+{
+       char buf[32];
+       int rc;
+
+       rc = format_devname(buf, 26 + 1, sizeof(buf), "FOO");
+       assert_int_equal(rc, 5);
+       assert_string_equal(buf, "FOOaa");
+}
+
+static void fd_mpathzz(void **state)
+{
+       char buf[32];
+       int rc;
+
+       rc = format_devname(buf, 26*26 + 26, sizeof(buf), "FOO");
+       assert_int_equal(rc, 5);
+       assert_string_equal(buf, "FOOzz");
+}
+
+static void fd_mpathaaa(void **state)
+{
+       char buf[32];
+       int rc;
+
+       rc = format_devname(buf, 26*26 + 27, sizeof(buf), "FOO");
+       assert_int_equal(rc, 6);
+       assert_string_equal(buf, "FOOaaa");
+}
+
+static void fd_mpathzzz(void **state)
+{
+       char buf[32];
+       int rc;
+
+       rc = format_devname(buf, 26*26*26 + 26*26 + 26, sizeof(buf), "FOO");
+       assert_int_equal(rc, 6);
+       assert_string_equal(buf, "FOOzzz");
+}
+
+static void fd_mpathaaaa(void **state)
+{
+       char buf[32];
+       int rc;
+
+       rc = format_devname(buf, 26*26*26 + 26*26 + 27, sizeof(buf), "FOO");
+       assert_int_equal(rc, 7);
+       assert_string_equal(buf, "FOOaaaa");
+}
+
+static void fd_mpathzzzz(void **state)
+{
+       char buf[32];
+       int rc;
+
+       rc = format_devname(buf, 26*26*26*26 + 26*26*26 + 26*26 + 26,
+                           sizeof(buf), "FOO");
+       assert_int_equal(rc, 7);
+       assert_string_equal(buf, "FOOzzzz");
+}
+
+#ifdef MPATH_ID_INT_MAX
+static void fd_mpath_max(void **state)
+{
+       char buf[32];
+       int rc;
+
+       rc  = format_devname(buf, INT_MAX, sizeof(buf), "");
+       assert_int_equal(rc, strlen(MPATH_ID_INT_MAX));
+       assert_string_equal(buf, MPATH_ID_INT_MAX);
+}
+#endif
+
+static void fd_mpath_max1(void **state)
+{
+       char buf[32];
+       int rc;
+
+       rc  = format_devname(buf, INT_MIN, sizeof(buf), "");
+       assert_int_equal(rc, -1);
+}
+
+static void fd_mpath_short(void **state)
+{
+       char buf[4];
+       int rc;
+
+       rc = format_devname(buf, 1, sizeof(buf), "FOO");
+       assert_int_equal(rc, -1);
+}
+
+static void fd_mpath_short1(void **state)
+{
+       char buf[5];
+       int rc;
+
+       rc = format_devname(buf, 27, sizeof(buf), "FOO");
+       assert_int_equal(rc, -1);
+}
+
+static int test_format_devname(void)
+{
+       const struct CMUnitTest tests[] = {
+               cmocka_unit_test(fd_mpatha),
+               cmocka_unit_test(fd_mpathz),
+               cmocka_unit_test(fd_mpathaa),
+               cmocka_unit_test(fd_mpathzz),
+               cmocka_unit_test(fd_mpathaaa),
+               cmocka_unit_test(fd_mpathzzz),
+               cmocka_unit_test(fd_mpathaaaa),
+               cmocka_unit_test(fd_mpathzzzz),
+#ifdef MPATH_ID_INT_MAX
+               cmocka_unit_test(fd_mpath_max),
+#endif
+               cmocka_unit_test(fd_mpath_max1),
+               cmocka_unit_test(fd_mpath_short),
+               cmocka_unit_test(fd_mpath_short1),
+       };
+
+       return cmocka_run_group_tests(tests, NULL, NULL);
+}
+
+static void sd_mpatha(void **state)
+{
+       int rc = scan_devname("MPATHa", "MPATH");
+
+       assert_int_equal(rc, 1);
+}
+
+/*
+ * Text after whitespace is ignored. But an overlong input
+ * errors out, even if it's just whitespace.
+ * It's kind of strange that scan_devname() treats whitespace
+ * like this. But I'm not sure if some corner case depends
+ * on this behavior.
+ */
+static void sd_mpatha_spc(void **state)
+{
+       int rc = scan_devname("MPATHa  00", "MPATH");
+
+       assert_int_equal(rc, 1);
+}
+
+static void sd_mpatha_tab(void **state)
+{
+       int rc = scan_devname("MPATHa\t00", "MPATH");
+
+       assert_int_equal(rc, 1);
+}
+
+static void sd_overlong(void **state)
+{
+       int rc = scan_devname("MPATHa       ", "MPATH");
+
+       assert_int_equal(rc, -1);
+}
+
+static void sd_overlong1(void **state)
+{
+       int rc = scan_devname("MPATHabcdefgh", "MPATH");
+
+       assert_int_equal(rc, -1);
+}
+
+static void sd_noprefix(void **state)
+{
+       int rc = scan_devname("MPATHa", NULL);
+
+       assert_int_equal(rc, -1);
+}
+
+static void sd_nomatchprefix(void **state)
+{
+       int rc = scan_devname("MPATHa", "mpath");
+
+       assert_int_equal(rc, -1);
+}
+
+static void sd_eq_prefix(void **state)
+{
+       int rc = scan_devname("MPATH", "MPATH");
+
+       assert_int_equal(rc, -1);
+}
+
+static void sd_bad_1(void **state)
+{
+       int rc = scan_devname("MPATH0", "MPATH");
+
+       assert_int_equal(rc, -1);
+}
+
+static void sd_bad_2(void **state)
+{
+       int rc = scan_devname("MPATHa0c", "MPATH");
+
+       assert_int_equal(rc, -1);
+}
+
+#ifdef MPATH_ID_INT_MAX
+static void sd_max(void **state)
+{
+       int rc = scan_devname("MPATH" MPATH_ID_INT_MAX, "MPATH");
+
+       assert_int_equal(rc, INT_MAX);
+}
+
+static void sd_max_p1(void **state)
+{
+       int rc = scan_devname("MPATH" MPATH_ID_INT_MAX_p1, "MPATH");
+
+       assert_int_equal(rc, -1);
+}
+#endif
+
+static void sd_fd_many(void **state)
+{
+       char buf[32];
+       int rc, i;
+
+       for (i = 1; i < 5000; i++) {
+               rc = format_devname(buf, i, sizeof(buf), "MPATH");
+               assert_in_range(rc, 6, 8);
+               rc = scan_devname(buf, "MPATH");
+               assert_int_equal(rc, i);
+       }
+}
+
+static void sd_fd_random(void **state)
+{
+       char buf[32];
+       int rc, i, n;
+
+       srandom(1);
+       for (i = 1; i < 1000; i++) {
+               n = random() & 0xffff;
+               rc = format_devname(buf, n, sizeof(buf), "MPATH");
+               assert_in_range(rc, 6, 9);
+               rc = scan_devname(buf, "MPATH");
+               assert_int_equal(rc, n);
+       }
+}
+
+static int test_scan_devname(void)
+{
+       const struct CMUnitTest tests[] = {
+               cmocka_unit_test(sd_mpatha),
+               cmocka_unit_test(sd_mpatha_spc),
+               cmocka_unit_test(sd_mpatha_tab),
+               cmocka_unit_test(sd_overlong),
+               cmocka_unit_test(sd_overlong1),
+               cmocka_unit_test(sd_noprefix),
+               cmocka_unit_test(sd_nomatchprefix),
+               cmocka_unit_test(sd_eq_prefix),
+               cmocka_unit_test(sd_bad_1),
+               cmocka_unit_test(sd_bad_2),
+#ifdef MPATH_ID_INT_MAX
+               cmocka_unit_test(sd_max),
+               cmocka_unit_test(sd_max_p1),
+#endif
+               cmocka_unit_test(sd_fd_many),
+               cmocka_unit_test(sd_fd_random),
+       };
+
+       return cmocka_run_group_tests(tests, NULL, NULL);
+}
+
+static void lb_empty(void **state)
+{
+       int rc;
+       char *alias;
+
+       will_return(__wrap_fgets, NULL);
+       expect_condlog(3, "No matching wwid [WWID0] in bindings file.\n");
+       rc = lookup_binding(NULL, "WWID0", &alias, NULL);
+       assert_int_equal(rc, 1);
+       assert_ptr_equal(alias, NULL);
+}
+
+static void lb_match_a(void **state)
+{
+       int rc;
+       char *alias;
+
+       will_return(__wrap_fgets, "MPATHa WWID0\n");
+       expect_condlog(3, "Found matching wwid [WWID0] in bindings file."
+                      " Setting alias to MPATHa\n");
+       rc = lookup_binding(NULL, "WWID0", &alias, "MPATH");
+       assert_int_equal(rc, 0);
+       assert_ptr_not_equal(alias, NULL);
+       assert_string_equal(alias, "MPATHa");
+       free(alias);
+}
+
+static void lb_nomatch_a(void **state)
+{
+       int rc;
+       char *alias;
+
+       will_return(__wrap_fgets, "MPATHa WWID0\n");
+       will_return(__wrap_fgets, NULL);
+       expect_condlog(3, "No matching wwid [WWID1] in bindings file.\n");
+       rc = lookup_binding(NULL, "WWID1", &alias, "MPATH");
+       assert_int_equal(rc, 2);
+       assert_ptr_equal(alias, NULL);
+}
+
+static void lb_match_c(void **state)
+{
+       int rc;
+       char *alias;
+
+       will_return(__wrap_fgets, "MPATHa WWID0\n");
+       will_return(__wrap_fgets, "MPATHc WWID1\n");
+       expect_condlog(3, "Found matching wwid [WWID1] in bindings file."
+                      " Setting alias to MPATHc\n");
+       rc = lookup_binding(NULL, "WWID1", &alias, "MPATH");
+       assert_int_equal(rc, 0);
+       assert_ptr_not_equal(alias, NULL);
+       assert_string_equal(alias, "MPATHc");
+       free(alias);
+}
+
+static void lb_nomatch_a_c(void **state)
+{
+       int rc;
+       char *alias;
+
+       will_return(__wrap_fgets, "MPATHa WWID0\n");
+       will_return(__wrap_fgets, "MPATHc WWID1\n");
+       will_return(__wrap_fgets, NULL);
+       expect_condlog(3, "No matching wwid [WWID2] in bindings file.\n");
+       rc = lookup_binding(NULL, "WWID2", &alias, "MPATH");
+       assert_int_equal(rc, 2);
+       assert_ptr_equal(alias, NULL);
+}
+
+static void lb_nomatch_c_a(void **state)
+{
+       int rc;
+       char *alias;
+
+       will_return(__wrap_fgets, "MPATHc WWID1\n");
+       will_return(__wrap_fgets, "MPATHa WWID0\n");
+       will_return(__wrap_fgets, NULL);
+       expect_condlog(3, "No matching wwid [WWID2] in bindings file.\n");
+       rc = lookup_binding(NULL, "WWID2", &alias, "MPATH");
+       assert_int_equal(rc, 2);
+       assert_ptr_equal(alias, NULL);
+}
+
+static void lb_nomatch_a_b(void **state)
+{
+       int rc;
+       char *alias;
+
+       will_return(__wrap_fgets, "MPATHa WWID0\n");
+       will_return(__wrap_fgets, "MPATHz WWID26\n");
+       will_return(__wrap_fgets, "MPATHb WWID1\n");
+       will_return(__wrap_fgets, NULL);
+       expect_condlog(3, "No matching wwid [WWID2] in bindings file.\n");
+       rc = lookup_binding(NULL, "WWID2", &alias, "MPATH");
+       assert_int_equal(rc, 3);
+       assert_ptr_equal(alias, NULL);
+}
+
+static void lb_nomatch_a_b_bad(void **state)
+{
+       int rc;
+       char *alias;
+
+       will_return(__wrap_fgets, "MPATHa WWID0\n");
+       will_return(__wrap_fgets, "MPATHz WWID26\n");
+       will_return(__wrap_fgets, "MPATHb\n");
+       will_return(__wrap_fgets, NULL);
+       expect_condlog(3, "Ignoring malformed line 3 in bindings file\n");
+       expect_condlog(3, "No matching wwid [WWID2] in bindings file.\n");
+       rc = lookup_binding(NULL, "WWID2", &alias, "MPATH");
+       assert_int_equal(rc, 3);
+       assert_ptr_equal(alias, NULL);
+}
+
+static void lb_nomatch_b_a(void **state)
+{
+       int rc;
+       char *alias;
+
+       will_return(__wrap_fgets, "MPATHb WWID1\n");
+       will_return(__wrap_fgets, "MPATHz WWID26\n");
+       will_return(__wrap_fgets, "MPATHa WWID0\n");
+       will_return(__wrap_fgets, NULL);
+       expect_condlog(3, "No matching wwid [WWID2] in bindings file.\n");
+       rc = lookup_binding(NULL, "WWID2", &alias, "MPATH");
+       assert_int_equal(rc, 27);
+       assert_ptr_equal(alias, NULL);
+}
+
+#ifdef MPATH_ID_INT_MAX
+static void lb_nomatch_int_max(void **state)
+{
+       int rc;
+       char *alias;
+
+       will_return(__wrap_fgets, "MPATHb WWID1\n");
+       will_return(__wrap_fgets, "MPATH" MPATH_ID_INT_MAX " WWIDMAX\n");
+       will_return(__wrap_fgets, "MPATHa WWID0\n");
+       will_return(__wrap_fgets, NULL);
+       expect_condlog(0, "no more available user_friendly_names\n");
+       rc = lookup_binding(NULL, "WWID2", &alias, "MPATH");
+       assert_int_equal(rc, -1);
+       assert_ptr_equal(alias, NULL);
+}
+
+static void lb_nomatch_int_max_m1(void **state)
+{
+       int rc;
+       char *alias;
+
+       will_return(__wrap_fgets, "MPATHb WWID1\n");
+       will_return(__wrap_fgets, "MPATH" MPATH_ID_INT_MAX_m1 " WWIDMAX\n");
+       will_return(__wrap_fgets, "MPATHa WWID0\n");
+       will_return(__wrap_fgets, NULL);
+       expect_condlog(3, "No matching wwid [WWID2] in bindings file.\n");
+       rc = lookup_binding(NULL, "WWID2", &alias, "MPATH");
+       assert_int_equal(rc, INT_MAX);
+       assert_ptr_equal(alias, NULL);
+}
+#endif
+
+static int test_lookup_binding(void)
+{
+       const struct CMUnitTest tests[] = {
+               cmocka_unit_test(lb_empty),
+               cmocka_unit_test(lb_match_a),
+               cmocka_unit_test(lb_nomatch_a),
+               cmocka_unit_test(lb_match_c),
+               cmocka_unit_test(lb_nomatch_a_c),
+               cmocka_unit_test(lb_nomatch_c_a),
+               cmocka_unit_test(lb_nomatch_a_b),
+               cmocka_unit_test(lb_nomatch_a_b_bad),
+               cmocka_unit_test(lb_nomatch_b_a),
+#ifdef MPATH_ID_INT_MAX
+               cmocka_unit_test(lb_nomatch_int_max),
+               cmocka_unit_test(lb_nomatch_int_max_m1),
+#endif
+       };
+
+       return cmocka_run_group_tests(tests, NULL, NULL);
+}
+
+static void rl_empty(void **state)
+{
+       int rc;
+       char buf[WWID_SIZE];
+
+       buf[0] = '\0';
+       will_return(__wrap_fgets, NULL);
+       expect_condlog(3, "No matching alias [MPATHa] in bindings file.\n");
+       rc = rlookup_binding(NULL, buf, "MPATHa");
+       assert_int_equal(rc, -1);
+       assert_string_equal(buf, "");
+}
+
+static void rl_match_a(void **state)
+{
+       int rc;
+       char buf[WWID_SIZE];
+
+       buf[0] = '\0';
+       will_return(__wrap_fgets, "MPATHa WWID0\n");
+       expect_condlog(3, "Found matching alias [MPATHa] in bindings file.\n"
+                      "Setting wwid to WWID0\n");
+       rc = rlookup_binding(NULL, buf, "MPATHa");
+       assert_int_equal(rc, 0);
+       assert_string_equal(buf, "WWID0");
+}
+
+static void rl_nomatch_a(void **state)
+{
+       int rc;
+       char buf[WWID_SIZE];
+
+       buf[0] = '\0';
+       will_return(__wrap_fgets, "MPATHa WWID0\n");
+       will_return(__wrap_fgets, NULL);
+       expect_condlog(3, "No matching alias [MPATHb] in bindings file.\n");
+       rc = rlookup_binding(NULL, buf, "MPATHb");
+       assert_int_equal(rc, -1);
+       assert_string_equal(buf, "");
+}
+
+static void rl_malformed_a(void **state)
+{
+       int rc;
+       char buf[WWID_SIZE];
+
+       buf[0] = '\0';
+       will_return(__wrap_fgets, "MPATHa     \n");
+       will_return(__wrap_fgets, NULL);
+       expect_condlog(3, "Ignoring malformed line 1 in bindings file\n");
+       expect_condlog(3, "No matching alias [MPATHa] in bindings file.\n");
+       rc = rlookup_binding(NULL, buf, "MPATHa");
+       assert_int_equal(rc, -1);
+       assert_string_equal(buf, "");
+}
+
+static void rl_overlong_a(void **state)
+{
+       int rc;
+       char buf[WWID_SIZE];
+       char line[WWID_SIZE + 10];
+
+       snprintf(line, sizeof(line), "MPATHa ");
+       memset(line + strlen(line), 'W', sizeof(line) - 2 - strlen(line));
+       snprintf(line + sizeof(line) - 2, 2, "\n");
+
+       buf[0] = '\0';
+       will_return(__wrap_fgets, line);
+       will_return(__wrap_fgets, NULL);
+       expect_condlog(3, "Ignoring too large wwid at 1 in bindings file\n");
+       expect_condlog(3, "No matching alias [MPATHa] in bindings file.\n");
+       rc = rlookup_binding(NULL, buf, "MPATHa");
+       assert_int_equal(rc, -1);
+       assert_string_equal(buf, "");
+}
+
+static void rl_match_b(void **state)
+{
+       int rc;
+       char buf[WWID_SIZE];
+
+       buf[0] = '\0';
+       will_return(__wrap_fgets, "MPATHa WWID0\n");
+       will_return(__wrap_fgets, "MPATHz WWID26\n");
+       will_return(__wrap_fgets, "MPATHb WWID2\n");
+       expect_condlog(3, "Found matching alias [MPATHb] in bindings file.\n"
+                      "Setting wwid to WWID2\n");
+       rc = rlookup_binding(NULL, buf, "MPATHb");
+       assert_int_equal(rc, 0);
+       assert_string_equal(buf, "WWID2");
+}
+
+static int test_rlookup_binding(void)
+{
+       const struct CMUnitTest tests[] = {
+               cmocka_unit_test(rl_empty),
+               cmocka_unit_test(rl_match_a),
+               cmocka_unit_test(rl_nomatch_a),
+               cmocka_unit_test(rl_malformed_a),
+               cmocka_unit_test(rl_overlong_a),
+               cmocka_unit_test(rl_match_b),
+       };
+
+       return cmocka_run_group_tests(tests, NULL, NULL);
+}
+
+static void al_a(void **state)
+{
+       static const char ln[] = "MPATHa WWIDa\n";
+       char *alias;
+
+       will_return(__wrap_lseek, 0);
+       expect_value(__wrap_write, count, strlen(ln));
+       expect_string(__wrap_write, buf, ln);
+       will_return(__wrap_write, strlen(ln));
+       expect_condlog(3, "Created new binding [MPATHa] for WWID [WWIDa]\n");
+
+       alias = allocate_binding(0, "WWIDa", 1, "MPATH");
+       assert_ptr_not_equal(alias, NULL);
+       assert_string_equal(alias, "MPATHa");
+}
+
+static void al_zz(void **state)
+{
+       static const char ln[] = "MPATHzz WWIDzz\n";
+       char *alias;
+
+       will_return(__wrap_lseek, 0);
+       expect_value(__wrap_write, count, strlen(ln));
+       expect_string(__wrap_write, buf, ln);
+       will_return(__wrap_write, strlen(ln));
+       expect_condlog(3, "Created new binding [MPATHzz] for WWID [WWIDzz]\n");
+
+       alias = allocate_binding(0, "WWIDzz", 26*26 + 26, "MPATH");
+       assert_ptr_not_equal(alias, NULL);
+       assert_string_equal(alias, "MPATHzz");
+}
+
+static void al_0(void **state)
+{
+       char *alias;
+
+       expect_condlog(0, "allocate_binding: cannot allocate new binding for id 0\n");
+       alias = allocate_binding(0, "WWIDa", 0, "MPATH");
+       assert_ptr_equal(alias, NULL);
+}
+
+static void al_m2(void **state)
+{
+       char *alias;
+
+       expect_condlog(0, "allocate_binding: cannot allocate new binding for id -2\n");
+       alias = allocate_binding(0, "WWIDa", -2, "MPATH");
+       assert_ptr_equal(alias, NULL);
+}
+
+static void al_lseek_err(void **state)
+{
+       char *alias;
+
+       will_return(__wrap_lseek, -ENODEV);
+       expect_condlog(0, "Cannot seek to end of bindings file : No such device\n");
+       alias = allocate_binding(0, "WWIDa", 1, "MPATH");
+       assert_ptr_equal(alias, NULL);
+}
+
+static void al_write_err(void **state)
+{
+       static const char ln[] = "MPATHa WWIDa\n";
+       const int offset = 20;
+       char *alias;
+
+       will_return(__wrap_lseek, offset);
+       expect_value(__wrap_write, count, strlen(ln));
+       expect_string(__wrap_write, buf, ln);
+       will_return(__wrap_write, strlen(ln) - 1);
+       expect_value(__wrap_ftruncate, length, offset);
+       will_return(__wrap_ftruncate, 0);
+       expect_condlog(0, "Cannot write binding to bindings file : Success\n");
+
+       alias = allocate_binding(0, "WWIDa", 1, "MPATH");
+       assert_ptr_equal(alias, NULL);
+}
+
+static int test_allocate_binding(void)
+{
+       const struct CMUnitTest tests[] = {
+               cmocka_unit_test(al_a),
+               cmocka_unit_test(al_zz),
+               cmocka_unit_test(al_0),
+               cmocka_unit_test(al_m2),
+               cmocka_unit_test(al_lseek_err),
+               cmocka_unit_test(al_write_err),
+       };
+
+       return cmocka_run_group_tests(tests, NULL, NULL);
+}
+
+int main(void)
+{
+       int ret = 0;
+
+       ret += test_format_devname();
+       ret += test_scan_devname();
+       ret += test_lookup_binding();
+       ret += test_rlookup_binding();
+       ret += test_allocate_binding();
+
+       return ret;
+}
index 362c44d..6e7c186 100644 (file)
@@ -21,7 +21,7 @@
 #include <cmocka.h>
 #include "globals.c"
 #include "blacklist.h"
-#include "log.h"
+#include "test-log.h"
 
 struct udev_device {
        const char *sysname;
@@ -40,8 +40,6 @@ struct udev_list_entry *
 __wrap_udev_device_get_properties_list_entry(struct udev_device *udev_device)
 {
        assert_non_null(udev_device);
-       if (!udev_device->property_list)
-               return NULL;
        if (!*udev_device->property_list)
                return NULL;
        return (struct udev_list_entry *)udev_device->property_list;
@@ -62,24 +60,6 @@ __wrap_udev_list_entry_get_name(struct udev_list_entry *list_entry)
        return *(const char **)list_entry;
 }
 
-void __wrap_dlog (int sink, int prio, const char * fmt, ...)
-{
-       char buff[MAX_MSG_SIZE];
-       va_list ap;
-
-       assert_int_equal(prio, mock_type(int));
-       va_start(ap, fmt);
-       vsnprintf(buff, MAX_MSG_SIZE, fmt, ap);
-       va_end(ap);
-       assert_string_equal(buff, mock_ptr_type(char *));
-}
-
-void expect_condlog(int prio, char *string)
-{
-       will_return(__wrap_dlog, prio);
-       will_return(__wrap_dlog, string);
-}
-
 vector blist_devnode_sdb;
 vector blist_all;
 vector blist_device_foo_bar;
diff --git a/tests/directio.c b/tests/directio.c
new file mode 100644 (file)
index 0000000..3cd7a52
--- /dev/null
@@ -0,0 +1,776 @@
+/*
+ * Copyright (c) 2018 Benjamin Marzinski, Redhat
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#define _GNU_SOURCE
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <stdlib.h>
+#include <cmocka.h>
+#include "globals.c"
+#include "../libmultipath/checkers/directio.c"
+
+int test_fd = 111;
+int ioctx_count = 0;
+struct io_event mock_events[AIO_GROUP_SIZE]; /* same as the checker max */
+int ev_off = 0;
+struct timespec zero_timeout = {0};
+struct timespec full_timeout = { .tv_sec = -1 };
+
+int __real_ioctl(int fd, unsigned long request, void *argp);
+
+int __wrap_ioctl(int fd, unsigned long request, void *argp)
+{
+#ifdef DIO_TEST_DEV
+       mock_type(int);
+       return __real_ioctl(fd, request, argp);
+#else
+       int *blocksize = (int *)argp;
+
+       assert_int_equal(fd, test_fd);
+       assert_int_equal(request, BLKBSZGET);
+       assert_non_null(blocksize);
+       *blocksize = mock_type(int);
+       return 0;
+#endif
+}
+
+int __real_fcntl(int fd, int cmd, long arg);
+
+int __wrap_fcntl(int fd, int cmd, long arg)
+{
+#ifdef DIO_TEST_DEV
+       return __real_fcntl(fd, cmd, arg);
+#else
+       assert_int_equal(fd, test_fd);
+       assert_int_equal(cmd, F_GETFL);
+       return O_DIRECT;
+#endif
+}
+
+int __real___fxstat(int ver, int fd, struct stat *statbuf);
+
+int __wrap___fxstat(int ver, int fd, struct stat *statbuf)
+{
+#ifdef DIO_TEST_DEV
+       return __real___fxstat(ver, fd, statbuf);
+#else
+       assert_int_equal(fd, test_fd);
+       assert_non_null(statbuf);
+       memset(statbuf, 0, sizeof(struct stat));
+       return 0;
+#endif
+}
+
+int __real_io_setup(int maxevents, io_context_t *ctxp);
+
+int __wrap_io_setup(int maxevents, io_context_t *ctxp)
+{
+       ioctx_count++;
+#ifdef DIO_TEST_DEV
+       int ret = mock_type(int);
+       assert_int_equal(ret, __real_io_setup(maxevents, ctxp));
+       return ret;
+#else
+       return mock_type(int);
+#endif
+}
+
+int __real_io_destroy(io_context_t ctx);
+
+int __wrap_io_destroy(io_context_t ctx)
+{
+       ioctx_count--;
+#ifdef DIO_TEST_DEV
+       int ret = mock_type(int);
+       assert_int_equal(ret, __real_io_destroy(ctx));
+       return ret;
+#else
+       return mock_type(int);
+#endif
+}
+
+int __real_io_submit(io_context_t ctx, long nr, struct iocb *ios[]);
+
+int __wrap_io_submit(io_context_t ctx, long nr, struct iocb *ios[])
+{
+#ifdef DIO_TEST_DEV
+       struct timespec dev_delay = { .tv_nsec = 100000 };
+       int ret = mock_type(int);
+       assert_int_equal(ret, __real_io_submit(ctx, nr, ios));
+       nanosleep(&dev_delay, NULL);
+       return ret;
+#else
+       return mock_type(int);
+#endif
+}
+
+int __real_io_cancel(io_context_t ctx, struct iocb *iocb, struct io_event *evt);
+
+int __wrap_io_cancel(io_context_t ctx, struct iocb *iocb, struct io_event *evt)
+{
+#ifdef DIO_TEST_DEV
+       mock_type(int);
+       return __real_io_cancel(ctx, iocb, evt);
+#else
+       return mock_type(int);
+#endif
+}
+
+int __real_io_getevents(io_context_t ctx, long min_nr, long nr,
+                       struct io_event *events, struct timespec *timeout);
+
+int __wrap_io_getevents(io_context_t ctx, long min_nr, long nr,
+                       struct io_event *events, struct timespec *timeout)
+{
+       int nr_evs;
+#ifndef DIO_TEST_DEV
+       struct timespec *sleep_tmo;
+       int i;
+       struct io_event *evs;
+#endif
+
+       assert_non_null(timeout);
+       nr_evs = mock_type(int);
+       assert_true(nr_evs <= nr);
+       if (!nr_evs)
+               return 0;
+#ifdef DIO_TEST_DEV
+       mock_ptr_type(struct timespec *);
+       mock_ptr_type(struct io_event *);
+       assert_int_equal(nr_evs, __real_io_getevents(ctx, min_nr, nr_evs,
+                                                    events, timeout));
+#else
+       sleep_tmo = mock_ptr_type(struct timespec *);
+       if (sleep_tmo) {
+               if (sleep_tmo->tv_sec < 0)
+                       nanosleep(timeout, NULL);
+               else
+                       nanosleep(sleep_tmo, NULL);
+       }
+       if (nr_evs < 0) {
+               errno = -nr_evs;
+               return -1;
+       }
+       evs = mock_ptr_type(struct io_event *);
+       for (i = 0; i < nr_evs; i++)
+               events[i] = evs[i];
+#endif
+       ev_off -= nr_evs;
+       return nr_evs;
+}
+
+static void return_io_getevents_none(void)
+{
+       will_return(__wrap_io_getevents, 0);
+}
+
+static void return_io_getevents_nr(struct timespec *ts, int nr,
+                                  struct async_req **reqs, int *res)
+{
+       int i, off = 0;
+
+       for(i = 0; i < nr; i++) {
+               mock_events[i + ev_off].obj = &reqs[i]->io;
+               if (res[i] == 0)
+                       mock_events[i + ev_off].res = reqs[i]->blksize;
+       }
+       while (nr > 0) {
+               will_return(__wrap_io_getevents, (nr > 128)? 128 : nr);
+               will_return(__wrap_io_getevents, ts);
+               will_return(__wrap_io_getevents, &mock_events[off + ev_off]);
+               ts = NULL;
+               off += 128;
+               nr -= 128;
+       }
+       if (nr == 0)
+               will_return(__wrap_io_getevents, 0);
+       ev_off += i;
+}
+
+void do_check_state(struct checker *c, int sync, int timeout, int chk_state)
+{
+       struct directio_context * ct = (struct directio_context *)c->context;
+
+       if (!ct->running)
+               will_return(__wrap_io_submit, 1);
+       assert_int_equal(check_state(test_fd, ct, sync, timeout), chk_state);
+       assert_int_equal(ev_off, 0);
+       memset(mock_events, 0, sizeof(mock_events));
+}
+
+void do_libcheck_reset(int nr_aio_grps)
+{
+       int count = 0;
+       struct aio_group *aio_grp;
+
+       list_for_each_entry(aio_grp, &aio_grp_list, node)
+               count++;
+       assert_int_equal(count, nr_aio_grps);
+       for (count = 0; count < nr_aio_grps; count++)
+               will_return(__wrap_io_destroy, 0);
+       libcheck_reset();
+       assert_true(list_empty(&aio_grp_list));
+       assert_int_equal(ioctx_count, 0);
+}
+
+static void do_libcheck_init(struct checker *c, int blocksize,
+                            struct async_req **req)
+{
+       struct directio_context * ct;
+
+       c->fd = test_fd;
+       will_return(__wrap_ioctl, blocksize);
+       assert_int_equal(libcheck_init(c), 0);
+       ct = (struct directio_context *)c->context;
+       assert_non_null(ct);
+       assert_non_null(ct->aio_grp);
+       assert_non_null(ct->req);
+       if (req)
+               *req = ct->req;
+#ifndef DIO_TEST_DEV
+       /* don't check fake blocksize on real devices */
+       assert_int_equal(ct->req->blksize, blocksize);
+#endif
+}
+
+static int is_checker_running(struct checker *c)
+{
+       struct directio_context * ct = (struct directio_context *)c->context;
+       return ct->running;
+}
+
+static struct aio_group *get_aio_grp(struct checker *c)
+{
+       struct directio_context * ct = (struct directio_context *)c->context;
+
+       assert_non_null(ct);
+       return ct->aio_grp;
+}
+
+static void check_aio_grp(struct aio_group *aio_grp, int holders,
+                         int orphans)
+{
+       int count = 0;
+       struct list_head *item;
+
+       list_for_each(item, &aio_grp->orphans)
+               count++;
+       assert_int_equal(holders, aio_grp->holders);
+       assert_int_equal(orphans, count);
+}
+
+/* simple resetting test */
+static void test_reset(void **state)
+{
+       assert_true(list_empty(&aio_grp_list));
+       do_libcheck_reset(0);
+}
+
+/* tests initializing, then resetting, and then initializing again */
+static void test_init_reset_init(void **state)
+{
+       struct checker c = {0};
+       struct aio_group *aio_grp, *tmp_grp;
+
+       assert_true(list_empty(&aio_grp_list));
+       will_return(__wrap_io_setup, 0);
+       do_libcheck_init(&c, 4096, NULL);
+       aio_grp = get_aio_grp(&c);
+       check_aio_grp(aio_grp, 1, 0);
+       list_for_each_entry(tmp_grp, &aio_grp_list, node)
+               assert_ptr_equal(aio_grp, tmp_grp);
+       libcheck_free(&c);
+       check_aio_grp(aio_grp, 0, 0);
+       do_libcheck_reset(1);
+       will_return(__wrap_io_setup, 0);
+       do_libcheck_init(&c, 4096, NULL);
+       aio_grp = get_aio_grp(&c);
+       check_aio_grp(aio_grp, 1, 0);
+       list_for_each_entry(tmp_grp, &aio_grp_list, node)
+               assert_ptr_equal(aio_grp, tmp_grp);
+       libcheck_free(&c);
+       check_aio_grp(aio_grp, 0, 0);
+       do_libcheck_reset(1);
+}
+
+/* test initializing and then freeing 4096 checkers */
+static void test_init_free(void **state)
+{
+       int i, count = 0;
+       struct checker c[4096] = {0};
+       struct aio_group *aio_grp;
+
+       assert_true(list_empty(&aio_grp_list));
+       will_return(__wrap_io_setup, 0);
+       will_return(__wrap_io_setup, 0);
+       will_return(__wrap_io_setup, 0);
+       will_return(__wrap_io_setup, 0);
+       for (i = 0; i < 4096; i++) {
+               struct directio_context * ct;
+
+               if (i % 3 == 0)
+                       do_libcheck_init(&c[i], 512, NULL);
+               else if (i % 3 == 1)
+                       do_libcheck_init(&c[i], 1024, NULL);
+               else
+                       do_libcheck_init(&c[i], 4096, NULL);
+               ct = (struct directio_context *)c[i].context;
+               assert_non_null(ct->aio_grp);
+               if ((i & 1023) == 0)
+                       aio_grp = ct->aio_grp;
+               else {
+                       assert_ptr_equal(ct->aio_grp, aio_grp);
+                       assert_int_equal(aio_grp->holders, (i & 1023) + 1);
+               }
+       }
+       count = 0;
+       list_for_each_entry(aio_grp, &aio_grp_list, node)
+               count++;
+       assert_int_equal(count, 4);
+       for (i = 0; i < 4096; i++) {
+               struct directio_context * ct = (struct directio_context *)c[i].context;
+
+               aio_grp = ct->aio_grp;
+               libcheck_free(&c[i]);
+               assert_int_equal(aio_grp->holders, 1023 - (i & 1023));
+       }
+       list_for_each_entry(aio_grp, &aio_grp_list, node)
+               assert_int_equal(aio_grp->holders, 0);
+       do_libcheck_reset(4);
+}
+
+/* check mixed initializing and freeing 4096 checkers */
+static void test_multi_init_free(void **state)
+{
+       int i, count;
+       struct checker c[4096] = {0};
+       struct aio_group *aio_grp;
+
+       assert_true(list_empty(&aio_grp_list));
+       will_return(__wrap_io_setup, 0);
+       will_return(__wrap_io_setup, 0);
+       will_return(__wrap_io_setup, 0);
+       will_return(__wrap_io_setup, 0);
+       for (count = 0, i = 0; i < 4096; count++) {
+               /* usually init, but occasionally free checkers */
+               if (count == 0 || (count % 5 != 0 && count % 7 != 0)) {
+                       do_libcheck_init(&c[i], 4096, NULL);
+                       i++;
+               } else {
+                       i--;
+                       libcheck_free(&c[i]);
+               }
+       }
+       count = 0;
+       list_for_each_entry(aio_grp, &aio_grp_list, node) {
+               assert_int_equal(aio_grp->holders, 1024);
+               count++;
+       }
+       assert_int_equal(count, 4);
+       for (count = 0, i = 4096; i > 0; count++) {
+               /* usually free, but occasionally init checkers */
+               if (count == 0 || (count % 5 != 0 && count % 7 != 0)) {
+                       i--;
+                       libcheck_free(&c[i]);
+               } else {
+                       do_libcheck_init(&c[i], 4096, NULL);
+                       i++;
+               }
+       }
+       do_libcheck_reset(4);
+}
+
+/* simple single checker sync test */
+static void test_check_state_simple(void **state)
+{
+       struct checker c = {0};
+       struct async_req *req;
+       int res = 0;
+
+       assert_true(list_empty(&aio_grp_list));
+       will_return(__wrap_io_setup, 0);
+       do_libcheck_init(&c, 4096, &req);
+       return_io_getevents_nr(NULL, 1, &req, &res);
+       do_check_state(&c, 1, 30, PATH_UP);
+       libcheck_free(&c);
+       do_libcheck_reset(1);
+}
+
+/* test sync timeout */
+static void test_check_state_timeout(void **state)
+{
+       struct checker c = {0};
+       struct aio_group *aio_grp;
+
+       assert_true(list_empty(&aio_grp_list));
+       will_return(__wrap_io_setup, 0);
+       do_libcheck_init(&c, 4096, NULL);
+       aio_grp = get_aio_grp(&c);
+       return_io_getevents_none();
+       will_return(__wrap_io_cancel, 0);
+       do_check_state(&c, 1, 30, PATH_DOWN);
+       check_aio_grp(aio_grp, 1, 0);
+#ifdef DIO_TEST_DEV
+       /* io_cancel will return negative value on timeout, so it happens again
+        * when freeing the checker */
+       will_return(__wrap_io_cancel, 0);
+#endif
+       libcheck_free(&c);
+       do_libcheck_reset(1);
+}
+
+/* test async timeout */
+static void test_check_state_async_timeout(void **state)
+{
+       struct checker c = {0};
+       struct aio_group *aio_grp;
+
+       assert_true(list_empty(&aio_grp_list));
+       will_return(__wrap_io_setup, 0);
+       do_libcheck_init(&c, 4096, NULL);
+       aio_grp = get_aio_grp(&c);
+       return_io_getevents_none();
+       do_check_state(&c, 0, 3, PATH_PENDING);
+       return_io_getevents_none();
+       do_check_state(&c, 0, 3, PATH_PENDING);
+       return_io_getevents_none();
+       do_check_state(&c, 0, 3, PATH_PENDING);
+       return_io_getevents_none();
+       will_return(__wrap_io_cancel, 0);
+       do_check_state(&c, 0, 3, PATH_DOWN);
+       check_aio_grp(aio_grp, 1, 0);
+#ifdef DIO_TEST_DEV
+       will_return(__wrap_io_cancel, 0);
+#endif
+       libcheck_free(&c);
+       do_libcheck_reset(1);
+}
+
+/* test freeing checkers with outstanding requests */
+static void test_free_with_pending(void **state)
+{
+        struct checker c[2] = {0};
+        struct aio_group *aio_grp;
+       struct async_req *req;
+       int res = 0;
+
+       assert_true(list_empty(&aio_grp_list));
+       will_return(__wrap_io_setup, 0);
+        do_libcheck_init(&c[0], 4096, &req);
+       do_libcheck_init(&c[1], 4096, NULL);
+        aio_grp = get_aio_grp(c);
+        return_io_getevents_none();
+        do_check_state(&c[0], 0, 30, PATH_PENDING);
+       return_io_getevents_nr(NULL, 1, &req, &res);
+       return_io_getevents_none();
+       do_check_state(&c[1], 0, 30, PATH_PENDING);
+       assert_true(is_checker_running(&c[0]));
+       assert_true(is_checker_running(&c[1]));
+       check_aio_grp(aio_grp, 2, 0);
+        libcheck_free(&c[0]);
+       check_aio_grp(aio_grp, 1, 0);
+        will_return(__wrap_io_cancel, 0);
+        libcheck_free(&c[1]);
+#ifdef DIO_TEST_DEV
+       check_aio_grp(aio_grp, 1, 1); /* real cancel doesn't remove request */
+#else
+        check_aio_grp(aio_grp, 0, 0);
+#endif
+        do_libcheck_reset(1);
+}
+
+/* test removing orpahed aio_group on free */
+static void test_orphaned_aio_group(void **state)
+{
+       struct checker c[AIO_GROUP_SIZE] = {0};
+       struct aio_group *aio_grp, *tmp_grp;
+       int i;
+
+       assert_true(list_empty(&aio_grp_list));
+       will_return(__wrap_io_setup, 0);
+       for (i = 0; i < AIO_GROUP_SIZE; i++) {
+               do_libcheck_init(&c[i], 4096, NULL);
+               return_io_getevents_none();
+               do_check_state(&c[i], 0, 30, PATH_PENDING);
+       }
+       aio_grp = get_aio_grp(c);
+       check_aio_grp(aio_grp, AIO_GROUP_SIZE, 0);
+       i = 0;
+       list_for_each_entry(tmp_grp, &aio_grp_list, node)
+               i++;
+       assert_int_equal(i, 1);
+       for (i = 0; i < AIO_GROUP_SIZE; i++) {
+               assert_true(is_checker_running(&c[i]));
+               will_return(__wrap_io_cancel, -1);
+               if (i == AIO_GROUP_SIZE - 1) {
+                       /* remove the orphaned group and create a new one */
+                       will_return(__wrap_io_destroy, 0);
+               }
+               libcheck_free(&c[i]);
+       }
+        do_libcheck_reset(0);
+}
+
+/* test sync timeout with failed cancel and cleanup by another
+ * checker */
+static void test_timeout_cancel_failed(void **state)
+{
+       struct checker c[2] = {0};
+       struct aio_group *aio_grp;
+       struct async_req *reqs[2];
+       int res[] = {0,0};
+       int i;
+
+       assert_true(list_empty(&aio_grp_list));
+       will_return(__wrap_io_setup, 0);
+       for (i = 0; i < 2; i++)
+               do_libcheck_init(&c[i], 4096, &reqs[i]);
+       aio_grp = get_aio_grp(c);
+       return_io_getevents_none();
+       will_return(__wrap_io_cancel, -1);
+       do_check_state(&c[0], 1, 30, PATH_DOWN);
+       assert_true(is_checker_running(&c[0]));
+       check_aio_grp(aio_grp, 2, 0);
+       return_io_getevents_none();
+       will_return(__wrap_io_cancel, -1);
+       do_check_state(&c[0], 1, 30, PATH_DOWN);
+       assert_true(is_checker_running(&c[0]));
+       return_io_getevents_nr(NULL, 1, &reqs[0], &res[0]);
+       return_io_getevents_nr(NULL, 1, &reqs[1], &res[1]);
+       do_check_state(&c[1], 1, 30, PATH_UP);
+       do_check_state(&c[0], 1, 30, PATH_UP);
+       for (i = 0; i < 2; i++) {
+               assert_false(is_checker_running(&c[i]));
+               libcheck_free(&c[i]);
+       }
+       do_libcheck_reset(1);
+}
+
+/* test async timeout with failed cancel and cleanup by another
+ * checker */
+static void test_async_timeout_cancel_failed(void **state)
+{
+       struct checker c[2] = {0};
+       struct async_req *reqs[2];
+       int res[] = {0,0};
+       int i;
+
+       assert_true(list_empty(&aio_grp_list));
+       will_return(__wrap_io_setup, 0);
+       for (i = 0; i < 2; i++)
+               do_libcheck_init(&c[i], 4096, &reqs[i]);
+       return_io_getevents_none();
+       do_check_state(&c[0], 0, 2, PATH_PENDING);
+       return_io_getevents_none();
+       do_check_state(&c[1], 0, 2, PATH_PENDING);
+       return_io_getevents_none();
+       do_check_state(&c[0], 0, 2, PATH_PENDING);
+       return_io_getevents_none();
+       do_check_state(&c[1], 0, 2, PATH_PENDING);
+       return_io_getevents_none();
+       will_return(__wrap_io_cancel, -1);
+       do_check_state(&c[0], 0, 2, PATH_DOWN);
+#ifndef DIO_TEST_DEV
+       /* can't pick which even gets returned on real devices */
+       return_io_getevents_nr(NULL, 1, &reqs[1], &res[1]);
+       do_check_state(&c[1], 0, 2, PATH_UP);
+#endif
+       return_io_getevents_none();
+       will_return(__wrap_io_cancel, -1);
+       do_check_state(&c[0], 0, 2, PATH_DOWN);
+       assert_true(is_checker_running(&c[0]));
+       return_io_getevents_nr(NULL, 2, reqs, res);
+       do_check_state(&c[1], 0, 2, PATH_UP);
+       do_check_state(&c[0], 0, 2, PATH_UP);
+       for (i = 0; i < 2; i++) {
+               assert_false(is_checker_running(&c[i]));
+               libcheck_free(&c[i]);
+       }
+       do_libcheck_reset(1);
+}
+
+/* test orphaning a request, and having another checker clean it up */
+static void test_orphan_checker_cleanup(void **state)
+{
+       struct checker c[2] = {0};
+       struct async_req *reqs[2];
+       int res[] = {0,0};
+       struct aio_group *aio_grp;
+       int i;
+
+       assert_true(list_empty(&aio_grp_list));
+       will_return(__wrap_io_setup, 0);
+       for (i = 0; i < 2; i++)
+               do_libcheck_init(&c[i], 4096, &reqs[i]);
+       aio_grp = get_aio_grp(c);
+       return_io_getevents_none();
+       do_check_state(&c[0], 0, 30, PATH_PENDING);
+       will_return(__wrap_io_cancel, -1);
+       check_aio_grp(aio_grp, 2, 0);
+       libcheck_free(&c[0]);
+       check_aio_grp(aio_grp, 2, 1);
+       return_io_getevents_nr(NULL, 2, reqs, res);
+       do_check_state(&c[1], 0, 2, PATH_UP);
+       check_aio_grp(aio_grp, 1, 0);
+       libcheck_free(&c[1]);
+       check_aio_grp(aio_grp, 0, 0);
+       do_libcheck_reset(1);
+}
+
+/* test orphaning a request, and having reset clean it up */
+static void test_orphan_reset_cleanup(void **state)
+{
+       struct checker c;
+       struct aio_group *orphan_aio_grp, *tmp_aio_grp;
+       int found, count;
+
+       assert_true(list_empty(&aio_grp_list));
+       will_return(__wrap_io_setup, 0);
+       do_libcheck_init(&c, 4096, NULL);
+       orphan_aio_grp = get_aio_grp(&c);
+       return_io_getevents_none();
+       do_check_state(&c, 0, 30, PATH_PENDING);
+       will_return(__wrap_io_cancel, -1);
+       check_aio_grp(orphan_aio_grp, 1, 0);
+       libcheck_free(&c);
+       check_aio_grp(orphan_aio_grp, 1, 1);
+       found = count = 0;
+       list_for_each_entry(tmp_aio_grp, &aio_grp_list, node) {
+               count++;
+               if (tmp_aio_grp == orphan_aio_grp)
+                       found = 1;
+       }
+       assert_int_equal(count, 1);
+       assert_int_equal(found, 1);
+       do_libcheck_reset(1);
+}
+
+/* test checkers with different blocksizes */
+static void test_check_state_blksize(void **state)
+{
+       int i;
+       struct checker c[3] = {0};
+       int blksize[] = {4096, 1024, 512};
+       struct async_req *reqs[3];
+       int res[] = {0,1,0};
+#ifdef DIO_TEST_DEV
+       /* can't pick event return state on real devices */
+       int chk_state[] = {PATH_UP, PATH_UP, PATH_UP};
+#else
+       int chk_state[] = {PATH_UP, PATH_DOWN, PATH_UP};
+#endif
+
+       assert_true(list_empty(&aio_grp_list));
+       will_return(__wrap_io_setup, 0);
+       for (i = 0; i < 3; i++)
+               do_libcheck_init(&c[i], blksize[i], &reqs[i]);
+       for (i = 0; i < 3; i++) {
+               return_io_getevents_nr(NULL, 1, &reqs[i], &res[i]);
+               do_check_state(&c[i], 1, 30, chk_state[i]);
+       }
+       for (i = 0; i < 3; i++) {
+               assert_false(is_checker_running(&c[i]));
+               libcheck_free(&c[i]);
+       }
+       do_libcheck_reset(1);
+}
+
+/* test async checkers pending and getting resovled by another checker
+ * as well as the loops for getting multiple events */
+static void test_check_state_async(void **state)
+{
+       int i;
+       struct checker c[257] = {0};
+       struct async_req *reqs[257];
+       int res[257] = {0};
+
+       assert_true(list_empty(&aio_grp_list));
+       will_return(__wrap_io_setup, 0);
+       for (i = 0; i < 257; i++)
+               do_libcheck_init(&c[i], 4096, &reqs[i]);
+       for (i = 0; i < 256; i++) {
+               return_io_getevents_none();
+               do_check_state(&c[i], 0, 30, PATH_PENDING);
+               assert_true(is_checker_running(&c[i]));
+       }
+       return_io_getevents_nr(&full_timeout, 256, reqs, res);
+       return_io_getevents_nr(NULL, 1, &reqs[256], &res[256]);
+       do_check_state(&c[256], 0, 30, PATH_UP);
+       assert_false(is_checker_running(&c[256]));
+       libcheck_free(&c[256]);
+       for (i = 0; i < 256; i++) {
+               do_check_state(&c[i], 0, 30, PATH_UP);
+               assert_false(is_checker_running(&c[i]));
+               libcheck_free(&c[i]);
+       }
+       do_libcheck_reset(1);
+}
+
+static int setup(void **state)
+{
+#ifdef DIO_TEST_DEV
+       test_fd = open(DIO_TEST_DEV, O_RDONLY);
+       if (test_fd < 0)
+               fail_msg("cannot open %s: %m", DIO_TEST_DEV);
+#endif
+       return 0;
+}
+
+static int teardown(void **state)
+{
+#ifdef DIO_TEST_DEV
+       assert_true(test_fd > 0);
+       assert_int_equal(close(test_fd), 0);
+#endif
+       return 0;
+}
+
+int test_directio(void)
+{
+       const struct CMUnitTest tests[] = {
+               cmocka_unit_test(test_reset),
+               cmocka_unit_test(test_init_reset_init),
+               cmocka_unit_test(test_init_free),
+               cmocka_unit_test(test_multi_init_free),
+               cmocka_unit_test(test_check_state_simple),
+               cmocka_unit_test(test_check_state_timeout),
+               cmocka_unit_test(test_check_state_async_timeout),
+               cmocka_unit_test(test_free_with_pending),
+               cmocka_unit_test(test_timeout_cancel_failed),
+               cmocka_unit_test(test_async_timeout_cancel_failed),
+               cmocka_unit_test(test_orphan_checker_cleanup),
+               cmocka_unit_test(test_orphan_reset_cleanup),
+               cmocka_unit_test(test_check_state_blksize),
+               cmocka_unit_test(test_check_state_async),
+               cmocka_unit_test(test_orphaned_aio_group),
+       };
+
+       return cmocka_run_group_tests(tests, setup, teardown);
+}
+
+int main(void)
+{
+       int ret = 0;
+
+       conf.verbosity = 2;
+       ret += test_directio();
+       return ret;
+}
index 977a566..473028b 100644 (file)
@@ -261,7 +261,7 @@ static void write_defaults(const struct hwt_state *hwt)
        defaults[0].value = hwt->dirname;
        defaults[1].value = buf;
        assert_ptr_not_equal(getcwd(dirbuf, sizeof(dirbuf)), NULL);
-       strncat(dirbuf, "/lib", sizeof(dirbuf));
+       strncat(dirbuf, "/lib", sizeof(dirbuf) - 5);
        defaults[2].value = dirbuf;
        write_section(hwt->config_file, "defaults",
                      ARRAY_SIZE(defaults), defaults);
diff --git a/tests/test-log.c b/tests/test-log.c
new file mode 100644 (file)
index 0000000..d685d58
--- /dev/null
@@ -0,0 +1,27 @@
+#include <setjmp.h>
+#include <stddef.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <cmocka.h>
+#include "log.h"
+#include "test-log.h"
+
+__attribute__((format(printf, 3, 0)))
+void __wrap_dlog (int sink, int prio, const char * fmt, ...)
+{
+       char buff[MAX_MSG_SIZE];
+       va_list ap;
+
+       assert_int_equal(prio, mock_type(int));
+       va_start(ap, fmt);
+       vsnprintf(buff, MAX_MSG_SIZE, fmt, ap);
+       va_end(ap);
+       assert_string_equal(buff, mock_ptr_type(char *));
+}
+
+void expect_condlog(int prio, char *string)
+{
+       will_return(__wrap_dlog, prio);
+       will_return(__wrap_dlog, string);
+}
+
diff --git a/tests/test-log.h b/tests/test-log.h
new file mode 100644 (file)
index 0000000..2c878c6
--- /dev/null
@@ -0,0 +1,7 @@
+#ifndef _TEST_LOG_H
+#define _TEST_LOG_H
+
+void __wrap_dlog (int sink, int prio, const char * fmt, ...);
+void expect_condlog(int prio, char *string);
+
+#endif
index 4e04a48..7c486fc 100644 (file)
@@ -328,11 +328,12 @@ static void test_strlcpy_5(void **state)
 {
        char *tst;
        int rc;
+       const int sz = sizeof(src_str);
 
-       tst = malloc(sizeof(src_str));
+       tst = malloc(sz);
        memset(tst, 'f', sizeof(src_str));
 
-       rc = strlcpy(tst, src_str, sizeof(src_str));
+       rc = strlcpy(tst, src_str, sz);
        assert_int_equal(rc, strlen(src_str));
        assert_string_equal(src_str, tst);
 
@@ -344,15 +345,16 @@ static void test_strlcpy_6(void **state)
 {
        char *tst;
        int rc;
+       const int sz = sizeof(src_str);
 
-       tst = malloc(sizeof(src_str) + 2);
-       memset(tst, 'f', sizeof(src_str) + 2);
+       tst = malloc(sz + 2);
+       memset(tst, 'f', sz + 2);
 
-       rc = strlcpy(tst, src_str, sizeof(src_str) + 2);
+       rc = strlcpy(tst, src_str, sz + 2);
        assert_int_equal(rc, strlen(src_str));
        assert_string_equal(src_str, tst);
-       assert_int_equal(tst[sizeof(src_str)], 'f');
-       assert_int_equal(tst[sizeof(src_str) + 1], 'f');
+       assert_int_equal(tst[sz], 'f');
+       assert_int_equal(tst[sz + 1], 'f');
 
        free(tst);
 }
index d9f80ea..3cbad81 100644 (file)
@@ -429,7 +429,7 @@ static void test_vpd_vnd_ ## len ## _ ## wlen(void **state)             \
        free(exp_wwid);                                                 \
        will_return(__wrap_ioctl, n);                                   \
        will_return(__wrap_ioctl, vt->vpdbuf);                          \
-       ret = get_vpd_sgio(10, 0x83, vt->wwid, wlen);                   \
+       ret = get_vpd_sgio(10, 0x83, 0, vt->wwid, wlen);                \
        assert_correct_wwid("test_vpd_vnd_" #len "_" #wlen,             \
                            exp_len, ret, '1', 0, false,                \
                            exp_subst, vt->wwid);                       \
@@ -459,7 +459,7 @@ static void test_vpd_str_ ## typ ## _ ## len ## _ ## wlen(void **state) \
                exp_len = wlen - 1;                                     \
        will_return(__wrap_ioctl, n);                                   \
        will_return(__wrap_ioctl, vt->vpdbuf);                          \
-       ret = get_vpd_sgio(10, 0x83, vt->wwid, wlen);                   \
+       ret = get_vpd_sgio(10, 0x83, 0, vt->wwid, wlen);                \
        assert_correct_wwid("test_vpd_str_" #typ "_" #len "_" #wlen,    \
                            exp_len, ret, byte0[type], 0,               \
                            type != STR_IQN,                            \
@@ -496,7 +496,7 @@ static void test_vpd_naa_ ## naa ## _ ## wlen(void **state)             \
                         3, naa, 0);                                    \
        will_return(__wrap_ioctl, n);                                   \
        will_return(__wrap_ioctl, vt->vpdbuf);                          \
-       ret = get_vpd_sgio(10, 0x83, vt->wwid, wlen);                   \
+       ret = get_vpd_sgio(10, 0x83, 0, vt->wwid, wlen);                \
        assert_correct_wwid("test_vpd_naa_" #naa "_" #wlen,             \
                            exp_len, ret, '3', '0' + naa, true,         \
                            test_id, vt->wwid);                         \
@@ -506,9 +506,10 @@ static void test_vpd_naa_ ## naa ## _ ## wlen(void **state)             \
  * test_vpd_eui_LEN_WLEN() - test code for VPD 83, EUI64
  * @LEN:       EUI64 length (8, 12, or 16)
  * @WLEN:      WWID buffer size
+ * @SML:       Use small VPD page size
  */
-#define make_test_vpd_eui(len, wlen)                                   \
-static void test_vpd_eui_ ## len ## _ ## wlen(void **state)             \
+#define make_test_vpd_eui(len, wlen, sml)                              \
+static void test_vpd_eui_ ## len ## _ ## wlen ## _ ## sml(void **state)        \
 {                                                                      \
        struct vpdtest *vt = *state;                                    \
        int n, ret;                                                     \
@@ -518,10 +519,17 @@ static void test_vpd_eui_ ## len ## _ ## wlen(void **state)             \
                                                                        \
        n = create_vpd83(vt->vpdbuf, sizeof(vt->vpdbuf), test_id,       \
                         2, 0, len);                                    \
+       if (sml) {                                                      \
+               /* overwrite the page size to DEFAULT_SGIO_LEN + 1 */   \
+               put_unaligned_be16(255, vt->vpdbuf + 2);                \
+               /* this causes get_vpd_sgio to do a second ioctl */     \
+               will_return(__wrap_ioctl, n);                           \
+               will_return(__wrap_ioctl, vt->vpdbuf);                  \
+       }                                                               \
        will_return(__wrap_ioctl, n);                                   \
        will_return(__wrap_ioctl, vt->vpdbuf);                          \
-       ret = get_vpd_sgio(10, 0x83, vt->wwid, wlen);                   \
-       assert_correct_wwid("test_vpd_eui_" #len "_" #wlen,             \
+       ret = get_vpd_sgio(10, 0x83, 0, vt->wwid, wlen);                \
+       assert_correct_wwid("test_vpd_eui_" #len "_" #wlen "_" #sml,    \
                            exp_len, ret, '2', 0, true,                 \
                            test_id, vt->wwid);                         \
 }
@@ -547,7 +555,7 @@ static void test_vpd80_ ## size ## _ ## len ## _ ## wlen(void **state)  \
                         size, len);                                    \
        will_return(__wrap_ioctl, n);                                   \
        will_return(__wrap_ioctl, vt->vpdbuf);                          \
-       ret = get_vpd_sgio(10, 0x80, vt->wwid, wlen);                   \
+       ret = get_vpd_sgio(10, 0x80, 0, vt->wwid, wlen);                \
        assert_correct_wwid("test_vpd80_" #size "_" #len "_" #wlen,     \
                            exp_len, ret, 0, 0, false,                  \
                            input, vt->wwid);                           \
@@ -603,25 +611,30 @@ make_test_vpd_vnd(20, 10);
 make_test_vpd_vnd(10, 10);
 
 /* EUI64 tests */
+/* small vpd page test */
+make_test_vpd_eui(8, 32, 1);
+make_test_vpd_eui(12, 32, 1);
+make_test_vpd_eui(16, 40, 1);
+
 /* 64bit, WWID size: 18 */
-make_test_vpd_eui(8, 32);
-make_test_vpd_eui(8, 18);
-make_test_vpd_eui(8, 17);
-make_test_vpd_eui(8, 16);
-make_test_vpd_eui(8, 10);
+make_test_vpd_eui(8, 32, 0);
+make_test_vpd_eui(8, 18, 0);
+make_test_vpd_eui(8, 17, 0);
+make_test_vpd_eui(8, 16, 0);
+make_test_vpd_eui(8, 10, 0);
 
 /* 96 bit, WWID size: 26 */
-make_test_vpd_eui(12, 32);
-make_test_vpd_eui(12, 26);
-make_test_vpd_eui(12, 25);
-make_test_vpd_eui(12, 20);
-make_test_vpd_eui(12, 10);
+make_test_vpd_eui(12, 32, 0);
+make_test_vpd_eui(12, 26, 0);
+make_test_vpd_eui(12, 25, 0);
+make_test_vpd_eui(12, 20, 0);
+make_test_vpd_eui(12, 10, 0);
 
 /* 128 bit, WWID size: 34 */
-make_test_vpd_eui(16, 40);
-make_test_vpd_eui(16, 34);
-make_test_vpd_eui(16, 33);
-make_test_vpd_eui(16, 20);
+make_test_vpd_eui(16, 40, 0);
+make_test_vpd_eui(16, 34, 0);
+make_test_vpd_eui(16, 33, 0);
+make_test_vpd_eui(16, 20, 0);
 
 /* NAA IEEE registered extended (36), WWID size: 34 */
 make_test_vpd_naa(6, 40);
@@ -722,20 +735,23 @@ static int test_vpd(void)
                cmocka_unit_test(test_vpd_vnd_19_20),
                cmocka_unit_test(test_vpd_vnd_20_10),
                cmocka_unit_test(test_vpd_vnd_10_10),
-               cmocka_unit_test(test_vpd_eui_8_32),
-               cmocka_unit_test(test_vpd_eui_8_18),
-               cmocka_unit_test(test_vpd_eui_8_17),
-               cmocka_unit_test(test_vpd_eui_8_16),
-               cmocka_unit_test(test_vpd_eui_8_10),
-               cmocka_unit_test(test_vpd_eui_12_32),
-               cmocka_unit_test(test_vpd_eui_12_26),
-               cmocka_unit_test(test_vpd_eui_12_25),
-               cmocka_unit_test(test_vpd_eui_12_20),
-               cmocka_unit_test(test_vpd_eui_12_10),
-               cmocka_unit_test(test_vpd_eui_16_40),
-               cmocka_unit_test(test_vpd_eui_16_34),
-               cmocka_unit_test(test_vpd_eui_16_33),
-               cmocka_unit_test(test_vpd_eui_16_20),
+               cmocka_unit_test(test_vpd_eui_8_32_1),
+               cmocka_unit_test(test_vpd_eui_12_32_1),
+               cmocka_unit_test(test_vpd_eui_16_40_1),
+               cmocka_unit_test(test_vpd_eui_8_32_0),
+               cmocka_unit_test(test_vpd_eui_8_18_0),
+               cmocka_unit_test(test_vpd_eui_8_17_0),
+               cmocka_unit_test(test_vpd_eui_8_16_0),
+               cmocka_unit_test(test_vpd_eui_8_10_0),
+               cmocka_unit_test(test_vpd_eui_12_32_0),
+               cmocka_unit_test(test_vpd_eui_12_26_0),
+               cmocka_unit_test(test_vpd_eui_12_25_0),
+               cmocka_unit_test(test_vpd_eui_12_20_0),
+               cmocka_unit_test(test_vpd_eui_12_10_0),
+               cmocka_unit_test(test_vpd_eui_16_40_0),
+               cmocka_unit_test(test_vpd_eui_16_34_0),
+               cmocka_unit_test(test_vpd_eui_16_33_0),
+               cmocka_unit_test(test_vpd_eui_16_20_0),
                cmocka_unit_test(test_vpd_naa_6_40),
                cmocka_unit_test(test_vpd_naa_6_34),
                cmocka_unit_test(test_vpd_naa_6_33),