summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDongHun Kwak <dh0128.kwak@samsung.com>2022-01-14 13:50:19 +0900
committerDongHun Kwak <dh0128.kwak@samsung.com>2022-01-14 13:50:19 +0900
commite15d163ead9b0d93282a756add758e11bea75879 (patch)
tree5c8e0d6adfb1620228d3aacd24e06f8269f2ec47
parent9f1483fb0bea7defd7bdcfedb8a5329171f6d1d9 (diff)
downloadmultipath-tools-e15d163ead9b0d93282a756add758e11bea75879.tar.gz
multipath-tools-e15d163ead9b0d93282a756add758e11bea75879.tar.bz2
multipath-tools-e15d163ead9b0d93282a756add758e11bea75879.zip
Imported Upstream version 0.8.4upstream/0.8.4
-rw-r--r--Makefile38
-rw-r--r--Makefile.inc15
-rw-r--r--kpartx/bsd.c4
-rw-r--r--kpartx/dasd.c9
-rw-r--r--kpartx/devmapper.c13
-rw-r--r--kpartx/devmapper.h7
-rw-r--r--kpartx/dos.c4
-rw-r--r--kpartx/gpt.c15
-rw-r--r--kpartx/gpt.h2
-rw-r--r--kpartx/kpartx.h20
-rw-r--r--kpartx/mac.c5
-rw-r--r--kpartx/ps3.c5
-rw-r--r--kpartx/solaris.c4
-rw-r--r--kpartx/sun.c4
-rw-r--r--kpartx/unixware.c4
-rw-r--r--libdmmp/libdmmp_private.h8
-rw-r--r--libmpathcmd/mpath_cmd.c5
-rw-r--r--libmpathpersist/mpath_persist.c5
-rw-r--r--libmpathpersist/mpath_pr_ioctl.c40
-rw-r--r--libmultipath/Makefile5
-rw-r--r--libmultipath/alias.c134
-rw-r--r--libmultipath/alias.h12
-rw-r--r--libmultipath/byteorder.h12
-rw-r--r--libmultipath/checkers.c17
-rw-r--r--libmultipath/checkers.h1
-rw-r--r--libmultipath/checkers/cciss_tur.c4
-rw-r--r--libmultipath/checkers/directio.c298
-rw-r--r--libmultipath/checkers/hp_sw.c8
-rw-r--r--libmultipath/checkers/rdac.c2
-rw-r--r--libmultipath/checkers/readsector0.c4
-rw-r--r--libmultipath/config.c59
-rw-r--r--libmultipath/config.h7
-rw-r--r--libmultipath/configure.c5
-rw-r--r--libmultipath/defaults.h7
-rw-r--r--libmultipath/devmapper.c12
-rw-r--r--libmultipath/dict.c100
-rw-r--r--libmultipath/discovery.c399
-rw-r--r--libmultipath/discovery.h4
-rw-r--r--libmultipath/dm-generic.c6
-rw-r--r--libmultipath/file.c5
-rw-r--r--libmultipath/foreign.c20
-rw-r--r--libmultipath/foreign/nvme.c26
-rw-r--r--libmultipath/generic.c2
-rw-r--r--libmultipath/hwtable.c8
-rw-r--r--libmultipath/io_err_stat.c14
-rw-r--r--libmultipath/log.h3
-rw-r--r--libmultipath/log_pthread.c2
-rw-r--r--libmultipath/log_pthread.h3
-rw-r--r--libmultipath/nvme/linux/nvme.h136
-rw-r--r--libmultipath/nvme/nvme-ioctl.c229
-rw-r--r--libmultipath/nvme/nvme-ioctl.h31
-rw-r--r--libmultipath/nvme/nvme.h121
-rw-r--r--libmultipath/parser.c2
-rw-r--r--libmultipath/pgpolicies.c2
-rw-r--r--libmultipath/print.c60
-rw-r--r--libmultipath/print.h8
-rw-r--r--libmultipath/prio.c6
-rw-r--r--libmultipath/prioritizers/alua_rtpg.c2
-rw-r--r--libmultipath/prioritizers/ana.c14
-rw-r--r--libmultipath/prioritizers/const.c4
-rw-r--r--libmultipath/prioritizers/datacore.c3
-rw-r--r--libmultipath/prioritizers/emc.c3
-rw-r--r--libmultipath/prioritizers/hds.c3
-rw-r--r--libmultipath/prioritizers/hp_sw.c3
-rw-r--r--libmultipath/prioritizers/iet.c3
-rw-r--r--libmultipath/prioritizers/ontap.c3
-rw-r--r--libmultipath/prioritizers/random.c4
-rw-r--r--libmultipath/prioritizers/rdac.c3
-rw-r--r--libmultipath/prioritizers/sysfs.c3
-rw-r--r--libmultipath/prioritizers/weightedpath.c3
-rw-r--r--libmultipath/propsel.c40
-rw-r--r--libmultipath/propsel.h1
-rw-r--r--libmultipath/structs.c26
-rw-r--r--libmultipath/structs.h25
-rw-r--r--libmultipath/structs_vec.c91
-rw-r--r--libmultipath/structs_vec.h3
-rw-r--r--libmultipath/sysfs.c13
-rw-r--r--libmultipath/time-util.c6
-rw-r--r--libmultipath/uevent.c5
-rw-r--r--libmultipath/unaligned.h4
-rw-r--r--libmultipath/util.c7
-rw-r--r--libmultipath/util.h15
-rw-r--r--libmultipath/uxsock.c3
-rw-r--r--libmultipath/vector.h4
-rw-r--r--libmultipath/version.h4
-rw-r--r--libmultipath/wwids.c40
-rw-r--r--mpathpersist/main.c3
-rw-r--r--multipath/main.c14
-rw-r--r--multipath/multipath.conf.515
-rw-r--r--multipathd/Makefile3
-rw-r--r--multipathd/cli_handlers.c41
-rw-r--r--multipathd/dmevents.c4
-rw-r--r--multipathd/main.c129
-rw-r--r--multipathd/pidfile.c2
-rw-r--r--multipathd/uxlsnr.c150
-rw-r--r--multipathd/waiter.c2
-rw-r--r--multipathd/waiter.h2
-rw-r--r--tests/Makefile43
-rw-r--r--tests/README.md72
-rw-r--r--tests/alias.c744
-rw-r--r--tests/blacklist.c22
-rw-r--r--tests/directio.c776
-rw-r--r--tests/hwtable.c2
-rw-r--r--tests/test-log.c27
-rw-r--r--tests/test-log.h7
-rw-r--r--tests/util.c16
-rw-r--r--tests/vpd.c88
107 files changed, 3587 insertions, 894 deletions
diff --git a/Makefile b/Makefile
index 4b145c5..1dee368 100644
--- 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
diff --git a/Makefile.inc b/Makefile.inc
index 56c3eda..d4d1e0d 100644
--- a/Makefile.inc
+++ b/Makefile.inc
@@ -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 $@ $<
diff --git a/kpartx/bsd.c b/kpartx/bsd.c
index f87175e..0e661fb 100644
--- a/kpartx/bsd.c
+++ b/kpartx/bsd.c
@@ -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)
diff --git a/kpartx/dasd.c b/kpartx/dasd.c
index 4e7e474..14b9d3a 100644
--- a/kpartx/dasd.c
+++ b/kpartx/dasd.c
@@ -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);
diff --git a/kpartx/devmapper.c b/kpartx/devmapper.c
index 3aa4988..86731ea 100644
--- a/kpartx/devmapper.c
+++ b/kpartx/devmapper.c
@@ -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;
diff --git a/kpartx/devmapper.h b/kpartx/devmapper.h
index 73b80f2..701bdf6 100644
--- a/kpartx/devmapper.h
+++ b/kpartx/devmapper.h
@@ -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 */
diff --git a/kpartx/dos.c b/kpartx/dos.c
index 4985152..0c70669 100644
--- a/kpartx/dos.c
+++ b/kpartx/dos.c
@@ -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;
diff --git a/kpartx/gpt.c b/kpartx/gpt.c
index e31611a..785b34e 100644
--- a/kpartx/gpt.c
+++ b/kpartx/gpt.c
@@ -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;
diff --git a/kpartx/gpt.h b/kpartx/gpt.h
index 7bb54b7..4e1b49a 100644
--- a/kpartx/gpt.h
+++ b/kpartx/gpt.h
@@ -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
diff --git a/kpartx/kpartx.h b/kpartx/kpartx.h
index 52920e4..67edeb8 100644
--- a/kpartx/kpartx.h
+++ b/kpartx/kpartx.h
@@ -16,8 +16,17 @@
#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);
}
diff --git a/kpartx/mac.c b/kpartx/mac.c
index 6e82c95..c21ac70 100644
--- a/kpartx/mac.c
+++ b/kpartx/mac.c
@@ -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);
diff --git a/kpartx/ps3.c b/kpartx/ps3.c
index 8455097..42551bc 100644
--- a/kpartx/ps3.c
+++ b/kpartx/ps3.c
@@ -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;
diff --git a/kpartx/solaris.c b/kpartx/solaris.c
index e7826c6..c2480b5 100644
--- a/kpartx/solaris.c
+++ b/kpartx/solaris.c
@@ -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 */
diff --git a/kpartx/sun.c b/kpartx/sun.c
index 276066d..df630a7 100644
--- a/kpartx/sun.c
+++ b/kpartx/sun.c
@@ -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);
diff --git a/kpartx/unixware.c b/kpartx/unixware.c
index c7b9786..2f663af 100644
--- a/kpartx/unixware.c
+++ b/kpartx/unixware.c
@@ -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)
diff --git a/libdmmp/libdmmp_private.h b/libdmmp/libdmmp_private.h
index 3e813cb..ac85b63 100644
--- a/libdmmp/libdmmp_private.h
+++ b/libdmmp/libdmmp_private.h
@@ -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)
diff --git a/libmpathcmd/mpath_cmd.c b/libmpathcmd/mpath_cmd.c
index f00bf7e..60b2d96 100644
--- a/libmpathcmd/mpath_cmd.c
+++ b/libmpathcmd/mpath_cmd.c
@@ -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;
}
diff --git a/libmpathpersist/mpath_persist.c b/libmpathpersist/mpath_persist.c
index 603cfc3..3da7a6c 100644
--- a/libmpathpersist/mpath_persist.c
+++ b/libmpathpersist/mpath_persist.c
@@ -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))
{
diff --git a/libmpathpersist/mpath_pr_ioctl.c b/libmpathpersist/mpath_pr_ioctl.c
index cf528fe..74b26b0 100644
--- a/libmpathpersist/mpath_pr_ioctl.c
+++ b/libmpathpersist/mpath_pr_ioctl.c
@@ -23,10 +23,8 @@
#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);
diff --git a/libmultipath/Makefile b/libmultipath/Makefile
index a2be42e..e5651e4 100644
--- a/libmultipath/Makefile
+++ b/libmultipath/Makefile
@@ -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
diff --git a/libmultipath/alias.c b/libmultipath/alias.c
index fd6b7f9..14401ca 100644
--- a/libmultipath/alias.c
+++ b/libmultipath/alias.c
@@ -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;
diff --git a/libmultipath/alias.h b/libmultipath/alias.h
index 95473ff..7c4b302 100644
--- a/libmultipath/alias.h
+++ b/libmultipath/alias.h
@@ -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);
diff --git a/libmultipath/byteorder.h b/libmultipath/byteorder.h
index 5c77146..0a86244 100644
--- a/libmultipath/byteorder.h
+++ b/libmultipath/byteorder.h
@@ -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
diff --git a/libmultipath/checkers.c b/libmultipath/checkers.c
index a08bf41..8d2be8a 100644
--- a/libmultipath/checkers.c
+++ b/libmultipath/checkers.c
@@ -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");
diff --git a/libmultipath/checkers.h b/libmultipath/checkers.h
index 5237e7e..b458118 100644
--- a/libmultipath/checkers.h
+++ b/libmultipath/checkers.h
@@ -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().
diff --git a/libmultipath/checkers/cciss_tur.c b/libmultipath/checkers/cciss_tur.c
index ea84374..eaf67b3 100644
--- a/libmultipath/checkers/cciss_tur.c
+++ b/libmultipath/checkers/cciss_tur.c
@@ -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;
}
diff --git a/libmultipath/checkers/directio.c b/libmultipath/checkers/directio.c
index 1b00b77..503519e 100644
--- a/libmultipath/checkers/directio.c
+++ b/libmultipath/checkers/directio.c
@@ -17,6 +17,32 @@
#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;
diff --git a/libmultipath/checkers/hp_sw.c b/libmultipath/checkers/hp_sw.c
index 1a82022..915918c 100644
--- a/libmultipath/checkers/hp_sw.c
+++ b/libmultipath/checkers/hp_sw.c
@@ -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;
diff --git a/libmultipath/checkers/rdac.c b/libmultipath/checkers/rdac.c
index 8a3b73e..d924a9f 100644
--- a/libmultipath/checkers/rdac.c
+++ b/libmultipath/checkers/rdac.c
@@ -133,7 +133,7 @@ out:
return 0;
}
-void libcheck_free (struct checker * c)
+void libcheck_free(__attribute__((unused)) struct checker *c)
{
return;
}
diff --git a/libmultipath/checkers/readsector0.c b/libmultipath/checkers/readsector0.c
index cf79e06..b041f11 100644
--- a/libmultipath/checkers/readsector0.c
+++ b/libmultipath/checkers/readsector0.c
@@ -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;
}
diff --git a/libmultipath/config.c b/libmultipath/config.c
index 20e3b8b..b4d8768 100644
--- a/libmultipath/config.c
+++ b/libmultipath/config.c
@@ -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();
diff --git a/libmultipath/config.h b/libmultipath/config.h
index ffec310..ceecff2 100644
--- a/libmultipath/config.h
+++ b/libmultipath/config.h
@@ -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;
diff --git a/libmultipath/configure.c b/libmultipath/configure.c
index 5ac7d90..c95848a 100644
--- a/libmultipath/configure.c
+++ b/libmultipath/configure.c
@@ -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);
diff --git a/libmultipath/defaults.h b/libmultipath/defaults.h
index 4dfe007..e5ee6af 100644
--- a/libmultipath/defaults.h
+++ b/libmultipath/defaults.h
@@ -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
@@ -51,11 +53,10 @@
/* 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"
diff --git a/libmultipath/devmapper.c b/libmultipath/devmapper.c
index 0f0c3a3..bed8ddc 100644
--- a/libmultipath/devmapper.c
+++ b/libmultipath/devmapper.c
@@ -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);
diff --git a/libmultipath/dict.c b/libmultipath/dict.c
index 2b046e1..3e25e74 100644
--- a/libmultipath/dict.c
+++ b/libmultipath/dict.c
@@ -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);
diff --git a/libmultipath/discovery.c b/libmultipath/discovery.c
index 72f455e..ee3290c 100644
--- a/libmultipath/discovery.c
+++ b/libmultipath/discovery.c
@@ -34,6 +34,11 @@
#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);
}
}
diff --git a/libmultipath/discovery.h b/libmultipath/discovery.h
index 8d04c2a..6444887 100644
--- a/libmultipath/discovery.h
+++ b/libmultipath/discovery.h
@@ -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);
diff --git a/libmultipath/dm-generic.c b/libmultipath/dm-generic.c
index d752991..1b42fa0 100644
--- a/libmultipath/dm-generic.c
+++ b/libmultipath/dm-generic.c
@@ -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);
diff --git a/libmultipath/file.c b/libmultipath/file.c
index 8727f16..72f1d24 100644
--- a/libmultipath/file.c
+++ b/libmultipath/file.c
@@ -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,
diff --git a/libmultipath/foreign.c b/libmultipath/foreign.c
index 4b34e14..0159a83 100644
--- a/libmultipath/foreign.c
+++ b/libmultipath/foreign.c
@@ -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)
diff --git a/libmultipath/foreign/nvme.c b/libmultipath/foreign/nvme.c
index 7e654ec..09cdddf 100644
--- a/libmultipath/foreign/nvme.c
+++ b/libmultipath/foreign/nvme.c
@@ -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);
diff --git a/libmultipath/generic.c b/libmultipath/generic.c
index 0d1e632..5f03b9e 100644
--- a/libmultipath/generic.c
+++ b/libmultipath/generic.c
@@ -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];
diff --git a/libmultipath/hwtable.c b/libmultipath/hwtable.c
index 16627ec..d1fcfdb 100644
--- a/libmultipath/hwtable.c
+++ b/libmultipath/hwtable.c
@@ -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
diff --git a/libmultipath/io_err_stat.c b/libmultipath/io_err_stat.c
index 554b777..1b9cd6c 100644
--- a/libmultipath/io_err_stat.c
+++ b/libmultipath/io_err_stat.c
@@ -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);
}
diff --git a/libmultipath/log.h b/libmultipath/log.h
index 6551b5c..d2448f6 100644
--- a/libmultipath/log.h
+++ b/libmultipath/log.h
@@ -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 *);
diff --git a/libmultipath/log_pthread.c b/libmultipath/log_pthread.c
index be57bb1..15baef8 100644
--- a/libmultipath/log_pthread.c
+++ b/libmultipath/log_pthread.c
@@ -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;
diff --git a/libmultipath/log_pthread.h b/libmultipath/log_pthread.h
index 7e138a0..810ac92 100644
--- a/libmultipath/log_pthread.h
+++ b/libmultipath/log_pthread.h
@@ -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);
diff --git a/libmultipath/nvme/linux/nvme.h b/libmultipath/nvme/linux/nvme.h
index 68000eb..a697554 100644
--- a/libmultipath/nvme/linux/nvme.h
+++ b/libmultipath/nvme/linux/nvme.h
@@ -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,
};
diff --git a/libmultipath/nvme/nvme-ioctl.c b/libmultipath/nvme/nvme-ioctl.c
index 70a16ce..6959976 100644
--- a/libmultipath/nvme/nvme-ioctl.c
+++ b/libmultipath/nvme/nvme-ioctl.c
@@ -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;
+}
diff --git a/libmultipath/nvme/nvme-ioctl.h b/libmultipath/nvme/nvme-ioctl.h
index 3fb740c..565f764 100644
--- a/libmultipath/nvme/nvme-ioctl.h
+++ b/libmultipath/nvme/nvme-ioctl.h
@@ -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 */
diff --git a/libmultipath/nvme/nvme.h b/libmultipath/nvme/nvme.h
index 685d179..7e0278b 100644
--- a/libmultipath/nvme/nvme.h
+++ b/libmultipath/nvme/nvme.h
@@ -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 */
diff --git a/libmultipath/parser.c b/libmultipath/parser.c
index e00c5ff..d478b17 100644
--- a/libmultipath/parser.c
+++ b/libmultipath/parser.c
@@ -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;
diff --git a/libmultipath/pgpolicies.c b/libmultipath/pgpolicies.c
index 8f7c6b1..02cafdc 100644
--- a/libmultipath/pgpolicies.c
+++ b/libmultipath/pgpolicies.c
@@ -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);
}
diff --git a/libmultipath/print.c b/libmultipath/print.c
index 907469a..b944ef3 100644
--- a/libmultipath/print.c
+++ b/libmultipath/print.c
@@ -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");
diff --git a/libmultipath/print.h b/libmultipath/print.h
index 7e36ec6..e8260d0 100644
--- a/libmultipath/print.h
+++ b/libmultipath/print.h
@@ -78,21 +78,21 @@
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 *);
diff --git a/libmultipath/prio.c b/libmultipath/prio.c
index 87de1f9..194563c 100644
--- a/libmultipath/prio.c
+++ b/libmultipath/prio.c
@@ -10,11 +10,11 @@
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;
}
diff --git a/libmultipath/prioritizers/alua_rtpg.c b/libmultipath/prioritizers/alua_rtpg.c
index 271a019..bbf5aac 100644
--- a/libmultipath/prioritizers/alua_rtpg.c
+++ b/libmultipath/prioritizers/alua_rtpg.c
@@ -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;
diff --git a/libmultipath/prioritizers/ana.c b/libmultipath/prioritizers/ana.c
index 2673d9d..b5c7873 100644
--- a/libmultipath/prioritizers/ana.c
+++ b/libmultipath/prioritizers/ana.c
@@ -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);
diff --git a/libmultipath/prioritizers/const.c b/libmultipath/prioritizers/const.c
index aad6927..059d859 100644
--- a/libmultipath/prioritizers/const.c
+++ b/libmultipath/prioritizers/const.c
@@ -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;
}
diff --git a/libmultipath/prioritizers/datacore.c b/libmultipath/prioritizers/datacore.c
index 59c9816..02dc2e2 100644
--- a/libmultipath/prioritizers/datacore.c
+++ b/libmultipath/prioritizers/datacore.c
@@ -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);
}
diff --git a/libmultipath/prioritizers/emc.c b/libmultipath/prioritizers/emc.c
index a2f7487..3b63cca 100644
--- a/libmultipath/prioritizers/emc.c
+++ b/libmultipath/prioritizers/emc.c
@@ -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);
}
diff --git a/libmultipath/prioritizers/hds.c b/libmultipath/prioritizers/hds.c
index 70fb5d1..88cac5f 100644
--- a/libmultipath/prioritizers/hds.c
+++ b/libmultipath/prioritizers/hds.c
@@ -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);
}
diff --git a/libmultipath/prioritizers/hp_sw.c b/libmultipath/prioritizers/hp_sw.c
index 6b0ed39..5b85ad2 100644
--- a/libmultipath/prioritizers/hp_sw.c
+++ b/libmultipath/prioritizers/hp_sw.c
@@ -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);
}
diff --git a/libmultipath/prioritizers/iet.c b/libmultipath/prioritizers/iet.c
index a4ea61e..e98773c 100644
--- a/libmultipath/prioritizers/iet.c
+++ b/libmultipath/prioritizers/iet.c
@@ -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);
}
diff --git a/libmultipath/prioritizers/ontap.c b/libmultipath/prioritizers/ontap.c
index 6505033..262e69d 100644
--- a/libmultipath/prioritizers/ontap.c
+++ b/libmultipath/prioritizers/ontap.c
@@ -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);
}
diff --git a/libmultipath/prioritizers/random.c b/libmultipath/prioritizers/random.c
index 4a27123..b742ac2 100644
--- a/libmultipath/prioritizers/random.c
+++ b/libmultipath/prioritizers/random.c
@@ -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;
diff --git a/libmultipath/prioritizers/rdac.c b/libmultipath/prioritizers/rdac.c
index f5df032..92a2fb8 100644
--- a/libmultipath/prioritizers/rdac.c
+++ b/libmultipath/prioritizers/rdac.c
@@ -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);
}
diff --git a/libmultipath/prioritizers/sysfs.c b/libmultipath/prioritizers/sysfs.c
index ff567df..a6feb42 100644
--- a/libmultipath/prioritizers/sysfs.c
+++ b/libmultipath/prioritizers/sysfs.c
@@ -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];
diff --git a/libmultipath/prioritizers/weightedpath.c b/libmultipath/prioritizers/weightedpath.c
index e0f3efb..916970d 100644
--- a/libmultipath/prioritizers/weightedpath.c
+++ b/libmultipath/prioritizers/weightedpath.c
@@ -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);
}
diff --git a/libmultipath/propsel.c b/libmultipath/propsel.c
index 27e8d68..897e48c 100644
--- a/libmultipath/propsel.c
+++ b/libmultipath/propsel.c
@@ -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;
+}
diff --git a/libmultipath/propsel.h b/libmultipath/propsel.h
index ddfd626..3d6edd8 100644
--- a/libmultipath/propsel.h
+++ b/libmultipath/propsel.h
@@ -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);
diff --git a/libmultipath/structs.c b/libmultipath/structs.c
index bf7fdd7..2dd378c 100644
--- a/libmultipath/structs.c
+++ b/libmultipath/structs.c
@@ -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;
diff --git a/libmultipath/structs.h b/libmultipath/structs.h
index a3adf90..9bd39eb 100644
--- a/libmultipath/structs.h
+++ b/libmultipath/structs.h
@@ -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 *);
diff --git a/libmultipath/structs_vec.c b/libmultipath/structs_vec.c
index c43b58f..3dbbaa0 100644
--- a/libmultipath/structs_vec.c
+++ b/libmultipath/structs_vec.c
@@ -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)
diff --git a/libmultipath/structs_vec.h b/libmultipath/structs_vec.h
index f8b9f63..2a5e3d6 100644
--- a/libmultipath/structs_vec.h
+++ b/libmultipath/structs_vec.h
@@ -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,
diff --git a/libmultipath/sysfs.c b/libmultipath/sysfs.c
index 65904d7..62ec2ed 100644
--- a/libmultipath/sysfs.c
+++ b/libmultipath/sysfs.c
@@ -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);
diff --git a/libmultipath/time-util.c b/libmultipath/time-util.c
index a3739a2..55f366c 100644
--- a/libmultipath/time-util.c
+++ b/libmultipath/time-util.c
@@ -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++;
}
}
diff --git a/libmultipath/uevent.c b/libmultipath/uevent.c
index 8f7b2ef..d38e8a7 100644
--- a/libmultipath/uevent.c
+++ b/libmultipath/uevent.c
@@ -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) {
diff --git a/libmultipath/unaligned.h b/libmultipath/unaligned.h
index 68c0774..b9eaa7c 100644
--- a/libmultipath/unaligned.h
+++ b/libmultipath/unaligned.h
@@ -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);
diff --git a/libmultipath/util.c b/libmultipath/util.c
index 28cbf4b..51c38c8 100644
--- a/libmultipath/util.c
+++ b/libmultipath/util.c
@@ -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;
diff --git a/libmultipath/util.h b/libmultipath/util.h
index 693991c..56bd78c 100644
--- a/libmultipath/util.h
+++ b/libmultipath/util.h
@@ -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))
diff --git a/libmultipath/uxsock.c b/libmultipath/uxsock.c
index 9b4e978..6adeedf 100644
--- a/libmultipath/uxsock.c
+++ b/libmultipath/uxsock.c
@@ -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
diff --git a/libmultipath/vector.h b/libmultipath/vector.h
index 344dffd..e16ec46 100644
--- a/libmultipath/vector.h
+++ b/libmultipath/vector.h
@@ -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--)
diff --git a/libmultipath/version.h b/libmultipath/version.h
index 5e4c328..7ddb4e8 100644
--- a/libmultipath/version.h
+++ b/libmultipath/version.h
@@ -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"
diff --git a/libmultipath/wwids.c b/libmultipath/wwids.c
index ef74812..28a2150 100644
--- a/libmultipath/wwids.c
+++ b/libmultipath/wwids.c
@@ -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;
}
diff --git a/mpathpersist/main.c b/mpathpersist/main.c
index 278b8d5..28bfe41 100644
--- a/mpathpersist/main.c
+++ b/mpathpersist/main.c
@@ -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;
}
diff --git a/multipath/main.c b/multipath/main.c
index 4f4d8e8..cf9d2a2 100644
--- a/multipath/main.c
+++ b/multipath/main.c
@@ -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) {
diff --git a/multipath/multipath.conf.5 b/multipath/multipath.conf.5
index e866da2..05a5e8f 100644
--- a/multipath/multipath.conf.5
+++ b/multipath/multipath.conf.5
@@ -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:
diff --git a/multipathd/Makefile b/multipathd/Makefile
index d1a9863..8d90117 100644
--- a/multipathd/Makefile
+++ b/multipathd/Makefile
@@ -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)
diff --git a/multipathd/cli_handlers.c b/multipathd/cli_handlers.c
index 8a89904..7d878c8 100644
--- a/multipathd/cli_handlers.c
+++ b/multipathd/cli_handlers.c
@@ -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;
}
diff --git a/multipathd/dmevents.c b/multipathd/dmevents.c
index 0034892..b22b47d 100644
--- a/multipathd/dmevents.c
+++ b/multipathd/dmevents.c
@@ -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;
diff --git a/multipathd/main.c b/multipathd/main.c
index 34a5768..8baf9ab 100644
--- a/multipathd/main.c
+++ b/multipathd/main.c
@@ -33,10 +33,6 @@
*/
#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;
diff --git a/multipathd/pidfile.c b/multipathd/pidfile.c
index 47d18e2..cc0fbec 100644
--- a/multipathd/pidfile.c
+++ b/multipathd/pidfile.c
@@ -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;
diff --git a/multipathd/uxlsnr.c b/multipathd/uxlsnr.c
index bc71679..1c5ce9d 100644
--- a/multipathd/uxlsnr.c
+++ b/multipathd/uxlsnr.c
@@ -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;
diff --git a/multipathd/waiter.c b/multipathd/waiter.c
index eb8d699..e645766 100644
--- a/multipathd/waiter.c
+++ b/multipathd/waiter.c
@@ -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;
diff --git a/multipathd/waiter.h b/multipathd/waiter.h
index 0cfae46..28e0f6d 100644
--- a/multipathd/waiter.h
+++ b/multipathd/waiter.h
@@ -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 */
diff --git a/tests/Makefile b/tests/Makefile
index a5cdf39..77ff324 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -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
index 0000000..6438a82
--- /dev/null
+++ b/tests/README.md
@@ -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
index 0000000..30414db
--- /dev/null
+++ b/tests/alias.c
@@ -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;
+}
diff --git a/tests/blacklist.c b/tests/blacklist.c
index 362c44d..6e7c186 100644
--- a/tests/blacklist.c
+++ b/tests/blacklist.c
@@ -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
index 0000000..3cd7a52
--- /dev/null
+++ b/tests/directio.c
@@ -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;
+}
diff --git a/tests/hwtable.c b/tests/hwtable.c
index 977a566..473028b 100644
--- a/tests/hwtable.c
+++ b/tests/hwtable.c
@@ -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
index 0000000..d685d58
--- /dev/null
+++ b/tests/test-log.c
@@ -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
index 0000000..2c878c6
--- /dev/null
+++ b/tests/test-log.h
@@ -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
diff --git a/tests/util.c b/tests/util.c
index 4e04a48..7c486fc 100644
--- a/tests/util.c
+++ b/tests/util.c
@@ -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);
}
diff --git a/tests/vpd.c b/tests/vpd.c
index d9f80ea..3cbad81 100644
--- a/tests/vpd.c
+++ b/tests/vpd.c
@@ -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),