From 63d655f424c6b8b9093d18d3a53dfa206744653e Mon Sep 17 00:00:00 2001 From: DongHun Kwak Date: Fri, 14 Jan 2022 13:50:14 +0900 Subject: [PATCH] Imported Upstream version 0.6.0 --- COPYING | 2 +- Makefile | 1 + Makefile.inc | 23 +- kpartx/devmapper.c | 73 +- kpartx/devmapper.h | 19 +- kpartx/dos.c | 8 +- kpartx/kpartx.c | 124 +- kpartx/kpartx.rules | 10 +- kpartx/lopart.c | 10 +- kpartx/sun.c | 2 - libmpathcmd/Makefile | 30 + libmpathcmd/mpath_cmd.c | 178 ++ libmpathcmd/mpath_cmd.h | 125 + libmpathpersist/Makefile | 13 +- libmpathpersist/mpath_persist.c | 192 +- libmpathpersist/mpath_updatepr.c | 30 +- libmpathpersist/mpathpr.h | 3 +- libmultipath/Makefile | 17 +- libmultipath/alias.c | 78 +- libmultipath/alias.h | 2 + libmultipath/blacklist.c | 39 +- libmultipath/blacklist.h | 2 +- libmultipath/callout.c | 2 +- libmultipath/checkers.c | 3 +- libmultipath/checkers.h | 9 + libmultipath/checkers/rdac.c | 2 +- libmultipath/config.c | 78 +- libmultipath/config.h | 43 +- libmultipath/configure.c | 345 ++- libmultipath/configure.h | 3 +- libmultipath/defaults.h | 14 +- libmultipath/devmapper.c | 467 ++- libmultipath/devmapper.h | 26 +- libmultipath/dict.c | 3279 ++++++---------------- libmultipath/dict.h | 8 + libmultipath/discovery.c | 678 ++++- libmultipath/discovery.h | 11 +- libmultipath/dmparser.c | 6 +- libmultipath/hwtable.c | 63 +- libmultipath/list.h | 49 +- libmultipath/log_pthread.c | 2 +- libmultipath/parser.c | 322 ++- libmultipath/parser.h | 9 +- libmultipath/pgpolicies.c | 2 +- libmultipath/print.c | 217 +- libmultipath/print.h | 9 +- libmultipath/prio.c | 11 +- libmultipath/prio.h | 1 + libmultipath/prioritizers/alua.c | 50 +- libmultipath/prioritizers/alua_rtpg.c | 70 +- libmultipath/prioritizers/alua_rtpg.h | 2 +- libmultipath/prioritizers/emc.c | 8 +- libmultipath/prioritizers/hds.c | 2 +- libmultipath/prioritizers/hp_sw.c | 2 +- libmultipath/prioritizers/iet.c | 2 + libmultipath/prioritizers/ontap.c | 4 +- libmultipath/prioritizers/rdac.c | 8 +- libmultipath/prioritizers/weightedpath.c | 42 +- libmultipath/prioritizers/weightedpath.h | 1 + libmultipath/propsel.c | 856 +++--- libmultipath/propsel.h | 3 + libmultipath/structs.c | 93 +- libmultipath/structs.h | 98 +- libmultipath/structs_vec.c | 78 +- libmultipath/structs_vec.h | 2 +- libmultipath/sysfs.c | 57 +- libmultipath/sysfs.h | 2 + libmultipath/uevent.c | 316 ++- libmultipath/util.c | 5 +- libmultipath/uxsock.c | 77 +- libmultipath/uxsock.h | 7 +- libmultipath/version.h | 4 +- libmultipath/wwids.c | 31 + libmultipath/wwids.h | 1 + mpathpersist/Makefile | 2 +- multipath.conf.annotated | 114 +- multipath.conf.defaults | 52 +- multipath.conf.synthetic | 3 + multipath/11-dm-mpath.rules | 40 + multipath/Makefile | 10 +- multipath/dev_t.h | 3 - multipath/main.c | 169 +- multipath/multipath.8 | 12 +- multipath/multipath.conf.5 | 228 +- multipath/multipath.rules | 22 + multipathd/Makefile | 11 +- multipathd/cli.c | 116 +- multipathd/cli.h | 40 +- multipathd/cli_handlers.c | 275 +- multipathd/cli_handlers.h | 3 + multipathd/main.c | 495 +++- multipathd/main.h | 1 + multipathd/multipathd.8 | 38 + multipathd/multipathd.init.suse | 2 +- multipathd/multipathd.service | 4 +- multipathd/multipathd.socket | 8 +- multipathd/uxclnt.c | 35 +- multipathd/uxclnt.h | 2 +- multipathd/uxlsnr.c | 138 +- multipathd/uxlsnr.h | 8 +- 100 files changed, 6119 insertions(+), 4173 deletions(-) create mode 100644 libmpathcmd/Makefile create mode 100644 libmpathcmd/mpath_cmd.c create mode 100644 libmpathcmd/mpath_cmd.h create mode 100644 multipath/11-dm-mpath.rules delete mode 100644 multipath/dev_t.h create mode 100644 multipath/multipath.rules diff --git a/COPYING b/COPYING index 9e31bbf..0ade0e9 100644 --- a/COPYING +++ b/COPYING @@ -3,7 +3,7 @@ Version 2, June 1991 Copyright (C) 1991 Free Software Foundation, Inc. - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. diff --git a/Makefile b/Makefile index baf7753..06f50c8 100644 --- a/Makefile +++ b/Makefile @@ -20,6 +20,7 @@ export KRNLSRC export KRNLOBJ BUILDDIRS = \ + libmpathcmd \ libmultipath \ libmultipath/prioritizers \ libmultipath/checkers \ diff --git a/Makefile.inc b/Makefile.inc index f445160..ecc20ef 100644 --- a/Makefile.inc +++ b/Makefile.inc @@ -21,35 +21,50 @@ ifndef LIB endif endif +ifndef RUN + ifeq ($(shell test -L /var/run -o ! -d /var/run && echo 1),1) + RUN=run + else + RUN=var/run + endif +endif + ifndef SYSTEMD ifeq ($(shell systemctl --version > /dev/null 2>&1 && echo 1), 1) SYSTEMD = $(shell systemctl --version 2> /dev/null | sed -n 's/systemd \([0-9]*\)/\1/p') endif endif +ifndef SYSTEMDPATH + SYSTEMDPATH=usr/lib +endif + prefix = exec_prefix = $(prefix) bindir = $(exec_prefix)/sbin -libudevdir = ${prefix}/lib/udev +libudevdir = $(prefix)/$(SYSTEMDPATH)/udev +udevrulesdir = $(libudevdir)/rules.d multipathdir = $(TOPDIR)/libmultipath mandir = $(prefix)/usr/share/man/man8 man5dir = $(prefix)/usr/share/man/man5 man3dir = $(prefix)/usr/share/man/man3 rcdir = $(prefix)/etc/init.d syslibdir = $(prefix)/$(LIB) +incdir = $(prefix)/usr/include libdir = $(prefix)/$(LIB)/multipath -unitdir = $(prefix)/usr/lib/systemd/system +unitdir = $(prefix)/$(SYSTEMDPATH)/systemd/system mpathpersistdir = $(TOPDIR)/libmpathpersist +mpathcmddir = $(TOPDIR)/libmpathcmd GZIP = gzip -9 -c INSTALL_PROGRAM = install ifndef RPM_OPT_FLAGS - RPM_OPT_FLAGS = -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 + RPM_OPT_FLAGS = -O2 -g -pipe -Wformat-security -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 endif OPTFLAGS = $(RPM_OPT_FLAGS) -Wunused -Wstrict-prototypes -CFLAGS = $(OPTFLAGS) -fPIC -DLIB_STRING=\"${LIB}\" +CFLAGS = $(OPTFLAGS) -fPIC -DLIB_STRING=\"${LIB}\" -DRUN_DIR=\"${RUN}\" SHARED_FLAGS = -shared %.o: %.c diff --git a/kpartx/devmapper.c b/kpartx/devmapper.c index 24a43ee..e006bc3 100644 --- a/kpartx/devmapper.c +++ b/kpartx/devmapper.c @@ -14,13 +14,6 @@ #define MAX_PREFIX_LEN 8 #define PARAMS_SIZE 1024 -#ifndef LIBDM_API_COOKIE -static inline int dm_task_set_cookie(struct dm_task *dmt, uint32_t *c, int a) -{ - return 1; -} -#endif - extern int dm_prereq (char * str, int x, int y, int z) { @@ -60,10 +53,13 @@ dm_prereq (char * str, int x, int y, int z) } extern int -dm_simplecmd (int task, const char *name, int no_flush, uint32_t *cookie) { +dm_simplecmd (int task, const char *name, int no_flush, uint16_t udev_flags) { int r = 0; int udev_wait_flag = (task == DM_DEVICE_RESUME || task == DM_DEVICE_REMOVE); +#ifdef LIBDM_API_COOKIE + uint32_t cookie = 0; +#endif struct dm_task *dmt; if (!(dmt = dm_task_create(task))) @@ -78,10 +74,23 @@ dm_simplecmd (int task, const char *name, int no_flush, uint32_t *cookie) { if (no_flush) dm_task_no_flush(dmt); - if (udev_wait_flag && !dm_task_set_cookie(dmt, cookie, (udev_sync)? 0 : DM_UDEV_DISABLE_LIBRARY_FALLBACK)) +#ifdef LIBDM_API_COOKIE + if (!udev_sync) + udev_flags |= DM_UDEV_DISABLE_LIBRARY_FALLBACK; + if (udev_wait_flag && !dm_task_set_cookie(dmt, &cookie, udev_flags)) { + dm_udev_complete(cookie); goto out; + } +#endif r = dm_task_run(dmt); - +#ifdef LIBDM_API_COOKIE + if (udev_wait_flag) { + if (!r) + dm_udev_complete(cookie); + else + dm_udev_wait(cookie); + } +#endif out: dm_task_destroy(dmt); return r; @@ -90,10 +99,14 @@ dm_simplecmd (int task, const char *name, int no_flush, uint32_t *cookie) { extern int dm_addmap (int task, const char *name, const char *target, const char *params, uint64_t size, int ro, const char *uuid, int part, - mode_t mode, uid_t uid, gid_t gid, uint32_t *cookie) { + mode_t mode, uid_t uid, gid_t gid) { int r = 0; struct dm_task *dmt; char *prefixed_uuid = NULL; +#ifdef LIBDM_API_COOKIE + uint32_t cookie = 0; + uint16_t udev_flags = 0; +#endif if (!(dmt = dm_task_create (task))) return 0; @@ -128,23 +141,42 @@ dm_addmap (int task, const char *name, const char *target, dm_task_no_open_count(dmt); - if (task == DM_DEVICE_CREATE && !dm_task_set_cookie(dmt, cookie, (udev_sync)? 0 : DM_UDEV_DISABLE_LIBRARY_FALLBACK)) +#ifdef LIBDM_API_COOKIE + if (!udev_sync) + udev_flags = DM_UDEV_DISABLE_LIBRARY_FALLBACK; + if (task == DM_DEVICE_CREATE && + !dm_task_set_cookie(dmt, &cookie, udev_flags)) { + dm_udev_complete(cookie); goto addout; + } +#endif r = dm_task_run (dmt); - - addout: +#ifdef LIBDM_API_COOKIE + if (task == DM_DEVICE_CREATE) { + if (!r) + dm_udev_complete(cookie); + else + dm_udev_wait(cookie); + } +#endif +addout: dm_task_destroy (dmt); + free(prefixed_uuid); return r; } extern int -dm_map_present (char * str) +dm_map_present (char * str, char **uuid) { int r = 0; struct dm_task *dmt; + const char *uuidtmp; struct dm_info info; + if (uuid) + *uuid = NULL; + if (!(dmt = dm_task_create(DM_DEVICE_INFO))) return 0; @@ -159,8 +191,15 @@ dm_map_present (char * str) if (!dm_task_get_info(dmt, &info)) goto out; - if (info.exists) - r = 1; + if (!info.exists) + goto out; + + r = 1; + if (uuid) { + uuidtmp = dm_task_get_uuid(dmt); + if (uuidtmp && strlen(uuidtmp)) + *uuid = strdup(uuidtmp); + } out: dm_task_destroy(dmt); return r; diff --git a/kpartx/devmapper.h b/kpartx/devmapper.h index 0edc063..436efe1 100644 --- a/kpartx/devmapper.h +++ b/kpartx/devmapper.h @@ -1,16 +1,23 @@ -#define MAJOR(dev) ((dev & 0xfff00) >> 8) -#define MINOR(dev) ((dev & 0xff) | ((dev >> 12) & 0xfff00)) -#define MKDEV(ma,mi) ((mi & 0xff) | (ma << 8) | ((mi & ~0xff) << 12)) +#ifndef _KPARTX_DEVMAPPER_H +#define _KPARTX_DEVMAPPER_H + +#ifdef DM_SUBSYSTEM_UDEV_FLAG0 +#define MPATH_UDEV_RELOAD_FLAG DM_SUBSYSTEM_UDEV_FLAG0 +#else +#define MPATH_UDEV_RELOAD_FLAG 0 +#endif extern int udev_sync; int dm_prereq (char *, int, int, int); -int dm_simplecmd (int, const char *, int, 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, uint32_t *); -int dm_map_present (char *); + int, const char *, int, mode_t, uid_t, gid_t); +int dm_map_present (char *, char **); char * dm_mapname(int major, int minor); dev_t dm_get_first_dep(char *devname); char * dm_mapuuid(int major, int minor); int dm_devn (char * mapname, int *major, int *minor); int dm_no_partitions(int major, int minor); + +#endif /* _KPARTX_DEVMAPPER_H */ diff --git a/kpartx/dos.c b/kpartx/dos.c index 0e57f0e..90ad3bd 100644 --- a/kpartx/dos.c +++ b/kpartx/dos.c @@ -101,8 +101,12 @@ read_dos_pt(int fd, struct slice all, struct slice *sp, int ns) { break; } if (is_extended(p.sys_type)) { - sp[i].size = sector_size_mul * 2; /* extended partitions only get two - sectors mapped for LILO to install */ + /* extended partitions only get one or + two sectors mapped for LILO to install, + whichever is needed to have 1kb of space */ + if (sector_size_mul == 1) + sp[i].size = 2; + else sp[i].size = sector_size_mul; n += read_extended_partition(fd, &p, i, sp+n, ns-n); } } diff --git a/kpartx/kpartx.c b/kpartx/kpartx.c index 9a9a5eb..e8c35d4 100644 --- a/kpartx/kpartx.c +++ b/kpartx/kpartx.c @@ -168,8 +168,8 @@ get_hotplug_device(void) if (stat(devname, &buf)) return NULL; - major = (unsigned int)MAJOR(buf.st_rdev); - minor = (unsigned int)MINOR(buf.st_rdev); + major = major(buf.st_rdev); + minor = minor(buf.st_rdev); if (!(mapname = dm_mapname(major, minor))) /* Not dm device. */ return NULL; @@ -191,6 +191,21 @@ get_hotplug_device(void) return device; } +static int +check_uuid(char *uuid, char *part_uuid, char **err_msg) { + char *map_uuid = strchr(part_uuid, '-'); + if (!map_uuid || strncmp(part_uuid, "part", 4) != 0) { + *err_msg = "not a kpartx partition"; + return -1; + } + map_uuid++; + if (strcmp(uuid, map_uuid) != 0) { + *err_msg = "a partition of a different device"; + return -1; + } + return 0; +} + int main(int argc, char **argv){ int i, j, m, n, op, off, arg, c, d, ro=0; @@ -208,7 +223,6 @@ main(int argc, char **argv){ int hotplug = 0; int loopcreated = 0; struct stat buf; - uint32_t cookie = 0; initpts(); init_crc32(); @@ -281,6 +295,8 @@ main(int argc, char **argv){ #ifdef LIBDM_API_COOKIE if (!udev_sync) dm_udev_set_sync_support(0); + else + dm_udev_set_sync_support(1); #endif if (dm_prereq(DM_TARGET, 0, 0, 0) && (what == ADD || what == DELETE || what == UPDATE)) { @@ -322,15 +338,18 @@ main(int argc, char **argv){ loopcreated = 1; } device = loopdev; + + if (stat(device, &buf)) { + printf("failed to stat() %s\n", device); + exit (1); + } } off = find_devname_offset(device); if (!loopdev) { - uuid = dm_mapuuid((unsigned int)MAJOR(buf.st_rdev), - (unsigned int)MINOR(buf.st_rdev)); - mapname = dm_mapname((unsigned int)MAJOR(buf.st_rdev), - (unsigned int)MINOR(buf.st_rdev)); + uuid = dm_mapuuid(major(buf.st_rdev), minor(buf.st_rdev)); + mapname = dm_mapname(major(buf.st_rdev), minor(buf.st_rdev)); } if (!uuid) @@ -339,8 +358,7 @@ main(int argc, char **argv){ if (!mapname) mapname = device + off; else if (!force_devmap && - dm_no_partitions((unsigned int)MAJOR(buf.st_rdev), - (unsigned int)MINOR(buf.st_rdev))) { + dm_no_partitions(major(buf.st_rdev), minor(buf.st_rdev))) { /* Feature 'no_partitions' is set, return */ return 0; } @@ -428,7 +446,9 @@ main(int argc, char **argv){ break; case DELETE: - for (j = n-1; j >= 0; j--) { + for (j = MAXSLICES-1; j >= 0; j--) { + char *part_uuid, *reason; + if (safe_sprintf(partname, "%s%s%d", mapname, delim, j+1)) { fprintf(stderr, "partname too small\n"); @@ -436,11 +456,20 @@ main(int argc, char **argv){ } strip_slash(partname); - if (!slices[j].size || !dm_map_present(partname)) + if (!dm_map_present(partname, &part_uuid)) continue; + if (part_uuid && uuid) { + if (check_uuid(uuid, part_uuid, &reason) != 0) { + fprintf(stderr, "%s is %s. Not removing\n", partname, reason); + free(part_uuid); + continue; + } + free(part_uuid); + } + if (!dm_simplecmd(DM_DEVICE_REMOVE, partname, - 0, &cookie)) { + 0, 0)) { r++; continue; } @@ -463,6 +492,8 @@ main(int argc, char **argv){ case UPDATE: /* ADD and UPDATE share the same code that adds new partitions. */ for (j = 0, c = 0; j < n; j++) { + char *part_uuid, *reason; + if (slices[j].size == 0) continue; @@ -479,30 +510,43 @@ main(int argc, char **argv){ } strip_slash(partname); - if (safe_sprintf(params, "%s %" PRIu64 , - device, slices[j].start)) { + if (safe_sprintf(params, "%d:%d %" PRIu64 , + major(buf.st_rdev), minor(buf.st_rdev), slices[j].start)) { fprintf(stderr, "params too small\n"); exit(1); } - op = (dm_map_present(partname) ? + op = (dm_map_present(partname, &part_uuid) ? DM_DEVICE_RELOAD : DM_DEVICE_CREATE); + if (part_uuid && uuid) { + if (check_uuid(uuid, part_uuid, &reason) != 0) { + fprintf(stderr, "%s is already in use, and %s\n", partname, reason); + r++; + free(part_uuid); + continue; + } + free(part_uuid); + } + if (!dm_addmap(op, partname, DM_TARGET, params, slices[j].size, ro, uuid, j+1, buf.st_mode & 0777, buf.st_uid, - buf.st_gid, &cookie)) { + buf.st_gid)) { fprintf(stderr, "create/reload failed on %s\n", partname); r++; + continue; } if (op == DM_DEVICE_RELOAD && !dm_simplecmd(DM_DEVICE_RESUME, partname, - 1, &cookie)) { + 1, MPATH_UDEV_RELOAD_FLAG)) { fprintf(stderr, "resume failed on %s\n", partname); r++; + continue; } + dm_devn(partname, &slices[j].major, &slices[j].minor); @@ -516,6 +560,7 @@ main(int argc, char **argv){ d = c; while (c) { for (j = 0; j < n; j++) { + char *part_uuid, *reason; int k = slices[j].container - 1; if (slices[j].size == 0) @@ -541,33 +586,41 @@ main(int argc, char **argv){ } strip_slash(partname); - if (safe_sprintf(params, "%s %" PRIu64, - device, + if (safe_sprintf(params, "%d:%d %" PRIu64, + major(buf.st_rdev), minor(buf.st_rdev), slices[j].start)) { fprintf(stderr, "params too small\n"); exit(1); } - op = (dm_map_present(partname) ? + op = (dm_map_present(partname, + &part_uuid) ? DM_DEVICE_RELOAD : DM_DEVICE_CREATE); + if (part_uuid && uuid) { + if (check_uuid(uuid, part_uuid, &reason) != 0) { + fprintf(stderr, "%s is already in use, and %s\n", partname, reason); + free(part_uuid); + continue; + } + free(part_uuid); + } + dm_addmap(op, partname, DM_TARGET, params, slices[j].size, ro, uuid, j+1, buf.st_mode & 0777, - buf.st_uid, buf.st_gid, - &cookie); + buf.st_uid, buf.st_gid); if (op == DM_DEVICE_RELOAD) dm_simplecmd(DM_DEVICE_RESUME, partname, 1, - &cookie); - + MPATH_UDEV_RELOAD_FLAG); dm_devn(partname, &slices[j].major, &slices[j].minor); if (verbose) - printf("add map %s : 0 %" PRIu64 " %s %s\n", - partname, slices[j].size, + printf("add map %s (%d:%d): 0 %" PRIu64 " %s %s\n", + partname, slices[j].major, slices[j].minor, slices[j].size, DM_TARGET, params); c--; } @@ -582,6 +635,7 @@ main(int argc, char **argv){ } for (j = MAXSLICES-1; j >= 0; j--) { + char *part_uuid, *reason; if (safe_sprintf(partname, "%s%s%d", mapname, delim, j+1)) { fprintf(stderr, "partname too small\n"); @@ -589,11 +643,21 @@ main(int argc, char **argv){ } strip_slash(partname); - if (slices[j].size || !dm_map_present(partname)) + if (slices[j].size || + !dm_map_present(partname, &part_uuid)) continue; + if (part_uuid && uuid) { + if (check_uuid(uuid, part_uuid, &reason) != 0) { + fprintf(stderr, "%s is %s. Not removing\n", partname, reason); + free(part_uuid); + continue; + } + free(part_uuid); + } + if (!dm_simplecmd(DM_DEVICE_REMOVE, - partname, 1, &cookie)) { + partname, 1, 0)) { r++; continue; } @@ -619,9 +683,7 @@ main(int argc, char **argv){ } printf("loop deleted : %s\n", device); } -#ifdef LIBDM_API_COOKIE - dm_udev_wait(cookie); -#endif + dm_lib_release(); dm_lib_exit(); diff --git a/kpartx/kpartx.rules b/kpartx/kpartx.rules index 5c0d0ff..022361f 100644 --- a/kpartx/kpartx.rules +++ b/kpartx/kpartx.rules @@ -12,12 +12,6 @@ ENV{DM_DEPS}=="0", GOTO="kpartx_end" ENV{DM_UUID}=="?*", IMPORT{program}=="kpartx_id %M %m $env{DM_UUID}" -ENV{DM_ACTION}=="PATH_FAILED", ENV{DM_NR_VALID_PATHS}=="0", \ - GOTO="blkid_end" -ENV{DM_UUID}=="mpath-*", IMPORT{program}="/sbin/blkid -o udev -p $tempnode" -ENV{DM_PART}=="?*", IMPORT{program}="/sbin/blkid -o udev -p $tempnode" -LABEL="blkid_end" - OPTIONS="link_priority=50" # Create persistent links for multipath tables @@ -46,8 +40,8 @@ ENV{ID_FS_USAGE}=="filesystem|other", ENV{ID_FS_LABEL_ENC}=="?*", \ SYMLINK+="disk/by-label/$env{ID_FS_LABEL_ENC}" # Create dm tables for partitions -ENV{DM_ACTION}=="PATH_FAILED", ENV{DM_NR_VALID_PATHS}=="0", \ - GOTO="kpartx_end" +ENV{DM_ACTION}=="PATH_FAILED|PATH_REINSTATED", GOTO="kpartx_end" +ENV{DM_NR_VALID_PATHS}=="0", GOTO="kpartx_end" ENV{DM_STATE}!="SUSPENDED", ENV{DM_UUID}=="mpath-*", \ RUN+="/sbin/kpartx -u -p -part /dev/$name" diff --git a/kpartx/lopart.c b/kpartx/lopart.c index 6f83048..26f3011 100644 --- a/kpartx/lopart.c +++ b/kpartx/lopart.c @@ -103,6 +103,14 @@ find_loop_by_file (const char * filename) int i, j, fd; struct stat statbuf; struct loop_info loopinfo; + dev_t file_dev; + ino_t file_ino; + + if (stat (filename, &statbuf) != 0) { + return NULL; + } + file_dev = statbuf.st_dev; + file_ino = statbuf.st_ino; for (j = 0; j < SIZE(loop_formats); j++) { @@ -123,7 +131,7 @@ find_loop_by_file (const char * filename) continue; } - if (0 == strcmp(filename, loopinfo.lo_name)) { + if (loopinfo.lo_device == file_dev && loopinfo.lo_inode == file_ino) { close (fd); return xstrdup(dev); /*found */ } diff --git a/kpartx/sun.c b/kpartx/sun.c index 3d88b21..b8c023b 100644 --- a/kpartx/sun.c +++ b/kpartx/sun.c @@ -82,8 +82,6 @@ read_sun_pt(int fd, struct slice all, struct slice *sp, int ns) { for(i=0, n=0; ipartitions[i]; - if (s->num_sectors == 0) - continue; if (n < ns) { sp[n].start = offset + be32_to_cpu(s->start_cylinder) * be16_to_cpu(l->nsect) * be16_to_cpu(l->ntrks); diff --git a/libmpathcmd/Makefile b/libmpathcmd/Makefile new file mode 100644 index 0000000..0304ecc --- /dev/null +++ b/libmpathcmd/Makefile @@ -0,0 +1,30 @@ +# Makefile +# +include ../Makefile.inc + +SONAME=0 +DEVLIB = libmpathcmd.so +LIBS = $(DEVLIB).$(SONAME) + +OBJS = mpath_cmd.o + +all: $(LIBS) + +$(LIBS): $(OBJS) + $(CC) $(LDFLAGS) $(SHARED_FLAGS) -Wl,-soname=$@ $(CFLAGS) -o $@ $(OBJS) $(LIBDEPS) + ln -sf $@ $(DEVLIB) + +install: $(LIBS) + $(INSTALL_PROGRAM) -d $(DESTDIR)$(syslibdir) + $(INSTALL_PROGRAM) -m 755 $(LIBS) $(DESTDIR)$(syslibdir)/$(LIBS) + ln -sf $(LIBS) $(DESTDIR)$(syslibdir)/$(DEVLIB) + $(INSTALL_PROGRAM) -d $(DESTDIR)$(incdir) + $(INSTALL_PROGRAM) -m 755 mpath_cmd.h $(DESTDIR)$(incdir) + +uninstall: + rm -f $(DESTDIR)$(syslibdir)/$(LIBS) + rm -f $(DESTDIR)$(syslibdir)/$(DEVLIB) + rm -f $(DESTDIR)$(incdir)/mpath_cmd.h + +clean: + rm -f core *.a *.o *.gz *.so *.so.* diff --git a/libmpathcmd/mpath_cmd.c b/libmpathcmd/mpath_cmd.c new file mode 100644 index 0000000..1aaf5ea --- /dev/null +++ b/libmpathcmd/mpath_cmd.c @@ -0,0 +1,178 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mpath_cmd.h" + +/* + * keep reading until its all read + */ +static ssize_t read_all(int fd, void *buf, size_t len, unsigned int timeout) +{ + size_t total = 0; + ssize_t n; + int ret; + struct pollfd pfd; + + while (len) { + pfd.fd = fd; + pfd.events = POLLIN; + ret = poll(&pfd, 1, timeout); + if (!ret) { + errno = ETIMEDOUT; + return -1; + } else if (ret < 0) { + if (errno == EINTR) + continue; + return -1; + } else if (!pfd.revents & POLLIN) + continue; + n = read(fd, buf, len); + if (n < 0) { + if ((errno == EINTR) || (errno == EAGAIN)) + continue; + return -1; + } + if (!n) + return total; + buf = n + (char *)buf; + len -= n; + total += n; + } + return total; +} + +/* + * keep writing until it's all sent + */ +static size_t write_all(int fd, const void *buf, size_t len) +{ + size_t total = 0; + + while (len) { + ssize_t n = write(fd, buf, len); + if (n < 0) { + if ((errno == EINTR) || (errno == EAGAIN)) + continue; + return total; + } + if (!n) + return total; + buf = n + (char *)buf; + len -= n; + total += n; + } + return total; +} + +/* + * connect to a unix domain socket + */ +int mpath_connect(void) +{ + int fd, len; + struct sockaddr_un addr; + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_LOCAL; + addr.sun_path[0] = '\0'; + len = strlen(DEFAULT_SOCKET) + 1 + sizeof(sa_family_t); + strncpy(&addr.sun_path[1], DEFAULT_SOCKET, len); + + fd = socket(AF_LOCAL, SOCK_STREAM, 0); + if (fd == -1) + return -1; + + if (connect(fd, (struct sockaddr *)&addr, len) == -1) { + close(fd); + return -1; + } + + return fd; +} + +int mpath_disconnect(int fd) +{ + return close(fd); +} + +ssize_t mpath_recv_reply_len(int fd, unsigned int timeout) +{ + size_t len; + ssize_t ret; + + ret = read_all(fd, &len, sizeof(len), timeout); + if (ret < 0) + return ret; + if (ret != sizeof(len)) { + errno = EIO; + return ret; + } + return len; +} + +int mpath_recv_reply_data(int fd, char *reply, size_t len, + unsigned int timeout) +{ + ssize_t ret; + + ret = read_all(fd, reply, len, timeout); + if (ret < 0) + return ret; + if (ret != len) { + errno = EIO; + return -1; + } + reply[len - 1] = '\0'; + return 0; +} + +int mpath_recv_reply(int fd, char **reply, unsigned int timeout) +{ + int err; + ssize_t len; + + *reply = NULL; + len = mpath_recv_reply_len(fd, timeout); + if (len <= 0) + return len; + *reply = malloc(len); + if (!*reply) + return -1; + err = mpath_recv_reply_data(fd, *reply, len, timeout); + if (err) { + free(*reply); + *reply = NULL; + return err; + } + return 0; +} + +int mpath_send_cmd(int fd, const char *cmd) +{ + size_t len; + + if (cmd != NULL) + len = strlen(cmd) + 1; + else + len = 0; + if (write_all(fd, &len, sizeof(len)) != sizeof(len)) + return -1; + if (len && write_all(fd, cmd, len) != len) + return -1; + return 0; +} + +int mpath_process_cmd(int fd, const char *cmd, char **reply, + unsigned int timeout) +{ + if (mpath_send_cmd(fd, cmd) != 0) + return -1; + return mpath_recv_reply(fd, reply, timeout); +} diff --git a/libmpathcmd/mpath_cmd.h b/libmpathcmd/mpath_cmd.h new file mode 100644 index 0000000..6d80a2f --- /dev/null +++ b/libmpathcmd/mpath_cmd.h @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2015 Red Hat, Inc. + * + * This file is part of the device-mapper multipath userspace tools. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifndef LIB_MPATH_CMD_H +#define LIB_MPATH_CMD_H + +#ifdef __cpluscplus +extern "C" { +#endif + +#define DEFAULT_SOCKET "/org/kernel/linux/storage/multipathd" +#define DEFAULT_REPLY_TIMEOUT 1000 + + +/* + * DESCRIPTION: + * Connect to the running multipathd daemon. On systems with the + * multipathd.socket systemd unit file installed, this command will + * start multipathd if it is not already running. This function + * must be run before any of the others in this library + * + * RETURNS: + * A file descriptor on success. -1 on failure (with errno set). + */ +int mpath_connect(void); + + +/* + * DESCRIPTION: + * Disconnect from the multipathd daemon. This function must be + * run after after processing all the multipath commands. + * + * RETURNS: + * 0 on success. -1 on failure (with errno set). + */ +int mpath_disconnect(int fd); + + +/* + * DESCRIPTION + * Send multipathd a command and return the reply. This function + * does the same as calling mpath_send_cmd() and then + * mpath_recv_reply() + * + * RETURNS: + * 0 on successs, and reply will either be NULL (if there was no + * reply data), or point to the reply string, which must be freed by + * the caller. -1 on failure (with errno set). + */ +int mpath_process_cmd(int fd, const char *cmd, char **reply, + unsigned int timeout); + + +/* + * DESCRIPTION: + * Send a command to multipathd + * + * RETURNS: + * 0 on success. -1 on failure (with errno set) + */ +int mpath_send_cmd(int fd, const char *cmd); + + +/* + * DESCRIPTION: + * Return a reply from multipathd for a previously sent command. + * This is equivalent to calling mpath_recv_reply_len(), allocating + * a buffer of the appropriate size, and then calling + * mpath_recv_reply_data() with that buffer. + * + * RETURNS: + * 0 on success, and reply will either be NULL (if there was no + * reply data), or point to the reply string, which must be freed by + * the caller, -1 on failure (with errno set). + */ +int mpath_recv_reply(int fd, char **reply, unsigned int timeout); + + +/* + * DESCRIPTION: + * Return the size of the upcoming reply data from the sent multipath + * command. This must be called before calling mpath_recv_reply_data(). + * + * RETURNS: + * The required size of the reply data buffer on success. -1 on + * failure (with errno set). + */ +ssize_t mpath_recv_reply_len(int fd, unsigned int timeout); + + +/* + * DESCRIPTION: + * Return the reply data from the sent multipath command. + * mpath_recv_reply_len must be called first. reply must point to a + * buffer of len size. + * + * RETURNS: + * 0 on success, and reply will contain the reply data string. -1 + * on failure (with errno set). + */ +int mpath_recv_reply_data(int fd, char *reply, size_t len, + unsigned int timeout); + +#ifdef __cplusplus +} +#endif +#endif /* LIB_MPATH_CMD_H */ diff --git a/libmpathpersist/Makefile b/libmpathpersist/Makefile index c4ec1c5..e49cdb9 100644 --- a/libmpathpersist/Makefile +++ b/libmpathpersist/Makefile @@ -10,8 +10,9 @@ DEVLIB = libmpathpersist.so LIBS = $(DEVLIB).$(SONAME) -CFLAGS += -I$(multipathdir) -I$(mpathpersistdir) -LIBDEPS += -lpthread -ldevmapper -ldl -L$(multipathdir) -lmultipath +CFLAGS += -I$(multipathdir) -I$(mpathpersistdir) -I$(mpathcmddir) +LIBDEPS += -lpthread -ldevmapper -ldl -L$(multipathdir) -lmultipath \ + -L$(mpathcmddir) -lmpathcmd OBJS = mpath_persist.o mpath_updatepr.o mpath_pr_ioctl.o @@ -30,17 +31,17 @@ install: $(LIBS) $(INSTALL_PROGRAM) -m 755 $(LIBS) $(DESTDIR)$(syslibdir)/$(LIBS) $(INSTALL_PROGRAM) -m 755 -d $(DESTDIR)$(syslibdir) $(INSTALL_PROGRAM) -m 755 -d $(DESTDIR)$(man3dir) - $(INSTALL_PROGRAM) -m 755 -d $(DESTDIR)/usr/include/ + $(INSTALL_PROGRAM) -m 755 -d $(DESTDIR)$(incdir) $(INSTALL_PROGRAM) -m 755 -d $(DESTDIR)/usr/share/doc/mpathpersist/ ln -sf $(DESTDIR)$(syslibdir)/$(LIBS) $(DESTDIR)$(syslibdir)/$(DEVLIB) install -m 644 mpath_persistent_reserve_in.3.gz $(DESTDIR)$(man3dir) install -m 644 mpath_persistent_reserve_out.3.gz $(DESTDIR)$(man3dir) - install -m 644 mpath_persist.h $(DESTDIR)/usr/include/ + install -m 644 mpath_persist.h $(DESTDIR)$(incdir) uninstall: rm -f $(DESTDIR)$(syslibdir)/$(LIBS) - rm $(DESTDIR)$(mandir)/mpath_persistent_reserve_in.3.gz - rm $(DESTDIR)$(mandir)/mpath_persistent_reserve_out.3.gz + rm $(DESTDIR)$(man3dir)/mpath_persistent_reserve_in.3.gz + rm $(DESTDIR)$(man3dir)/mpath_persistent_reserve_out.3.gz clean: rm -f core *.a *.o diff --git a/libmpathpersist/mpath_persist.c b/libmpathpersist/mpath_persist.c index bd30125..b23e116 100644 --- a/libmpathpersist/mpath_persist.c +++ b/libmpathpersist/mpath_persist.c @@ -1,7 +1,7 @@ #include #include #include -#include +#include #include #include #include @@ -19,6 +19,7 @@ #include #include #include +#include #include "mpath_persist.h" #include "mpathpr.h" @@ -28,6 +29,8 @@ #include #include #include +#include +#include #define __STDC_FORMAT_MACROS 1 @@ -40,6 +43,16 @@ mpath_lib_init (struct udev *udev) return 1; } + if (conf->max_fds) { + struct rlimit fd_limit; + + fd_limit.rlim_cur = conf->max_fds; + fd_limit.rlim_max = conf->max_fds; + if (setrlimit(RLIMIT_NOFILE, &fd_limit) < 0) + condlog(0, "can't set open fds limit to %d : %s", + conf->max_fds, strerror(errno)); + } + return 0; } @@ -71,7 +84,8 @@ updatepaths (struct multipath * mpp) vector_foreach_slot (pgp->paths, pp, j){ if (!strlen(pp->dev)){ - if (devt2devname(pp->dev, pp->dev_t)){ + if (devt2devname(pp->dev, PATH_SIZE, + pp->dev_t)){ /* * path is not in sysfs anymore */ @@ -94,7 +108,7 @@ updatepaths (struct multipath * mpp) return 0; } -int +int mpath_prin_activepath (struct multipath *mpp, int rq_servact, struct prin_resp * resp, int noisy) { @@ -104,14 +118,19 @@ mpath_prin_activepath (struct multipath *mpp, int rq_servact, vector_foreach_slot (mpp->pg, pgp, j){ vector_foreach_slot (pgp->paths, pp, i){ - if (!((pp->state == PATH_UP) || (pp->state == PATH_GHOST))){ - condlog(2, "%s: %s not available. Skip.", mpp->wwid, pp->dev); - condlog(3, "%s: status = %d.", mpp->wwid, pp->state); + if (!((pp->state == PATH_UP) || + (pp->state == PATH_GHOST))){ + condlog(2, "%s: %s not available. Skip.", + mpp->wwid, pp->dev); + condlog(3, "%s: status = %d.", + mpp->wwid, pp->state); continue; } - condlog(3, "%s: sending pr in command to %s ", mpp->wwid, pp->dev); - ret = mpath_send_prin_activepath(pp->dev, rq_servact, resp, noisy); + condlog(3, "%s: sending pr in command to %s ", + mpp->wwid, pp->dev); + ret = mpath_send_prin_activepath(pp->dev, rq_servact, + resp, noisy); switch(ret) { case MPATH_PR_SUCCESS: @@ -122,10 +141,11 @@ mpath_prin_activepath (struct multipath *mpp, int rq_servact, } } } - return ret; + return ret; } -int mpath_persistent_reserve_in (int fd, int rq_servact, struct prin_resp *resp, int noisy, int verbose) +int mpath_persistent_reserve_in (int fd, int rq_servact, + struct prin_resp *resp, int noisy, int verbose) { struct stat info; vector curmp = NULL; @@ -141,14 +161,14 @@ int mpath_persistent_reserve_in (int fd, int rq_servact, struct prin_resp *resp, if (fstat( fd, &info) != 0){ condlog(0, "stat error %d", fd); return MPATH_PR_FILE_ERROR; - } + } if(!S_ISBLK(info.st_mode)){ condlog(0, "Failed to get major:minor. fd = %d", fd); return MPATH_PR_FILE_ERROR; } - major = (int)MAJOR(info.st_rdev); - minor = (int)MINOR(info.st_rdev); + major = major(info.st_rdev); + minor = minor(info.st_rdev); condlog(4, "Device %d:%d: ", major, minor); /* get alias from major:minor*/ @@ -160,7 +180,7 @@ int mpath_persistent_reserve_in (int fd, int rq_servact, struct prin_resp *resp, condlog(3, "alias = %s", alias); map_present = dm_map_present(alias); - if (map_present && dm_type(alias, TGT_MPATH) <= 0){ + if (map_present && !dm_is_mpath(alias)){ condlog( 0, "%s: not a multipath device.", alias); ret = MPATH_PR_DMMP_ERROR; goto out; @@ -178,7 +198,7 @@ int mpath_persistent_reserve_in (int fd, int rq_servact, struct prin_resp *resp, goto out; } - if (path_discovery(pathvec, conf, DI_SYSFS | DI_CHECKER)) { + if (path_discovery(pathvec, conf, DI_SYSFS | DI_CHECKER) < 0) { ret = MPATH_PR_DMMP_ERROR; goto out1; } @@ -201,14 +221,14 @@ int mpath_persistent_reserve_in (int fd, int rq_servact, struct prin_resp *resp, out1: free_multipathvec(curmp, KEEP_PATHS); - free_pathvec(pathvec, FREE_PATHS); + free_pathvec(pathvec, FREE_PATHS); out: FREE(alias); - return ret; + return ret; } int mpath_persistent_reserve_out ( int fd, int rq_servact, int rq_scope, - unsigned int rq_type, struct prout_param_descriptor *paramp, int noisy, int verbose) + unsigned int rq_type, struct prout_param_descriptor *paramp, int noisy, int verbose) { struct stat info; @@ -223,7 +243,7 @@ int mpath_persistent_reserve_out ( int fd, int rq_servact, int rq_scope, int ret; int j; unsigned char *keyp; - uint64_t prkey; + uint64_t prkey; conf->verbosity = verbose; @@ -234,11 +254,11 @@ int mpath_persistent_reserve_out ( int fd, int rq_servact, int rq_scope, if(!S_ISBLK(info.st_mode)){ condlog(3, "Failed to get major:minor. fd=%d", fd); - return MPATH_PR_FILE_ERROR; - } + return MPATH_PR_FILE_ERROR; + } - major = (int)MAJOR(info.st_rdev); - minor = (int)MINOR(info.st_rdev); + major = major(info.st_rdev); + minor = minor(info.st_rdev); condlog(4, "Device %d:%d", major, minor); /* get WWN of the device from major:minor*/ @@ -250,7 +270,7 @@ int mpath_persistent_reserve_out ( int fd, int rq_servact, int rq_scope, condlog(3, "alias = %s", alias); map_present = dm_map_present(alias); - if (map_present && dm_type(alias, TGT_MPATH) <= 0){ + if (map_present && !dm_is_mpath(alias)){ condlog(3, "%s: not a multipath device.", alias); ret = MPATH_PR_DMMP_ERROR; goto out; @@ -262,13 +282,13 @@ int mpath_persistent_reserve_out ( int fd, int rq_servact, int rq_scope, curmp = vector_alloc (); pathvec = vector_alloc (); - if (!curmp || !pathvec){ - condlog (0, "%s: vector allocation failed.", alias); - ret = MPATH_PR_DMMP_ERROR; - goto out; - } + if (!curmp || !pathvec){ + condlog (0, "%s: vector allocation failed.", alias); + ret = MPATH_PR_DMMP_ERROR; + goto out; + } - if (path_discovery(pathvec, conf, DI_SYSFS | DI_CHECKER)) { + if (path_discovery(pathvec, conf, DI_SYSFS | DI_CHECKER) < 0) { ret = MPATH_PR_DMMP_ERROR; goto out1; } @@ -292,22 +312,22 @@ int mpath_persistent_reserve_out ( int fd, int rq_servact, int rq_scope, switch(rq_servact) { - case MPATH_PROUT_REG_SA: - case MPATH_PROUT_REG_IGN_SA: - ret= mpath_prout_reg(mpp, rq_servact, rq_scope, rq_type, paramp, noisy); - break; - case MPATH_PROUT_RES_SA : - case MPATH_PROUT_PREE_SA : - case MPATH_PROUT_PREE_AB_SA : - case MPATH_PROUT_CLEAR_SA: - ret = mpath_prout_common(mpp, rq_servact, rq_scope, rq_type, paramp, noisy); - break; - case MPATH_PROUT_REL_SA: - ret = mpath_prout_rel(mpp, rq_servact, rq_scope, rq_type, paramp, noisy); - break; - default: - ret = MPATH_PR_OTHER; - goto out1; + case MPATH_PROUT_REG_SA: + case MPATH_PROUT_REG_IGN_SA: + ret= mpath_prout_reg(mpp, rq_servact, rq_scope, rq_type, paramp, noisy); + break; + case MPATH_PROUT_RES_SA : + case MPATH_PROUT_PREE_SA : + case MPATH_PROUT_PREE_AB_SA : + case MPATH_PROUT_CLEAR_SA: + ret = mpath_prout_common(mpp, rq_servact, rq_scope, rq_type, paramp, noisy); + break; + case MPATH_PROUT_REL_SA: + ret = mpath_prout_rel(mpp, rq_servact, rq_scope, rq_type, paramp, noisy); + break; + default: + ret = MPATH_PR_OTHER; + goto out1; } if ((ret == MPATH_PR_SUCCESS) && ((rq_servact == MPATH_PROUT_REG_SA) || @@ -326,7 +346,7 @@ int mpath_persistent_reserve_out ( int fd, int rq_servact, int rq_scope, else update_prflag(alias, "set", noisy); } else { - if ((ret == MPATH_PR_SUCCESS) && ((rq_servact == MPATH_PROUT_CLEAR_SA) || + if ((ret == MPATH_PR_SUCCESS) && ((rq_servact == MPATH_PROUT_CLEAR_SA) || (rq_servact == MPATH_PROUT_PREE_AB_SA ))){ update_prflag(alias, "unset", noisy); } @@ -337,7 +357,7 @@ out1: out: FREE(alias); - return ret; + return ret; } int @@ -365,9 +385,9 @@ get_mpvec (vector curmp, vector pathvec, char * refwwid) dm_get_map(mpp->alias, &mpp->size, params); condlog(3, "params = %s", params); dm_get_status(mpp->alias, status); - condlog(3, "status = %s", status); + condlog(3, "status = %s", status); disassemble_map (pathvec, params, mpp); - + /* * disassemble_map() can add new paths to pathvec. * If not in "fast list mode", we need to fetch information @@ -386,18 +406,20 @@ void * mpath_prin_pthread_fn (void *p) int ret; struct prin_param * pparam = (struct prin_param *)p; - ret = prin_do_scsi_ioctl(pparam->dev, pparam->rq_servact, pparam->resp, pparam->noisy); - pparam->status = ret; - pthread_exit(NULL); + ret = prin_do_scsi_ioctl(pparam->dev, pparam->rq_servact, + pparam->resp, pparam->noisy); + pparam->status = ret; + pthread_exit(NULL); } -int mpath_send_prin_activepath (char * dev, int rq_servact, struct prin_resp * resp, int noisy) +int mpath_send_prin_activepath (char * dev, int rq_servact, + struct prin_resp * resp, int noisy) { int rc; rc = prin_do_scsi_ioctl(dev, rq_servact, resp, noisy); - + return (rc); } @@ -409,14 +431,14 @@ int mpath_prout_reg(struct multipath *mpp,int rq_servact, int rq_scope, struct pathgroup *pgp = NULL; struct path *pp = NULL; int rollback = 0; - int active_pathcount=0; + int active_pathcount=0; int rc; int count=0; int status = MPATH_PR_SUCCESS; uint64_t sa_key = 0; if (!mpp) - return MPATH_PR_DMMP_ERROR; + return MPATH_PR_DMMP_ERROR; active_pathcount = pathcount(mpp, PATH_UP) + pathcount(mpp, PATH_GHOST); @@ -444,12 +466,13 @@ int mpath_prout_reg(struct multipath *mpp,int rq_servact, int rq_scope, condlog (3, "THRED ID [%d] INFO]", i); condlog (3, "rq_servact=%d ", thread[i].param.rq_servact); - condlog (3, "rq_scope=%d ", thread[i].param.rq_scope); - condlog (3, "rq_type=%d ", thread[i].param.rq_type); - condlog (3, "rkey="); - condlog (3, "paramp->sa_flags =%02x ", thread[i].param.paramp->sa_flags); - condlog (3, "noisy=%d ", thread[i].param.noisy); - condlog (3, "status=%d ", thread[i].param.status); + condlog (3, "rq_scope=%d ", thread[i].param.rq_scope); + condlog (3, "rq_type=%d ", thread[i].param.rq_type); + condlog (3, "rkey="); + condlog (3, "paramp->sa_flags =%02x ", + thread[i].param.paramp->sa_flags); + condlog (3, "noisy=%d ", thread[i].param.noisy); + condlog (3, "status=%d ", thread[i].param.status); } pthread_attr_t attr; @@ -508,7 +531,7 @@ int mpath_prout_reg(struct multipath *mpp,int rq_servact, int rq_scope, memset(&thread[i].param.paramp->sa_key, 0, 8); thread[i].param.status = MPATH_PR_SUCCESS; rc = pthread_create(&thread[i].id, &attr, mpath_prout_pthread_fn, - (void *)(&thread[count].param)); + (void *)(&thread[i].param)); if (rc){ condlog (0, "%s: failed to create thread for rollback. %d", mpp->wwid, rc); } @@ -539,7 +562,7 @@ void * mpath_prout_pthread_fn(void *p) } int mpath_prout_common(struct multipath *mpp,int rq_servact, int rq_scope, - unsigned int rq_type, struct prout_param_descriptor* paramp, int noisy) + unsigned int rq_type, struct prout_param_descriptor* paramp, int noisy) { int i,j, ret; struct pathgroup *pgp = NULL; @@ -548,13 +571,15 @@ int mpath_prout_common(struct multipath *mpp,int rq_servact, int rq_scope, vector_foreach_slot (mpp->pg, pgp, j){ vector_foreach_slot (pgp->paths, pp, i){ if (!((pp->state == PATH_UP) || (pp->state == PATH_GHOST))){ - condlog (1, "%s: %s path not up. Skip", mpp->wwid, pp->dev); + condlog (1, "%s: %s path not up. Skip", + mpp->wwid, pp->dev); continue; } condlog (3, "%s: sending pr out command to %s", mpp->wwid, pp->dev); - ret = send_prout_activepath(pp->dev, rq_servact, rq_scope, rq_type, - paramp, noisy); + ret = send_prout_activepath(pp->dev, rq_servact, + rq_scope, rq_type, + paramp, noisy); return ret ; } } @@ -585,7 +610,7 @@ int send_prout_activepath(char * dev, int rq_servact, int rq_scope, rc = pthread_create(&thread, &attr, mpath_prout_pthread_fn, (void *)(¶m)); if (rc){ condlog (3, "%s: failed to create thread %d", dev, rc); - exit(-1); + return MPATH_PR_OTHER; } /* Free attribute and wait for the other threads */ pthread_attr_destroy(&attr); @@ -595,7 +620,7 @@ int send_prout_activepath(char * dev, int rq_servact, int rq_scope, } int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope, - unsigned int rq_type, struct prout_param_descriptor * paramp, int noisy) + unsigned int rq_type, struct prout_param_descriptor * paramp, int noisy) { int i, j; int num = 0; @@ -610,7 +635,7 @@ int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope, struct prout_param_descriptor *pamp; struct prin_resp *pr_buff; int length; - struct transportid *pptr; + struct transportid *pptr; if (!mpp) return MPATH_PR_DMMP_ERROR; @@ -644,7 +669,7 @@ int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope, condlog (1, "%s: %s path not up.", mpp->wwid, pp->dev); continue; } - + strncpy(thread[count].param.dev, pp->dev, FILE_NAME_SIZE); condlog (3, "%s: sending pr out command to %s", mpp->wwid, pp->dev); rc = pthread_create (&thread[count].id, &attr, mpath_prout_pthread_fn, @@ -681,13 +706,13 @@ int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope, num = resp.prin_descriptor.prin_readresv.additional_length / 8; if (num == 0){ condlog (2, "%s: Path holding reservation is released.", mpp->wwid); - return MPATH_PR_SUCCESS; + return MPATH_PR_SUCCESS; } condlog (2, "%s: Path holding reservation is not avialable.", mpp->wwid); pr_buff = mpath_alloc_prin_response(MPATH_PRIN_RFSTAT_SA); if (!pr_buff){ - condlog (0, "%s: failed to alloc pr in response buffer.", mpp->wwid); + condlog (0, "%s: failed to alloc pr in response buffer.", mpp->wwid); return MPATH_PR_OTHER; } @@ -723,16 +748,21 @@ int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope, condlog (3, "%s: reservation key set.", mpp->wwid); } - mpath_prout_common (mpp, MPATH_PROUT_CLEAR_SA, rq_scope, rq_type, pamp, - noisy); + status = mpath_prout_common (mpp, MPATH_PROUT_CLEAR_SA, + rq_scope, rq_type, pamp, noisy); + + if (status) { + condlog(0, "%s: failed to send CLEAR_SA", mpp->wwid); + goto out1; + } pamp->num_transportid = 1; pptr=pamp->trnptid_list[0]; for (i = 0; i < num; i++){ - if (mpp->reservation_key && + if (mpp->reservation_key && memcmp(pr_buff->prin_descriptor.prin_readfd.descriptors[i]->key, - mpp->reservation_key, 8)){ + mpp->reservation_key, 8)){ /*register with tarnsport id*/ memset(pamp, 0, length); pamp->trnptid_list[0] = pptr; @@ -768,7 +798,7 @@ int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope, memset (pamp, 0, length); memcpy (pamp->sa_key, mpp->reservation_key, 8); memset (pamp->key, 0, 8); - status = mpath_prout_reg(mpp, MPATH_PROUT_REG_SA, rq_scope, rq_type, pamp, noisy); + status = mpath_prout_reg(mpp, MPATH_PROUT_REG_SA, rq_scope, rq_type, pamp, noisy); } @@ -802,7 +832,7 @@ void * mpath_alloc_prin_response(int prin_sa) memset(ptr, 0, size); break; case MPATH_PRIN_RFSTAT_SA: - size = sizeof(struct print_fulldescr_list) + + size = sizeof(struct print_fulldescr_list) + sizeof(struct prin_fulldescr *)*MPATH_MX_TIDS; ptr = malloc(size); memset(ptr, 0, size); @@ -822,7 +852,7 @@ int update_map_pr(struct multipath *mpp) if (!mpp->reservation_key) { /* Nothing to do. Assuming pr mgmt feature is disabled*/ - condlog(3, "%s: reservation_key not set in multiapth.conf", mpp->alias); + condlog(3, "%s: reservation_key not set in multipath.conf", mpp->alias); return MPATH_PR_SUCCESS; } diff --git a/libmpathpersist/mpath_updatepr.c b/libmpathpersist/mpath_updatepr.c index 8597d40..bda8991 100644 --- a/libmpathpersist/mpath_updatepr.c +++ b/libmpathpersist/mpath_updatepr.c @@ -12,9 +12,9 @@ #include #include #include +#include +#include #include "memory.h" -#include "../libmultipath/uxsock.h" -#include "../libmultipath/defaults.h" unsigned long mem_allocated; /* Total memory used in Bytes */ @@ -23,10 +23,9 @@ int update_prflag(char * arg1, char * arg2, int noisy) int fd; char str[64]; char *reply; - size_t len; int ret = 0; - fd = ux_socket_connect(DEFAULT_SOCKET); + fd = mpath_connect(); if (fd == -1) { condlog (0, "ux socket connect error"); return 1 ; @@ -34,18 +33,23 @@ int update_prflag(char * arg1, char * arg2, int noisy) snprintf(str,sizeof(str),"map %s %s", arg1, arg2); condlog (2, "%s: pr flag message=%s", arg1, str); - send_packet(fd, str, strlen(str) + 1); - recv_packet(fd, &reply, &len); - - condlog (2, "%s: message=%s reply=%s", arg1, str, reply); - if (!reply || strncmp(reply,"ok", 2) == 0) - ret = -1; - else if (strncmp(reply, "fail", 4) == 0) + send_packet(fd, str); + ret = recv_packet(fd, &reply, DEFAULT_REPLY_TIMEOUT); + if (ret < 0) { + condlog(2, "%s: message=%s error=%d", arg1, str, errno); ret = -2; - else{ - ret = atoi(reply); + } else { + condlog (2, "%s: message=%s reply=%s", arg1, str, reply); + if (!reply || strncmp(reply,"ok", 2) == 0) + ret = -1; + else if (strncmp(reply, "fail", 4) == 0) + ret = -2; + else{ + ret = atoi(reply); + } } free(reply); + mpath_disconnect(fd); return ret; } diff --git a/libmpathpersist/mpathpr.h b/libmpathpersist/mpathpr.h index 54dfb3e..d69a732 100644 --- a/libmpathpersist/mpathpr.h +++ b/libmpathpersist/mpathpr.h @@ -50,6 +50,5 @@ int send_prout_activepath(char * dev, int rq_servact, int rq_scope, int update_prflag(char * arg1, char * arg2, int noisy); void * mpath_alloc_prin_response(int prin_sa); int update_map_pr(struct multipath *mpp); -int devt2devname (char *devname, char *devt); -#endif +#endif diff --git a/libmultipath/Makefile b/libmultipath/Makefile index 6364364..1ee968e 100644 --- a/libmultipath/Makefile +++ b/libmultipath/Makefile @@ -7,9 +7,14 @@ include ../Makefile.inc SONAME=0 DEVLIB = libmultipath.so LIBS = $(DEVLIB).$(SONAME) -LIBDEPS = -lpthread -ldl -ldevmapper -ludev +CFLAGS += -I$(mpathcmddir) +LIBDEPS = -lpthread -ldl -ldevmapper -ludev -L$(mpathcmddir) -lmpathcmd ifdef SYSTEMD - LIBDEPS += -lsystemd-daemon + ifeq ($(shell test $(SYSTEMD) -gt 209 && echo 1), 1) + LIBDEPS += -lsystemd + else + LIBDEPS += -lsystemd-daemon + endif endif OBJS = memory.o parser.o vector.o devmapper.o callout.o \ @@ -32,7 +37,7 @@ ifneq ($(strip $(LIBDM_API_COOKIE)),0) CFLAGS += -DLIBDM_API_COOKIE endif -LIBUDEV_API_RECVBUF = $(shell grep -Ecs '^[a-z]*[[:space:]]+udev_monitor_set_resolve_buffer_size' /usr/include/libudev.h) +LIBUDEV_API_RECVBUF = $(shell grep -Ecs '^[a-z]*[[:space:]]+udev_monitor_set_receive_buffer_size' /usr/include/libudev.h) ifneq ($(strip $(LIBUDEV_API_RECVBUF)),0) CFLAGS += -DLIBUDEV_API_RECVBUF @@ -42,6 +47,12 @@ ifdef SYSTEMD CFLAGS += -DUSE_SYSTEMD=$(SYSTEMD) endif +LIBDM_API_DEFERRED = $(shell grep -Ecs '^[a-z]*[[:space:]]+dm_task_deferred_remove' /usr/include/libdevmapper.h) + +ifneq ($(strip $(LIBDM_API_DEFERRED)),0) + CFLAGS += -DLIBDM_API_DEFERRED +endif + all: $(LIBS) $(LIBS): $(OBJS) diff --git a/libmultipath/alias.c b/libmultipath/alias.c index ab15185..7d12a0c 100644 --- a/libmultipath/alias.c +++ b/libmultipath/alias.c @@ -67,7 +67,7 @@ scan_devname(char *alias, char *prefix) return -1; if (strlen(alias) == strlen(prefix)) - return -1; + return -1; if (strlen(alias) > strlen(prefix) + 7) /* id of 'aaaaaaaa' overflows int */ @@ -145,17 +145,15 @@ lookup_binding(FILE *f, char *map_wwid, char **map_alias, char *prefix) } static int -rlookup_binding(FILE *f, char *buff, char *map_alias) +rlookup_binding(FILE *f, char *buff, char *map_alias, char *prefix) { char line[LINE_MAX]; unsigned int line_nr = 0; - int id = 0; buff[0] = '\0'; while (fgets(line, LINE_MAX, f)) { char *c, *alias, *wwid; - int curr_id; line_nr++; c = strpbrk(line, "#\n\r"); @@ -164,9 +162,6 @@ rlookup_binding(FILE *f, char *buff, char *map_alias) alias = strtok(line, " \t"); if (!alias) /* blank line */ continue; - curr_id = scan_devname(alias, NULL); /* TBD: Why this call? */ - if (curr_id >= id) - id = curr_id + 1; wwid = strtok(NULL, " \t"); if (!wwid){ condlog(3, @@ -184,11 +179,12 @@ rlookup_binding(FILE *f, char *buff, char *map_alias) "\nSetting wwid to %s", alias, wwid); strncpy(buff, wwid, WWID_SIZE); buff[WWID_SIZE - 1] = '\0'; - return id; + return 0; } } condlog(3, "No matching alias [%s] in bindings file.", map_alias); - return id; + + return -1; } static char * @@ -236,6 +232,67 @@ allocate_binding(int fd, char *wwid, int id, char *prefix) return alias; } +char * +use_existing_alias (char *wwid, char *file, char *alias_old, + char *prefix, int bindings_read_only) +{ + char *alias = NULL; + int id = 0; + int fd, can_write; + char buff[WWID_SIZE]; + FILE *f; + + fd = open_file(file, &can_write, BINDINGS_FILE_HEADER); + if (fd < 0) + return NULL; + + f = fdopen(fd, "r"); + if (!f) { + condlog(0, "cannot fdopen on bindings file descriptor"); + close(fd); + return NULL; + } + /* lookup the binding. if it exsists, the wwid will be in buff + * either way, id contains the id for the alias + */ + rlookup_binding(f, buff, alias_old, prefix); + + if (strlen(buff) > 0) { + /* if buff is our wwid, it's already + * allocated correctly + */ + if (strcmp(buff, wwid) == 0) + alias = STRDUP(alias_old); + else { + alias = NULL; + condlog(0, "alias %s already bound to wwid %s, cannot reuse", + alias_old, buff); + } + goto out; + } + + /* allocate the existing alias in the bindings file */ + id = scan_devname(alias_old, prefix); + if (id <= 0) + goto out; + + if (fflush(f) != 0) { + condlog(0, "cannot fflush bindings file stream : %s", + strerror(errno)); + goto out; + } + + if (can_write && !bindings_read_only) { + alias = allocate_binding(fd, wwid, id, prefix); + condlog(0, "Allocated existing binding [%s] for WWID [%s]", + alias, wwid); + } + +out: + fclose(f); + return alias; +} + char * get_user_friendly_alias(char *wwid, char *file, char *prefix, int bindings_read_only) @@ -271,6 +328,7 @@ get_user_friendly_alias(char *wwid, char *file, char *prefix, if (fflush(f) != 0) { condlog(0, "cannot fflush bindings file stream : %s", strerror(errno)); + free(alias); fclose(f); return NULL; } @@ -305,7 +363,7 @@ get_user_friendly_wwid(char *alias, char *buff, char *file) return -1; } - rlookup_binding(f, buff, alias); + rlookup_binding(f, buff, alias, NULL); if (!strlen(buff)) { fclose(f); return -1; diff --git a/libmultipath/alias.h b/libmultipath/alias.h index 8ddd0b5..9cb3e8f 100644 --- a/libmultipath/alias.h +++ b/libmultipath/alias.h @@ -10,3 +10,5 @@ char *get_user_friendly_alias(char *wwid, char *file, 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); diff --git a/libmultipath/blacklist.c b/libmultipath/blacklist.c index 79ddcde..2400eda 100644 --- a/libmultipath/blacklist.c +++ b/libmultipath/blacklist.c @@ -80,6 +80,8 @@ set_ble_device (vector blist, char * vendor, char * product, int origin) if (regcomp(&ble->vendor_reg, vendor, REG_EXTENDED|REG_NOSUB)) { FREE(vendor); + if (product) + FREE(product); return 1; } ble->vendor = vendor; @@ -88,6 +90,10 @@ set_ble_device (vector blist, char * vendor, char * product, int origin) if (regcomp(&ble->product_reg, product, REG_EXTENDED|REG_NOSUB)) { FREE(product); + if (vendor) { + ble->vendor = NULL; + FREE(vendor); + } return 1; } ble->product = product; @@ -129,8 +135,12 @@ _blacklist_exceptions_device(vector elist, char * vendor, char * product) struct blentry_device * ble; vector_foreach_slot (elist, ble, i) { - if (!regexec(&ble->vendor_reg, vendor, 0, NULL, 0) && - !regexec(&ble->product_reg, product, 0, NULL, 0)) + if (!ble->vendor && !ble->product) + continue; + if ((!ble->vendor || + !regexec(&ble->vendor_reg, vendor, 0, NULL, 0)) && + (!ble->product || + !regexec(&ble->product_reg, product, 0, NULL, 0))) return 1; } return 0; @@ -143,8 +153,12 @@ _blacklist_device (vector blist, char * vendor, char * product) struct blentry_device * ble; vector_foreach_slot (blist, ble, i) { - if (!regexec(&ble->vendor_reg, vendor, 0, NULL, 0) && - !regexec(&ble->product_reg, product, 0, NULL, 0)) + if (!ble->vendor && !ble->product) + continue; + if ((!ble->vendor || + !regexec(&ble->vendor_reg, vendor, 0, NULL, 0)) && + (!ble->product || + !regexec(&ble->product_reg, product, 0, NULL, 0))) return 1; } return 0; @@ -164,7 +178,7 @@ setup_default_blist (struct config * conf) if (store_ble(conf->blist_devnode, str, ORIGIN_DEFAULT)) return 1; - str = STRDUP("^(td|hd)[a-z]"); + str = STRDUP("^(td|hd|vd)[a-z]"); if (!str) return 1; if (store_ble(conf->blist_devnode, str, ORIGIN_DEFAULT)) @@ -176,7 +190,13 @@ setup_default_blist (struct config * conf) if (store_ble(conf->blist_devnode, str, ORIGIN_DEFAULT)) return 1; - str = STRDUP("(ID_SCSI_VPD|ID_WWN)"); + str = STRDUP("^nvme.*"); + if (!str) + return 1; + if (store_ble(conf->blist_devnode, str, ORIGIN_DEFAULT)) + return 1; + + str = STRDUP("(SCSI_IDENT_.*|ID_WWN)"); if (!str) return 1; if (store_ble(conf->elist_property, str, ORIGIN_DEFAULT)) @@ -196,6 +216,7 @@ setup_default_blist (struct config * conf) STRDUP(hwe->bl_product), ORIGIN_DEFAULT)) { FREE(ble); + vector_del_slot(conf->blist_device, VECTOR_SIZE(conf->blist_device) - 1); return 1; } } @@ -207,6 +228,8 @@ setup_default_blist (struct config * conf) if (vendor && product) \ condlog(3, "%s: (%s:%s) %s %s", \ dev, vendor, product, (M), (S)); \ + else if (wwid && !dev) \ + condlog(3, "%s: %s %s", wwid, (M), (S)); \ else if (wwid) \ condlog(3, "%s: %s %s %s", dev, (M), wwid, (S)); \ else if (env) \ @@ -307,10 +330,10 @@ _filter_wwid (vector blist, vector elist, char * wwid) } int -filter_wwid (vector blist, vector elist, char * wwid) +filter_wwid (vector blist, vector elist, char * wwid, char * dev) { int r = _filter_wwid(blist, elist, wwid); - log_filter(NULL, NULL, NULL, wwid, NULL, r); + log_filter(dev, NULL, NULL, wwid, NULL, r); return r; } diff --git a/libmultipath/blacklist.h b/libmultipath/blacklist.h index 0e90e9a..24a5fa5 100644 --- a/libmultipath/blacklist.h +++ b/libmultipath/blacklist.h @@ -32,7 +32,7 @@ struct blentry_device { int setup_default_blist (struct config *); int alloc_ble_device (vector); int filter_devnode (vector, vector, char *); -int filter_wwid (vector, vector, char *); +int filter_wwid (vector, vector, char *, char *); int filter_device (vector, vector, char *, char *); int filter_path (struct config *, struct path *); int filter_property(struct config *, struct udev_device *); diff --git a/libmultipath/callout.c b/libmultipath/callout.c index c35c7c0..4cbbd06 100644 --- a/libmultipath/callout.c +++ b/libmultipath/callout.c @@ -125,7 +125,7 @@ int execute_program(char *path, char *value, int len) if (status == 0) retval = 0; else - condlog(0, "%s exitted with %d", argv[0], status); + condlog(0, "%s exited with %d", argv[0], status); } else if (WIFSIGNALED(status)) condlog(0, "%s was terminated by signal %d", argv[0], WTERMSIG(status)); diff --git a/libmultipath/checkers.c b/libmultipath/checkers.c index 4a4cd7c..ef1d099 100644 --- a/libmultipath/checkers.c +++ b/libmultipath/checkers.c @@ -19,6 +19,7 @@ char *checker_state_names[] = { "pending", "timeout", "removed", + "delayed", }; static LIST_HEAD(checkers); @@ -194,7 +195,7 @@ void checker_put (struct checker * dst) { struct checker * src; - if (!dst) + if (!dst || !dst->check) return; src = checker_lookup(dst->name); if (dst->free) diff --git a/libmultipath/checkers.h b/libmultipath/checkers.h index e62b52f..a935b3f 100644 --- a/libmultipath/checkers.h +++ b/libmultipath/checkers.h @@ -54,6 +54,14 @@ * PATH REMOVED: * - Use: All checkers * - Description: Device has been removed from the system + * + * PATH_DELAYED: + * - Use: None of the checkers (returned if the path is being delayed before + * reintegration. + * - Description: If a path fails after being up for less than + * delay_watch_checks checks, when it comes back up again, it will not + * be marked as up until it has been up for delay_wait_checks checks. + * During this time, it is marked as "delayed" */ enum path_check_state { PATH_WILD, @@ -65,6 +73,7 @@ enum path_check_state { PATH_PENDING, PATH_TIMEOUT, PATH_REMOVED, + PATH_DELAYED, PATH_MAX_STATE }; diff --git a/libmultipath/checkers/rdac.c b/libmultipath/checkers/rdac.c index e0b2ea4..00e3c44 100644 --- a/libmultipath/checkers/rdac.c +++ b/libmultipath/checkers/rdac.c @@ -308,7 +308,7 @@ libcheck_check (struct checker * c) done: switch (ret) { case PATH_DOWN: - MSG(c, (inqfail) ? MSG_RDAC_DOWN_TYPE("inquiry failed") : + MSG(c, "%s", (inqfail) ? MSG_RDAC_DOWN_TYPE("inquiry failed") : checker_msg_string(&inq)); break; case PATH_UP: diff --git a/libmultipath/config.c b/libmultipath/config.c index e13c307..8b9e770 100644 --- a/libmultipath/config.c +++ b/libmultipath/config.c @@ -6,6 +6,9 @@ #include #include #include +#include +#include +#include #include "checkers.h" #include "memory.h" @@ -21,6 +24,7 @@ #include "defaults.h" #include "prio.h" #include "devmapper.h" +#include "mpath_cmd.h" static int hwe_strmatch (struct hwentry *hwe1, struct hwentry *hwe2) @@ -340,6 +344,9 @@ merge_hwe (struct hwentry * dst, struct hwentry * src) merge_num(user_friendly_names); merge_num(retain_hwhandler); merge_num(detect_prio); + merge_num(deferred_remove); + merge_num(delay_watch_checks); + merge_num(delay_wait_checks); /* * Make sure features is consistent with @@ -412,6 +419,7 @@ store_hwe (vector hwtable, struct hwentry * dhwe) hwe->user_friendly_names = dhwe->user_friendly_names; hwe->retain_hwhandler = dhwe->retain_hwhandler; hwe->detect_prio = dhwe->detect_prio; + conf->deferred_remove = DEFAULT_DEFERRED_REMOVE; if (dhwe->bl_product && !(hwe->bl_product = set_param_str(dhwe->bl_product))) goto out; @@ -497,17 +505,24 @@ free_config (struct config * conf) if (conf->wwids_file) FREE(conf->wwids_file); + if (conf->prio_name) FREE(conf->prio_name); if (conf->alias_prefix) FREE(conf->alias_prefix); + if (conf->partition_delim) + FREE(conf->partition_delim); if (conf->prio_args) FREE(conf->prio_args); if (conf->checker_name) FREE(conf->checker_name); + + if (conf->config_dir) + FREE(conf->config_dir); + if (conf->reservation_key) FREE(conf->reservation_key); @@ -523,10 +538,48 @@ free_config (struct config * conf) free_mptable(conf->mptable); free_hwtable(conf->hwtable); + free_hwe(conf->overrides); free_keywords(conf->keywords); FREE(conf); } +/* if multipath fails to process the config directory, it should continue, + * with just a warning message */ +static void +process_config_dir(vector keywords, char *dir) +{ + struct dirent **namelist; + int i, n; + char path[LINE_MAX]; + int old_hwtable_size; + + if (dir[0] != '/') { + condlog(1, "config_dir '%s' must be a fully qualified path", + dir); + return; + } + n = scandir(dir, &namelist, NULL, alphasort); + if (n < 0) { + if (errno == ENOENT) + condlog(3, "No configuration dir '%s'", dir); + else + condlog(0, "couldn't open configuration dir '%s': %s", + dir, strerror(errno)); + return; + } + for (i = 0; i < n; i++) { + if (!strstr(namelist[i]->d_name, ".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'; + process_file(path); + if (VECTOR_SIZE(conf->hwtable) > old_hwtable_size) + factorize_hwtable(conf->hwtable, old_hwtable_size); + + } +} + int load_config (char * file, struct udev *udev) { @@ -549,18 +602,26 @@ load_config (char * file, struct udev *udev) get_sys_max_fds(&conf->max_fds); conf->bindings_file = set_default(DEFAULT_BINDINGS_FILE); conf->wwids_file = set_default(DEFAULT_WWIDS_FILE); - conf->bindings_read_only = 0; conf->multipath_dir = set_default(DEFAULT_MULTIPATHDIR); conf->features = set_default(DEFAULT_FEATURES); conf->flush_on_last_del = 0; conf->attribute_flags = 0; conf->reassign_maps = DEFAULT_REASSIGN_MAPS; conf->checkint = DEFAULT_CHECKINT; - conf->max_checkint = MAX_CHECKINT(conf->checkint); + conf->max_checkint = 0; conf->pgfailback = DEFAULT_FAILBACK; conf->fast_io_fail = DEFAULT_FAST_IO_FAIL; conf->retain_hwhandler = DEFAULT_RETAIN_HWHANDLER; conf->detect_prio = DEFAULT_DETECT_PRIO; + conf->force_sync = 0; + conf->partition_delim = NULL; + conf->processed_main_config = 0; + conf->find_multipaths = DEFAULT_FIND_MULTIPATHS; + conf->uxsock_timeout = DEFAULT_REPLY_TIMEOUT; + conf->uid_attribute = set_default(DEFAULT_UID_ATTRIBUTE); + conf->retrigger_tries = DEFAULT_RETRIGGER_TRIES; + conf->retrigger_delay = DEFAULT_RETRIGGER_DELAY; + conf->uev_wait_timeout = DEFAULT_UEV_WAIT_TIMEOUT; /* * preload default hwtable @@ -579,11 +640,12 @@ load_config (char * file, struct udev *udev) */ set_current_keywords(&conf->keywords); alloc_keywords(); + init_keywords(); if (filepresent(file)) { int builtin_hwtable_size; builtin_hwtable_size = VECTOR_SIZE(conf->hwtable); - if (init_data(file, init_keywords)) { + if (process_file(file)) { condlog(0, "error parsing config file"); goto out; } @@ -595,13 +657,19 @@ load_config (char * file, struct udev *udev) factorize_hwtable(conf->hwtable, builtin_hwtable_size); } - } else { - init_keywords(); } + conf->processed_main_config = 1; + 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->keywords, conf->config_dir); + /* * fill the voids left in the config file */ + if (conf->max_checkint == 0) + conf->max_checkint = MAX_CHECKINT(conf->checkint); if (conf->blist_devnode == NULL) { conf->blist_devnode = vector_alloc(); diff --git a/libmultipath/config.h b/libmultipath/config.h index 445525b..e9828ea 100644 --- a/libmultipath/config.h +++ b/libmultipath/config.h @@ -19,7 +19,19 @@ enum devtypes { DEV_NONE, DEV_DEVT, DEV_DEVNODE, - DEV_DEVMAP + DEV_DEVMAP, + DEV_UEVENT +}; + +enum mpath_cmds { + CMD_CREATE, + CMD_DRY_RUN, + CMD_LIST_SHORT, + CMD_LIST_LONG, + CMD_VALID_PATH, + CMD_REMOVE_WWID, + CMD_RESET_WWIDS, + CMD_ADD_WWID, }; struct hwentry { @@ -48,6 +60,9 @@ struct hwentry { int user_friendly_names; int retain_hwhandler; int detect_prio; + int deferred_remove; + int delay_watch_checks; + int delay_wait_checks; char * bl_product; }; @@ -71,6 +86,9 @@ struct mpentry { int flush_on_last_del; int attribute_flags; int user_friendly_names; + int deferred_remove; + int delay_watch_checks; + int delay_wait_checks; uid_t uid; gid_t gid; mode_t mode; @@ -78,8 +96,7 @@ struct mpentry { struct config { int verbosity; - int dry_run; - int list; + enum mpath_cmds cmd; int pgpolicy_flag; int pgpolicy; enum devtypes dev_type; @@ -96,24 +113,33 @@ struct config { int max_fds; int force_reload; int queue_without_daemon; + int ignore_wwids; int checker_timeout; int daemon; -#ifdef USE_SYSTEMD - int watchdog; -#endif int flush_on_last_del; int attribute_flags; int fast_io_fail; unsigned int dev_loss; int log_checker_err; int allow_queueing; + int find_multipaths; uid_t uid; gid_t gid; mode_t mode; - uint32_t cookie; int reassign_maps; int retain_hwhandler; int detect_prio; + int force_sync; + int deferred_remove; + int processed_main_config; + int delay_watch_checks; + int delay_wait_checks; + int uxsock_timeout; + int retrigger_tries; + int retrigger_delay; + int ignore_new_devs; + int delayed_reconfig; + int uev_wait_timeout; unsigned int version[3]; char * dev; @@ -130,11 +156,14 @@ struct config { char * prio_args; char * checker_name; char * alias_prefix; + char * partition_delim; + char * config_dir; unsigned char * reservation_key; vector keywords; vector mptable; vector hwtable; + struct hwentry *overrides; vector blist_devnode; vector blist_wwid; diff --git a/libmultipath/configure.c b/libmultipath/configure.c index 8c09791..30c7259 100644 --- a/libmultipath/configure.c +++ b/libmultipath/configure.c @@ -14,6 +14,7 @@ #include #include #include +#include #include "checkers.h" #include "vector.h" @@ -39,6 +40,219 @@ #include "uxsock.h" #include "wwids.h" +/* group paths in pg by host adapter + */ +int group_by_host_adapter(struct pathgroup *pgp, vector adapters) +{ + struct adapter_group *agp; + struct host_group *hgp; + struct path *pp, *pp1; + char adapter_name1[SLOT_NAME_SIZE]; + char adapter_name2[SLOT_NAME_SIZE]; + int i, j; + int found_hostgroup = 0; + + while (VECTOR_SIZE(pgp->paths) > 0) { + + pp = VECTOR_SLOT(pgp->paths, 0); + + if (sysfs_get_host_adapter_name(pp, adapter_name1)) + goto out; + /* create a new host adapter group + */ + agp = alloc_adaptergroup(); + if (!agp) + goto out; + agp->pgp = pgp; + + strncpy(agp->adapter_name, adapter_name1, SLOT_NAME_SIZE); + store_adaptergroup(adapters, agp); + + /* create a new host port group + */ + hgp = alloc_hostgroup(); + if (!hgp) + goto out; + if (store_hostgroup(agp->host_groups, hgp)) + goto out; + + hgp->host_no = pp->sg_id.host_no; + agp->num_hosts++; + if (store_path(hgp->paths, pp)) + goto out; + + hgp->num_paths++; + /* delete path from path group + */ + vector_del_slot(pgp->paths, 0); + + /* add all paths belonging to same host adapter + */ + vector_foreach_slot(pgp->paths, pp1, i) { + if (sysfs_get_host_adapter_name(pp1, adapter_name2)) + goto out; + if (strcmp(adapter_name1, adapter_name2) == 0) { + found_hostgroup = 0; + vector_foreach_slot(agp->host_groups, hgp, j) { + if (hgp->host_no == pp1->sg_id.host_no) { + if (store_path(hgp->paths, pp1)) + goto out; + hgp->num_paths++; + found_hostgroup = 1; + break; + } + } + if (!found_hostgroup) { + /* this path belongs to new host port + * within this adapter + */ + hgp = alloc_hostgroup(); + if (!hgp) + goto out; + + if (store_hostgroup(agp->host_groups, hgp)) + goto out; + + agp->num_hosts++; + if (store_path(hgp->paths, pp1)) + goto out; + + hgp->host_no = pp1->sg_id.host_no; + hgp->num_paths++; + } + /* delete paths from original path_group + * as they are added into adapter group now + */ + vector_del_slot(pgp->paths, i); + i--; + } + } + } + return 0; + +out: /* add back paths into pg as re-ordering failed + */ + vector_foreach_slot(adapters, agp, i) { + vector_foreach_slot(agp->host_groups, hgp, j) { + while (VECTOR_SIZE(hgp->paths) > 0) { + pp = VECTOR_SLOT(hgp->paths, 0); + if (store_path(pgp->paths, pp)) + condlog(3, "failed to restore " + "path %s into path group", + pp->dev); + vector_del_slot(hgp->paths, 0); + } + } + } + free_adaptergroup(adapters); + return 1; +} + +/* re-order paths in pg by alternating adapters and host ports + * for optimized selection + */ +int order_paths_in_pg_by_alt_adapters(struct pathgroup *pgp, vector adapters, + int total_paths) +{ + int next_adapter_index = 0; + struct adapter_group *agp; + struct host_group *hgp; + struct path *pp; + + while (total_paths > 0) { + agp = VECTOR_SLOT(adapters, next_adapter_index); + if (!agp) { + condlog(0, "can't get adapter group %d", next_adapter_index); + return 1; + } + + hgp = VECTOR_SLOT(agp->host_groups, agp->next_host_index); + if (!hgp) { + condlog(0, "can't get host group %d of adapter group %d", next_adapter_index, agp->next_host_index); + return 1; + } + + if (!hgp->num_paths) { + agp->next_host_index++; + agp->next_host_index %= agp->num_hosts; + next_adapter_index++; + next_adapter_index %= VECTOR_SIZE(adapters); + continue; + } + + pp = VECTOR_SLOT(hgp->paths, 0); + + if (store_path(pgp->paths, pp)) + return 1; + + total_paths--; + + vector_del_slot(hgp->paths, 0); + + hgp->num_paths--; + + agp->next_host_index++; + agp->next_host_index %= agp->num_hosts; + next_adapter_index++; + next_adapter_index %= VECTOR_SIZE(adapters); + } + + /* all paths are added into path_group + * in crafted child order + */ + return 0; +} + +/* round-robin: order paths in path group to alternate + * between all host adapters + */ +int rr_optimize_path_order(struct pathgroup *pgp) +{ + vector adapters; + struct path *pp; + int total_paths; + int i; + + total_paths = VECTOR_SIZE(pgp->paths); + vector_foreach_slot(pgp->paths, pp, i) { + if (pp->sg_id.proto_id != SCSI_PROTOCOL_FCP && + pp->sg_id.proto_id != SCSI_PROTOCOL_SAS && + pp->sg_id.proto_id != SCSI_PROTOCOL_ISCSI && + pp->sg_id.proto_id != SCSI_PROTOCOL_SRP) { + /* return success as default path order + * is maintained in path group + */ + return 0; + } + } + adapters = vector_alloc(); + if (!adapters) + return 0; + + /* group paths in path group by host adapters + */ + if (group_by_host_adapter(pgp, adapters)) { + /* already freed adapters */ + condlog(3, "Failed to group paths by adapters"); + return 0; + } + + /* re-order paths in pg to alternate between adapters and host ports + */ + if (order_paths_in_pg_by_alt_adapters(pgp, adapters, total_paths)) { + condlog(3, "Failed to re-order paths in pg by adapters " + "and host ports"); + free_adaptergroup(adapters); + /* return failure as original paths are + * removed form pgp + */ + return 1; + } + + free_adaptergroup(adapters); + return 0; +} + extern int setup_map (struct multipath * mpp, char * params, int params_size) { @@ -76,6 +290,9 @@ setup_map (struct multipath * mpp, char * params, int params_size) select_dev_loss(mpp); select_reservation_key(mpp); select_retain_hwhandler(mpp); + select_deferred_remove(mpp); + select_delay_watch_checks(mpp); + select_delay_wait_checks(mpp); sysfs_set_scsi_tmo(mpp); /* @@ -100,6 +317,22 @@ setup_map (struct multipath * mpp, char * params, int params_size) */ mpp->bestpg = select_path_group(mpp); + /* re-order paths in all path groups in an optimized way + * for round-robin path selectors to get maximum throughput. + */ + if (!strncmp(mpp->selector, "round-robin", 11)) { + vector_foreach_slot(mpp->pg, pgp, i) { + if (VECTOR_SIZE(pgp->paths) <= 2) + continue; + if (rr_optimize_path_order(pgp)) { + condlog(2, "cannot re-order paths for " + "optimization: %s", + mpp->alias); + return 1; + } + } + } + /* * transform the mp->pg vector of vectors of paths * into a mp->params strings to feed the device-mapper @@ -164,6 +397,8 @@ select_action (struct multipath * mpp, vector curmp, int force_reload) cmpp->alias, mpp->alias); strncpy(mpp->alias_old, cmpp->alias, WWID_SIZE); mpp->action = ACT_RENAME; + if (force_reload) + mpp->action = ACT_RENAME2; return; } mpp->action = ACT_CREATE; @@ -187,6 +422,9 @@ select_action (struct multipath * mpp, vector curmp, int force_reload) condlog(2, "%s: unable to rename %s to %s (%s is used by %s)", mpp->wwid, cmpp->alias, mpp->alias, mpp->alias, cmpp_by_name->wwid); + /* reset alias to existing alias */ + FREE(mpp->alias); + mpp->alias = STRDUP(cmpp->alias); mpp->action = ACT_NOTHING; return; } @@ -343,16 +581,22 @@ fail: extern int domap (struct multipath * mpp, char * params) { - int r = 0; + int r = DOMAP_FAIL; /* * last chance to quit before touching the devmaps */ - if (conf->dry_run && mpp->action != ACT_NOTHING) { + if (conf->cmd == CMD_DRY_RUN && mpp->action != ACT_NOTHING) { print_multipath_topology(mpp, conf->verbosity); return DOMAP_DRY; } + if (mpp->action == ACT_CREATE && + dm_map_present(mpp->alias)) { + condlog(3, "%s: map already present", mpp->alias); + mpp->action = ACT_RELOAD; + } + switch (mpp->action) { case ACT_REJECT: case ACT_NOTHING: @@ -375,12 +619,6 @@ domap (struct multipath * mpp, char * params) return DOMAP_RETRY; } - if (dm_map_present(mpp->alias)) { - condlog(3, "%s: map already present", mpp->alias); - lock_multipath(mpp, 0); - break; - } - r = dm_addmap_create(mpp, params); lock_multipath(mpp, 0); @@ -389,24 +627,34 @@ domap (struct multipath * mpp, char * params) case ACT_RELOAD: r = dm_addmap_reload(mpp, params); if (r) - r = dm_simplecmd_noflush(DM_DEVICE_RESUME, mpp->alias); + r = dm_simplecmd_noflush(DM_DEVICE_RESUME, mpp->alias, + 0, MPATH_UDEV_RELOAD_FLAG); break; case ACT_RESIZE: r = dm_addmap_reload(mpp, params); if (r) - r = dm_simplecmd_flush(DM_DEVICE_RESUME, mpp->alias, 1); + r = dm_simplecmd_flush(DM_DEVICE_RESUME, mpp->alias, 1, 0); break; case ACT_RENAME: r = dm_rename(mpp->alias_old, mpp->alias); break; + case ACT_RENAME2: + r = dm_rename(mpp->alias_old, mpp->alias); + if (r) { + r = dm_addmap_reload(mpp, params); + if (r) + r = dm_simplecmd_noflush(DM_DEVICE_RESUME, mpp->alias, 0, MPATH_UDEV_RELOAD_FLAG); + } + break; + default: break; } - if (r) { + if (r == DOMAP_OK) { /* * DM_DEVICE_CREATE, DM_DEVICE_RENAME, or DM_DEVICE_RELOAD * succeeded @@ -428,6 +676,10 @@ domap (struct multipath * mpp, char * params) */ if (mpp->action != ACT_CREATE) mpp->action = ACT_NOTHING; + else { + mpp->wait_for_udev = 1; + mpp->uev_wait_tick = conf->uev_wait_timeout; + } } dm_setgeometry(mpp); return DOMAP_OK; @@ -461,16 +713,15 @@ int check_daemon(void) { int fd; char *reply; - size_t len; int ret = 0; - fd = ux_socket_connect(DEFAULT_SOCKET); + fd = mpath_connect(); if (fd == -1) return 0; - if (send_packet(fd, "show daemon", 12) != 0) + if (send_packet(fd, "show daemon") != 0) goto out; - if (recv_packet(fd, &reply, &len) != 0) + if (recv_packet(fd, &reply, conf->uxsock_timeout) != 0) goto out; if (strstr(reply, "shutdown")) @@ -481,7 +732,7 @@ int check_daemon(void) out_free: FREE(reply); out: - close(fd); + mpath_disconnect(fd); return ret; } @@ -490,7 +741,6 @@ coalesce_paths (struct vectors * vecs, vector newmp, char * refwwid, int force_r { int r = 1; int k, i; - char empty_buff[WWID_SIZE]; char params[PARAMS_SIZE]; struct multipath * mpp; struct path * pp1; @@ -498,7 +748,9 @@ coalesce_paths (struct vectors * vecs, vector newmp, char * refwwid, int force_r vector curmp = vecs->mpvec; vector pathvec = vecs->pathvec; - memset(empty_buff, 0, WWID_SIZE); + /* ignore refwwid if it's empty */ + if (refwwid && !strlen(refwwid)) + refwwid = NULL; if (force_reload) { vector_foreach_slot (pathvec, pp1, k) { @@ -509,7 +761,7 @@ coalesce_paths (struct vectors * vecs, vector newmp, char * refwwid, int force_r /* skip this path for some reason */ /* 1. if path has no unique id or wwid blacklisted */ - if (memcmp(empty_buff, pp1->wwid, WWID_SIZE) == 0 || + if (strlen(pp1->wwid) == 0 || filter_path(conf, pp1) > 0) { orphan_path(pp1, "wwid blacklisted"); continue; @@ -520,8 +772,8 @@ coalesce_paths (struct vectors * vecs, vector newmp, char * refwwid, int force_r continue; /* 3. if path has disappeared */ - if (!pp1->size) { - orphan_path(pp1, "invalid size"); + if (pp1->state == PATH_REMOVED) { + orphan_path(pp1, "path removed"); continue; } @@ -529,6 +781,12 @@ coalesce_paths (struct vectors * vecs, vector newmp, char * refwwid, int force_r if (refwwid && strncmp(pp1->wwid, refwwid, WWID_SIZE)) continue; + /* If find_multipaths was selected check if the path is valid */ + if (!refwwid && !should_multipath(pp1, pathvec)) { + orphan_path(pp1, "only one path"); + continue; + } + /* * at this point, we know we really got a new mp */ @@ -551,10 +809,11 @@ coalesce_paths (struct vectors * vecs, vector newmp, char * refwwid, int force_r if (strcmp(pp1->wwid, pp2->wwid)) continue; - if (!pp2->size) - continue; + if (!mpp->size && pp2->size) + mpp->size = pp2->size; - if (pp2->size != mpp->size) { + if (mpp->size && pp2->size && + pp2->size != mpp->size) { /* * ouch, avoid feeding that to the DM */ @@ -566,7 +825,7 @@ coalesce_paths (struct vectors * vecs, vector newmp, char * refwwid, int force_r if (pp2->priority == PRIO_UNDEF) mpp->action = ACT_REJECT; } - verify_paths(mpp, vecs, NULL); + verify_paths(mpp, vecs); params[0] = '\0'; if (setup_map(mpp, params, PARAMS_SIZE)) { @@ -703,11 +962,14 @@ get_refwwid (char * dev, enum devtypes dev_type, vector pathvec, char **wwid) udev_device_unref(udevice); if (!pp) { if (ret == 1) - condlog(0, "%s can't store path info", - buff); + condlog(0, "%s: can't store path info", + dev); return ret; } } + if (pp->udev && filter_property(conf, pp->udev) > 0) + return 2; + refwwid = pp->wwid; goto out; } @@ -732,9 +994,36 @@ get_refwwid (char * dev, enum devtypes dev_type, vector pathvec, char **wwid) return ret; } } + if (pp->udev && filter_property(conf, pp->udev) > 0) + return 2; + + refwwid = pp->wwid; + goto out; + } + + if (dev_type == DEV_UEVENT) { + struct udev_device *udevice = udev_device_new_from_environment(conf->udev); + + if (!udevice) { + condlog(2, "%s: can't get udev device", dev); + return 1; + } + ret = store_pathinfo(pathvec, conf->hwtable, udevice, + DI_SYSFS | DI_WWID, &pp); + udev_device_unref(udevice); + if (!pp) { + if (ret == 1) + condlog(0, "%s: can't store path info", + dev); + return ret; + } + if (pp->udev && filter_property(conf, pp->udev) > 0) + return 2; + refwwid = pp->wwid; goto out; } + if (dev_type == DEV_DEVMAP) { if (((dm_get_uuid(dev, tmpwwid)) == 0) && (strlen(tmpwwid))) { @@ -765,7 +1054,7 @@ get_refwwid (char * dev, enum devtypes dev_type, vector pathvec, char **wwid) check: if (refwwid && strlen(refwwid)) { if (filter_wwid(conf->blist_wwid, conf->elist_wwid, - refwwid) > 0) + refwwid, NULL) > 0) return 2; } } diff --git a/libmultipath/configure.h b/libmultipath/configure.h index 650f080..c014b55 100644 --- a/libmultipath/configure.h +++ b/libmultipath/configure.h @@ -18,6 +18,7 @@ enum actions { ACT_RENAME, ACT_CREATE, ACT_RESIZE, + ACT_RENAME2, }; #define FLUSH_ONE 1 @@ -29,4 +30,4 @@ int reinstate_paths (struct multipath *mpp); int coalesce_paths (struct vectors *vecs, vector curmp, char * refwwid, int force_reload); int get_refwwid (char * dev, enum devtypes dev_type, vector pathvec, char **wwid); int reload_map(struct vectors *vecs, struct multipath *mpp, int refresh); - +int sysfs_get_host_adapter_name(struct path *pp, char *adapter_name); diff --git a/libmultipath/defaults.h b/libmultipath/defaults.h index b83d9fb..bce8bcc 100644 --- a/libmultipath/defaults.h +++ b/libmultipath/defaults.h @@ -11,22 +11,28 @@ #define DEFAULT_FAILBACK -FAILBACK_MANUAL #define DEFAULT_RR_WEIGHT RR_WEIGHT_NONE #define DEFAULT_NO_PATH_RETRY NO_PATH_RETRY_UNDEF -#define DEFAULT_PGTIMEOUT -PGTIMEOUT_NONE -#define DEFAULT_USER_FRIENDLY_NAMES 0 #define DEFAULT_VERBOSITY 2 -#define DEFAULT_REASSIGN_MAPS 1 +#define DEFAULT_REASSIGN_MAPS 0 +#define DEFAULT_FIND_MULTIPATHS 0 #define DEFAULT_FAST_IO_FAIL 5 #define DEFAULT_RETAIN_HWHANDLER RETAIN_HWHANDLER_OFF #define DEFAULT_DETECT_PRIO DETECT_PRIO_OFF +#define DEFAULT_DEFERRED_REMOVE DEFERRED_REMOVE_OFF +#define DEFAULT_DELAY_CHECKS DELAY_CHECKS_OFF +#define DEFAULT_UEVENT_STACKSIZE 256 +#define DEFAULT_RETRIGGER_DELAY 10 +#define DEFAULT_RETRIGGER_TRIES 3 +#define DEFAULT_UEV_WAIT_TIMEOUT 30 #define DEFAULT_CHECKINT 5 #define MAX_CHECKINT(a) (a << 2) #define MAX_DEV_LOSS_TMO 0x7FFFFFFF -#define DEFAULT_PIDFILE "/var/run/multipathd.pid" +#define DEFAULT_PIDFILE "/" RUN_DIR "/multipathd.pid" #define DEFAULT_SOCKET "/org/kernel/linux/storage/multipathd" #define DEFAULT_CONFIGFILE "/etc/multipath.conf" #define DEFAULT_BINDINGS_FILE "/etc/multipath/bindings" #define DEFAULT_WWIDS_FILE "/etc/multipath/wwids" +#define DEFAULT_CONFIG_DIR "/etc/multipath/conf.d" char * set_default (char * str); diff --git a/libmultipath/devmapper.c b/libmultipath/devmapper.c index 6eb2d96..36c1a20 100644 --- a/libmultipath/devmapper.c +++ b/libmultipath/devmapper.c @@ -32,29 +32,28 @@ #define UUID_PREFIX "mpath-" #define UUID_PREFIX_LEN 6 +#ifdef LIBDM_API_DEFERRED +static int dm_cancel_remove_partmaps(const char * mapname); +#endif + +static int do_foreach_partmaps(const char * mapname, + int (*partmap_func)(const char *, void *), + void *data); + #ifndef LIBDM_API_COOKIE static inline int dm_task_set_cookie(struct dm_task *dmt, uint32_t *c, int a) { return 1; } -void udev_wait(unsigned int c) +void dm_udev_wait(unsigned int c) { } -void udev_set_sync_support(int c) -{ -} -#else -void udev_wait(unsigned int c) +void dm_udev_set_sync_support(int c) { - dm_udev_wait(c); } -void udev_set_sync_support(int c) -{ - dm_udev_set_sync_support(c); -} #endif static void @@ -105,7 +104,11 @@ dm_lib_prereq (void) { char version[64]; int v[3]; -#ifdef LIBDM_API_COOKIE +#if defined(LIBDM_API_DEFERRED) + int minv[3] = {1, 2, 89}; +#elif defined(DM_SUBSYSTEM_UDEV_FLAG0) + int minv[3] = {1, 2, 82}; +#elif defined(LIBDM_API_COOKIE) int minv[3] = {1, 2, 38}; #else int minv[3] = {1, 2, 8}; @@ -201,11 +204,14 @@ dm_prereq (void) return dm_drv_prereq(); } +#define do_deferred(x) ((x) == DEFERRED_REMOVE_ON || (x) == DEFERRED_REMOVE_IN_PROGRESS) + static int -dm_simplecmd (int task, const char *name, int no_flush, int need_sync) { +dm_simplecmd (int task, const char *name, int no_flush, int need_sync, uint16_t udev_flags, int deferred_remove) { int r = 0; int udev_wait_flag = (need_sync && (task == DM_DEVICE_RESUME || task == DM_DEVICE_REMOVE)); + uint32_t cookie = 0; struct dm_task *dmt; if (!(dmt = dm_task_create (task))) @@ -220,24 +226,41 @@ dm_simplecmd (int task, const char *name, int no_flush, int need_sync) { if (no_flush) dm_task_no_flush(dmt); /* for DM_DEVICE_SUSPEND/RESUME */ #endif - - if (udev_wait_flag && !dm_task_set_cookie(dmt, &conf->cookie, (conf->daemon)? DM_UDEV_DISABLE_LIBRARY_FALLBACK : 0)) +#ifdef LIBDM_API_DEFERRED + if (do_deferred(deferred_remove)) + dm_task_deferred_remove(dmt); +#endif + if (udev_wait_flag && !dm_task_set_cookie(dmt, &cookie, ((conf->daemon)? DM_UDEV_DISABLE_LIBRARY_FALLBACK : 0) | udev_flags)) { + dm_udev_complete(cookie); goto out; + } r = dm_task_run (dmt); + if (udev_wait_flag) { + if (!r) + dm_udev_complete(cookie); + else + dm_udev_wait(cookie); + } out: dm_task_destroy (dmt); return r; } extern int -dm_simplecmd_flush (int task, const char *name, int needsync) { - return dm_simplecmd(task, name, 0, needsync); +dm_simplecmd_flush (int task, const char *name, int needsync, uint16_t udev_flags) { + return dm_simplecmd(task, name, 0, needsync, udev_flags, 0); } extern int -dm_simplecmd_noflush (int task, const char *name) { - return dm_simplecmd(task, name, 1, 1); +dm_simplecmd_noflush (int task, const char *name, int needsync, uint16_t udev_flags) { + return dm_simplecmd(task, name, 1, needsync, udev_flags, 0); +} + +static int +dm_device_remove (const char *name, int needsync, int deferred_remove) { + return dm_simplecmd(DM_DEVICE_REMOVE, name, 0, needsync, 0, + deferred_remove); } extern int @@ -246,6 +269,7 @@ dm_addmap (int task, const char *target, struct multipath *mpp, char * params, int r = 0; struct dm_task *dmt; char *prefixed_uuid = NULL; + uint32_t cookie = 0; if (!(dmt = dm_task_create (task))) return 0; @@ -286,10 +310,18 @@ dm_addmap (int task, const char *target, struct multipath *mpp, char * params, dm_task_no_open_count(dmt); if (task == DM_DEVICE_CREATE && - !dm_task_set_cookie(dmt, &conf->cookie, (conf->daemon)? DM_UDEV_DISABLE_LIBRARY_FALLBACK : 0)) + !dm_task_set_cookie(dmt, &cookie, (conf->daemon)? DM_UDEV_DISABLE_LIBRARY_FALLBACK : 0)) { + dm_udev_complete(cookie); goto freeout; + } r = dm_task_run (dmt); + if (task == DM_DEVICE_CREATE) { + if (!r) + dm_udev_complete(cookie); + else + dm_udev_wait(cookie); + } freeout: if (prefixed_uuid) FREE(prefixed_uuid); @@ -307,7 +339,8 @@ dm_addmap_create (struct multipath *mpp, char * params) { for (ro = 0; ro <= 1; ro++) { int err; - if (dm_addmap(DM_DEVICE_CREATE, TGT_MPATH, mpp, params, 1, ro)) + if (dm_addmap(DM_DEVICE_CREATE, TGT_MPATH, + mpp, params, 1, ro)) return 1; /* * DM_DEVICE_CREATE is actually DM_DEV_CREATE + DM_TABLE_LOAD. @@ -318,8 +351,11 @@ dm_addmap_create (struct multipath *mpp, char * params) { condlog(3, "%s: failed to load map (a path might be in use)", mpp->alias); dm_flush_map_nosync(mpp->alias); } - if (err != EROFS) + if (err != EROFS) { + condlog(3, "%s: failed to load map, error %d", + mpp->alias, err); break; + } } return 0; } @@ -549,6 +585,48 @@ out: return r; } +extern int +dm_is_mpath(const char * name) +{ + int r = 0; + struct dm_task *dmt; + struct dm_info info; + uint64_t start, length; + char *target_type = NULL; + char *params; + const char *uuid; + + if (!(dmt = dm_task_create(DM_DEVICE_TABLE))) + return 0; + + if (!dm_task_set_name(dmt, name)) + goto out; + + dm_task_no_open_count(dmt); + + if (!dm_task_run(dmt)) + goto out; + + if (!dm_task_get_info(dmt, &info) || !info.exists) + goto out; + + uuid = dm_task_get_uuid(dmt); + + if (!uuid || strncmp(uuid, UUID_PREFIX, UUID_PREFIX_LEN) != 0) + goto out; + + /* Fetch 1st target */ + dm_get_next_target(dmt, NULL, &start, &length, &target_type, ¶ms); + + if (!target_type || strcmp(target_type, TGT_MPATH) != 0) + goto out; + + r = 1; +out: + dm_task_destroy(dmt); + return r; +} + static int dm_dev_t (const char * mapname, char * dev_t, int len) { @@ -565,10 +643,9 @@ dm_dev_t (const char * mapname, char * dev_t, int len) if (!dm_task_run(dmt)) goto out; - if (!dm_task_get_info(dmt, &info)) + if (!dm_task_get_info(dmt, &info) || !info.exists) goto out; - r = info.open_count; if (snprintf(dev_t, len, "%i:%i", info.major, info.minor) > len) goto out; @@ -597,6 +674,9 @@ dm_get_opencount (const char * mapname) if (!dm_task_get_info(dmt, &info)) goto out; + if (!info.exists) + goto out; + r = info.open_count; out: dm_task_destroy(dmt); @@ -622,6 +702,9 @@ dm_get_major (char * mapname) if (!dm_task_get_info(dmt, &info)) goto out; + if (!info.exists) + goto out; + r = info.major; out: dm_task_destroy(dmt); @@ -647,40 +730,88 @@ dm_get_minor (char * mapname) if (!dm_task_get_info(dmt, &info)) goto out; + if (!info.exists) + goto out; + r = info.minor; out: dm_task_destroy(dmt); return r; } +static int +partmap_in_use(const char *name, void *data) +{ + int part_count, *ret_count = (int *)data; + int open_count = dm_get_opencount(name); + + if (ret_count) + (*ret_count)++; + part_count = 0; + if (open_count) { + if (do_foreach_partmaps(name, partmap_in_use, &part_count)) + return 1; + if (open_count != part_count) { + condlog(2, "%s: map in use", name); + return 1; + } + } + return 0; +} + extern int -_dm_flush_map (const char * mapname, int need_sync) +_dm_flush_map (const char * mapname, int need_sync, int deferred_remove) { int r; - if (!dm_map_present(mapname)) - return 0; - - if (dm_type(mapname, TGT_MPATH) <= 0) + if (!dm_is_mpath(mapname)) return 0; /* nothing to do */ - if (dm_remove_partmaps(mapname, need_sync)) + /* If you aren't doing a deferred remove, make sure that no + * devices are in use */ + if (!do_deferred(deferred_remove) && partmap_in_use(mapname, NULL)) + return 1; + + if (dm_remove_partmaps(mapname, need_sync, deferred_remove)) return 1; - if (dm_get_opencount(mapname)) { + if (!do_deferred(deferred_remove) && dm_get_opencount(mapname)) { condlog(2, "%s: map in use", mapname); return 1; } - r = dm_simplecmd_flush(DM_DEVICE_REMOVE, mapname, need_sync); + r = dm_device_remove(mapname, need_sync, deferred_remove); if (r) { + if (do_deferred(deferred_remove) && dm_map_present(mapname)) { + condlog(4, "multipath map %s remove deferred", + mapname); + return 2; + } condlog(4, "multipath map %s removed", mapname); return 0; } return 1; } +#ifdef LIBDM_API_DEFERRED + +int +dm_flush_map_nopaths(const char * mapname, int deferred_remove) +{ + return _dm_flush_map(mapname, 1, deferred_remove); +} + +#else + +int +dm_flush_map_nopaths(const char * mapname, int deferred_remove) +{ + return _dm_flush_map(mapname, 1, 0); +} + +#endif + extern int dm_suspend_and_flush_map (const char * mapname) { @@ -688,10 +819,7 @@ dm_suspend_and_flush_map (const char * mapname) unsigned long long mapsize; char params[PARAMS_SIZE] = {0}; - if (!dm_map_present(mapname)) - return 0; - - if (dm_type(mapname, TGT_MPATH) <= 0) + if (!dm_is_mpath(mapname)) return 0; /* nothing to do */ if (!dm_get_map(mapname, &mapsize, params)) { @@ -705,14 +833,14 @@ dm_suspend_and_flush_map (const char * mapname) if (s) queue_if_no_path = 0; else - s = dm_simplecmd_flush(DM_DEVICE_SUSPEND, mapname, 0); + s = dm_simplecmd_flush(DM_DEVICE_SUSPEND, mapname, 1, 0); if (!dm_flush_map(mapname)) { condlog(4, "multipath map %s removed", mapname); return 0; } condlog(2, "failed to remove multipath map %s", mapname); - dm_simplecmd_noflush(DM_DEVICE_RESUME, mapname); + dm_simplecmd_noflush(DM_DEVICE_RESUME, mapname, 1, 0); if (queue_if_no_path) s = dm_queue_if_no_path((char *)mapname, 1); return 1; @@ -752,7 +880,7 @@ dm_flush_maps (void) } int -dm_message(char * mapname, char * message) +dm_message(const char * mapname, char * message) { int r = 1; struct dm_task *dmt; @@ -852,7 +980,6 @@ dm_get_maps (vector mp) { struct multipath * mpp; int r = 1; - int info; struct dm_task *dmt; struct dm_names *names; unsigned next = 0; @@ -877,9 +1004,7 @@ dm_get_maps (vector mp) } do { - info = dm_type(names->name, TGT_MPATH); - - if (info <= 0) + if (!dm_is_mpath(names->name)) goto next; mpp = alloc_multipath(); @@ -892,13 +1017,11 @@ dm_get_maps (vector mp) if (!mpp->alias) goto out1; - if (info > 0) { - if (dm_get_map(names->name, &mpp->size, NULL)) - goto out1; + if (dm_get_map(names->name, &mpp->size, NULL)) + goto out1; - dm_get_uuid(names->name, mpp->wwid); - dm_get_info(names->name, &mpp->dmi); - } + dm_get_uuid(names->name, mpp->wwid); + dm_get_info(names->name, &mpp->dmi); if (!vector_alloc_slot(mp)) goto out1; @@ -997,8 +1120,10 @@ bad: return NULL; } -int -dm_remove_partmaps (const char * mapname, int need_sync) +static int +do_foreach_partmaps (const char * mapname, + int (*partmap_func)(const char *, void *), + void *data) { struct dm_task *dmt; struct dm_names *names; @@ -1050,26 +1175,8 @@ dm_remove_partmaps (const char * mapname, int need_sync) */ strstr(params, dev_t) ) { - /* - * then it's a kpartx generated partition. - * remove it. - */ - /* - * if the opencount is 0 maybe some other - * partitions depend on it. - */ - if (dm_get_opencount(names->name)) { - dm_remove_partmaps(names->name, need_sync); - if (dm_get_opencount(names->name)) { - condlog(2, "%s: map in use", - names->name); - goto out; - } - } - condlog(4, "partition map %s removed", - names->name); - dm_simplecmd_flush(DM_DEVICE_REMOVE, names->name, - need_sync); + if (partmap_func(names->name, data) != 0) + goto out; } next = names->next; @@ -1082,6 +1189,109 @@ out: return r; } +struct remove_data { + int need_sync; + int deferred_remove; +}; + +static int +remove_partmap(const char *name, void *data) +{ + struct remove_data *rd = (struct remove_data *)data; + + if (dm_get_opencount(name)) { + dm_remove_partmaps(name, rd->need_sync, rd->deferred_remove); + if (!do_deferred(rd->deferred_remove) && + dm_get_opencount(name)) { + condlog(2, "%s: map in use", name); + return 1; + } + } + condlog(4, "partition map %s removed", name); + dm_device_remove(name, rd->need_sync, rd->deferred_remove); + return 0; +} + +int +dm_remove_partmaps (const char * mapname, int need_sync, int deferred_remove) +{ + struct remove_data rd = { need_sync, deferred_remove }; + return do_foreach_partmaps(mapname, remove_partmap, &rd); +} + +#ifdef LIBDM_API_DEFERRED + +static int +cancel_remove_partmap (const char *name, void *unused) +{ + if (dm_get_opencount(name)) + dm_cancel_remove_partmaps(name); + if (dm_message(name, "@cancel_deferred_remove") != 0) + condlog(0, "%s: can't cancel deferred remove: %s", name, + strerror(errno)); + return 0; +} + +static int +dm_get_deferred_remove (char * mapname) +{ + int r = -1; + struct dm_task *dmt; + struct dm_info info; + + if (!(dmt = dm_task_create(DM_DEVICE_INFO))) + return -1; + + if (!dm_task_set_name(dmt, mapname)) + goto out; + + if (!dm_task_run(dmt)) + goto out; + + if (!dm_task_get_info(dmt, &info)) + goto out; + + r = info.deferred_remove; +out: + dm_task_destroy(dmt); + return r; +} + +static int +dm_cancel_remove_partmaps(const char * mapname) { + return do_foreach_partmaps(mapname, cancel_remove_partmap, NULL); +} + +int +dm_cancel_deferred_remove (struct multipath *mpp) +{ + int r = 0; + + if (!dm_get_deferred_remove(mpp->alias)) + return 0; + if (mpp->deferred_remove == DEFERRED_REMOVE_IN_PROGRESS) + mpp->deferred_remove = DEFERRED_REMOVE_ON; + + dm_cancel_remove_partmaps(mpp->alias); + r = dm_message(mpp->alias, "@cancel_deferred_remove"); + if (r) + condlog(0, "%s: can't cancel deferred remove: %s", mpp->alias, + strerror(errno)); + else + condlog(2, "%s: canceled deferred remove", mpp->alias); + return r; +} + +#else + +int +dm_cancel_deferred_remove (struct multipath *mpp) +{ + return 0; +} + +#endif + static struct dm_info * alloc_dminfo (void) { @@ -1131,85 +1341,52 @@ out: return r; } -int -dm_rename_partmaps (char * old, char * new) +struct rename_data { + const char *old; + char *new; + char *delim; +}; + +static int +rename_partmap (const char *name, void *data) { - struct dm_task *dmt; - struct dm_names *names; - unsigned next = 0; char buff[PARAMS_SIZE]; - unsigned long long size; - char dev_t[32]; - int r = 1; - - if (!(dmt = dm_task_create(DM_DEVICE_LIST))) - return 1; - - dm_task_no_open_count(dmt); + int offset; + struct rename_data *rd = (struct rename_data *)data; - if (!dm_task_run(dmt)) - goto out; - - if (!(names = dm_task_get_names(dmt))) - goto out; - - if (!names->dev) { - r = 0; /* this is perfectly valid */ - goto out; - } - - if (dm_dev_t(old, &dev_t[0], 32)) - goto out; - - do { - if ( - /* - * if devmap target is "linear" - */ - (dm_type(names->name, TGT_PART) > 0) && - - /* - * and the multipath mapname and the part mapname start - * the same - */ - !strncmp(names->name, old, strlen(old)) && - - /* - * and we can fetch the map table from the kernel - */ - !dm_get_map(names->name, &size, &buff[0]) && + if (strncmp(name, rd->old, strlen(rd->old)) != 0) + return 0; + for (offset = strlen(rd->old); name[offset] && !(isdigit(name[offset])); offset++); /* do nothing */ + snprintf(buff, PARAMS_SIZE, "%s%s%s", rd->new, rd->delim, + name + offset); + dm_rename(name, buff); + condlog(4, "partition map %s renamed", name); + return 0; +} - /* - * and the table maps over the multipath map - */ - strstr(buff, dev_t) - ) { - /* - * then it's a kpartx generated partition. - * Rename it. - */ - snprintf(buff, PARAMS_SIZE, "%s%s", - new, names->name + strlen(old)); - dm_rename(names->name, buff); - condlog(4, "partition map %s renamed", - names->name); - } +int +dm_rename_partmaps (const char * old, char * new) +{ + struct rename_data rd; - next = names->next; - names = (void *) names + next; - } while (next); + rd.old = old; + rd.new = new; - r = 0; -out: - dm_task_destroy (dmt); - return r; + if (conf->partition_delim) + rd.delim = conf->partition_delim; + if (isdigit(new[strlen(new)-1])) + rd.delim = "p"; + else + rd.delim = ""; + return do_foreach_partmaps(old, rename_partmap, &rd); } int -dm_rename (char * old, char * new) +dm_rename (const char * old, char * new) { int r = 0; struct dm_task *dmt; + uint32_t cookie; if (dm_rename_partmaps(old, new)) return r; @@ -1225,14 +1402,18 @@ dm_rename (char * old, char * new) dm_task_no_open_count(dmt); - if (!dm_task_set_cookie(dmt, &conf->cookie, (conf->daemon)? DM_UDEV_DISABLE_LIBRARY_FALLBACK : 0)) - goto out; - if (!dm_task_run(dmt)) + if (!dm_task_set_cookie(dmt, &cookie, (conf->daemon)? DM_UDEV_DISABLE_LIBRARY_FALLBACK : 0)) goto out; + r = dm_task_run(dmt); + + if (!r) + dm_udev_complete(cookie); + else + dm_udev_wait(cookie); - r = 1; out: dm_task_destroy(dmt); + return r; } @@ -1297,7 +1478,7 @@ int dm_reassign_table(const char *name, char *old, char *new) condlog(3, "%s: failed to reassign targets", name); goto out_reload; } - dm_simplecmd_noflush(DM_DEVICE_RESUME, name); + dm_simplecmd_noflush(DM_DEVICE_RESUME, name, 1, MPATH_UDEV_RELOAD_FLAG); } r = 1; diff --git a/libmultipath/devmapper.h b/libmultipath/devmapper.h index 58cd718..0d27723 100644 --- a/libmultipath/devmapper.h +++ b/libmultipath/devmapper.h @@ -6,20 +6,29 @@ #define TGT_MPATH "multipath" #define TGT_PART "linear" +#ifdef DM_SUBSYSTEM_UDEV_FLAG0 +#define MPATH_UDEV_RELOAD_FLAG DM_SUBSYSTEM_UDEV_FLAG0 +#else +#define MPATH_UDEV_RELOAD_FLAG 0 +#endif + void dm_init(void); int dm_prereq (void); int dm_drv_version (unsigned int * version, char * str); -int dm_simplecmd_flush (int, const char *, int); -int dm_simplecmd_noflush (int, const char *); +int dm_simplecmd_flush (int, const char *, int, uint16_t); +int dm_simplecmd_noflush (int, const char *, int, uint16_t); int dm_addmap_create (struct multipath *mpp, char *params); int dm_addmap_reload (struct multipath *mpp, char *params); int dm_map_present (const char *); int dm_get_map(const char *, unsigned long long *, char *); int dm_get_status(char *, char *); int dm_type(const char *, char *); -int _dm_flush_map (const char *, int); -#define dm_flush_map(mapname) _dm_flush_map(mapname, 1) -#define dm_flush_map_nosync(mapname) _dm_flush_map(mapname, 0) +int dm_is_mpath(const char *); +int _dm_flush_map (const char *, int, int); +int dm_flush_map_nopaths(const char * mapname, int deferred_remove); +#define dm_flush_map(mapname) _dm_flush_map(mapname, 1, 0) +#define dm_flush_map_nosync(mapname) _dm_flush_map(mapname, 0, 0) +int dm_cancel_deferred_remove(struct multipath *mpp); int dm_suspend_and_flush_map(const char * mapname); int dm_flush_maps (void); int dm_fail_path(char * mapname, char * path); @@ -33,15 +42,14 @@ int dm_geteventnr (char *name); int dm_get_major (char *name); int dm_get_minor (char *name); char * dm_mapname(int major, int minor); -int dm_remove_partmaps (const char * mapname, int need_sync); +int dm_remove_partmaps (const char * mapname, int need_sync, + int deferred_remove); int dm_get_uuid(char *name, char *uuid); int dm_get_info (char * mapname, struct dm_info ** dmi); -int dm_rename (char * old, char * new); +int dm_rename (const char * old, char * new); int dm_reassign(const char * mapname); int dm_reassign_table(const char *name, char *old, char *new); int dm_setgeometry(struct multipath *mpp); -void udev_wait(unsigned int c); -void udev_set_sync_support(int c); #define VERSION_GE(v, minv) ( \ (v[0] > minv[0]) || \ diff --git a/libmultipath/dict.c b/libmultipath/dict.c index 9db4725..89d42a1 100644 --- a/libmultipath/dict.c +++ b/libmultipath/dict.c @@ -21,243 +21,661 @@ #include "prio.h" #include "errno.h" #include +#include -/* - * default block handlers - */ static int -polling_interval_handler(vector strvec) +set_int(vector strvec, void *ptr) { + int *int_ptr = (int *)ptr; char * buff; buff = VECTOR_SLOT(strvec, 1); - conf->checkint = atoi(buff); - conf->max_checkint = MAX_CHECKINT(conf->checkint); + *int_ptr = atoi(buff); return 0; } static int -def_fast_io_fail_handler(vector strvec) +set_str(vector strvec, void *ptr) { - char * buff; + char **str_ptr = (char **)ptr; - buff = set_value(strvec); - if (strlen(buff) == 3 && !strcmp(buff, "off")) - conf->fast_io_fail = MP_FAST_IO_FAIL_OFF; - else if (sscanf(buff, "%d", &conf->fast_io_fail) != 1 || - conf->fast_io_fail < MP_FAST_IO_FAIL_ZERO) - conf->fast_io_fail = MP_FAST_IO_FAIL_UNSET; - else if (conf->fast_io_fail == 0) - conf->fast_io_fail = MP_FAST_IO_FAIL_ZERO; + if (*str_ptr) + FREE(*str_ptr); + *str_ptr = set_value(strvec); + + if (!*str_ptr) + return 1; - FREE(buff); return 0; } static int -def_dev_loss_handler(vector strvec) +set_yes_no(vector strvec, void *ptr) { char * buff; + int *int_ptr = (int *)ptr; buff = set_value(strvec); if (!buff) return 1; - if (strlen(buff) == 8 && !strcmp(buff, "infinity")) - conf->dev_loss = MAX_DEV_LOSS_TMO; - else if (sscanf(buff, "%u", &conf->dev_loss) != 1) - conf->dev_loss = 0; + if (strcmp(buff, "yes") == 0 || strcmp(buff, "1") == 0) + *int_ptr = YN_YES; + else + *int_ptr = YN_NO; FREE(buff); return 0; } static int -verbosity_handler(vector strvec) +set_yes_no_undef(vector strvec, void *ptr) { char * buff; + int *int_ptr = (int *)ptr; - buff = VECTOR_SLOT(strvec, 1); - conf->verbosity = atoi(buff); + buff = set_value(strvec); + if (!buff) + return 1; + if (strcmp(buff, "no") == 0 || strcmp(buff, "0") == 0) + *int_ptr = YNU_NO; + else if (strcmp(buff, "yes") == 0 || strcmp(buff, "1") == 0) + *int_ptr = YNU_YES; + else + *int_ptr = YNU_UNDEF; + + FREE(buff); return 0; } static int -max_polling_interval_handler(vector strvec) +print_int (char *buff, int len, void *ptr) { - char *buff; - - buff = VECTOR_SLOT(strvec, 1); - conf->max_checkint = atoi(buff); - - return 0; + int *int_ptr = (int *)ptr; + return snprintf(buff, len, "%i", *int_ptr); } static int -reassign_maps_handler(vector strvec) +print_nonzero (char *buff, int len, void *ptr) { - char * buff; + int *int_ptr = (int *)ptr; + if (!*int_ptr) + return 0; + return snprintf(buff, len, "%i", *int_ptr); +} - buff = set_value(strvec); - if (!strcmp(buff, "yes")) - conf->reassign_maps = 1; - else if (!strcmp(buff, "no")) - conf->reassign_maps = 0; - else - return 1; +static int +print_str (char *buff, int len, void *ptr) +{ + char **str_ptr = (char **)ptr; + if (!*str_ptr) + return 0; + return snprintf(buff, len, "\"%s\"", *str_ptr); +} - return 0; +static int +print_yes_no (char *buff, int len, void *ptr) +{ + int *int_ptr = (int *)ptr; + return snprintf(buff, len, "\"%s\"", + (*int_ptr == YN_NO)? "no" : "yes"); } static int -multipath_dir_handler(vector strvec) +print_yes_no_undef (char *buff, int len, void *ptr) { - conf->multipath_dir = set_value(strvec); + int *int_ptr = (int *)ptr; + if (!*int_ptr) + return 0; + return snprintf(buff, len, "\"%s\"", + (*int_ptr == YNU_NO)? "no" : "yes"); +} - if (!conf->multipath_dir) - return 1; +#define declare_def_handler(option, function) \ +static int \ +def_ ## option ## _handler (vector strvec) \ +{ \ + return function (strvec, &conf->option); \ +} + +#define declare_def_snprint(option, function) \ +static int \ +snprint_def_ ## option (char * buff, int len, void * data) \ +{ \ + return function (buff, len, &conf->option); \ +} + +#define declare_def_snprint_defint(option, function, value) \ +static int \ +snprint_def_ ## option (char * buff, int len, void * data) \ +{ \ + int i = value; \ + if (!conf->option) \ + return function (buff, len, &i); \ + return function (buff, len, &conf->option); \ +} +#define declare_def_snprint_defstr(option, function, value) \ +static int \ +snprint_def_ ## option (char * buff, int len, void * data) \ +{ \ + char *s = value; \ + if (!conf->option) \ + return function (buff, len, &s); \ + return function (buff, len, &conf->option); \ +} + +#define declare_hw_handler(option, function) \ +static int \ +hw_ ## option ## _handler (vector strvec) \ +{ \ + struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable); \ + if (!hwe) \ + return 1; \ + return function (strvec, &hwe->option); \ +} + +#define declare_hw_snprint(option, function) \ +static int \ +snprint_hw_ ## option (char * buff, int len, void * data) \ +{ \ + struct hwentry * hwe = (struct hwentry *)data; \ + return function (buff, len, &hwe->option); \ +} + +#define declare_ovr_handler(option, function) \ +static int \ +ovr_ ## option ## _handler (vector strvec) \ +{ \ + if (!conf->overrides) \ + return 1; \ + return function (strvec, &conf->overrides->option); \ +} + +#define declare_ovr_snprint(option, function) \ +static int \ +snprint_ovr_ ## option (char * buff, int len, void * data) \ +{ \ + return function (buff, len, &conf->overrides->option); \ +} + +#define declare_mp_handler(option, function) \ +static int \ +mp_ ## option ## _handler (vector strvec) \ +{ \ + struct mpentry * mpe = VECTOR_LAST_SLOT(conf->mptable); \ + if (!mpe) \ + return 1; \ + return function (strvec, &mpe->option); \ +} + +#define declare_mp_snprint(option, function) \ +static int \ +snprint_mp_ ## option (char * buff, int len, void * data) \ +{ \ + struct mpentry * mpe = (struct mpentry *)data; \ + return function (buff, len, &mpe->option); \ +} + +declare_def_handler(checkint, set_int) +declare_def_snprint(checkint, print_int) + +declare_def_handler(max_checkint, set_int) +declare_def_snprint(max_checkint, print_int) + +declare_def_handler(verbosity, set_int) +declare_def_snprint(verbosity, print_int) + +declare_def_handler(reassign_maps, set_yes_no) +declare_def_snprint(reassign_maps, print_yes_no) + +declare_def_handler(multipath_dir, set_str) +declare_def_snprint(multipath_dir, print_str) + +declare_def_handler(partition_delim, set_str) +declare_def_snprint(partition_delim, print_str) + +declare_def_handler(find_multipaths, set_yes_no) +declare_def_snprint(find_multipaths, print_yes_no) + +declare_def_handler(selector, set_str) +declare_def_snprint_defstr(selector, print_str, DEFAULT_SELECTOR) +declare_hw_handler(selector, set_str) +declare_hw_snprint(selector, print_str) +declare_ovr_handler(selector, set_str) +declare_ovr_snprint(selector, print_str) +declare_mp_handler(selector, set_str) +declare_mp_snprint(selector, print_str) + +declare_def_handler(uid_attribute, set_str) +declare_def_snprint_defstr(uid_attribute, print_str, DEFAULT_UID_ATTRIBUTE) +declare_ovr_handler(uid_attribute, set_str) +declare_ovr_snprint(uid_attribute, print_str) +declare_hw_handler(uid_attribute, set_str) +declare_hw_snprint(uid_attribute, print_str) + +declare_def_handler(getuid, set_str) +declare_def_snprint(getuid, print_str) +declare_ovr_handler(getuid, set_str) +declare_ovr_snprint(getuid, print_str) +declare_hw_handler(getuid, set_str) +declare_hw_snprint(getuid, print_str) + +declare_def_handler(prio_name, set_str) +declare_def_snprint_defstr(prio_name, print_str, DEFAULT_PRIO) +declare_ovr_handler(prio_name, set_str) +declare_ovr_snprint(prio_name, print_str) +declare_hw_handler(prio_name, set_str) +declare_hw_snprint(prio_name, print_str) +declare_mp_handler(prio_name, set_str) +declare_mp_snprint(prio_name, print_str) + +declare_def_handler(alias_prefix, set_str) +declare_def_snprint_defstr(alias_prefix, print_str, DEFAULT_ALIAS_PREFIX) +declare_ovr_handler(alias_prefix, set_str) +declare_ovr_snprint(alias_prefix, print_str) +declare_hw_handler(alias_prefix, set_str) +declare_hw_snprint(alias_prefix, print_str) + +declare_def_handler(prio_args, set_str) +declare_def_snprint_defstr(prio_args, print_str, DEFAULT_PRIO_ARGS) +declare_ovr_handler(prio_args, set_str) +declare_ovr_snprint(prio_args, print_str) +declare_hw_handler(prio_args, set_str) +declare_hw_snprint(prio_args, print_str) +declare_mp_handler(prio_args, set_str) +declare_mp_snprint(prio_args, print_str) + +declare_def_handler(features, set_str) +declare_def_snprint_defstr(features, print_str, DEFAULT_FEATURES) +declare_ovr_handler(features, set_str) +declare_ovr_snprint(features, print_str) +declare_hw_handler(features, set_str) +declare_hw_snprint(features, print_str) +declare_mp_handler(features, set_str) +declare_mp_snprint(features, print_str) + +declare_def_handler(checker_name, set_str) +declare_def_snprint_defstr(checker_name, print_str, DEFAULT_CHECKER) +declare_ovr_handler(checker_name, set_str) +declare_ovr_snprint(checker_name, print_str) +declare_hw_handler(checker_name, set_str) +declare_hw_snprint(checker_name, print_str) + +declare_def_handler(minio, set_int) +declare_def_snprint_defint(minio, print_int, DEFAULT_MINIO) +declare_ovr_handler(minio, set_int) +declare_ovr_snprint(minio, print_nonzero) +declare_hw_handler(minio, set_int) +declare_hw_snprint(minio, print_nonzero) +declare_mp_handler(minio, set_int) +declare_mp_snprint(minio, print_nonzero) + +declare_def_handler(minio_rq, set_int) +declare_def_snprint_defint(minio_rq, print_int, DEFAULT_MINIO_RQ) +declare_ovr_handler(minio_rq, set_int) +declare_ovr_snprint(minio_rq, print_nonzero) +declare_hw_handler(minio_rq, set_int) +declare_hw_snprint(minio_rq, print_nonzero) +declare_mp_handler(minio_rq, set_int) +declare_mp_snprint(minio_rq, print_nonzero) + +declare_def_handler(queue_without_daemon, set_yes_no) +static int +snprint_def_queue_without_daemon (char * buff, int len, void * data) +{ + switch (conf->queue_without_daemon) { + case QUE_NO_DAEMON_OFF: + return snprintf(buff, len, "\"no\""); + case QUE_NO_DAEMON_ON: + return snprintf(buff, len, "\"yes\""); + case QUE_NO_DAEMON_FORCE: + return snprintf(buff, len, "\"forced\""); + } return 0; } +declare_def_handler(checker_timeout, set_int) +declare_def_snprint(checker_timeout, print_nonzero) + +declare_def_handler(flush_on_last_del, set_yes_no_undef) +declare_def_snprint_defint(flush_on_last_del, print_yes_no_undef, YNU_NO) +declare_ovr_handler(flush_on_last_del, set_yes_no_undef) +declare_ovr_snprint(flush_on_last_del, print_yes_no_undef) +declare_hw_handler(flush_on_last_del, set_yes_no_undef) +declare_hw_snprint(flush_on_last_del, print_yes_no_undef) +declare_mp_handler(flush_on_last_del, set_yes_no_undef) +declare_mp_snprint(flush_on_last_del, print_yes_no_undef) + +declare_def_handler(user_friendly_names, set_yes_no_undef) +declare_def_snprint_defint(user_friendly_names, print_yes_no_undef, YNU_NO) +declare_ovr_handler(user_friendly_names, set_yes_no_undef) +declare_ovr_snprint(user_friendly_names, print_yes_no_undef) +declare_hw_handler(user_friendly_names, set_yes_no_undef) +declare_hw_snprint(user_friendly_names, print_yes_no_undef) +declare_mp_handler(user_friendly_names, set_yes_no_undef) +declare_mp_snprint(user_friendly_names, print_yes_no_undef) + +declare_def_handler(bindings_file, set_str) +declare_def_snprint(bindings_file, print_str) + +declare_def_handler(wwids_file, set_str) +declare_def_snprint(wwids_file, print_str) + +declare_def_handler(retain_hwhandler, set_yes_no_undef) +declare_def_snprint_defint(retain_hwhandler, print_yes_no_undef, YNU_NO) +declare_ovr_handler(retain_hwhandler, set_yes_no_undef) +declare_ovr_snprint(retain_hwhandler, print_yes_no_undef) +declare_hw_handler(retain_hwhandler, set_yes_no_undef) +declare_hw_snprint(retain_hwhandler, print_yes_no_undef) + +declare_def_handler(detect_prio, set_yes_no_undef) +declare_def_snprint_defint(detect_prio, print_yes_no_undef, YNU_NO) +declare_ovr_handler(detect_prio, set_yes_no_undef) +declare_ovr_snprint(detect_prio, print_yes_no_undef) +declare_hw_handler(detect_prio, set_yes_no_undef) +declare_hw_snprint(detect_prio, print_yes_no_undef) + +declare_def_handler(force_sync, set_yes_no) +declare_def_snprint(force_sync, print_yes_no) + +declare_def_handler(deferred_remove, set_yes_no_undef) +declare_def_snprint_defint(deferred_remove, print_yes_no_undef, YNU_NO) +declare_ovr_handler(deferred_remove, set_yes_no_undef) +declare_ovr_snprint(deferred_remove, print_yes_no_undef) +declare_hw_handler(deferred_remove, set_yes_no_undef) +declare_hw_snprint(deferred_remove, print_yes_no_undef) +declare_mp_handler(deferred_remove, set_yes_no_undef) +declare_mp_snprint(deferred_remove, print_yes_no_undef) + +declare_def_handler(retrigger_tries, set_int) +declare_def_snprint(retrigger_tries, print_int) + +declare_def_handler(retrigger_delay, set_int) +declare_def_snprint(retrigger_delay, print_int) + +declare_def_handler(uev_wait_timeout, set_int) +declare_def_snprint(uev_wait_timeout, print_int) + static int -def_selector_handler(vector strvec) +def_config_dir_handler(vector strvec) { - conf->selector = set_value(strvec); + /* this is only valid in the main config file */ + if (conf->processed_main_config) + return 0; + return set_str(strvec, &conf->config_dir); +} +declare_def_snprint(config_dir, print_str) - if (!conf->selector) - return 1; +#define declare_def_attr_handler(option, function) \ +static int \ +def_ ## option ## _handler (vector strvec) \ +{ \ + return function (strvec, &conf->option, &conf->attribute_flags);\ +} - return 0; +#define declare_def_attr_snprint(option, function) \ +static int \ +snprint_def_ ## option (char * buff, int len, void * data) \ +{ \ + return function (buff, len, &conf->option, \ + &conf->attribute_flags); \ +} + +#define declare_mp_attr_handler(option, function) \ +static int \ +mp_ ## option ## _handler (vector strvec) \ +{ \ + struct mpentry * mpe = VECTOR_LAST_SLOT(conf->mptable); \ + if (!mpe) \ + return 1; \ + return function (strvec, &mpe->option, &mpe->attribute_flags); \ +} + +#define declare_mp_attr_snprint(option, function) \ +static int \ +snprint_mp_ ## option (char * buff, int len, void * data) \ +{ \ + struct mpentry * mpe = (struct mpentry *)data; \ + return function (buff, len, &mpe->option, \ + &mpe->attribute_flags); \ } static int -def_pgpolicy_handler(vector strvec) +set_mode(vector strvec, void *ptr, int *flags) { - char * buff; + mode_t mode; + mode_t *mode_ptr = (mode_t *)ptr; + char *buff; buff = set_value(strvec); if (!buff) return 1; - conf->pgpolicy = get_pgpolicy_id(buff); - FREE(buff); + if (sscanf(buff, "%o", &mode) == 1 && mode <= 0777) { + *flags |= (1 << ATTR_MODE); + *mode_ptr = mode; + } + FREE(buff); return 0; } static int -def_uid_attribute_handler(vector strvec) +set_uid(vector strvec, void *ptr, int *flags) { - conf->uid_attribute = set_value(strvec); + uid_t uid; + uid_t *uid_ptr = (uid_t *)ptr; + char *buff; + char passwd_buf[1024]; + struct passwd info, *found; - if (!conf->uid_attribute) + buff = set_value(strvec); + if (!buff) return 1; + if (getpwnam_r(buff, &info, passwd_buf, 1024, &found) == 0 && found) { + *flags |= (1 << ATTR_UID); + *uid_ptr = info.pw_uid; + } + else if (sscanf(buff, "%u", &uid) == 1){ + *flags |= (1 << ATTR_UID); + *uid_ptr = uid; + } + FREE(buff); return 0; } static int -def_getuid_callout_handler(vector strvec) +set_gid(vector strvec, void *ptr, int *flags) { - conf->getuid = set_value(strvec); + gid_t gid; + gid_t *gid_ptr = (gid_t *)ptr; + char *buff; + char passwd_buf[1024]; + struct passwd info, *found; - if (!conf->getuid) + buff = set_value(strvec); + if (!buff) return 1; + if (getpwnam_r(buff, &info, passwd_buf, 1024, &found) == 0 && found) { + *flags |= (1 << ATTR_GID); + *gid_ptr = info.pw_gid; + } + else if (sscanf(buff, "%u", &gid) == 1){ + *flags |= (1 << ATTR_GID); + *gid_ptr = gid; + } + FREE(buff); return 0; } static int -def_prio_handler(vector strvec) +print_mode(char * buff, int len, void *ptr, int *flags) { - conf->prio_name = set_value(strvec); - - if (!conf->prio_name) - return 1; - - return 0; + mode_t *mode_ptr = (mode_t *)ptr; + if ((*flags & (1 << ATTR_MODE)) == 0) + return 0; + return snprintf(buff, len, "0%o", *mode_ptr); } static int -def_alias_prefix_handler(vector strvec) +print_uid(char * buff, int len, void *ptr, int *flags) { - conf->alias_prefix = set_value(strvec); - - if (!conf->alias_prefix) - return 1; - - return 0; + uid_t *uid_ptr = (uid_t *)ptr; + if ((*flags & (1 << ATTR_UID)) == 0) + return 0; + return snprintf(buff, len, "0%o", *uid_ptr); } static int -def_prio_args_handler(vector strvec) +print_gid(char * buff, int len, void *ptr, int *flags) { - conf->prio_args = set_value(strvec); + gid_t *gid_ptr = (gid_t *)ptr; + if ((*flags & (1 << ATTR_GID)) == 0) + return 0; + return snprintf(buff, len, "0%o", *gid_ptr); +} - if (!conf->prio_args) - return 1; +declare_def_attr_handler(mode, set_mode) +declare_def_attr_snprint(mode, print_mode) +declare_mp_attr_handler(mode, set_mode) +declare_mp_attr_snprint(mode, print_mode) - return 0; -} +declare_def_attr_handler(uid, set_uid) +declare_def_attr_snprint(uid, print_uid) +declare_mp_attr_handler(uid, set_uid) +declare_mp_attr_snprint(uid, print_uid) + +declare_def_attr_handler(gid, set_gid) +declare_def_attr_snprint(gid, print_gid) +declare_mp_attr_handler(gid, set_gid) +declare_mp_attr_snprint(gid, print_gid) static int -def_features_handler(vector strvec) +set_fast_io_fail(vector strvec, void *ptr) { - conf->features = set_value(strvec); + char * buff; + int *int_ptr = (int *)ptr; - if (!conf->features) + buff = set_value(strvec); + if (!buff) return 1; + if (strcmp(buff, "off") == 0) + *int_ptr = MP_FAST_IO_FAIL_OFF; + else if (sscanf(buff, "%d", int_ptr) != 1 || + *int_ptr < MP_FAST_IO_FAIL_ZERO) + *int_ptr = MP_FAST_IO_FAIL_UNSET; + else if (*int_ptr == 0) + *int_ptr = MP_FAST_IO_FAIL_ZERO; + + FREE(buff); return 0; } -static int -def_path_checker_handler(vector strvec) +int +print_fast_io_fail(char * buff, int len, void *ptr) { - conf->checker_name = set_value(strvec); - - if (!conf->checker_name) - return 1; + int *int_ptr = (int *)ptr; - return 0; + if (*int_ptr == MP_FAST_IO_FAIL_UNSET) + return 0; + if (*int_ptr == MP_FAST_IO_FAIL_OFF) + return snprintf(buff, len, "\"off\""); + if (*int_ptr == MP_FAST_IO_FAIL_ZERO) + return snprintf(buff, len, "0"); + return snprintf(buff, len, "%d", *int_ptr); } +declare_def_handler(fast_io_fail, set_fast_io_fail) +declare_def_snprint(fast_io_fail, print_fast_io_fail) +declare_ovr_handler(fast_io_fail, set_fast_io_fail) +declare_ovr_snprint(fast_io_fail, print_fast_io_fail) +declare_hw_handler(fast_io_fail, set_fast_io_fail) +declare_hw_snprint(fast_io_fail, print_fast_io_fail) + static int -def_minio_handler(vector strvec) +set_dev_loss(vector strvec, void *ptr) { char * buff; + unsigned int *uint_ptr = (unsigned int *)ptr; buff = set_value(strvec); - if (!buff) return 1; - conf->minio = atoi(buff); - FREE(buff); + if (!strcmp(buff, "infinity")) + *uint_ptr = MAX_DEV_LOSS_TMO; + else if (sscanf(buff, "%u", uint_ptr) != 1) + *uint_ptr = 0; + FREE(buff); return 0; } +int +print_dev_loss(char * buff, int len, void *ptr) +{ + unsigned int *uint_ptr = (unsigned int *)ptr; + + if (!*uint_ptr) + return 0; + if (*uint_ptr >= MAX_DEV_LOSS_TMO) + return snprintf(buff, len, "\"infinity\""); + return snprintf(buff, len, "%u", *uint_ptr); +} + +declare_def_handler(dev_loss, set_dev_loss) +declare_def_snprint(dev_loss, print_dev_loss) +declare_ovr_handler(dev_loss, set_dev_loss) +declare_ovr_snprint(dev_loss, print_dev_loss) +declare_hw_handler(dev_loss, set_dev_loss) +declare_hw_snprint(dev_loss, print_dev_loss) + static int -def_minio_rq_handler(vector strvec) +set_pgpolicy(vector strvec, void *ptr) { char * buff; + int *int_ptr = (int *)ptr; buff = set_value(strvec); - if (!buff) return 1; - conf->minio_rq = atoi(buff); + *int_ptr = get_pgpolicy_id(buff); FREE(buff); return 0; } +int +print_pgpolicy(char * buff, int len, void *ptr) +{ + char str[POLICY_NAME_SIZE]; + int pgpolicy = *(int *)ptr; + + if (!pgpolicy) + return 0; + + get_pgpolicy_name(str, POLICY_NAME_SIZE, pgpolicy); + + return snprintf(buff, len, "\"%s\"", str); +} + +declare_def_handler(pgpolicy, set_pgpolicy) +declare_def_snprint_defint(pgpolicy, print_pgpolicy, DEFAULT_PGPOLICY) +declare_ovr_handler(pgpolicy, set_pgpolicy) +declare_ovr_snprint(pgpolicy, print_pgpolicy) +declare_hw_handler(pgpolicy, set_pgpolicy) +declare_hw_snprint(pgpolicy, print_pgpolicy) +declare_mp_handler(pgpolicy, set_pgpolicy) +declare_mp_snprint(pgpolicy, print_pgpolicy) + int get_sys_max_fds(int *max_fds) { @@ -317,221 +735,170 @@ max_fds_handler(vector strvec) } static int -def_mode_handler(vector strvec) +snprint_max_fds (char * buff, int len, void * data) { - mode_t mode; - char *buff; - - buff = set_value(strvec); - - if (!buff) - return 1; + int r = 0, max_fds; - if (sscanf(buff, "%o", &mode) == 1 && mode <= 0777) { - conf->attribute_flags |= (1 << ATTR_MODE); - conf->mode = mode; - } + if (!conf->max_fds) + return 0; - FREE(buff); - return 0; + r = get_sys_max_fds(&max_fds); + if (!r && conf->max_fds >= max_fds) + return snprintf(buff, len, "\"max\""); + else + return snprintf(buff, len, "%d", conf->max_fds); } static int -def_uid_handler(vector strvec) +set_rr_weight(vector strvec, void *ptr) { - uid_t uid; - char *buff; - char passwd_buf[1024]; - struct passwd info, *found; + int *int_ptr = (int *)ptr; + char * buff; buff = set_value(strvec); + if (!buff) return 1; - if (getpwnam_r(buff, &info, passwd_buf, 1024, &found) == 0 && found) { - conf->attribute_flags |= (1 << ATTR_UID); - conf->uid = info.pw_uid; - } - else if (sscanf(buff, "%u", &uid) == 1){ - conf->attribute_flags |= (1 << ATTR_UID); - conf->uid = uid; - } - - FREE(buff); - return 0; -} -static int -def_gid_handler(vector strvec) -{ - gid_t gid; - char *buff; - char passwd_buf[1024]; - struct passwd info, *found; + if (!strcmp(buff, "priorities")) + *int_ptr = RR_WEIGHT_PRIO; - buff = set_value(strvec); - if (!buff) - return 1; + if (!strcmp(buff, "uniform")) + *int_ptr = RR_WEIGHT_NONE; - if (getpwnam_r(buff, &info, passwd_buf, 1024, &found) == 0 && found) { - conf->attribute_flags |= (1 << ATTR_GID); - conf->gid = info.pw_gid; - } - else if (sscanf(buff, "%u", &gid) == 1){ - conf->attribute_flags |= (1 << ATTR_GID); - conf->gid = gid; - } FREE(buff); + return 0; } -static int -def_weight_handler(vector strvec) +int +print_rr_weight (char * buff, int len, void *ptr) { - char * buff; - - buff = set_value(strvec); - - if (!buff) - return 1; - - if (strlen(buff) == 10 && - !strcmp(buff, "priorities")) - conf->rr_weight = RR_WEIGHT_PRIO; + int *int_ptr = (int *)ptr; - if (strlen(buff) == strlen("uniform") && - !strcmp(buff, "uniform")) - conf->rr_weight = RR_WEIGHT_NONE; - - FREE(buff); + if (!*int_ptr) + return 0; + if (*int_ptr == RR_WEIGHT_PRIO) + return snprintf(buff, len, "\"priorities\""); + if (*int_ptr == RR_WEIGHT_NONE) + return snprintf(buff, len, "\"uniform\""); return 0; } +declare_def_handler(rr_weight, set_rr_weight) +declare_def_snprint_defint(rr_weight, print_rr_weight, RR_WEIGHT_NONE) +declare_ovr_handler(rr_weight, set_rr_weight) +declare_ovr_snprint(rr_weight, print_rr_weight) +declare_hw_handler(rr_weight, set_rr_weight) +declare_hw_snprint(rr_weight, print_rr_weight) +declare_mp_handler(rr_weight, set_rr_weight) +declare_mp_snprint(rr_weight, print_rr_weight) + static int -default_failback_handler(vector strvec) +set_pgfailback(vector strvec, void *ptr) { + int *int_ptr = (int *)ptr; char * buff; buff = set_value(strvec); if (strlen(buff) == 6 && !strcmp(buff, "manual")) - conf->pgfailback = -FAILBACK_MANUAL; + *int_ptr = -FAILBACK_MANUAL; else if (strlen(buff) == 9 && !strcmp(buff, "immediate")) - conf->pgfailback = -FAILBACK_IMMEDIATE; + *int_ptr = -FAILBACK_IMMEDIATE; else if (strlen(buff) == 10 && !strcmp(buff, "followover")) - conf->pgfailback = -FAILBACK_FOLLOWOVER; + *int_ptr = -FAILBACK_FOLLOWOVER; else - conf->pgfailback = atoi(buff); + *int_ptr = atoi(buff); FREE(buff); return 0; } +int +print_pgfailback (char * buff, int len, void *ptr) +{ + int *int_ptr = (int *)ptr; + + switch(*int_ptr) { + case FAILBACK_UNDEF: + return 0; + case -FAILBACK_MANUAL: + return snprintf(buff, len, "\"manual\""); + case -FAILBACK_IMMEDIATE: + return snprintf(buff, len, "\"immediate\""); + case -FAILBACK_FOLLOWOVER: + return snprintf(buff, len, "\"followover\""); + default: + return snprintf(buff, len, "%i", *int_ptr); + } +} + +declare_def_handler(pgfailback, set_pgfailback) +declare_def_snprint_defint(pgfailback, print_pgfailback, DEFAULT_FAILBACK) +declare_ovr_handler(pgfailback, set_pgfailback) +declare_ovr_snprint(pgfailback, print_pgfailback) +declare_hw_handler(pgfailback, set_pgfailback) +declare_hw_snprint(pgfailback, print_pgfailback) +declare_mp_handler(pgfailback, set_pgfailback) +declare_mp_snprint(pgfailback, print_pgfailback) + static int -def_no_path_retry_handler(vector strvec) +set_no_path_retry(vector strvec, void *ptr) { + int *int_ptr = (int *)ptr; char * buff; buff = set_value(strvec); if (!buff) return 1; - if ((strlen(buff) == 4 && !strcmp(buff, "fail")) || - (strlen(buff) == 1 && !strcmp(buff, "0"))) - conf->no_path_retry = NO_PATH_RETRY_FAIL; - else if (strlen(buff) == 5 && !strcmp(buff, "queue")) - conf->no_path_retry = NO_PATH_RETRY_QUEUE; - else if ((conf->no_path_retry = atoi(buff)) < 1) - conf->no_path_retry = NO_PATH_RETRY_UNDEF; + if (!strcmp(buff, "fail") || !strcmp(buff, "0")) + *int_ptr = NO_PATH_RETRY_FAIL; + else if (!strcmp(buff, "queue")) + *int_ptr = NO_PATH_RETRY_QUEUE; + else if ((*int_ptr = atoi(buff)) < 1) + *int_ptr = NO_PATH_RETRY_UNDEF; FREE(buff); return 0; } +int +print_no_path_retry(char * buff, int len, void *ptr) +{ + int *int_ptr = (int *)ptr; + + switch(*int_ptr) { + case NO_PATH_RETRY_UNDEF: + return 0; + case NO_PATH_RETRY_FAIL: + return snprintf(buff, len, "\"fail\""); + case NO_PATH_RETRY_QUEUE: + return snprintf(buff, len, "\"queue\""); + default: + return snprintf(buff, len, "%i", *int_ptr); + } +} + +declare_def_handler(no_path_retry, set_no_path_retry) +declare_def_snprint(no_path_retry, print_no_path_retry) +declare_ovr_handler(no_path_retry, set_no_path_retry) +declare_ovr_snprint(no_path_retry, print_no_path_retry) +declare_hw_handler(no_path_retry, set_no_path_retry) +declare_hw_snprint(no_path_retry, print_no_path_retry) +declare_mp_handler(no_path_retry, set_no_path_retry) +declare_mp_snprint(no_path_retry, print_no_path_retry) + static int -def_queue_without_daemon(vector strvec) +def_log_checker_err_handler(vector strvec) { char * buff; buff = set_value(strvec); - if (!buff) - return 1; - - if (!strncmp(buff, "on", 2) || !strncmp(buff, "yes", 3) || - !strncmp(buff, "1", 1)) - conf->queue_without_daemon = QUE_NO_DAEMON_ON; - else - conf->queue_without_daemon = QUE_NO_DAEMON_OFF; - - free(buff); - return 0; -} - -static int -def_checker_timeout_handler(vector strvec) -{ - unsigned int checker_timeout; - char *buff; - - buff = set_value(strvec); - if (!buff) - return 1; - - if (sscanf(buff, "%u", &checker_timeout) == 1) - conf->checker_timeout = checker_timeout; - else - conf->checker_timeout = 0; - - free(buff); - return 0; -} - -static int -def_pg_timeout_handler(vector strvec) -{ - char * buff; - - buff = set_value(strvec); - - if (!buff) - return 1; - - /* Deprecated; device-mapper support has been removed */ - - FREE(buff); - return 0; -} - -static int -def_flush_on_last_del_handler(vector strvec) -{ - char * buff; - - buff = set_value(strvec); - if (!buff) - return 1; - - if ((strlen(buff) == 2 && strcmp(buff, "no") == 0) || - (strlen(buff) == 1 && strcmp(buff, "0") == 0)) - conf->flush_on_last_del = FLUSH_DISABLED; - else if ((strlen(buff) == 3 && strcmp(buff, "yes") == 0) || - (strlen(buff) == 1 && strcmp(buff, "1") == 0)) - conf->flush_on_last_del = FLUSH_ENABLED; - else - conf->flush_on_last_del = FLUSH_UNDEF; - - FREE(buff); - return 0; -} - -static int -def_log_checker_err_handler(vector strvec) -{ - char * buff; - - buff = set_value(strvec); - + if (!buff) return 1; @@ -545,8 +912,17 @@ def_log_checker_err_handler(vector strvec) } static int -def_reservation_key_handler(vector strvec) +snprint_def_log_checker_err (char * buff, int len, void * data) +{ + if (conf->log_checker_err == LOG_CHKR_ERR_ONCE) + return snprintf(buff, len, "once"); + return snprintf(buff, len, "always"); +} + +static int +set_reservation_key(vector strvec, void *ptr) { + unsigned char **uchar_ptr = (unsigned char **)ptr; char *buff; char *tbuff; int j, k; @@ -577,13 +953,13 @@ def_reservation_key_handler(vector strvec) return 1; } - if (!conf->reservation_key) - conf->reservation_key = (unsigned char *) malloc(8); + if (!*uchar_ptr) + *uchar_ptr = (unsigned char *) malloc(8); - memset(conf->reservation_key, 0, 8); + memset(*uchar_ptr, 0, 8); for (j = 7; j >= 0; --j) { - conf->reservation_key[j] = (prkey & 0xff); + (*uchar_ptr)[j] = (prkey & 0xff); prkey >>= 8; } @@ -591,94 +967,100 @@ def_reservation_key_handler(vector strvec) return 0; } +int +print_reservation_key(char * buff, int len, void * ptr) +{ + unsigned char **uchar_ptr = (unsigned char **)ptr; + int i; + unsigned char *keyp; + uint64_t prkey = 0; + + if (!*uchar_ptr) + return 0; + keyp = (unsigned char *)(*uchar_ptr); + for (i = 0; i < 8; i++) { + if (i > 0) + prkey <<= 8; + prkey |= *keyp; + keyp++; + } + return snprintf(buff, len, "0x%" PRIx64, prkey); +} + +declare_def_handler(reservation_key, set_reservation_key) +declare_def_snprint(reservation_key, print_reservation_key) +declare_mp_handler(reservation_key, set_reservation_key) +declare_mp_snprint(reservation_key, print_reservation_key) + static int -def_names_handler(vector strvec) +set_delay_checks(vector strvec, void *ptr) { + int *int_ptr = (int *)ptr; char * buff; buff = set_value(strvec); - if (!buff) return 1; - if ((strlen(buff) == 2 && !strcmp(buff, "no")) || - (strlen(buff) == 1 && !strcmp(buff, "0"))) - conf->user_friendly_names = USER_FRIENDLY_NAMES_OFF; - else if ((strlen(buff) == 3 && !strcmp(buff, "yes")) || - (strlen(buff) == 1 && !strcmp(buff, "1"))) - conf->user_friendly_names = USER_FRIENDLY_NAMES_ON; - else - conf->user_friendly_names = USER_FRIENDLY_NAMES_UNDEF; + if (!strcmp(buff, "no") || !strcmp(buff, "0")) + *int_ptr = DELAY_CHECKS_OFF; + else if ((*int_ptr = atoi(buff)) < 1) + *int_ptr = DELAY_CHECKS_UNDEF; FREE(buff); return 0; } -static int -bindings_file_handler(vector strvec) -{ - conf->bindings_file = set_value(strvec); - - if (!conf->bindings_file) - return 1; - - return 0; -} - -static int -wwids_file_handler(vector strvec) +int +print_delay_checks(char * buff, int len, void *ptr) { - conf->wwids_file = set_value(strvec); - - if (!conf->wwids_file) - return 1; + int *int_ptr = (int *)ptr; - return 0; + switch(*int_ptr) { + case DELAY_CHECKS_UNDEF: + return 0; + case DELAY_CHECKS_OFF: + return snprintf(buff, len, "\"off\""); + default: + return snprintf(buff, len, "%i", *int_ptr); + } } -static int -def_retain_hwhandler_handler(vector strvec) -{ - char * buff; - - buff = set_value(strvec); - - if (!buff) - return 1; +declare_def_handler(delay_watch_checks, set_delay_checks) +declare_def_snprint(delay_watch_checks, print_delay_checks) +declare_ovr_handler(delay_watch_checks, set_delay_checks) +declare_ovr_snprint(delay_watch_checks, print_delay_checks) +declare_hw_handler(delay_watch_checks, set_delay_checks) +declare_hw_snprint(delay_watch_checks, print_delay_checks) +declare_mp_handler(delay_watch_checks, set_delay_checks) +declare_mp_snprint(delay_watch_checks, print_delay_checks) - if ((strlen(buff) == 2 && !strcmp(buff, "no")) || - (strlen(buff) == 1 && !strcmp(buff, "0"))) - conf->retain_hwhandler = RETAIN_HWHANDLER_OFF; - else if ((strlen(buff) == 3 && !strcmp(buff, "yes")) || - (strlen(buff) == 1 && !strcmp(buff, "1"))) - conf->retain_hwhandler = RETAIN_HWHANDLER_ON; - else - conf->retain_hwhandler = RETAIN_HWHANDLER_UNDEF; - - FREE(buff); - return 0; -} +declare_def_handler(delay_wait_checks, set_delay_checks) +declare_def_snprint(delay_wait_checks, print_delay_checks) +declare_ovr_handler(delay_wait_checks, set_delay_checks) +declare_ovr_snprint(delay_wait_checks, print_delay_checks) +declare_hw_handler(delay_wait_checks, set_delay_checks) +declare_hw_snprint(delay_wait_checks, print_delay_checks) +declare_mp_handler(delay_wait_checks, set_delay_checks) +declare_mp_snprint(delay_wait_checks, print_delay_checks) static int -def_detect_prio_handler(vector strvec) +def_uxsock_timeout_handler(vector strvec) { - char * buff; + unsigned int uxsock_timeout; + char *buff; buff = set_value(strvec); - if (!buff) return 1; - if ((strlen(buff) == 2 && !strcmp(buff, "no")) || - (strlen(buff) == 1 && !strcmp(buff, "0"))) - conf->detect_prio = DETECT_PRIO_OFF; - else if ((strlen(buff) == 3 && !strcmp(buff, "yes")) || - (strlen(buff) == 1 && !strcmp(buff, "1"))) - conf->detect_prio = DETECT_PRIO_ON; + if (sscanf(buff, "%u", &uxsock_timeout) == 1 && + uxsock_timeout > DEFAULT_REPLY_TIMEOUT) + conf->uxsock_timeout = uxsock_timeout; else - conf->detect_prio = DETECT_PRIO_UNDEF; + conf->uxsock_timeout = DEFAULT_REPLY_TIMEOUT; - FREE(buff); + free(buff); return 0; } @@ -688,10 +1070,14 @@ def_detect_prio_handler(vector strvec) static int blacklist_handler(vector strvec) { - conf->blist_devnode = vector_alloc(); - conf->blist_wwid = vector_alloc(); - conf->blist_device = vector_alloc(); - conf->blist_property = vector_alloc(); + if (!conf->blist_devnode) + conf->blist_devnode = vector_alloc(); + if (!conf->blist_wwid) + conf->blist_wwid = vector_alloc(); + if (!conf->blist_device) + conf->blist_device = vector_alloc(); + if (!conf->blist_property) + conf->blist_property = vector_alloc(); if (!conf->blist_devnode || !conf->blist_wwid || !conf->blist_device || !conf->blist_property) @@ -703,10 +1089,14 @@ blacklist_handler(vector strvec) static int blacklist_exceptions_handler(vector strvec) { - conf->elist_devnode = vector_alloc(); - conf->elist_wwid = vector_alloc(); - conf->elist_device = vector_alloc(); - conf->elist_property = vector_alloc(); + if (!conf->elist_devnode) + conf->elist_devnode = vector_alloc(); + if (!conf->elist_wwid) + conf->elist_wwid = vector_alloc(); + if (!conf->elist_device) + conf->elist_device = vector_alloc(); + if (!conf->elist_property) + conf->elist_property = vector_alloc(); if (!conf->elist_devnode || !conf->elist_wwid || !conf->elist_device || !conf->elist_property) @@ -715,82 +1105,57 @@ blacklist_exceptions_handler(vector strvec) return 0; } -static int -ble_devnode_handler(vector strvec) -{ - char * buff; - - buff = set_value(strvec); - - if (!buff) - return 1; - - return store_ble(conf->blist_devnode, buff, ORIGIN_CONFIG); -} - -static int -ble_except_devnode_handler(vector strvec) -{ - char * buff; - - buff = set_value(strvec); - - if (!buff) - return 1; - - return store_ble(conf->elist_devnode, buff, ORIGIN_CONFIG); +#define declare_ble_handler(option) \ +static int \ +ble_ ## option ## _handler (vector strvec) \ +{ \ + char * buff; \ + \ + if (!conf->option) \ + return 1; \ + \ + buff = set_value(strvec); \ + if (!buff) \ + return 1; \ + \ + return store_ble(conf->option, buff, ORIGIN_CONFIG); \ } -static int -ble_wwid_handler(vector strvec) -{ - char * buff; - - buff = set_value(strvec); - - if (!buff) - return 1; - - return store_ble(conf->blist_wwid, buff, ORIGIN_CONFIG); +#define declare_ble_device_handler(name, option, vend, prod) \ +static int \ +ble_ ## option ## _ ## name ## _handler (vector strvec) \ +{ \ + char * buff; \ + \ + if (!conf->option) \ + return 1; \ + \ + buff = set_value(strvec); \ + if (!buff) \ + return 1; \ + \ + return set_ble_device(conf->option, vend, prod, ORIGIN_CONFIG); \ } -static int -ble_except_wwid_handler(vector strvec) -{ - char * buff; - - buff = set_value(strvec); - - if (!buff) - return 1; - - return store_ble(conf->elist_wwid, buff, ORIGIN_CONFIG); -} +declare_ble_handler(blist_devnode) +declare_ble_handler(elist_devnode) +declare_ble_handler(blist_wwid) +declare_ble_handler(elist_wwid) +declare_ble_handler(blist_property) +declare_ble_handler(elist_property) static int -ble_property_handler(vector strvec) +snprint_def_uxsock_timeout(char * buff, int len, void * data) { - char * buff; - - buff = set_value(strvec); - - if (!buff) - return 1; - - return store_ble(conf->blist_property, buff, ORIGIN_CONFIG); + return snprintf(buff, len, "%u", conf->uxsock_timeout); } static int -ble_except_property_handler(vector strvec) +snprint_ble_simple (char * buff, int len, void * data) { - char * buff; - - buff = set_value(strvec); - - if (!buff) - return 1; + struct blentry * ble = (struct blentry *)data; - return store_ble(conf->elist_property, buff, ORIGIN_CONFIG); + return snprintf(buff, len, "\"%s\"", ble->str); } static int @@ -805,56 +1170,25 @@ ble_except_device_handler(vector strvec) return alloc_ble_device(conf->elist_device); } -static int -ble_vendor_handler(vector strvec) -{ - char * buff; - - buff = set_value(strvec); - - if (!buff) - return 1; - - return set_ble_device(conf->blist_device, buff, NULL, ORIGIN_CONFIG); -} - -static int -ble_except_vendor_handler(vector strvec) -{ - char * buff; - - buff = set_value(strvec); - - if (!buff) - return 1; - - return set_ble_device(conf->elist_device, buff, NULL, ORIGIN_CONFIG); -} +declare_ble_device_handler(vendor, blist_device, buff, NULL) +declare_ble_device_handler(vendor, elist_device, buff, NULL) +declare_ble_device_handler(product, blist_device, NULL, buff) +declare_ble_device_handler(product, elist_device, NULL, buff) static int -ble_product_handler(vector strvec) +snprint_bled_vendor (char * buff, int len, void * data) { - char * buff; - - buff = set_value(strvec); - - if (!buff) - return 1; + struct blentry_device * bled = (struct blentry_device *)data; - return set_ble_device(conf->blist_device, NULL, buff, ORIGIN_CONFIG); + return snprintf(buff, len, "\"%s\"", bled->vendor); } static int -ble_except_product_handler(vector strvec) +snprint_bled_product (char * buff, int len, void * data) { - char * buff; - - buff = set_value(strvec); - - if (!buff) - return 1; + struct blentry_device * bled = (struct blentry_device *)data; - return set_ble_device(conf->elist_device, NULL, buff, ORIGIN_CONFIG); + return snprintf(buff, len, "\"%s\"", bled->product); } /* @@ -891,1985 +1225,180 @@ device_handler(vector strvec) return 0; } -static int -vendor_handler(vector strvec) -{ - struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable); +declare_hw_handler(vendor, set_str) +declare_hw_snprint(vendor, print_str) - if (!hwe) - return 1; +declare_hw_handler(product, set_str) +declare_hw_snprint(product, print_str) - hwe->vendor = set_value(strvec); +declare_hw_handler(revision, set_str) +declare_hw_snprint(revision, print_str) - if (!hwe->vendor) - return 1; +declare_hw_handler(bl_product, set_str) +declare_hw_snprint(bl_product, print_str) - return 0; -} +declare_hw_handler(hwhandler, set_str) +declare_hw_snprint(hwhandler, print_str) +/* + * overrides handlers + */ static int -product_handler(vector strvec) +overrides_handler(vector strvec) { - struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable); - - if (!hwe) - return 1; + if (!conf->overrides) + conf->overrides = alloc_hwe(); - hwe->product = set_value(strvec); - - if (!hwe->product) + if (!conf->overrides) return 1; return 0; } -static int -revision_handler(vector strvec) -{ - struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable); - if (!hwe) - return 1; - hwe->revision = set_value(strvec); +/* + * multipaths block handlers + */ +static int +multipaths_handler(vector strvec) +{ + if (!conf->mptable) + conf->mptable = vector_alloc(); - if (!hwe->revision) + if (!conf->mptable) return 1; return 0; } static int -bl_product_handler(vector strvec) +multipath_handler(vector strvec) { - struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable); + struct mpentry * mpe; - if (!hwe) + mpe = alloc_mpe(); + + if (!mpe) return 1; - hwe->bl_product = set_value(strvec); - if (!hwe->bl_product) + if (!vector_alloc_slot(conf->mptable)) { + free_mpe(mpe); return 1; + } + vector_set_slot(conf->mptable, mpe); return 0; } +declare_mp_handler(wwid, set_str) +declare_mp_snprint(wwid, print_str) + +declare_mp_handler(alias, set_str) +declare_mp_snprint(alias, print_str) + +/* + * deprecated handlers + */ + static int -hw_fast_io_fail_handler(vector strvec) +deprecated_handler(vector strvec) { char * buff; - struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable); buff = set_value(strvec); - if (strlen(buff) == 3 && !strcmp(buff, "off")) - hwe->fast_io_fail = MP_FAST_IO_FAIL_OFF; - else if (sscanf(buff, "%d", &hwe->fast_io_fail) != 1 || - hwe->fast_io_fail < MP_FAST_IO_FAIL_ZERO) - hwe->fast_io_fail = MP_FAST_IO_FAIL_UNSET; - else if (hwe->fast_io_fail == 0) - hwe->fast_io_fail = MP_FAST_IO_FAIL_ZERO; + + if (!buff) + return 1; FREE(buff); return 0; } static int -hw_dev_loss_handler(vector strvec) +snprint_deprecated (char * buff, int len, void * data) { - char * buff; - struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable); - - buff = set_value(strvec); - if (!buff) - return 1; - - if (strlen(buff) == 8 && !strcmp(buff, "infinity")) - hwe->dev_loss = MAX_DEV_LOSS_TMO; - else if (sscanf(buff, "%u", &hwe->dev_loss) != 1) - hwe->dev_loss = 0; - - FREE(buff); - return 0; -} - -static int -hw_pgpolicy_handler(vector strvec) -{ - char * buff; - struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable); - - buff = set_value(strvec); - - if (!buff) - return 1; - - hwe->pgpolicy = get_pgpolicy_id(buff); - FREE(buff); - - return 0; -} - -static int -hw_uid_attribute_handler(vector strvec) -{ - struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable); - - hwe->uid_attribute = set_value(strvec); - - if (!hwe->uid_attribute) - return 1; - - return 0; -} - -static int -hw_getuid_callout_handler(vector strvec) -{ - struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable); - - hwe->getuid = set_value(strvec); - - if (!hwe->getuid) - return 1; - - return 0; -} - -static int -hw_selector_handler(vector strvec) -{ - struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable); - - if (!hwe) - return 1; - - hwe->selector = set_value(strvec); - - if (!hwe->selector) - return 1; - - return 0; -} - -static int -hw_path_checker_handler(vector strvec) -{ - struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable); - - if (!hwe) - return 1; - - hwe->checker_name = set_value(strvec); - - if (!hwe->checker_name) - return 1; - - return 0; -} - -static int -hw_features_handler(vector strvec) -{ - struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable); - - if (!hwe) - return 1; - - hwe->features = set_value(strvec); - - if (!hwe->features) - return 1; - - return 0; -} - -static int -hw_handler_handler(vector strvec) -{ - struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable); - - if (!hwe) - return 1; - - hwe->hwhandler = set_value(strvec); - - if (!hwe->hwhandler) - return 1; - - return 0; -} - -static int -hw_prio_handler(vector strvec) -{ - struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable); - - if (!hwe) - return 1; - - hwe->prio_name = set_value(strvec); - - if (!hwe->prio_name) - return 1; - - return 0; -} - -static int -hw_alias_prefix_handler(vector strvec) -{ - struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable); - - if (!hwe) - return 1; - - hwe->alias_prefix = set_value(strvec); - - if (!hwe->alias_prefix) - return 1; - - return 0; -} - -static int -hw_prio_args_handler(vector strvec) -{ - struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable); - - if (!hwe) - return 1; - - hwe->prio_args = set_value(strvec); - - if (!hwe->prio_args) - return 1; - - return 0; -} - -static int -hw_failback_handler(vector strvec) -{ - struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable); - char * buff; - - if (!hwe) - return 1; - - buff = set_value(strvec); - - if (strlen(buff) == 6 && !strcmp(buff, "manual")) - hwe->pgfailback = -FAILBACK_MANUAL; - else if (strlen(buff) == 9 && !strcmp(buff, "immediate")) - hwe->pgfailback = -FAILBACK_IMMEDIATE; - else if (strlen(buff) == 10 && !strcmp(buff, "followover")) - hwe->pgfailback = -FAILBACK_FOLLOWOVER; - else - hwe->pgfailback = atoi(buff); - - FREE(buff); - - return 0; -} - -static int -hw_weight_handler(vector strvec) -{ - struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable); - char * buff; - - if (!hwe) - return 1; - - buff = set_value(strvec); - - if (!buff) - return 1; - - if (strlen(buff) == 10 && - !strcmp(buff, "priorities")) - hwe->rr_weight = RR_WEIGHT_PRIO; - - if (strlen(buff) == strlen("uniform") && - !strcmp(buff, "uniform")) - hwe->rr_weight = RR_WEIGHT_NONE; - - FREE(buff); - - return 0; -} - -static int -hw_no_path_retry_handler(vector strvec) -{ - struct hwentry *hwe = VECTOR_LAST_SLOT(conf->hwtable); - char *buff; - - if (!hwe) - return 1; - - buff = set_value(strvec); - if (!buff) - return 1; - - if ((strlen(buff) == 4 && !strcmp(buff, "fail")) || - (strlen(buff) == 1 && !strcmp(buff, "0"))) - hwe->no_path_retry = NO_PATH_RETRY_FAIL; - else if (strlen(buff) == 5 && !strcmp(buff, "queue")) - hwe->no_path_retry = NO_PATH_RETRY_QUEUE; - else if ((hwe->no_path_retry = atoi(buff)) < 1) - hwe->no_path_retry = NO_PATH_RETRY_UNDEF; - - FREE(buff); - return 0; -} - -static int -hw_minio_handler(vector strvec) -{ - struct hwentry *hwe = VECTOR_LAST_SLOT(conf->hwtable); - char * buff; - - if (!hwe) - return 1; - - buff = set_value(strvec); - - if (!buff) - return 1; - - hwe->minio = atoi(buff); - FREE(buff); - - return 0; -} - -static int -hw_minio_rq_handler(vector strvec) -{ - struct hwentry *hwe = VECTOR_LAST_SLOT(conf->hwtable); - char * buff; - - if (!hwe) - return 1; - - buff = set_value(strvec); - - if (!buff) - return 1; - - hwe->minio_rq = atoi(buff); - FREE(buff); - - return 0; -} - -static int -hw_pg_timeout_handler(vector strvec) -{ - char *buff; - - buff = set_value(strvec); - - if (!buff) - return 1; - - /* Deprecated; device-mapper support has been removed */ - - FREE(buff); - return 0; -} - -static int -hw_flush_on_last_del_handler(vector strvec) -{ - struct hwentry *hwe = VECTOR_LAST_SLOT(conf->hwtable); - char * buff; - - if (!hwe) - return 1; - - buff = set_value(strvec); - if (!buff) - return 1; - - if ((strlen(buff) == 2 && strcmp(buff, "no") == 0) || - (strlen(buff) == 1 && strcmp(buff, "0") == 0)) - hwe->flush_on_last_del = FLUSH_DISABLED; - else if ((strlen(buff) == 3 && strcmp(buff, "yes") == 0) || - (strlen(buff) == 1 && strcmp(buff, "1") == 0)) - hwe->flush_on_last_del = FLUSH_ENABLED; - else - hwe->flush_on_last_del = FLUSH_UNDEF; - - FREE(buff); - return 0; -} - -static int -hw_names_handler(vector strvec) -{ - struct hwentry *hwe = VECTOR_LAST_SLOT(conf->hwtable); - char * buff; - - if (!hwe) - return 1; - - buff = set_value(strvec); - if (!buff) - return 1; - - if ((strlen(buff) == 2 && strcmp(buff, "no") == 0) || - (strlen(buff) == 1 && strcmp(buff, "0") == 0)) - hwe->user_friendly_names = USER_FRIENDLY_NAMES_OFF; - else if ((strlen(buff) == 3 && strcmp(buff, "yes") == 0) || - (strlen(buff) == 1 && strcmp(buff, "1") == 0)) - hwe->user_friendly_names = USER_FRIENDLY_NAMES_ON; - else - hwe->user_friendly_names = USER_FRIENDLY_NAMES_UNDEF; - - FREE(buff); - return 0; -} - -static int -hw_retain_hwhandler_handler(vector strvec) -{ - struct hwentry *hwe = VECTOR_LAST_SLOT(conf->hwtable); - char * buff; - - if (!hwe) - return 1; - - buff = set_value(strvec); - - if (!buff) - return 1; - - if ((strlen(buff) == 2 && !strcmp(buff, "no")) || - (strlen(buff) == 1 && !strcmp(buff, "0"))) - hwe->retain_hwhandler = RETAIN_HWHANDLER_OFF; - else if ((strlen(buff) == 3 && !strcmp(buff, "yes")) || - (strlen(buff) == 1 && !strcmp(buff, "1"))) - hwe->retain_hwhandler = RETAIN_HWHANDLER_ON; - else - hwe->user_friendly_names = RETAIN_HWHANDLER_UNDEF; - - FREE(buff); - return 0; -} - -static int -hw_detect_prio_handler(vector strvec) -{ - struct hwentry *hwe = VECTOR_LAST_SLOT(conf->hwtable); - char * buff; - - if (!hwe) - return 1; - - buff = set_value(strvec); - - if (!buff) - return 1; - - if ((strlen(buff) == 2 && !strcmp(buff, "no")) || - (strlen(buff) == 1 && !strcmp(buff, "0"))) - hwe->detect_prio = DETECT_PRIO_OFF; - else if ((strlen(buff) == 3 && !strcmp(buff, "yes")) || - (strlen(buff) == 1 && !strcmp(buff, "1"))) - hwe->detect_prio = DETECT_PRIO_ON; - else - hwe->detect_prio = DETECT_PRIO_UNDEF; - - FREE(buff); - return 0; -} - -/* - * multipaths block handlers - */ -static int -multipaths_handler(vector strvec) -{ - conf->mptable = vector_alloc(); - - if (!conf->mptable) - return 1; - - return 0; -} - -static int -multipath_handler(vector strvec) -{ - struct mpentry * mpe; - - mpe = alloc_mpe(); - - if (!mpe) - return 1; - - if (!vector_alloc_slot(conf->mptable)) { - free_mpe(mpe); - return 1; - } - vector_set_slot(conf->mptable, mpe); - - return 0; -} - -static int -wwid_handler(vector strvec) -{ - struct mpentry * mpe = VECTOR_LAST_SLOT(conf->mptable); - - if (!mpe) - return 1; - - mpe->wwid = set_value(strvec); - - if (!mpe->wwid) - return 1; - - return 0; -} - -static int -alias_handler(vector strvec) -{ - struct mpentry * mpe = VECTOR_LAST_SLOT(conf->mptable); - - if (!mpe) - return 1; - - mpe->alias = set_value(strvec); - - if (!mpe->alias) - return 1; - - return 0; -} - -static int -mp_pgpolicy_handler(vector strvec) -{ - char * buff; - struct mpentry * mpe = VECTOR_LAST_SLOT(conf->mptable); - - if (!mpe) - return 1; - - buff = set_value(strvec); - - if (!buff) - return 1; - - mpe->pgpolicy = get_pgpolicy_id(buff); - FREE(buff); - - return 0; -} - -static int -mp_selector_handler(vector strvec) -{ - struct mpentry * mpe = VECTOR_LAST_SLOT(conf->mptable); - - if (!mpe) - return 1; - - mpe->selector = set_value(strvec); - - if (!mpe->selector) - return 1; - - return 0; -} - -static int -mp_failback_handler(vector strvec) -{ - struct mpentry * mpe = VECTOR_LAST_SLOT(conf->mptable); - char * buff; - - if (!mpe) - return 1; - - buff = set_value(strvec); - - if (strlen(buff) == 6 && !strcmp(buff, "manual")) - mpe->pgfailback = -FAILBACK_MANUAL; - else if (strlen(buff) == 9 && !strcmp(buff, "immediate")) - mpe->pgfailback = -FAILBACK_IMMEDIATE; - else if (strlen(buff) == 10 && !strcmp(buff, "followover")) - mpe->pgfailback = -FAILBACK_FOLLOWOVER; - else - mpe->pgfailback = atoi(buff); - - FREE(buff); - - return 0; -} - -static int -mp_mode_handler(vector strvec) -{ - mode_t mode; - struct mpentry *mpe = VECTOR_LAST_SLOT(conf->mptable); - char *buff; - - if (!mpe) - return 1; - - buff = set_value(strvec); - if (!buff) - return 1; - if (sscanf(buff, "%o", &mode) == 1 && mode <= 0777) { - mpe->attribute_flags |= (1 << ATTR_MODE); - mpe->mode = mode; - } - - FREE(buff); - return 0; -} - -static int -mp_uid_handler(vector strvec) -{ - uid_t uid; - char *buff; - char passwd_buf[1024]; - struct passwd info, *found; - - struct mpentry *mpe = VECTOR_LAST_SLOT(conf->mptable); - - if (!mpe) - return 1; - - buff = set_value(strvec); - if (!buff) - return 1; - - if (getpwnam_r(buff, &info, passwd_buf, 1024, &found) == 0 && found) { - mpe->attribute_flags |= (1 << ATTR_UID); - mpe->uid = info.pw_uid; - } - else if (sscanf(buff, "%u", &uid) == 1){ - mpe->attribute_flags |= (1 << ATTR_UID); - mpe->uid = uid; - } - FREE(buff); - return 0; -} - -static int -mp_gid_handler(vector strvec) -{ - gid_t gid; - char *buff; - char passwd_buf[1024]; - struct passwd info, *found; - - struct mpentry *mpe = VECTOR_LAST_SLOT(conf->mptable); - - if (!mpe) - return 1; - - buff = set_value(strvec); - if (!buff) - return 1; - - if (getpwnam_r(buff, &info, passwd_buf, 1024, &found) == 0 && found) { - mpe->attribute_flags |= (1 << ATTR_GID); - mpe->gid = info.pw_gid; - } - else if (sscanf(buff, "%u", &gid) == 1) { - mpe->attribute_flags |= (1 << ATTR_GID); - mpe->gid = gid; - } - FREE(buff); - return 0; -} - -static int -mp_weight_handler(vector strvec) -{ - struct mpentry * mpe = VECTOR_LAST_SLOT(conf->mptable); - char * buff; - - if (!mpe) - return 1; - - buff = set_value(strvec); - - if (!buff) - return 1; - - if (strlen(buff) == 10 && - !strcmp(buff, "priorities")) - mpe->rr_weight = RR_WEIGHT_PRIO; - - if (strlen(buff) == strlen("uniform") && - !strcmp(buff, "uniform")) - mpe->rr_weight = RR_WEIGHT_NONE; - - FREE(buff); - - return 0; -} - -static int -mp_no_path_retry_handler(vector strvec) -{ - struct mpentry *mpe = VECTOR_LAST_SLOT(conf->mptable); - char *buff; - - if (!mpe) - return 1; - - buff = set_value(strvec); - if (!buff) - return 1; - - if ((strlen(buff) == 4 && !strcmp(buff, "fail")) || - (strlen(buff) == 1 && !strcmp(buff, "0"))) - mpe->no_path_retry = NO_PATH_RETRY_FAIL; - else if (strlen(buff) == 5 && !strcmp(buff, "queue")) - mpe->no_path_retry = NO_PATH_RETRY_QUEUE; - else if ((mpe->no_path_retry = atoi(buff)) < 1) - mpe->no_path_retry = NO_PATH_RETRY_UNDEF; - - FREE(buff); - return 0; -} - -static int -mp_minio_handler(vector strvec) -{ - struct mpentry *mpe = VECTOR_LAST_SLOT(conf->mptable); - char * buff; - - if (!mpe) - return 1; - - buff = set_value(strvec); - - if (!buff) - return 1; - - mpe->minio = atoi(buff); - FREE(buff); - - return 0; -} - -static int -mp_minio_rq_handler(vector strvec) -{ - struct mpentry *mpe = VECTOR_LAST_SLOT(conf->mptable); - char * buff; - - if (!mpe) - return 1; - - buff = set_value(strvec); - - if (!buff) - return 1; - - mpe->minio_rq = atoi(buff); - FREE(buff); - - return 0; -} - -static int -mp_pg_timeout_handler(vector strvec) -{ - char *buff; - - buff = set_value(strvec); - - if (!buff) - return 1; - - /* Deprecated; device-mapper support has been removed */ - - FREE(buff); - return 0; -} - -static int -mp_features_handler(vector strvec) -{ - struct mpentry * mpe = VECTOR_LAST_SLOT(conf->mptable); - - if (!mpe) - return 1; - - mpe->features = set_value(strvec); - - if (!mpe->features) - return 1; - - return 0; -} - -static int -mp_flush_on_last_del_handler(vector strvec) -{ - struct mpentry *mpe = VECTOR_LAST_SLOT(conf->mptable); - char * buff; - - if (!mpe) - return 1; - - buff = set_value(strvec); - if (!buff) - return 1; - - if ((strlen(buff) == 2 && strcmp(buff, "no") == 0) || - (strlen(buff) == 1 && strcmp(buff, "0") == 0)) - mpe->flush_on_last_del = FLUSH_DISABLED; - else if ((strlen(buff) == 3 && strcmp(buff, "yes") == 0) || - (strlen(buff) == 1 && strcmp(buff, "1") == 0)) - mpe->flush_on_last_del = FLUSH_ENABLED; - else - mpe->flush_on_last_del = FLUSH_UNDEF; - - FREE(buff); - return 0; -} - -static int -mp_prio_handler(vector strvec) -{ - struct mpentry * mpe = VECTOR_LAST_SLOT(conf->mptable); - - if (!mpe) - return 1; - - mpe->prio_name = set_value(strvec); - - if (!mpe->prio_name) - return 1; - - return 0; -} - -static int -mp_prio_args_handler (vector strvec) -{ - struct mpentry *mpe = VECTOR_LAST_SLOT(conf->mptable); - - if (!mpe) - return 1; - - mpe->prio_args = set_value(strvec); - if (!mpe->prio_args) - return 1; - - return 0; -} - -static int -mp_reservation_key_handler (vector strvec) -{ - char *buff; - char *tbuff; - struct mpentry *mpe = VECTOR_LAST_SLOT(conf->mptable); - - int j, k, len; - uint64_t prkey; - - if (!mpe) - return 1; - - buff = set_value(strvec); - if (!buff) - return 1; - - tbuff = buff; - if (!memcmp(buff, "0x", 2)) - buff = buff + 2; - - len = strlen(buff); - - k = strspn(buff, "0123456789aAbBcCdDeEfF"); - if (len != k) { - FREE(tbuff); - return 1; - } - - if (1 != sscanf (buff, "%" SCNx64 "", &prkey)) - { - FREE(tbuff); - return 1; - } - - if (!mpe->reservation_key) - mpe->reservation_key = (unsigned char *) malloc(8); - - memset(mpe->reservation_key, 0, 8); - - for (j = 7; j >= 0; --j) { - mpe->reservation_key[j] = (prkey & 0xff); - prkey >>= 8; - } - - FREE(tbuff); - return 0; -} - -static int -mp_names_handler(vector strvec) -{ - struct mpentry *mpe = VECTOR_LAST_SLOT(conf->mptable); - char * buff; - - if (!mpe) - return 1; - - buff = set_value(strvec); - if (!buff) - return 1; - - if ((strlen(buff) == 2 && strcmp(buff, "no") == 0) || - (strlen(buff) == 1 && strcmp(buff, "0") == 0)) - mpe->user_friendly_names = USER_FRIENDLY_NAMES_OFF; - else if ((strlen(buff) == 3 && strcmp(buff, "yes") == 0) || - (strlen(buff) == 1 && strcmp(buff, "1") == 0)) - mpe->user_friendly_names = USER_FRIENDLY_NAMES_ON; - else - mpe->user_friendly_names = USER_FRIENDLY_NAMES_UNDEF; - - FREE(buff); - return 0; -} - -/* - * config file keywords printing - */ -static int -snprint_mp_wwid (char * buff, int len, void * data) -{ - struct mpentry * mpe = (struct mpentry *)data; - - return snprintf(buff, len, "%s", mpe->wwid); -} - -static int -snprint_mp_alias (char * buff, int len, void * data) -{ - struct mpentry * mpe = (struct mpentry *)data; - - if (!mpe->alias) - return 0; - - return snprintf(buff, len, "%s", mpe->alias); -} - -static int -snprint_mp_path_grouping_policy (char * buff, int len, void * data) -{ - struct mpentry * mpe = (struct mpentry *)data; - char str[POLICY_NAME_SIZE]; - - if (!mpe->pgpolicy) - return 0; - get_pgpolicy_name(str, POLICY_NAME_SIZE, mpe->pgpolicy); - - return snprintf(buff, len, "\"%s\"", str); -} - -static int -snprint_mp_selector (char * buff, int len, void * data) -{ - struct mpentry * mpe = (struct mpentry *)data; - - if (!mpe->selector) - return 0; - - return snprintf(buff, len, "\"%s\"", mpe->selector); -} - -static int -snprint_mp_failback (char * buff, int len, void * data) -{ - struct mpentry * mpe = (struct mpentry *)data; - - if (mpe->pgfailback == FAILBACK_UNDEF || - mpe->pgfailback == DEFAULT_FAILBACK) - return 0; - - switch(mpe->pgfailback) { - case -FAILBACK_MANUAL: - return snprintf(buff, len, "\"manual\""); - case -FAILBACK_IMMEDIATE: - return snprintf(buff, len, "\"immediate\""); - case -FAILBACK_FOLLOWOVER: - return snprintf(buff, len, "\"followover\""); - default: - return snprintf(buff, len, "%i", mpe->pgfailback); - } - return 0; -} - -static int -snprint_mp_mode(char * buff, int len, void * data) -{ - struct mpentry * mpe = (struct mpentry *)data; - - if ((mpe->attribute_flags & (1 << ATTR_MODE)) == 0) - return 0; - return snprintf(buff, len, "0%o", mpe->mode); -} - -static int -snprint_mp_uid(char * buff, int len, void * data) -{ - struct mpentry * mpe = (struct mpentry *)data; - - if ((mpe->attribute_flags & (1 << ATTR_UID)) == 0) - return 0; - return snprintf(buff, len, "0%o", mpe->uid); -} - -static int -snprint_mp_gid(char * buff, int len, void * data) -{ - struct mpentry * mpe = (struct mpentry *)data; - - if ((mpe->attribute_flags & (1 << ATTR_GID)) == 0) - return 0; - return snprintf(buff, len, "0%o", mpe->gid); -} - -static int -snprint_mp_rr_weight (char * buff, int len, void * data) -{ - struct mpentry * mpe = (struct mpentry *)data; - - if (!mpe->rr_weight) - return 0; - if (mpe->rr_weight == RR_WEIGHT_PRIO) - return snprintf(buff, len, "\"priorities\""); - if (mpe->rr_weight == RR_WEIGHT_NONE) - return snprintf(buff, len, "\"uniform\""); - - return 0; -} - -static int -snprint_mp_no_path_retry (char * buff, int len, void * data) -{ - struct mpentry * mpe = (struct mpentry *)data; - - if (!mpe->no_path_retry) - return 0; - - switch(mpe->no_path_retry) { - case NO_PATH_RETRY_UNDEF: - break; - case NO_PATH_RETRY_FAIL: - return snprintf(buff, len, "\"fail\""); - case NO_PATH_RETRY_QUEUE: - return snprintf(buff, len, "\"queue\""); - default: - return snprintf(buff, len, "%i", - mpe->no_path_retry); - } - return 0; -} - -static int -snprint_mp_rr_min_io (char * buff, int len, void * data) -{ - struct mpentry * mpe = (struct mpentry *)data; - - if (!mpe->minio) - return 0; - - return snprintf(buff, len, "%u", mpe->minio); -} - -static int -snprint_mp_rr_min_io_rq (char * buff, int len, void * data) -{ - struct mpentry * mpe = (struct mpentry *)data; - - if (!mpe->minio_rq) - return 0; - - return snprintf(buff, len, "%u", mpe->minio_rq); -} - -static int -snprint_mp_pg_timeout (char * buff, int len, void * data) -{ - return 0; -} - -static int -snprint_mp_features (char * buff, int len, void * data) -{ - struct mpentry * mpe = (struct mpentry *)data; - - if (!mpe->features) - return 0; - if (strlen(mpe->features) == strlen(conf->features) && - !strcmp(mpe->features, conf->features)) - return 0; - - return snprintf(buff, len, "\"%s\"", mpe->features); -} - -static int -snprint_mp_flush_on_last_del (char * buff, int len, void * data) -{ - struct mpentry * mpe = (struct mpentry *)data; - - switch (mpe->flush_on_last_del) { - case FLUSH_DISABLED: - return snprintf(buff, len, "\"no\""); - case FLUSH_ENABLED: - return snprintf(buff, len, "\"yes\""); - } - return 0; -} - -static int -snprint_mp_prio(char * buff, int len, void * data) -{ - struct mpentry * mpe = (struct mpentry *)data; - - if (!mpe->prio_name) - return 0; - - return snprintf(buff, len, "\"%s\"", mpe->prio_name); -} - -static int -snprint_mp_prio_args(char * buff, int len, void * data) -{ - struct mpentry * mpe = (struct mpentry *)data; - - if (!mpe->prio_args) - return 0; - - return snprintf(buff, len, "\"%s\"", mpe->prio_args); -} - -static int -snprint_mp_reservation_key (char * buff, int len, void * data) -{ - int i; - unsigned char *keyp; - uint64_t prkey = 0; - struct mpentry * mpe = (struct mpentry *)data; - - if (!mpe->reservation_key) - return 0; - keyp = (unsigned char *)mpe->reservation_key; - for (i = 0; i < 8; i++) { - if (i > 0) - prkey <<= 8; - prkey |= *keyp; - keyp++; - } - - return snprintf(buff, len, "0x%" PRIx64, prkey); -} - - static int -snprint_mp_user_friendly_names (char * buff, int len, void * data) -{ - struct mpentry * mpe = (struct mpentry *)data; - - if (mpe->user_friendly_names == USER_FRIENDLY_NAMES_UNDEF) - return 0; - else if (mpe->user_friendly_names == USER_FRIENDLY_NAMES_OFF) - return snprintf(buff, len, "\"no\""); - else - return snprintf(buff, len, "\"yes\""); -} - -static int -snprint_hw_fast_io_fail(char * buff, int len, void * data) -{ - struct hwentry * hwe = (struct hwentry *)data; - if (hwe->fast_io_fail == MP_FAST_IO_FAIL_UNSET) - return 0; - if (hwe->fast_io_fail == conf->fast_io_fail) - return 0; - if (hwe->fast_io_fail == MP_FAST_IO_FAIL_OFF) - return snprintf(buff, len, "\"off\""); - if (hwe->fast_io_fail == MP_FAST_IO_FAIL_ZERO) - return snprintf(buff, len, "0"); - return snprintf(buff, len, "%d", hwe->fast_io_fail); -} - -static int -snprint_hw_dev_loss(char * buff, int len, void * data) -{ - struct hwentry * hwe = (struct hwentry *)data; - if (!hwe->dev_loss) - return 0; - if (hwe->dev_loss == conf->dev_loss) - return 0; - if (hwe->dev_loss >= MAX_DEV_LOSS_TMO) - return snprintf(buff, len, "\"infinity\""); - - return snprintf(buff, len, "%u", hwe->dev_loss); -} - -static int -snprint_hw_vendor (char * buff, int len, void * data) -{ - struct hwentry * hwe = (struct hwentry *)data; - - if (!hwe->vendor) - return 0; - - return snprintf(buff, len, "\"%s\"", hwe->vendor); -} - -static int -snprint_hw_product (char * buff, int len, void * data) -{ - struct hwentry * hwe = (struct hwentry *)data; - - if (!hwe->product) - return 0; - - return snprintf(buff, len, "\"%s\"", hwe->product); -} - -static int -snprint_hw_revision (char * buff, int len, void * data) -{ - struct hwentry * hwe = (struct hwentry *)data; - - if (!hwe->revision) - return 0; - - return snprintf(buff, len, "\"%s\"", hwe->revision); -} - -static int -snprint_hw_bl_product (char * buff, int len, void * data) -{ - struct hwentry * hwe = (struct hwentry *)data; - - if (!hwe->bl_product) - return 0; - - return snprintf(buff, len, "\"%s\"", hwe->bl_product); -} - -static int -snprint_hw_uid_attribute (char * buff, int len, void * data) -{ - struct hwentry * hwe = (struct hwentry *)data; - - if (!hwe->uid_attribute) - return 0; - - return snprintf(buff, len, "\"%s\"", hwe->uid_attribute); -} - -static int -snprint_hw_getuid_callout (char * buff, int len, void * data) -{ - struct hwentry * hwe = (struct hwentry *)data; - - if (!hwe->getuid) - return 0; - - return snprintf(buff, len, "\"%s\"", hwe->getuid); -} - -static int -snprint_hw_prio (char * buff, int len, void * data) -{ - struct hwentry * hwe = (struct hwentry *)data; - - if (!hwe->prio_name) - return 0; - - return snprintf(buff, len, "\"%s\"", hwe->prio_name); -} - -static int -snprint_hw_alias_prefix (char * buff, int len, void * data) -{ - struct hwentry * hwe = (struct hwentry *)data; - - if (!hwe->alias_prefix) - return 0; - - return snprintf(buff, len, "\"%s\"", hwe->alias_prefix); -} - -static int -snprint_hw_prio_args (char * buff, int len, void * data) -{ - struct hwentry * hwe = (struct hwentry *)data; - - if (!hwe->prio_args) - return 0; - - return snprintf(buff, len, "\"%s\"", hwe->prio_args); -} - -static int -snprint_hw_features (char * buff, int len, void * data) -{ - struct hwentry * hwe = (struct hwentry *)data; - - if (!hwe->features) - return 0; - - return snprintf(buff, len, "\"%s\"", hwe->features); -} - -static int -snprint_hw_hardware_handler (char * buff, int len, void * data) -{ - struct hwentry * hwe = (struct hwentry *)data; - - if (!hwe->hwhandler) - return 0; - - return snprintf(buff, len, "\"%s\"", hwe->hwhandler); -} - -static int -snprint_hw_selector (char * buff, int len, void * data) -{ - struct hwentry * hwe = (struct hwentry *)data; - - if (!hwe->selector) - return 0; - - return snprintf(buff, len, "\"%s\"", hwe->selector); -} - -static int -snprint_hw_path_grouping_policy (char * buff, int len, void * data) -{ - struct hwentry * hwe = (struct hwentry *)data; - - char str[POLICY_NAME_SIZE]; - - if (!hwe->pgpolicy) - return 0; - - get_pgpolicy_name(str, POLICY_NAME_SIZE, hwe->pgpolicy); - - return snprintf(buff, len, "\"%s\"", str); -} - -static int -snprint_hw_failback (char * buff, int len, void * data) -{ - struct hwentry * hwe = (struct hwentry *)data; - - if (hwe->pgfailback == FAILBACK_UNDEF || - hwe->pgfailback == DEFAULT_FAILBACK) - return 0; - - switch(hwe->pgfailback) { - case -FAILBACK_MANUAL: - return snprintf(buff, len, "\"manual\""); - case -FAILBACK_IMMEDIATE: - return snprintf(buff, len, "\"immediate\""); - case -FAILBACK_FOLLOWOVER: - return snprintf(buff, len, "\"followover\""); - default: - return snprintf(buff, len, "%i", hwe->pgfailback); - } - return 0; -} - -static int -snprint_hw_rr_weight (char * buff, int len, void * data) -{ - struct hwentry * hwe = (struct hwentry *)data; - - if (!hwe->rr_weight) - return 0; - if (hwe->rr_weight == RR_WEIGHT_PRIO) - return snprintf(buff, len, "\"priorities\""); - if (hwe->rr_weight == RR_WEIGHT_NONE) - return snprintf(buff, len, "\"uniform\""); - - return 0; -} - -static int -snprint_hw_no_path_retry (char * buff, int len, void * data) -{ - struct hwentry * hwe = (struct hwentry *)data; - - if (!hwe->no_path_retry) - return 0; - - switch(hwe->no_path_retry) { - case NO_PATH_RETRY_UNDEF: - break; - case NO_PATH_RETRY_FAIL: - return snprintf(buff, len, "\"fail\""); - case NO_PATH_RETRY_QUEUE: - return snprintf(buff, len, "\"queue\""); - default: - return snprintf(buff, len, "%i", - hwe->no_path_retry); - } - return 0; -} - -static int -snprint_hw_rr_min_io (char * buff, int len, void * data) -{ - struct hwentry * hwe = (struct hwentry *)data; - - if (!hwe->minio) - return 0; - - return snprintf(buff, len, "%u", hwe->minio); -} - -static int -snprint_hw_rr_min_io_rq (char * buff, int len, void * data) -{ - struct hwentry * hwe = (struct hwentry *)data; - - if (!hwe->minio_rq) - return 0; - - return snprintf(buff, len, "%u", hwe->minio_rq); -} - -static int -snprint_hw_pg_timeout (char * buff, int len, void * data) -{ - return 0; -} - -static int -snprint_hw_flush_on_last_del (char * buff, int len, void * data) -{ - struct hwentry * hwe = (struct hwentry *)data; - - switch (hwe->flush_on_last_del) { - case FLUSH_DISABLED: - return snprintf(buff, len, "\"no\""); - case FLUSH_ENABLED: - return snprintf(buff, len, "\"yes\""); - } - return 0; -} - -static int -snprint_hw_path_checker (char * buff, int len, void * data) -{ - struct hwentry * hwe = (struct hwentry *)data; - - if (!hwe->checker_name) - return 0; - - return snprintf(buff, len, "\"%s\"", hwe->checker_name); -} - - static int -snprint_hw_user_friendly_names (char * buff, int len, void * data) -{ - struct hwentry * hwe = (struct hwentry *)data; - - if (hwe->user_friendly_names == USER_FRIENDLY_NAMES_UNDEF) - return 0; - else if (hwe->user_friendly_names == USER_FRIENDLY_NAMES_OFF) - return snprintf(buff, len, "\"no\""); - else - return snprintf(buff, len, "\"yes\""); -} - -static int -snprint_hw_retain_hwhandler_handler(char * buff, int len, void * data) -{ - struct hwentry * hwe = (struct hwentry *)data; - - if (hwe->retain_hwhandler == RETAIN_HWHANDLER_ON) - return snprintf(buff, len, "\"yes\""); - else if (hwe->retain_hwhandler == RETAIN_HWHANDLER_OFF) - return snprintf(buff, len, "\"no\""); - else - return 0; -} - -static int -snprint_detect_prio(char * buff, int len, void * data) -{ - struct hwentry * hwe = (struct hwentry *)data; - - if (hwe->detect_prio == DETECT_PRIO_ON) - return snprintf(buff, len, "\"yes\""); - else if (hwe->detect_prio == DETECT_PRIO_OFF) - return snprintf(buff, len, "\"no\""); - else - return 0; -} - -static int -snprint_def_polling_interval (char * buff, int len, void * data) -{ - return snprintf(buff, len, "%i", conf->checkint); -} - -static int -snprint_def_fast_io_fail(char * buff, int len, void * data) -{ - if (conf->fast_io_fail == MP_FAST_IO_FAIL_UNSET) - return 0; - if (conf->fast_io_fail == MP_FAST_IO_FAIL_OFF) - return snprintf(buff, len, "\"off\""); - if (conf->fast_io_fail == MP_FAST_IO_FAIL_ZERO) - return snprintf(buff, len, "0"); - return snprintf(buff, len, "%d", conf->fast_io_fail); -} - -static int -snprint_def_dev_loss(char * buff, int len, void * data) -{ - if (!conf->dev_loss) - return 0; - if (conf->dev_loss >= MAX_DEV_LOSS_TMO) - return snprintf(buff, len, "\"infinity\""); - return snprintf(buff, len, "%u", conf->dev_loss); -} - -static int -snprint_def_verbosity (char * buff, int len, void * data) -{ - return snprintf(buff, len, "%i", conf->verbosity); -} - -static int -snprint_def_max_polling_interval (char * buff, int len, void * data) -{ - return snprintf(buff, len, "%i", conf->max_checkint); -} - -static int -snprint_reassign_maps (char * buff, int len, void * data) -{ - return snprintf(buff, len, "\"%s\"", - conf->reassign_maps?"yes":"no"); -} - -static int -snprint_def_multipath_dir (char * buff, int len, void * data) -{ - if (!conf->multipath_dir) - return 0; - - return snprintf(buff, len, "\"%s\"", conf->multipath_dir); -} - -static int -snprint_def_selector (char * buff, int len, void * data) -{ - if (!conf->selector) - return snprintf(buff, len, "\"%s\"", DEFAULT_SELECTOR); - - return snprintf(buff, len, "\"%s\"", conf->selector); -} - -static int -snprint_def_path_grouping_policy (char * buff, int len, void * data) -{ - char str[POLICY_NAME_SIZE]; - int pgpolicy = conf->pgpolicy; - - if (!pgpolicy) - pgpolicy = DEFAULT_PGPOLICY; - - get_pgpolicy_name(str, POLICY_NAME_SIZE, pgpolicy); - - return snprintf(buff, len, "\"%s\"", str); -} - -static int -snprint_def_uid_attribute (char * buff, int len, void * data) -{ - if (!conf->uid_attribute) - return snprintf(buff, len, "\"%s\"", DEFAULT_UID_ATTRIBUTE); - - return snprintf(buff, len, "\"%s\"", conf->uid_attribute); -} - -static int -snprint_def_getuid_callout (char * buff, int len, void * data) -{ - if (!conf->getuid) - return 0; - - return snprintf(buff, len, "\"%s\"", conf->getuid); -} - -static int -snprint_def_prio (char * buff, int len, void * data) -{ - if (!conf->prio_name) - return snprintf(buff, len, "\"%s\"", DEFAULT_PRIO); - - return snprintf(buff, len, "\"%s\"", conf->prio_name); -} - -static int -snprint_def_prio_args (char * buff, int len, void * data) -{ - if (!conf->prio_args) - return snprintf(buff, len, "\"%s\"", DEFAULT_PRIO_ARGS); - - return snprintf(buff, len, "\"%s\"", conf->prio_args); -} - -static int -snprint_def_features (char * buff, int len, void * data) -{ - if (!conf->features) - return snprintf(buff, len, "\"%s\"", DEFAULT_FEATURES); - - return snprintf(buff, len, "\"%s\"", conf->features); -} - -static int -snprint_def_path_checker (char * buff, int len, void * data) -{ - if (!conf->checker_name) - return snprintf(buff, len, "\"%s\"", DEFAULT_CHECKER); - - return snprintf(buff, len, "\"%s\"", conf->checker_name); -} - -static int -snprint_def_failback (char * buff, int len, void * data) -{ - switch(conf->pgfailback) { - case FAILBACK_UNDEF: - return snprintf(buff, len, "\"undef\""); - case -FAILBACK_MANUAL: - return snprintf(buff, len, "\"manual\""); - case -FAILBACK_IMMEDIATE: - return snprintf(buff, len, "\"immediate\""); - case -FAILBACK_FOLLOWOVER: - return snprintf(buff, len, "\"followover\""); - default: - return snprintf(buff, len, "%i", conf->pgfailback); - } - return 0; -} - -static int -snprint_def_rr_min_io (char * buff, int len, void * data) -{ - if (!conf->minio) - return 0; - - return snprintf(buff, len, "%u", conf->minio); -} - -static int -snprint_def_rr_min_io_rq (char * buff, int len, void * data) -{ - if (!conf->minio_rq) - return 0; - - return snprintf(buff, len, "%u", conf->minio_rq); -} - -static int -snprint_max_fds (char * buff, int len, void * data) -{ - int r = 0, max_fds; - - if (!conf->max_fds) - return 0; - - r = get_sys_max_fds(&max_fds); - if (!r && conf->max_fds >= max_fds) - return snprintf(buff, len, "\"max\""); - else - return snprintf(buff, len, "%d", conf->max_fds); -} - -static int -snprint_def_mode(char * buff, int len, void * data) -{ - if ((conf->attribute_flags & (1 << ATTR_MODE)) == 0) - return 0; - return snprintf(buff, len, "0%o", conf->mode); -} - -static int -snprint_def_uid(char * buff, int len, void * data) -{ - if ((conf->attribute_flags & (1 << ATTR_UID)) == 0) - return 0; - return snprintf(buff, len, "0%o", conf->uid); -} - -static int -snprint_def_gid(char * buff, int len, void * data) -{ - if ((conf->attribute_flags & (1 << ATTR_GID)) == 0) - return 0; - return snprintf(buff, len, "0%o", conf->gid); -} - -static int -snprint_def_rr_weight (char * buff, int len, void * data) -{ - if (!conf->rr_weight || conf->rr_weight == RR_WEIGHT_NONE) - return snprintf(buff, len, "\"uniform\""); - if (conf->rr_weight == RR_WEIGHT_PRIO) - return snprintf(buff, len, "\"priorities\""); - - return 0; -} - -static int -snprint_def_no_path_retry (char * buff, int len, void * data) -{ - switch(conf->no_path_retry) { - case NO_PATH_RETRY_UNDEF: - break; - case NO_PATH_RETRY_FAIL: - return snprintf(buff, len, "\"fail\""); - case NO_PATH_RETRY_QUEUE: - return snprintf(buff, len, "\"queue\""); - default: - return snprintf(buff, len, "%i", - conf->no_path_retry); - } - return 0; -} - -static int -snprint_def_queue_without_daemon (char * buff, int len, void * data) -{ - switch (conf->queue_without_daemon) { - case QUE_NO_DAEMON_OFF: - return snprintf(buff, len, "\"no\""); - case QUE_NO_DAEMON_ON: - return snprintf(buff, len, "\"yes\""); - case QUE_NO_DAEMON_FORCE: - return snprintf(buff, len, "\"forced\""); - } - return 0; -} - -static int -snprint_def_checker_timeout (char *buff, int len, void *data) -{ - if (!conf->checker_timeout) - return 0; - - return snprintf(buff, len, "%u", conf->checker_timeout); -} - -static int -snprint_def_pg_timeout (char * buff, int len, void * data) -{ - return 0; -} - -static int -snprint_def_flush_on_last_del (char * buff, int len, void * data) -{ - switch (conf->flush_on_last_del) { - case FLUSH_UNDEF: - case FLUSH_DISABLED: - return snprintf(buff, len, "\"no\""); - case FLUSH_ENABLED: - case FLUSH_IN_PROGRESS: - return snprintf(buff, len, "\"yes\""); - } return 0; } -static int -snprint_def_log_checker_err (char * buff, int len, void * data) -{ - if (conf->log_checker_err == LOG_CHKR_ERR_ONCE) - return snprintf(buff, len, "once"); - return snprintf(buff, len, "always"); -} - -static int -snprint_def_user_friendly_names (char * buff, int len, void * data) -{ - if (conf->user_friendly_names == USER_FRIENDLY_NAMES_ON) - return snprintf(buff, len, "\"yes\""); - else - return snprintf(buff, len, "\"no\""); -} - -static int -snprint_def_alias_prefix (char * buff, int len, void * data) -{ - if (!conf->alias_prefix) - return snprintf(buff, len, "\"%s\"", DEFAULT_ALIAS_PREFIX); - return snprintf(buff, len, "\"%s\"", conf->alias_prefix); -} - -static int -snprint_def_bindings_file (char * buff, int len, void * data) -{ - if (conf->bindings_file == NULL) - return 0; - return snprintf(buff, len, "\"%s\"", conf->bindings_file); -} - -static int -snprint_def_wwids_file (char * buff, int len, void * data) -{ - if (conf->wwids_file == NULL) - return 0; - return snprintf(buff, len, "%s", conf->wwids_file); -} - -static int -snprint_def_reservation_key(char * buff, int len, void * data) -{ - int i; - unsigned char *keyp; - uint64_t prkey = 0; - - if (!conf->reservation_key) - return 0; - keyp = (unsigned char *)conf->reservation_key; - for (i = 0; i < 8; i++) { - if (i > 0) - prkey <<= 8; - prkey |= *keyp; - keyp++; - } - return snprintf(buff, len, "0x%" PRIx64, prkey); -} - -static int -snprint_def_retain_hwhandler_handler(char * buff, int len, void * data) -{ - if (conf->retain_hwhandler == RETAIN_HWHANDLER_ON) - return snprintf(buff, len, "yes"); - else - return snprintf(buff, len, "no"); -} - -static int -snprint_def_detect_prio(char * buff, int len, void * data) -{ - if (conf->detect_prio == DETECT_PRIO_ON) - return snprintf(buff, len, "yes"); - else - return snprintf(buff, len, "no"); -} - -static int -snprint_ble_simple (char * buff, int len, void * data) -{ - struct blentry * ble = (struct blentry *)data; - - return snprintf(buff, len, "\"%s\"", ble->str); -} - -static int -snprint_bled_vendor (char * buff, int len, void * data) -{ - struct blentry_device * bled = (struct blentry_device *)data; - - return snprintf(buff, len, "\"%s\"", bled->vendor); -} - -static int -snprint_bled_product (char * buff, int len, void * data) -{ - struct blentry_device * bled = (struct blentry_device *)data; - - return snprintf(buff, len, "\"%s\"", bled->product); -} - #define __deprecated void init_keywords(void) { install_keyword_root("defaults", NULL); - install_keyword("verbosity", &verbosity_handler, &snprint_def_verbosity); - install_keyword("polling_interval", &polling_interval_handler, &snprint_def_polling_interval); - install_keyword("max_polling_interval", &max_polling_interval_handler, &snprint_def_max_polling_interval); - install_keyword("reassign_maps", &reassign_maps_handler, &snprint_reassign_maps); - install_keyword("multipath_dir", &multipath_dir_handler, &snprint_def_multipath_dir); + install_keyword("verbosity", &def_verbosity_handler, &snprint_def_verbosity); + install_keyword("polling_interval", &def_checkint_handler, &snprint_def_checkint); + install_keyword("max_polling_interval", &def_max_checkint_handler, &snprint_def_max_checkint); + install_keyword("reassign_maps", &def_reassign_maps_handler, &snprint_def_reassign_maps); + install_keyword("multipath_dir", &def_multipath_dir_handler, &snprint_def_multipath_dir); install_keyword("path_selector", &def_selector_handler, &snprint_def_selector); - install_keyword("path_grouping_policy", &def_pgpolicy_handler, &snprint_def_path_grouping_policy); + install_keyword("path_grouping_policy", &def_pgpolicy_handler, &snprint_def_pgpolicy); install_keyword("uid_attribute", &def_uid_attribute_handler, &snprint_def_uid_attribute); - install_keyword("getuid_callout", &def_getuid_callout_handler, &snprint_def_getuid_callout); - install_keyword("prio", &def_prio_handler, &snprint_def_prio); + install_keyword("getuid_callout", &def_getuid_handler, &snprint_def_getuid); + install_keyword("prio", &def_prio_name_handler, &snprint_def_prio_name); install_keyword("prio_args", &def_prio_args_handler, &snprint_def_prio_args); install_keyword("features", &def_features_handler, &snprint_def_features); - install_keyword("path_checker", &def_path_checker_handler, &snprint_def_path_checker); - install_keyword("checker", &def_path_checker_handler, NULL); + install_keyword("path_checker", &def_checker_name_handler, &snprint_def_checker_name); + install_keyword("checker", &def_checker_name_handler, NULL); install_keyword("alias_prefix", &def_alias_prefix_handler, &snprint_def_alias_prefix); - install_keyword("failback", &default_failback_handler, &snprint_def_failback); - install_keyword("rr_min_io", &def_minio_handler, &snprint_def_rr_min_io); - install_keyword("rr_min_io_rq", &def_minio_rq_handler, &snprint_def_rr_min_io_rq); + install_keyword("failback", &def_pgfailback_handler, &snprint_def_pgfailback); + install_keyword("rr_min_io", &def_minio_handler, &snprint_def_minio); + install_keyword("rr_min_io_rq", &def_minio_rq_handler, &snprint_def_minio_rq); install_keyword("max_fds", &max_fds_handler, &snprint_max_fds); - install_keyword("rr_weight", &def_weight_handler, &snprint_def_rr_weight); + install_keyword("rr_weight", &def_rr_weight_handler, &snprint_def_rr_weight); install_keyword("no_path_retry", &def_no_path_retry_handler, &snprint_def_no_path_retry); - install_keyword("queue_without_daemon", &def_queue_without_daemon, &snprint_def_queue_without_daemon); + install_keyword("queue_without_daemon", &def_queue_without_daemon_handler, &snprint_def_queue_without_daemon); install_keyword("checker_timeout", &def_checker_timeout_handler, &snprint_def_checker_timeout); - install_keyword("pg_timeout", &def_pg_timeout_handler, &snprint_def_pg_timeout); + install_keyword("pg_timeout", &deprecated_handler, &snprint_deprecated); install_keyword("flush_on_last_del", &def_flush_on_last_del_handler, &snprint_def_flush_on_last_del); - install_keyword("user_friendly_names", &def_names_handler, &snprint_def_user_friendly_names); + install_keyword("user_friendly_names", &def_user_friendly_names_handler, &snprint_def_user_friendly_names); install_keyword("mode", &def_mode_handler, &snprint_def_mode); install_keyword("uid", &def_uid_handler, &snprint_def_uid); install_keyword("gid", &def_gid_handler, &snprint_def_gid); install_keyword("fast_io_fail_tmo", &def_fast_io_fail_handler, &snprint_def_fast_io_fail); install_keyword("dev_loss_tmo", &def_dev_loss_handler, &snprint_def_dev_loss); - install_keyword("bindings_file", &bindings_file_handler, &snprint_def_bindings_file); - install_keyword("wwids_file", &wwids_file_handler, &snprint_def_wwids_file); + install_keyword("bindings_file", &def_bindings_file_handler, &snprint_def_bindings_file); + install_keyword("wwids_file", &def_wwids_file_handler, &snprint_def_wwids_file); install_keyword("log_checker_err", &def_log_checker_err_handler, &snprint_def_log_checker_err); install_keyword("reservation_key", &def_reservation_key_handler, &snprint_def_reservation_key); - install_keyword("retain_attached_hw_handler", &def_retain_hwhandler_handler, &snprint_def_retain_hwhandler_handler); + install_keyword("retain_attached_hw_handler", &def_retain_hwhandler_handler, &snprint_def_retain_hwhandler); install_keyword("detect_prio", &def_detect_prio_handler, &snprint_def_detect_prio); + install_keyword("force_sync", &def_force_sync_handler, &snprint_def_force_sync); + install_keyword("deferred_remove", &def_deferred_remove_handler, &snprint_def_deferred_remove); + install_keyword("partition_delimiter", &def_partition_delim_handler, &snprint_def_partition_delim); + install_keyword("config_dir", &def_config_dir_handler, &snprint_def_config_dir); + install_keyword("delay_watch_checks", &def_delay_watch_checks_handler, &snprint_def_delay_watch_checks); + install_keyword("delay_wait_checks", &def_delay_wait_checks_handler, &snprint_def_delay_wait_checks); + install_keyword("find_multipaths", &def_find_multipaths_handler, &snprint_def_find_multipaths); + install_keyword("uxsock_timeout", &def_uxsock_timeout_handler, &snprint_def_uxsock_timeout); + install_keyword("retrigger_tries", &def_retrigger_tries_handler, &snprint_def_retrigger_tries); + install_keyword("retrigger_delay", &def_retrigger_delay_handler, &snprint_def_retrigger_delay); + install_keyword("missing_uev_wait_timeout", &def_uev_wait_timeout_handler, &snprint_def_uev_wait_timeout); __deprecated install_keyword("default_selector", &def_selector_handler, NULL); __deprecated install_keyword("default_path_grouping_policy", &def_pgpolicy_handler, NULL); __deprecated install_keyword("default_uid_attribute", &def_uid_attribute_handler, NULL); - __deprecated install_keyword("default_getuid_callout", &def_getuid_callout_handler, NULL); + __deprecated install_keyword("default_getuid_callout", &def_getuid_handler, NULL); __deprecated install_keyword("default_features", &def_features_handler, NULL); - __deprecated install_keyword("default_path_checker", &def_path_checker_handler, NULL); + __deprecated install_keyword("default_path_checker", &def_checker_name_handler, NULL); install_keyword_root("blacklist", &blacklist_handler); - install_keyword_multi("devnode", &ble_devnode_handler, &snprint_ble_simple); - install_keyword_multi("wwid", &ble_wwid_handler, &snprint_ble_simple); - install_keyword_multi("property", &ble_property_handler, &snprint_ble_simple); + install_keyword_multi("devnode", &ble_blist_devnode_handler, &snprint_ble_simple); + install_keyword_multi("wwid", &ble_blist_wwid_handler, &snprint_ble_simple); + install_keyword_multi("property", &ble_blist_property_handler, &snprint_ble_simple); install_keyword_multi("device", &ble_device_handler, NULL); install_sublevel(); - install_keyword("vendor", &ble_vendor_handler, &snprint_bled_vendor); - install_keyword("product", &ble_product_handler, &snprint_bled_product); + install_keyword("vendor", &ble_blist_device_vendor_handler, &snprint_bled_vendor); + install_keyword("product", &ble_blist_device_product_handler, &snprint_bled_product); install_sublevel_end(); install_keyword_root("blacklist_exceptions", &blacklist_exceptions_handler); - install_keyword_multi("devnode", &ble_except_devnode_handler, &snprint_ble_simple); - install_keyword_multi("wwid", &ble_except_wwid_handler, &snprint_ble_simple); - install_keyword_multi("property", &ble_except_property_handler, &snprint_ble_simple); + install_keyword_multi("devnode", &ble_elist_devnode_handler, &snprint_ble_simple); + install_keyword_multi("wwid", &ble_elist_wwid_handler, &snprint_ble_simple); + install_keyword_multi("property", &ble_elist_property_handler, &snprint_ble_simple); install_keyword_multi("device", &ble_except_device_handler, NULL); install_sublevel(); - install_keyword("vendor", &ble_except_vendor_handler, &snprint_bled_vendor); - install_keyword("product", &ble_except_product_handler, &snprint_bled_product); + install_keyword("vendor", &ble_elist_device_vendor_handler, &snprint_bled_vendor); + install_keyword("product", &ble_elist_device_product_handler, &snprint_bled_product); install_sublevel_end(); #if 0 @@ -2886,56 +1415,88 @@ init_keywords(void) install_keyword_root("devices", &devices_handler); install_keyword_multi("device", &device_handler, NULL); install_sublevel(); - install_keyword("vendor", &vendor_handler, &snprint_hw_vendor); - install_keyword("product", &product_handler, &snprint_hw_product); - install_keyword("revision", &revision_handler, &snprint_hw_revision); - install_keyword("product_blacklist", &bl_product_handler, &snprint_hw_bl_product); - install_keyword("path_grouping_policy", &hw_pgpolicy_handler, &snprint_hw_path_grouping_policy); + install_keyword("vendor", &hw_vendor_handler, &snprint_hw_vendor); + install_keyword("product", &hw_product_handler, &snprint_hw_product); + install_keyword("revision", &hw_revision_handler, &snprint_hw_revision); + install_keyword("product_blacklist", &hw_bl_product_handler, &snprint_hw_bl_product); + install_keyword("path_grouping_policy", &hw_pgpolicy_handler, &snprint_hw_pgpolicy); install_keyword("uid_attribute", &hw_uid_attribute_handler, &snprint_hw_uid_attribute); - install_keyword("getuid_callout", &hw_getuid_callout_handler, &snprint_hw_getuid_callout); + install_keyword("getuid_callout", &hw_getuid_handler, &snprint_hw_getuid); install_keyword("path_selector", &hw_selector_handler, &snprint_hw_selector); - install_keyword("path_checker", &hw_path_checker_handler, &snprint_hw_path_checker); - install_keyword("checker", &hw_path_checker_handler, NULL); + install_keyword("path_checker", &hw_checker_name_handler, &snprint_hw_checker_name); + install_keyword("checker", &hw_checker_name_handler, NULL); install_keyword("alias_prefix", &hw_alias_prefix_handler, &snprint_hw_alias_prefix); install_keyword("features", &hw_features_handler, &snprint_hw_features); - install_keyword("hardware_handler", &hw_handler_handler, &snprint_hw_hardware_handler); - install_keyword("prio", &hw_prio_handler, &snprint_hw_prio); + install_keyword("hardware_handler", &hw_hwhandler_handler, &snprint_hw_hwhandler); + install_keyword("prio", &hw_prio_name_handler, &snprint_hw_prio_name); install_keyword("prio_args", &hw_prio_args_handler, &snprint_hw_prio_args); - install_keyword("failback", &hw_failback_handler, &snprint_hw_failback); - install_keyword("rr_weight", &hw_weight_handler, &snprint_hw_rr_weight); + install_keyword("failback", &hw_pgfailback_handler, &snprint_hw_pgfailback); + install_keyword("rr_weight", &hw_rr_weight_handler, &snprint_hw_rr_weight); install_keyword("no_path_retry", &hw_no_path_retry_handler, &snprint_hw_no_path_retry); - install_keyword("rr_min_io", &hw_minio_handler, &snprint_hw_rr_min_io); - install_keyword("rr_min_io_rq", &hw_minio_rq_handler, &snprint_hw_rr_min_io_rq); - install_keyword("pg_timeout", &hw_pg_timeout_handler, &snprint_hw_pg_timeout); + install_keyword("rr_min_io", &hw_minio_handler, &snprint_hw_minio); + install_keyword("rr_min_io_rq", &hw_minio_rq_handler, &snprint_hw_minio_rq); + install_keyword("pg_timeout", &deprecated_handler, &snprint_deprecated); install_keyword("flush_on_last_del", &hw_flush_on_last_del_handler, &snprint_hw_flush_on_last_del); install_keyword("fast_io_fail_tmo", &hw_fast_io_fail_handler, &snprint_hw_fast_io_fail); install_keyword("dev_loss_tmo", &hw_dev_loss_handler, &snprint_hw_dev_loss); - install_keyword("user_friendly_names", &hw_names_handler, &snprint_hw_user_friendly_names); - install_keyword("retain_attached_hw_handler", &hw_retain_hwhandler_handler, &snprint_hw_retain_hwhandler_handler); - install_keyword("detect_prio", &hw_detect_prio_handler, &snprint_detect_prio); + install_keyword("user_friendly_names", &hw_user_friendly_names_handler, &snprint_hw_user_friendly_names); + install_keyword("retain_attached_hw_handler", &hw_retain_hwhandler_handler, &snprint_hw_retain_hwhandler); + install_keyword("detect_prio", &hw_detect_prio_handler, &snprint_hw_detect_prio); + install_keyword("deferred_remove", &hw_deferred_remove_handler, &snprint_hw_deferred_remove); + install_keyword("delay_watch_checks", &hw_delay_watch_checks_handler, &snprint_hw_delay_watch_checks); + install_keyword("delay_wait_checks", &hw_delay_wait_checks_handler, &snprint_hw_delay_wait_checks); install_sublevel_end(); + install_keyword_root("overrides", &overrides_handler); + install_keyword("path_grouping_policy", &ovr_pgpolicy_handler, &snprint_ovr_pgpolicy); + install_keyword("uid_attribute", &ovr_uid_attribute_handler, &snprint_ovr_uid_attribute); + install_keyword("getuid_callout", &ovr_getuid_handler, &snprint_ovr_getuid); + install_keyword("path_selector", &ovr_selector_handler, &snprint_ovr_selector); + install_keyword("path_checker", &ovr_checker_name_handler, &snprint_ovr_checker_name); + install_keyword("checker", &ovr_checker_name_handler, NULL); + install_keyword("alias_prefix", &ovr_alias_prefix_handler, &snprint_ovr_alias_prefix); + install_keyword("features", &ovr_features_handler, &snprint_ovr_features); + install_keyword("prio", &ovr_prio_name_handler, &snprint_ovr_prio_name); + install_keyword("prio_args", &ovr_prio_args_handler, &snprint_ovr_prio_args); + install_keyword("failback", &ovr_pgfailback_handler, &snprint_ovr_pgfailback); + install_keyword("rr_weight", &ovr_rr_weight_handler, &snprint_ovr_rr_weight); + install_keyword("no_path_retry", &ovr_no_path_retry_handler, &snprint_ovr_no_path_retry); + install_keyword("rr_min_io", &ovr_minio_handler, &snprint_ovr_minio); + install_keyword("rr_min_io_rq", &ovr_minio_rq_handler, &snprint_ovr_minio_rq); + install_keyword("flush_on_last_del", &ovr_flush_on_last_del_handler, &snprint_ovr_flush_on_last_del); + install_keyword("fast_io_fail_tmo", &ovr_fast_io_fail_handler, &snprint_ovr_fast_io_fail); + install_keyword("dev_loss_tmo", &ovr_dev_loss_handler, &snprint_ovr_dev_loss); + install_keyword("user_friendly_names", &ovr_user_friendly_names_handler, &snprint_ovr_user_friendly_names); + install_keyword("retain_attached_hw_handler", &ovr_retain_hwhandler_handler, &snprint_ovr_retain_hwhandler); + install_keyword("detect_prio", &ovr_detect_prio_handler, &snprint_ovr_detect_prio); + install_keyword("deferred_remove", &ovr_deferred_remove_handler, &snprint_ovr_deferred_remove); + install_keyword("delay_watch_checks", &ovr_delay_watch_checks_handler, &snprint_ovr_delay_watch_checks); + install_keyword("delay_wait_checks", &ovr_delay_wait_checks_handler, &snprint_ovr_delay_wait_checks); + install_keyword_root("multipaths", &multipaths_handler); install_keyword_multi("multipath", &multipath_handler, NULL); install_sublevel(); - install_keyword("wwid", &wwid_handler, &snprint_mp_wwid); - install_keyword("alias", &alias_handler, &snprint_mp_alias); - install_keyword("path_grouping_policy", &mp_pgpolicy_handler, &snprint_mp_path_grouping_policy); + install_keyword("wwid", &mp_wwid_handler, &snprint_mp_wwid); + install_keyword("alias", &mp_alias_handler, &snprint_mp_alias); + install_keyword("path_grouping_policy", &mp_pgpolicy_handler, &snprint_mp_pgpolicy); install_keyword("path_selector", &mp_selector_handler, &snprint_mp_selector); - install_keyword("prio", &mp_prio_handler, &snprint_mp_prio); + install_keyword("prio", &mp_prio_name_handler, &snprint_mp_prio_name); install_keyword("prio_args", &mp_prio_args_handler, &snprint_mp_prio_args); - install_keyword("failback", &mp_failback_handler, &snprint_mp_failback); - install_keyword("rr_weight", &mp_weight_handler, &snprint_mp_rr_weight); + install_keyword("failback", &mp_pgfailback_handler, &snprint_mp_pgfailback); + install_keyword("rr_weight", &mp_rr_weight_handler, &snprint_mp_rr_weight); install_keyword("no_path_retry", &mp_no_path_retry_handler, &snprint_mp_no_path_retry); - install_keyword("rr_min_io", &mp_minio_handler, &snprint_mp_rr_min_io); - install_keyword("rr_min_io_rq", &mp_minio_rq_handler, &snprint_mp_rr_min_io_rq); - install_keyword("pg_timeout", &mp_pg_timeout_handler, &snprint_mp_pg_timeout); + install_keyword("rr_min_io", &mp_minio_handler, &snprint_mp_minio); + install_keyword("rr_min_io_rq", &mp_minio_rq_handler, &snprint_mp_minio_rq); + install_keyword("pg_timeout", &deprecated_handler, &snprint_deprecated); install_keyword("flush_on_last_del", &mp_flush_on_last_del_handler, &snprint_mp_flush_on_last_del); install_keyword("features", &mp_features_handler, &snprint_mp_features); install_keyword("mode", &mp_mode_handler, &snprint_mp_mode); install_keyword("uid", &mp_uid_handler, &snprint_mp_uid); install_keyword("gid", &mp_gid_handler, &snprint_mp_gid); install_keyword("reservation_key", &mp_reservation_key_handler, &snprint_mp_reservation_key); - install_keyword("user_friendly_names", &mp_names_handler, &snprint_mp_user_friendly_names); + install_keyword("user_friendly_names", &mp_user_friendly_names_handler, &snprint_mp_user_friendly_names); + install_keyword("deferred_remove", &mp_deferred_remove_handler, &snprint_mp_deferred_remove); + install_keyword("delay_watch_checks", &mp_delay_watch_checks_handler, &snprint_mp_delay_watch_checks); + install_keyword("delay_wait_checks", &mp_delay_wait_checks_handler, &snprint_mp_delay_wait_checks); install_sublevel_end(); } diff --git a/libmultipath/dict.h b/libmultipath/dict.h index 688eab7..4fdd576 100644 --- a/libmultipath/dict.h +++ b/libmultipath/dict.h @@ -7,5 +7,13 @@ void init_keywords(void); int get_sys_max_fds(int *); +int print_rr_weight (char * buff, int len, void *ptr); +int print_pgfailback (char * buff, int len, void *ptr); +int print_pgpolicy(char * buff, int len, void *ptr); +int print_no_path_retry(char * buff, int len, void *ptr); +int print_fast_io_fail(char * buff, int len, void *ptr); +int print_dev_loss(char * buff, int len, void *ptr); +int print_reservation_key(char * buff, int len, void * ptr); +int print_delay_checks(char * buff, int len, void *ptr); #endif /* _DICT_H */ diff --git a/libmultipath/discovery.c b/libmultipath/discovery.c index 228ffd3..bd65354 100644 --- a/libmultipath/discovery.c +++ b/libmultipath/discovery.c @@ -4,6 +4,7 @@ * Copyright (c) 2005 Mike Anderson */ #include +#include #include #include #include @@ -29,11 +30,45 @@ #include "prio.h" #include "defaults.h" +int +alloc_path_with_pathinfo (vector hwtable, struct udev_device *udevice, + int flag, struct path **pp_ptr) +{ + int err = PATHINFO_FAILED; + struct path * pp; + const char * devname; + + if (pp_ptr) + *pp_ptr = NULL; + + devname = udev_device_get_sysname(udevice); + if (!devname) + return PATHINFO_FAILED; + + pp = alloc_path(); + + if (!pp) + return PATHINFO_FAILED; + + if (safe_sprintf(pp->dev, "%s", devname)) { + condlog(0, "pp->dev too small"); + } else { + pp->udev = udev_device_ref(udevice); + err = pathinfo(pp, hwtable, flag | DI_BLACKLIST); + } + + if (err) + free_path(pp); + else if (pp_ptr) + *pp_ptr = pp; + return err; +} + int store_pathinfo (vector pathvec, vector hwtable, struct udev_device *udevice, int flag, struct path **pp_ptr) { - int err = 1; + int err = PATHINFO_FAILED; struct path * pp; const char * devname; @@ -42,12 +77,12 @@ store_pathinfo (vector pathvec, vector hwtable, struct udev_device *udevice, devname = udev_device_get_sysname(udevice); if (!devname) - return 1; + return PATHINFO_FAILED; pp = alloc_path(); if (!pp) - return 1; + return PATHINFO_FAILED; if(safe_sprintf(pp->dev, "%s", devname)) { condlog(0, "pp->dev too small"); @@ -55,7 +90,8 @@ store_pathinfo (vector pathvec, vector hwtable, struct udev_device *udevice, } pp->udev = udev_device_ref(udevice); err = pathinfo(pp, hwtable, - (conf->dry_run == 3)? flag : (flag | DI_BLACKLIST)); + (conf->cmd == CMD_REMOVE_WWID)? flag : + (flag | DI_BLACKLIST)); if (err) goto out; @@ -80,22 +116,19 @@ path_discover (vector pathvec, struct config * conf, devname = udev_device_get_sysname(udevice); if (!devname) - return 0; + return PATHINFO_FAILED; if (filter_property(conf, udevice) > 0) - return 0; + return PATHINFO_SKIPPED; if (filter_devnode(conf->blist_devnode, conf->elist_devnode, (char *)devname) > 0) - return 0; + return PATHINFO_SKIPPED; pp = find_path_by_dev(pathvec, (char *)devname); if (!pp) { - if (store_pathinfo(pathvec, conf->hwtable, - udevice, flag, NULL) != 1) - return 0; - else - return 1; + return store_pathinfo(pathvec, conf->hwtable, + udevice, flag, NULL); } return pathinfo(pp, conf->hwtable, flag); } @@ -107,11 +140,11 @@ path_discovery (vector pathvec, struct config * conf, int flag) struct udev_list_entry *entry; struct udev_device *udevice; const char *devpath; - int r = 0; + int num_paths = 0, total_paths = 0; udev_iter = udev_enumerate_new(conf->udev); if (!udev_iter) - return 1; + return -ENOMEM; udev_enumerate_add_match_subsystem(udev_iter, "block"); udev_enumerate_scan_devices(udev_iter); @@ -124,25 +157,27 @@ path_discovery (vector pathvec, struct config * conf, int flag) udevice = udev_device_new_from_syspath(conf->udev, devpath); if (!udevice) { condlog(4, "%s: no udev information", devpath); - r++; continue; } devtype = udev_device_get_devtype(udevice); if(devtype && !strncmp(devtype, "disk", 4)) { - r += path_discover(pathvec, conf, - udevice, flag); + total_paths++; + if (path_discover(pathvec, conf, + udevice, flag) == PATHINFO_OK) + num_paths++; } udev_device_unref(udevice); } udev_enumerate_unref(udev_iter); - condlog(4, "Discovery status %d", r); - return r; + condlog(4, "Discovered %d/%d paths", num_paths, total_paths); + return (total_paths - num_paths); } #define declare_sysfs_get_str(fname) \ extern ssize_t \ sysfs_get_##fname (struct udev_device * udev, char * buff, size_t len) \ { \ + int l; \ const char * attr; \ const char * devname; \ \ @@ -157,19 +192,44 @@ sysfs_get_##fname (struct udev_device * udev, char * buff, size_t len) \ devname, #fname); \ return -ENXIO; \ } \ - if (strchop(attr) > len) { \ + for (l = strlen(attr); l >= 1 && isspace(attr[l-1]); l--); \ + if (l > len) { \ condlog(3, "%s: overflow in attribute %s", \ devname, #fname); \ return -EINVAL; \ } \ - return strlcpy(buff, attr, len); \ + strlcpy(buff, attr, len); \ + return strchop(buff); \ } declare_sysfs_get_str(devtype); declare_sysfs_get_str(vendor); declare_sysfs_get_str(model); declare_sysfs_get_str(rev); -declare_sysfs_get_str(dev); + +ssize_t +sysfs_get_vpd (struct udev_device * udev, int pg, + unsigned char * buff, size_t len) +{ + ssize_t attr_len; + char attrname[9]; + const char * devname; + + if (!udev) { + condlog(3, "No udev device given\n"); + return -ENOSYS; + } + + devname = udev_device_get_sysname(udev); + sprintf(attrname, "vpd_pg%02x", pg); + attr_len = sysfs_bin_attr_get_value(udev, attrname, buff, len); + if (attr_len < 0) { + condlog(3, "%s: attribute %s not found in sysfs", + devname, attrname); + return attr_len; + } + return attr_len; +} int sysfs_get_timeout(struct path *pp, unsigned int *timeout) @@ -239,6 +299,19 @@ sysfs_get_tgt_nodename (struct path *pp, char * node) } } + /* Check for USB */ + tgtdev = udev_device_get_parent(parent); + while (tgtdev) { + value = udev_device_get_subsystem(tgtdev); + if (value && !strcmp(value, "usb")) { + pp->sg_id.proto_id = SCSI_PROTOCOL_UNSPEC; + tgtname = udev_device_get_sysname(tgtdev); + strncpy(node, tgtname, strlen(tgtname)); + condlog(3, "%s: skip USB device %s", pp->dev, node); + return 1; + } + tgtdev = udev_device_get_parent(tgtdev); + } parent = udev_device_get_parent_with_subsystem_devtype(pp->udev, "scsi", "scsi_target"); if (!parent) return 1; @@ -285,7 +358,7 @@ sysfs_get_tgt_nodename (struct path *pp, char * node) if (tgtdev) { const char *value; - value = udev_device_get_sysattr_value(tgtdev, "tgtname"); + value = udev_device_get_sysattr_value(tgtdev, "targetname"); if (value) { pp->sg_id.proto_id = SCSI_PROTOCOL_ISCSI; pp->sg_id.transport_id = tgtid; @@ -313,7 +386,95 @@ sysfs_get_tgt_nodename (struct path *pp, char * node) snprintf(node, NODE_NAME_SIZE, "ata-%d.00", tgtid); return 0; } + /* Unknown SCSI transport. Keep fingers crossed */ pp->sg_id.proto_id = SCSI_PROTOCOL_UNSPEC; + return 0; +} + +int sysfs_get_host_adapter_name(struct path *pp, char *adapter_name) +{ + int proto_id; + + if (!pp || !adapter_name) + return 1; + + proto_id = pp->sg_id.proto_id; + + if (proto_id != SCSI_PROTOCOL_FCP && + proto_id != SCSI_PROTOCOL_SAS && + proto_id != SCSI_PROTOCOL_ISCSI && + proto_id != SCSI_PROTOCOL_SRP) { + return 1; + } + /* iscsi doesn't have adapter info in sysfs + * get ip_address for grouping paths + */ + if (pp->sg_id.proto_id == SCSI_PROTOCOL_ISCSI) + return sysfs_get_iscsi_ip_address(pp, adapter_name); + + /* fetch adapter pci name for other protocols + */ + return sysfs_get_host_pci_name(pp, adapter_name); +} + +int sysfs_get_host_pci_name(struct path *pp, char *pci_name) +{ + struct udev_device *hostdev, *parent; + char host_name[HOST_NAME_LEN]; + const char *driver_name, *value; + + if (!pp || !pci_name) + return 1; + + sprintf(host_name, "host%d", pp->sg_id.host_no); + hostdev = udev_device_new_from_subsystem_sysname(conf->udev, + "scsi_host", host_name); + if (!hostdev) + return 1; + + parent = udev_device_get_parent(hostdev); + while (parent) { + driver_name = udev_device_get_driver(parent); + if (!driver_name) { + parent = udev_device_get_parent(parent); + continue; + } + if (!strcmp(driver_name, "pcieport")) + break; + parent = udev_device_get_parent(parent); + } + if (parent) { + /* pci_device found + */ + value = udev_device_get_sysname(parent); + + strncpy(pci_name, value, SLOT_NAME_SIZE); + udev_device_unref(hostdev); + return 0; + } + udev_device_unref(hostdev); + return 1; +} + +int sysfs_get_iscsi_ip_address(struct path *pp, char *ip_address) +{ + struct udev_device *hostdev; + char host_name[HOST_NAME_LEN]; + const char *value; + + sprintf(host_name, "host%d", pp->sg_id.host_no); + hostdev = udev_device_new_from_subsystem_sysname(conf->udev, + "iscsi_host", host_name); + if (hostdev) { + value = udev_device_get_sysattr_value(hostdev, + "ipaddress"); + if (value) { + strncpy(ip_address, value, SLOT_NAME_SIZE); + udev_device_unref(hostdev); + return 0; + } else + udev_device_unref(hostdev); + } return 1; } @@ -609,6 +770,29 @@ get_serial (char * str, int maxlen, int fd) return 1; } +#define DEFAULT_SGIO_LEN 254 + +static int +sgio_get_vpd (unsigned char * buff, int maxlen, int fd) +{ + int len = DEFAULT_SGIO_LEN; + + if (fd < 0) { + errno = EBADF; + return -1; + } +retry: + if (0 == do_inq(fd, 0, 1, 0x83, buff, len)) { + len = buff[3] + (buff[2] << 8); + if (len >= maxlen) + return len; + if (len > DEFAULT_SGIO_LEN) + goto retry; + return 0; + } + return -1; +} + static int get_geometry(struct path *pp) { @@ -626,6 +810,256 @@ get_geometry(struct path *pp) return 0; } +static int +parse_vpd_pg80(const unsigned char *in, char *out, size_t out_len) +{ + char *p = NULL; + int len = in[3] + (in[2] << 8); + + if (len >= out_len) { + condlog(2, "vpd pg80 overflow, %d/%d bytes required", + len, (int)out_len); + len = out_len; + } + if (len > 0) { + memcpy(out, in + 4, len); + out[len] = '\0'; + } + /* + * Strip trailing whitspaces + */ + p = out + len - 1; + while (p > out && *p == ' ') { + *p = '\0'; + p--; + len --; + } + return len; +} + +static int +parse_vpd_pg83(const unsigned char *in, size_t in_len, + char *out, size_t out_len) +{ + unsigned char *d; + unsigned char *vpd = NULL; + int len = -ENODATA, vpd_type, vpd_len, prio = -1, i, naa_prio; + + d = (unsigned char *)in + 4; + while (d < (unsigned char *)in + in_len) { + /* Select 'association: LUN' */ + if ((d[1] & 0x30) != 0) { + d += d[3] + 4; + continue; + } + switch (d[1] & 0xf) { + case 0x3: + /* NAA: Prio 5 */ + switch (d[4] >> 4) { + case 6: + /* IEEE Registered Extended: Prio 8 */ + naa_prio = 8; + break; + case 5: + /* IEEE Registered: Prio 7 */ + naa_prio = 7; + break; + case 2: + /* IEEE Extended: Prio 6 */ + naa_prio = 6; + break; + case 3: + /* IEEE Locally assigned: Prio 1 */ + naa_prio = 1; + break; + default: + /* Default: no priority */ + naa_prio = -1; + break; + } + if (prio < naa_prio) { + prio = naa_prio; + vpd = d; + } + break; + case 0x8: + /* SCSI Name: Prio 4 */ + if (memcmp(d + 4, "eui.", 4) && + memcmp(d + 4, "naa.", 4) && + memcmp(d + 4, "iqn.", 4)) + continue; + if (prio < 4) { + prio = 4; + vpd = d; + } + break; + case 0x2: + /* EUI-64: Prio 3 */ + if (prio < 3) { + prio = 3; + vpd = d; + } + break; + case 0x1: + /* T-10 Vendor ID: Prio 2 */ + if (prio < 2) { + prio = 2; + vpd = d; + } + break; + } + d += d[3] + 4; + } + if (prio > 0) { + vpd_type = vpd[1] & 0xf; + vpd_len = vpd[3]; + vpd += 4; + if (vpd_type == 0x2 || vpd_type == 0x3) { + int i; + + len = sprintf(out, "%d", vpd_type); + for (i = 0; i < vpd_len; i++) { + len += sprintf(out + len, + "%02x", vpd[i]); + if (len >= out_len) + break; + } + } else if (vpd_type == 0x8) { + if (!memcmp("eui.", vpd, 4)) { + out[0] = '2'; + len = 1; + vpd += 4; + vpd_len -= 4; + for (i = 0; i < vpd_len; i++) { + len += sprintf(out + len, "%c", + tolower(vpd[i])); + if (len >= out_len) + break; + } + len = vpd_len + 1; + out[len] = '\0'; + } else if (!memcmp("naa.", vpd, 4)) { + out[0] = '3'; + len = 1; + vpd += 4; + vpd_len -= 4; + for (i = 0; i < vpd_len; i++) { + len += sprintf(out + len, "%c", + tolower(vpd[i])); + if (len >= out_len) + break; + } + len = vpd_len + 1; + out[len] = '\0'; + } else { + out[0] = '8'; + len = 1; + vpd += 4; + vpd_len -= 4; + if (vpd_len > out_len + 2) + vpd_len = out_len - 2; + memcpy(out, vpd, vpd_len); + len = vpd_len + 1; + out[len] = '\0'; + } + } else if (vpd_type == 0x1) { + 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) + p_len = out_len - len - 2; + memcpy(out + len, vpd, p_len); + len += p_len; + if (len >= out_len - 1) { + out[len] = '\0'; + break; + } + out[len] = '_'; + len ++; + vpd = p; + vpd_len -= p_len; + while (vpd && *vpd == ' ') { + vpd++; + vpd_len --; + } + } + if (len > 1 && out[len - 1] == '_') { + out[len - 1] = '\0'; + len--; + } + } + } + return len; +} + +static int +get_vpd_sysfs (struct udev_device *parent, int pg, char * str, int maxlen) +{ + int len, buff_len; + unsigned char buff[4096]; + + memset(buff, 0x0, 4096); + if (!parent || sysfs_get_vpd(parent, pg, buff, 4096) <= 0) { + condlog(3, "failed to read sysfs vpd pg%02x", pg); + return -EINVAL; + } + + if (buff[1] != pg) { + condlog(3, "vpd pg%02x error, invalid vpd page %02x", + pg, buff[1]); + return -ENODATA; + } + buff_len = (buff[2] << 8) + buff[3] + 4; + if (buff_len > 4096) + condlog(3, "vpd pg%02x page truncated", pg); + + if (pg == 0x80) + len = parse_vpd_pg80(buff, str, maxlen); + else if (pg == 0x83) + len = parse_vpd_pg83(buff, buff_len, str, maxlen); + else + len = -ENOSYS; + + return len; +} + +static int +get_vpd_sgio (int fd, int pg, char * str, int maxlen) +{ + int len, buff_len; + unsigned char buff[4096]; + + memset(buff, 0x0, 4096); + if (sgio_get_vpd(buff, 4096, fd) <= 0) { + condlog(3, "failed to issue vpd inquiry for pg%02x", + pg); + return -errno; + } + + if (buff[1] != pg) { + condlog(3, "vpd pg%02x error, invalid vpd page %02x", + pg, buff[1]); + return -ENODATA; + } + buff_len = (buff[2] << 8) + buff[3] + 4; + if (buff_len > 4096) + condlog(3, "vpd pg%02x page truncated", pg); + + if (pg == 0x80) + len = parse_vpd_pg80(buff, str, maxlen); + else if (pg == 0x83) + len = parse_vpd_pg83(buff, buff_len, str, maxlen); + else + len = -ENOSYS; + + return len; +} + static int scsi_sysfs_pathinfo (struct path * pp) { @@ -684,10 +1118,11 @@ scsi_sysfs_pathinfo (struct path * pp) /* * target node name */ - if(!sysfs_get_tgt_nodename(pp, pp->tgt_node_name)) { - condlog(3, "%s: tgt_node_name = %s", - pp->dev, pp->tgt_node_name); - } + if(sysfs_get_tgt_nodename(pp, pp->tgt_node_name)) + return 1; + + condlog(3, "%s: tgt_node_name = %s", + pp->dev, pp->tgt_node_name); return 0; } @@ -810,6 +1245,8 @@ cciss_sysfs_pathinfo (struct path * pp) static int common_sysfs_pathinfo (struct path * pp) { + dev_t devt; + if (!pp) return 1; @@ -817,10 +1254,8 @@ common_sysfs_pathinfo (struct path * pp) condlog(4, "%s: udev not initialised", pp->dev); return 1; } - if (sysfs_get_dev(pp->udev, pp->dev_t, BLK_DEV_SIZE) <= 0) { - condlog(3, "%s: no 'dev' attribute in sysfs", pp->dev); - return 1; - } + devt = udev_device_get_devnum(pp->udev); + snprintf(pp->dev_t, BLK_DEV_SIZE, "%d:%d", major(devt), minor(devt)); condlog(3, "%s: dev_t = %s", pp->dev, pp->dev_t); @@ -867,9 +1302,7 @@ path_offline (struct path * pp) condlog(3, "%s: path state = %s", pp->dev, buff); - if (!strncmp(buff, "offline", 7) || - !strncmp(buff, "quiesce", 7) || - !strncmp(buff, "transport-offline", 17)) { + if (!strncmp(buff, "offline", 7)) { pp->offline = 1; return PATH_DOWN; } @@ -914,10 +1347,34 @@ sysfs_pathinfo(struct path * pp) static int scsi_ioctl_pathinfo (struct path * pp, int mask) { - if (mask & DI_SERIAL) { - get_serial(pp->serial, SERIAL_SIZE, pp->fd); - condlog(3, "%s: serial = %s", pp->dev, pp->serial); + struct udev_device *parent; + const char *attr_path = NULL; + + if (!(mask & DI_SERIAL)) + return 0; + + parent = pp->udev; + while (parent) { + const char *subsys = udev_device_get_subsystem(parent); + if (subsys && !strncmp(subsys, "scsi", 4)) { + attr_path = udev_device_get_sysname(parent); + if (!attr_path) + break; + if (sscanf(attr_path, "%i:%i:%i:%i", + &pp->sg_id.host_no, + &pp->sg_id.channel, + &pp->sg_id.scsi_id, + &pp->sg_id.lun) == 4) + break; + } + parent = udev_device_get_parent(parent); } + if (!attr_path || pp->sg_id.host_no == -1) + return 0; + + if (get_vpd_sysfs(parent, 0x80, pp->serial, SERIAL_SIZE) > 0) + condlog(3, "%s: serial = %s", + pp->dev, pp->serial); return 0; } @@ -942,7 +1399,7 @@ get_state (struct path * pp, int daemon) if (!checker_selected(c)) { if (daemon) { - if (pathinfo(pp, conf->hwtable, DI_SYSFS) != 0) { + if (pathinfo(pp, conf->hwtable, DI_SYSFS) != PATHINFO_OK) { condlog(3, "%s: couldn't get sysfs pathinfo", pp->dev); return PATH_UNCHECKED; @@ -961,8 +1418,12 @@ get_state (struct path * pp, int daemon) } } checker_clear_message(c); - if (daemon) - checker_set_async(c); + if (daemon) { + 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; @@ -1003,11 +1464,53 @@ get_prio (struct path * pp) return 0; } +static int +get_udev_uid(struct path * pp, char *uid_attribute) +{ + ssize_t len; + const char *value; + + value = udev_device_get_property_value(pp->udev, + uid_attribute); + if ((!value || strlen(value) == 0) && conf->cmd == CMD_VALID_PATH) + value = getenv(uid_attribute); + if (value && strlen(value)) { + if (strlen(value) + 1 > WWID_SIZE) { + condlog(0, "%s: wwid overflow", pp->dev); + len = WWID_SIZE; + } else { + len = strlen(value); + } + strncpy(pp->wwid, value, len); + } else { + condlog(3, "%s: no %s attribute", pp->dev, + uid_attribute); + len = -EINVAL; + } + return len; +} + +static int +get_vpd_uid(struct path * pp) +{ + struct udev_device *parent = pp->udev; + + while (parent) { + const char *subsys = udev_device_get_subsystem(parent); + if (subsys && !strncmp(subsys, "scsi", 4)) + break; + parent = udev_device_get_parent(parent); + } + + return get_vpd_sysfs(parent, 0x83, pp->wwid, WWID_SIZE); +} + static int get_uid (struct path * pp) { char *c; - const char *origin; + const char *origin = "unknown"; + ssize_t len = 0; if (!pp->uid_attribute && !pp->getuid) select_getuid(pp); @@ -1026,39 +1529,50 @@ get_uid (struct path * pp) if (apply_format(pp->getuid, &buff[0], pp)) { condlog(0, "error formatting uid callout command"); memset(pp->wwid, 0, WWID_SIZE); + len = -EINVAL; } else if (execute_program(buff, pp->wwid, WWID_SIZE)) { condlog(3, "error calling out %s", buff); memset(pp->wwid, 0, WWID_SIZE); - } + len = -EIO; + } else + len = strlen(pp->wwid); origin = "callout"; } else { - const char *value; - - value = udev_device_get_property_value(pp->udev, - pp->uid_attribute); - if ((!value || strlen(value) == 0) && conf->dry_run == 2) - value = getenv(pp->uid_attribute); - if (value && strlen(value)) { - size_t len = WWID_SIZE; + if (pp->uid_attribute) { + len = get_udev_uid(pp, pp->uid_attribute); + origin = "udev"; + if (len <= 0) + condlog(1, + "%s: failed to get udev uid: %s", + pp->dev, strerror(-len)); - if (strlen(value) + 1 > WWID_SIZE) { - condlog(0, "%s: wwid overflow", pp->dev); - } else { - len = strlen(value); + } + if (len <= 0 && pp->retriggers >= conf->retrigger_tries && + !strcmp(pp->uid_attribute, DEFAULT_UID_ATTRIBUTE)) { + len = get_vpd_uid(pp); + origin = "sysfs"; + pp->uid_attribute = NULL; + if (len < 0) { + condlog(1, "%s: failed to get sysfs uid: %s", + pp->dev, strerror(-len)); + len = get_vpd_sgio(pp->fd, 0x83, pp->wwid, + WWID_SIZE); + origin = "sgio"; } - strncpy(pp->wwid, value, len); - } else { - condlog(3, "%s: no %s attribute", pp->dev, - pp->uid_attribute); } - origin = "udev"; } - /* Strip any trailing blanks */ - c = strchr(pp->wwid, '\0'); - c--; - while (c && c >= pp->wwid && *c == ' ') { - *c = '\0'; + if ( len < 0 ) { + condlog(1, "%s: failed to get %s uid: %s", + pp->dev, origin, strerror(-len)); + memset(pp->wwid, 0x0, WWID_SIZE); + } else { + /* Strip any trailing blanks */ + c = strchr(pp->wwid, '\0'); c--; + while (c && c >= pp->wwid && *c == ' ') { + *c = '\0'; + c--; + } } condlog(3, "%s: uid = %s (%s)", pp->dev, *pp->wwid == '\0' ? "" : pp->wwid, origin); @@ -1071,7 +1585,7 @@ pathinfo (struct path *pp, vector hwtable, int mask) int path_state; if (!pp) - return 1; + return PATHINFO_FAILED; condlog(3, "%s: mask = 0x%x", pp->dev, mask); @@ -1079,12 +1593,12 @@ pathinfo (struct path *pp, vector hwtable, int mask) * fetch info available in sysfs */ if (mask & DI_SYSFS && sysfs_pathinfo(pp)) - return 1; + return PATHINFO_FAILED; if (mask & DI_BLACKLIST && mask & DI_SYSFS) { if (filter_device(conf->blist_device, conf->elist_device, pp->vendor_id, pp->product_id) > 0) { - return 2; + return PATHINFO_SKIPPED; } } @@ -1123,6 +1637,11 @@ pathinfo (struct path *pp, vector hwtable, int mask) goto blank; if (pp->state == PATH_TIMEOUT) pp->state = PATH_DOWN; + if (pp->state == PATH_UP && !pp->size) { + condlog(3, "%s: device size is 0, " + "path unuseable", pp->dev); + pp->state = PATH_GHOST; + } } else { condlog(3, "%s: path inaccessible", pp->dev); pp->chkrstate = pp->state = path_state; @@ -1132,12 +1651,21 @@ pathinfo (struct path *pp, vector hwtable, int mask) } } - if ((mask & DI_WWID) && !strlen(pp->wwid)) + if ((mask & DI_WWID) && !strlen(pp->wwid)) { get_uid(pp); + if (!strlen(pp->wwid)) { + pp->initialized = INIT_MISSING_UDEV; + pp->tick = conf->retrigger_delay; + return PATHINFO_OK; + } + else + pp->tick = 1; + } + if (mask & DI_BLACKLIST && mask & DI_WWID) { if (filter_wwid(conf->blist_wwid, conf->elist_wwid, - pp->wwid) > 0) { - return 2; + pp->wwid, pp->dev) > 0) { + return PATHINFO_SKIPPED; } } @@ -1145,15 +1673,14 @@ pathinfo (struct path *pp, vector hwtable, int mask) * Retrieve path priority, even for PATH_DOWN paths if it has never * been successfully obtained before. */ - if ((mask & DI_PRIO) && path_state == PATH_UP) { + if ((mask & DI_PRIO) && path_state == PATH_UP && strlen(pp->wwid)) { if (pp->state != PATH_DOWN || pp->priority == PRIO_UNDEF) { - if (!strlen(pp->wwid)) - get_uid(pp); get_prio(pp); } } - return 0; + pp->initialized = INIT_OK; + return PATHINFO_OK; blank: /* @@ -1161,6 +1688,7 @@ blank: */ memset(pp->wwid, 0, WWID_SIZE); pp->chkrstate = pp->state = PATH_DOWN; + pp->initialized = INIT_FAILED; - return 0; + return PATHINFO_OK; } diff --git a/libmultipath/discovery.h b/libmultipath/discovery.h index 3d2d0ce..5931bc6 100644 --- a/libmultipath/discovery.h +++ b/libmultipath/discovery.h @@ -24,20 +24,29 @@ #define SCSI_COMMAND_TERMINATED 0x22 #define SG_ERR_DRIVER_SENSE 0x08 +#define PATHINFO_OK 0 +#define PATHINFO_FAILED 1 +#define PATHINFO_SKIPPED 2 + struct config; -ssize_t sysfs_get_dev (struct udev_device *udev, char * buff, size_t len); int path_discovery (vector pathvec, struct config * conf, int flag); int do_tur (char *); int path_offline (struct path *); int get_state (struct path * pp, int daemon); int pathinfo (struct path *, vector hwtable, int mask); +int alloc_path_with_pathinfo (vector hwtable, struct udev_device *udevice, + int flag, struct path **pp_ptr); int store_pathinfo (vector pathvec, vector hwtable, struct udev_device *udevice, int flag, struct path **pp_ptr); int sysfs_set_scsi_tmo (struct multipath *mpp); int sysfs_get_timeout(struct path *pp, unsigned int *timeout); +int sysfs_get_host_pci_name(struct path *pp, char *pci_name); +int sysfs_get_iscsi_ip_address(struct path *pp, char *ip_address); +ssize_t sysfs_get_vpd (struct udev_device * udev, int pg, unsigned char * buff, + size_t len); /* * discovery bitmask diff --git a/libmultipath/dmparser.c b/libmultipath/dmparser.c index 2562ba1..2209b2d 100644 --- a/libmultipath/dmparser.c +++ b/libmultipath/dmparser.c @@ -20,14 +20,16 @@ static int merge_words (char ** dst, char * word, int space) { - char * p; + char * p = *dst; int len; len = strlen(*dst) + strlen(word) + space; *dst = REALLOC(*dst, len + 1); - if (!*dst) + if (!*dst) { + free(p); return 1; + } p = *dst; diff --git a/libmultipath/hwtable.c b/libmultipath/hwtable.c index bb73a40..a4ae053 100644 --- a/libmultipath/hwtable.c +++ b/libmultipath/hwtable.c @@ -272,6 +272,8 @@ static struct hwentry default_hw[] = { .checker_name = EMC_CLARIION, .prio_name = PRIO_EMC, .prio_args = NULL, + .retain_hwhandler = RETAIN_HWHANDLER_ON, + .detect_prio = DETECT_PRIO_ON, }, { .vendor = "EMC", @@ -287,6 +289,19 @@ static struct hwentry default_hw[] = { .prio_name = DEFAULT_PRIO, .prio_args = NULL, }, + { + .vendor = "XtremIO", + .product = "XtremApp", + .features = DEFAULT_FEATURES, + .hwhandler = DEFAULT_HWHANDLER, + .selector = "queue-length 0", + .pgpolicy = MULTIBUS, + .pgfailback = FAILBACK_UNDEF, + .checker_name = TUR, + .fast_io_fail = 5, + .prio_name = DEFAULT_PRIO, + .prio_args = NULL, + }, /* * Fujitsu controller family * @@ -308,7 +323,7 @@ static struct hwentry default_hw[] = { }, { .vendor = "FUJITSU", - .product = "ETERNUS_DX(L|400|8000)", + .product = "ETERNUS_DX(H|L|M|400|8000)", .features = "1 queue_if_no_path", .hwhandler = DEFAULT_HWHANDLER, .pgpolicy = GROUP_BY_PRIO, @@ -772,6 +787,36 @@ static struct hwentry default_hw[] = { .prio_name = PRIO_RDAC, .prio_args = NULL, }, + { + /* DELL MD36xxi */ + .vendor = "DELL", + .product = "MD36xxi", + .bl_product = "Universal Xport", + .features = "2 pg_init_retries 50", + .hwhandler = "1 rdac", + .pgpolicy = GROUP_BY_PRIO, + .pgfailback = -FAILBACK_IMMEDIATE, + .rr_weight = RR_WEIGHT_NONE, + .no_path_retry = 15, + .checker_name = RDAC, + .prio_name = PRIO_RDAC, + .prio_args = NULL, + }, + { + /* DELL MD36xxf */ + .vendor = "DELL", + .product = "MD36xxf", + .bl_product = "Universal Xport", + .features = "2 pg_init_retries 50", + .hwhandler = "1 rdac", + .pgpolicy = GROUP_BY_PRIO, + .pgfailback = -FAILBACK_IMMEDIATE, + .rr_weight = RR_WEIGHT_NONE, + .no_path_retry = 15, + .checker_name = RDAC, + .prio_name = PRIO_RDAC, + .prio_args = NULL, + }, /* * NETAPP controller family * @@ -1121,6 +1166,22 @@ static struct hwentry default_hw[] = { .prio_name = PRIO_ALUA, .prio_args = NULL, }, + { + .vendor = "PURE", + .product = "FlashArray", + .features = DEFAULT_FEATURES, + .hwhandler = DEFAULT_HWHANDLER, + .selector = "queue-length 0", + .pgpolicy = MULTIBUS, + .pgfailback = -FAILBACK_IMMEDIATE, + .checker_name = TUR, + .fast_io_fail = 10, + .user_friendly_names = USER_FRIENDLY_NAMES_OFF, + .prio_name = DEFAULT_PRIO, + .no_path_retry = 0, + .dev_loss = 60, + .prio_args = NULL, + }, /* * EOL */ diff --git a/libmultipath/list.h b/libmultipath/list.h index 8626630..a0d8184 100644 --- a/libmultipath/list.h +++ b/libmultipath/list.h @@ -8,6 +8,8 @@ #ifndef _LIST_H #define _LIST_H +#include + /** * container_of - cast a member of a structure out to the containing structure * @@ -161,18 +163,18 @@ static inline int list_empty(struct list_head *head) return head->next == head; } -static inline void __list_splice(struct list_head *list, - struct list_head *head) +static inline void __list_splice(const struct list_head *list, + struct list_head *prev, + struct list_head *next) { struct list_head *first = list->next; struct list_head *last = list->prev; - struct list_head *at = head->next; - first->prev = head; - head->next = first; + first->prev = prev; + prev->next = first; - last->next = at; - at->prev = last; + last->next = next; + next->prev = last; } /** @@ -183,7 +185,19 @@ static inline void __list_splice(struct list_head *list, static inline void list_splice(struct list_head *list, struct list_head *head) { if (!list_empty(list)) - __list_splice(list, head); + __list_splice(list, head, head->next); +} + +/** + * list_splice_tail - join two lists, each list being a queue + * @list: the new list to add. + * @head: the place to add it in the first list. + */ +static inline void list_splice_tail(struct list_head *list, + struct list_head *head) +{ + if (!list_empty(list)) + __list_splice(list, head->prev, head); } /** @@ -197,7 +211,24 @@ static inline void list_splice_init(struct list_head *list, struct list_head *head) { if (!list_empty(list)) { - __list_splice(list, head); + __list_splice(list, head, head->next); + INIT_LIST_HEAD(list); + } +} + +/** + * list_splice_tail_init - join two lists and reinitialise the emptied list + * @list: the new list to add. + * @head: the place to add it in the first list. + * + * Each of the lists is a queue. + * The list at @list is reinitialised + */ +static inline void list_splice_tail_init(struct list_head *list, + struct list_head *head) +{ + if (!list_empty(list)) { + __list_splice(list, head->prev, head); INIT_LIST_HEAD(list); } } diff --git a/libmultipath/log_pthread.c b/libmultipath/log_pthread.c index 47d75a1..e6f4b5c 100644 --- a/libmultipath/log_pthread.c +++ b/libmultipath/log_pthread.c @@ -25,7 +25,7 @@ int logq_running; void log_safe (int prio, const char * fmt, va_list ap) { if (log_thr == (pthread_t)0) { - syslog(prio, fmt, ap); + vsyslog(prio, fmt, ap); return; } diff --git a/libmultipath/parser.c b/libmultipath/parser.c index 526c45b..e4296ee 100644 --- a/libmultipath/parser.c +++ b/libmultipath/parser.c @@ -18,6 +18,7 @@ */ #include +#include #include "parser.h" #include "memory.h" @@ -279,110 +280,15 @@ out: return NULL; } -int -read_line(char *buf, int size) +static int +read_line(FILE *stream, char *buf, int size) { - int ch; - int count = 0; - - while ((ch = fgetc(stream)) != EOF && (int) ch != '\n' - && (int) ch != '\r') { - if (count < size) - buf[count] = (int) ch; - else - break; - count++; - } - return (ch == EOF) ? 0 : 1; -} - -vector -read_value_block(void) -{ - char *buf; - int i; - char *str = NULL; - char *dup; - vector vec = NULL; - vector elements = vector_alloc(); - - if (!elements) - return NULL; + char *p; - buf = (char *) MALLOC(MAXBUF); - - if (!buf) { - vector_free(elements); - return NULL; - } - - while (read_line(buf, MAXBUF)) { - vec = alloc_strvec(buf); - if (vec) { - str = VECTOR_SLOT(vec, 0); - if (!strcmp(str, EOB)) { - free_strvec(vec); - break; - } - - for (i = 0; i < VECTOR_SIZE(vec); i++) { - str = VECTOR_SLOT(vec, i); - dup = (char *) MALLOC(strlen(str) + 1); - if (!dup) - goto out; - memcpy(dup, str, strlen(str)); - - if (!vector_alloc_slot(elements)) { - free_strvec(vec); - goto out1; - } - - vector_set_slot(elements, dup); - } - free_strvec(vec); - } - memset(buf, 0, MAXBUF); - } - FREE(buf); - return elements; -out1: - FREE(dup); -out: - FREE(buf); - vector_free(elements); - return NULL; -} - -int -alloc_value_block(vector strvec, void (*alloc_func) (vector)) -{ - char *buf; - char *str = NULL; - vector vec = NULL; - - buf = (char *) MALLOC(MAXBUF); - - if (!buf) - return 1; - - while (read_line(buf, MAXBUF)) { - vec = alloc_strvec(buf); - if (vec) { - str = VECTOR_SLOT(vec, 0); - if (!strcmp(str, EOB)) { - free_strvec(vec); - break; - } - - if (VECTOR_SIZE(vec)) - (*alloc_func) (vec); - - free_strvec(vec); - } - memset(buf, 0, MAXBUF); - } - FREE(buf); - return 0; + if (fgets(buf, size, stream) == NULL) + return 0; + strtok_r(buf, "\n\r", &p); + return 1; } void * @@ -395,36 +301,57 @@ set_value(vector strvec) char *alloc = NULL; char *tmp; - if (!str) + if (!str) { + condlog(0, "option '%s' missing value", + (char *)VECTOR_SLOT(strvec, 0)); return NULL; - + } size = strlen(str); - if (size == 0) + if (size == 0) { + condlog(0, "option '%s' has empty value", + (char *)VECTOR_SLOT(strvec, 0)); return NULL; - - if (*str == '"') { - for (i = 2; i < VECTOR_SIZE(strvec); i++) { - str = VECTOR_SLOT(strvec, i); - len += strlen(str); - if (!alloc) - alloc = - (char *) MALLOC(sizeof (char *) * - (len + 1)); - else { - alloc = - REALLOC(alloc, sizeof (char *) * (len + 1)); - tmp = VECTOR_SLOT(strvec, i-1); - if (alloc && *str != '"' && *tmp != '"') - strncat(alloc, " ", 1); - } - - if (alloc && i != VECTOR_SIZE(strvec)-1) - strncat(alloc, str, strlen(str)); - } - } else { - alloc = MALLOC(sizeof (char *) * (size + 1)); + } + if (*str != '"') { + alloc = MALLOC(sizeof (char) * (size + 1)); if (alloc) memcpy(alloc, str, size); + else + condlog(0, "can't allocate memeory for option '%s'", + (char *)VECTOR_SLOT(strvec, 0)); + return alloc; + } + /* Even empty quotes counts as a value (An empty string) */ + alloc = (char *) MALLOC(sizeof (char)); + if (!alloc) { + condlog(0, "can't allocate memeory for option '%s'", + (char *)VECTOR_SLOT(strvec, 0)); + return NULL; + } + for (i = 2; i < VECTOR_SIZE(strvec); i++) { + str = VECTOR_SLOT(strvec, i); + if (!str) { + free(alloc); + condlog(0, "parse error for option '%s'", + (char *)VECTOR_SLOT(strvec, 0)); + return NULL; + } + if (*str == '"') + break; + tmp = alloc; + /* The first +1 is for the NULL byte. The rest are for the + * spaces between words */ + len += strlen(str) + 1; + alloc = REALLOC(alloc, sizeof (char) * len); + if (!alloc) { + FREE(tmp); + condlog(0, "can't allocate memeory for option '%s'", + (char *)VECTOR_SLOT(strvec, 0)); + return NULL; + } + if (*alloc != '\0') + strncat(alloc, " ", 1); + strncat(alloc, str, strlen(str)); } return alloc; } @@ -432,14 +359,15 @@ set_value(vector strvec) /* non-recursive configuration stream handler */ static int kw_level = 0; -int warn_on_duplicates(vector uniques, char *str) +int warn_on_duplicates(vector uniques, char *str, char *file) { char *tmp; int i; vector_foreach_slot(uniques, tmp, i) { if (!strcmp(str, tmp)) { - condlog(1, "multipath.conf line %d, duplicate keyword: %s", line_nr, str); + condlog(1, "%s line %d, duplicate keyword: %s", + file, line_nr, str); return 0; } } @@ -465,10 +393,83 @@ void free_uniques(vector uniques) } int -process_stream(vector keywords) +is_sublevel_keyword(char *str) +{ + return (strcmp(str, "defaults") == 0 || strcmp(str, "blacklist") == 0 || + strcmp(str, "blacklist_exceptions") == 0 || + strcmp(str, "devices") == 0 || strcmp(str, "devices") == 0 || + strcmp(str, "device") == 0 || strcmp(str, "multipaths") == 0 || + strcmp(str, "multipath") == 0); +} + +int +validate_config_strvec(vector strvec, char *file) +{ + char *str; + int i; + + str = VECTOR_SLOT(strvec, 0); + if (str == NULL) { + condlog(0, "can't parse option on line %d of %s", + line_nr, file); + return -1; + } + if (*str == '}') { + if (VECTOR_SIZE(strvec) > 1) + condlog(0, "ignoring extra data starting with '%s' on line %d of %s", (char *)VECTOR_SLOT(strvec, 1), line_nr, file); + return 0; + } + if (*str == '{') { + condlog(0, "invalid keyword '%s' on line %d of %s", + str, line_nr, file); + return -1; + } + if (is_sublevel_keyword(str)) { + str = VECTOR_SLOT(strvec, 1); + if (str == NULL) + condlog(0, "missing '{' on line %d of %s", + line_nr, file); + else if (*str != '{') + condlog(0, "expecting '{' on line %d of %s. found '%s'", + line_nr, file, str); + else if (VECTOR_SIZE(strvec) > 2) + condlog(0, "ignoring extra data starting with '%s' on line %d of %s", (char *)VECTOR_SLOT(strvec, 2), line_nr, file); + return 0; + } + str = VECTOR_SLOT(strvec, 1); + if (str == NULL) { + condlog(0, "missing value for option '%s' on line %d of %s", + (char *)VECTOR_SLOT(strvec, 0), line_nr, file); + return -1; + } + if (*str != '"') { + if (VECTOR_SIZE(strvec) > 2) + condlog(0, "ignoring extra data starting with '%s' on line %d of %s", (char *)VECTOR_SLOT(strvec, 2), line_nr, file); + return 0; + } + for (i = 2; i < VECTOR_SIZE(strvec); i++) { + str = VECTOR_SLOT(strvec, i); + if (str == NULL) { + condlog(0, "can't parse value on line %d of %s", + line_nr, file); + return -1; + } + if (*str == '"') { + if (VECTOR_SIZE(strvec) > i + 1) + condlog(0, "ignoring extra data starting with '%s' on line %d of %s", (char *)VECTOR_SLOT(strvec, (i + 1)), line_nr, file); + return 0; + } + } + condlog(0, "missing closing quotes on line %d of %s", + line_nr, file); + return 0; +} + +static int +process_stream(FILE *stream, vector keywords, char *file) { int i; - int r = 0; + int r = 0, t; struct keyword *keyword; char *str; char *buf; @@ -486,19 +487,26 @@ process_stream(vector keywords) return 1; } - while (read_line(buf, MAXBUF)) { + while (read_line(stream, buf, MAXBUF)) { line_nr++; strvec = alloc_strvec(buf); - memset(buf,0, MAXBUF); - if (!strvec) continue; + if (validate_config_strvec(strvec, file) != 0) { + free_strvec(strvec); + continue; + } + str = VECTOR_SLOT(strvec, 0); - if (!strcmp(str, EOB) && kw_level > 0) { - free_strvec(strvec); - break; + if (!strcmp(str, EOB)) { + if (kw_level > 0) { + free_strvec(strvec); + break; + } + condlog(0, "unmatched '%s' at line %d of %s", + EOB, line_nr, file); } for (i = 0; i < VECTOR_SIZE(keywords); i++) { @@ -506,25 +514,31 @@ process_stream(vector keywords) if (!strcmp(keyword->string, str)) { if (keyword->unique && - warn_on_duplicates(uniques, str)) { + warn_on_duplicates(uniques, str, file)) { r = 1; free_strvec(strvec); goto out; } - if (keyword->handler) - r += (*keyword->handler) (strvec); + if (keyword->handler) { + t = (*keyword->handler) (strvec); + r += t; + if (t) + condlog(1, "multipath.conf +%d, parsing failed: %s", + line_nr, buf); + } if (keyword->sub) { kw_level++; - r += process_stream(keyword->sub); + r += process_stream(stream, + keyword->sub, file); kw_level--; } break; } } if (i >= VECTOR_SIZE(keywords)) - condlog(1, "multipath.conf +%d, invalid keyword: %s", - line_nr, str); + condlog(1, "%s line %d, invalid keyword: %s", + file, line_nr, str); free_strvec(strvec); } @@ -548,27 +562,25 @@ int alloc_keywords(void) /* Data initialization */ int -init_data(char *conf_file, void (*init_keywords) (void)) +process_file(char *file) { int r; + FILE *stream; - stream = fopen(conf_file, "r"); + if (!keywords) { + condlog(0, "No keywords alocated"); + return 1; + } + stream = fopen(file, "r"); if (!stream) { - syslog(LOG_WARNING, "Configuration file open problem"); + condlog(0, "couldn't open configuration file '%s': %s", + file, strerror(errno)); return 1; } - /* Init Keywords structure */ - (*init_keywords) (); - -/* Dump configuration * - vector_dump(keywords); - dump_keywords(keywords, 0); -*/ - /* Stream handling */ line_nr = 0; - r = process_stream(keywords); + r = process_stream(stream, keywords, file); fclose(stream); //free_keywords(keywords); diff --git a/libmultipath/parser.h b/libmultipath/parser.h index 8bf1c76..ba6859e 100644 --- a/libmultipath/parser.h +++ b/libmultipath/parser.h @@ -47,9 +47,6 @@ struct keyword { int unique; }; -/* global var exported */ -FILE *stream; - /* Reloading helpers */ #define SET_RELOAD (reload = 1) #define UNSET_RELOAD (reload = 0) @@ -72,13 +69,9 @@ extern int _install_keyword(char *string, int (*handler) (vector), extern void dump_keywords(vector keydump, int level); extern void free_keywords(vector keywords); extern vector alloc_strvec(char *string); -extern int read_line(char *buf, int size); -extern vector read_value_block(void); -extern int alloc_value_block(vector strvec, void (*alloc_func) (vector)); extern void *set_value(vector strvec); -extern int process_stream(vector keywords); extern int alloc_keywords(void); -extern int init_data(char *conf_file, void (*init_keywords) (void)); +extern int process_file(char *conf_file); extern struct keyword * find_keyword(vector v, char * name); void set_current_keywords (vector *k); int snprint_keyword(char *buff, int len, char *fmt, struct keyword *kw, diff --git a/libmultipath/pgpolicies.c b/libmultipath/pgpolicies.c index f76ad60..2981d51 100644 --- a/libmultipath/pgpolicies.c +++ b/libmultipath/pgpolicies.c @@ -27,7 +27,7 @@ get_pgpolicy_id (char * str) if (0 == strncmp(str, "group_by_node_name", 18)) return GROUP_BY_NODE_NAME; - return -1; + return IOPOLICY_UNDEF; } extern int diff --git a/libmultipath/print.c b/libmultipath/print.c index 3c526c2..76bda93 100644 --- a/libmultipath/print.c +++ b/libmultipath/print.c @@ -10,6 +10,7 @@ #include #include #include +#include #include "checkers.h" #include "vector.h" @@ -31,20 +32,27 @@ #define MAX(x,y) (x > y) ? x : y #define TAIL (line + len - 1 - c) #define NOPAD s = c -#define PAD(x) while ((int)(c - s) < (x) && (c < (line + len - 1))) \ - *c++ = ' '; s = c +#define PAD(x) \ +do { \ + while ((int)(c - s) < (x) && (c < (line + len - 1))) \ + *c++ = ' '; \ + s = c; \ +} while (0) + #define ENDLINE \ if (c > line) \ line[c - line - 1] = '\n' -#define PRINT(var, size, format, args...) \ - fwd = snprintf(var, size, format, ##args); \ - c += (fwd >= size) ? size : fwd; +#define PRINT(var, size, format, args...) \ +do { \ + fwd = snprintf(var, size, format, ##args); \ + c += (fwd >= size) ? size : fwd; \ +} while (0) /* * information printing helpers */ static int -snprint_str (char * buff, size_t len, char * str) +snprint_str (char * buff, size_t len, const char * str) { return snprintf(buff, len, "%s", str); } @@ -248,11 +256,20 @@ snprint_multipath_uuid (char * buff, size_t len, struct multipath * mpp) static int snprint_multipath_vpr (char * buff, size_t len, struct multipath * mpp) { - struct path * pp = first_path(mpp); - if (!pp) - return 0; - return snprintf(buff, len, "%s,%s", - pp->vendor_id, pp->product_id); + struct pathgroup * pgp; + struct path * pp; + int i, j; + + vector_foreach_slot(mpp->pg, pgp, i) { + if (!pgp) + continue; + vector_foreach_slot(pgp->paths, pp, j) { + if (strlen(pp->vendor_id) && strlen(pp->product_id)) + return snprintf(buff, len, "%s,%s", + pp->vendor_id, pp->product_id); + } + } + return snprintf(buff, len, "##,##"); } static int @@ -286,7 +303,7 @@ snprint_path_uuid (char * buff, size_t len, struct path * pp) static int snprint_hcil (char * buff, size_t len, struct path * pp) { - if (pp->sg_id.host_no < 0) + if (!pp || pp->sg_id.host_no < 0) return snprintf(buff, len, "#:#:#:#"); return snprintf(buff, len, "%i:%i:%i:%i", @@ -299,7 +316,7 @@ snprint_hcil (char * buff, size_t len, struct path * pp) static int snprint_dev (char * buff, size_t len, struct path * pp) { - if (!strlen(pp->dev)) + if (!pp || !strlen(pp->dev)) return snprintf(buff, len, "-"); else return snprint_str(buff, len, pp->dev); @@ -308,7 +325,7 @@ snprint_dev (char * buff, size_t len, struct path * pp) static int snprint_dev_t (char * buff, size_t len, struct path * pp) { - if (!strlen(pp->dev)) + if (!pp || !strlen(pp->dev)) return snprintf(buff, len, "#:#"); else return snprint_str(buff, len, pp->dev_t); @@ -317,7 +334,9 @@ snprint_dev_t (char * buff, size_t len, struct path * pp) static int snprint_offline (char * buff, size_t len, struct path * pp) { - if (pp->offline) + if (!pp) + return snprintf(buff, len, "unknown"); + else if (pp->offline) return snprintf(buff, len, "offline"); else return snprintf(buff, len, "running"); @@ -326,6 +345,9 @@ snprint_offline (char * buff, size_t len, struct path * pp) static int snprint_chk_state (char * buff, size_t len, struct path * pp) { + if (!pp) + return snprintf(buff, len, "undef"); + switch (pp->state) { case PATH_UP: return snprintf(buff, len, "ready"); @@ -339,6 +361,8 @@ snprint_chk_state (char * buff, size_t len, struct path * pp) return snprintf(buff, len, "i/o pending"); case PATH_TIMEOUT: return snprintf(buff, len, "i/o timeout"); + case PATH_DELAYED: + return snprintf(buff, len, "delayed"); default: return snprintf(buff, len, "undef"); } @@ -347,6 +371,9 @@ snprint_chk_state (char * buff, size_t len, struct path * pp) static int snprint_dm_path_state (char * buff, size_t len, struct path * pp) { + if (!pp) + return snprintf(buff, len, "undef"); + switch (pp->dmstate) { case PSTATE_ACTIVE: return snprintf(buff, len, "active"); @@ -367,7 +394,7 @@ snprint_vpr (char * buff, size_t len, struct path * pp) static int snprint_next_check (char * buff, size_t len, struct path * pp) { - if (!pp->mpp) + if (!pp || !pp->mpp) return snprintf(buff, len, "orphan"); return snprint_progress(buff, len, pp->tick, pp->checkint); @@ -376,7 +403,7 @@ snprint_next_check (char * buff, size_t len, struct path * pp) static int snprint_pri (char * buff, size_t len, struct path * pp) { - return snprint_int(buff, len, pp->priority); + return snprint_int(buff, len, pp ? pp->priority : -1); } static int @@ -435,6 +462,93 @@ snprint_path_mpp (char * buff, size_t len, struct path * pp) return snprint_str(buff, len, pp->mpp->alias); } +static int +snprint_host_attr (char * buff, size_t len, struct path * pp, char *attr) +{ + struct udev_device *host_dev = NULL; + char host_id[32]; + const char *value = NULL; + int ret; + + if (pp->sg_id.proto_id != SCSI_PROTOCOL_FCP) + return snprintf(buff, len, "[undef]"); + sprintf(host_id, "host%d", pp->sg_id.host_no); + host_dev = udev_device_new_from_subsystem_sysname(conf->udev, "fc_host", + host_id); + if (!host_dev) { + condlog(1, "%s: No fc_host device for '%s'", pp->dev, host_id); + goto out; + } + value = udev_device_get_sysattr_value(host_dev, attr); + if (value) + ret = snprint_str(buff, len, value); + udev_device_unref(host_dev); +out: + if (!value) + ret = snprintf(buff, len, "[unknown]"); + return ret; +} + +int +snprint_host_wwnn (char * buff, size_t len, struct path * pp) +{ + return snprint_host_attr(buff, len, pp, "node_name"); +} + +int +snprint_host_wwpn (char * buff, size_t len, struct path * pp) +{ + return snprint_host_attr(buff, len, pp, "port_name"); +} + +int +snprint_tgt_wwpn (char * buff, size_t len, struct path * pp) +{ + struct udev_device *rport_dev = NULL; + char rport_id[32]; + const char *value = NULL; + int ret; + + if (pp->sg_id.proto_id != SCSI_PROTOCOL_FCP) + return snprintf(buff, len, "[undef]"); + sprintf(rport_id, "rport-%d:%d-%d", + pp->sg_id.host_no, pp->sg_id.channel, pp->sg_id.transport_id); + rport_dev = udev_device_new_from_subsystem_sysname(conf->udev, + "fc_remote_ports", rport_id); + if (!rport_dev) { + condlog(1, "%s: No fc_remote_port device for '%s'", pp->dev, + rport_id); + goto out; + } + value = udev_device_get_sysattr_value(rport_dev, "port_name"); + if (value) + ret = snprint_str(buff, len, value); + udev_device_unref(rport_dev); +out: + if (!value) + ret = snprintf(buff, len, "[unknown]"); + return ret; +} + + +int +snprint_tgt_wwnn (char * buff, size_t len, struct path * pp) +{ + if (pp->tgt_node_name[0] == '\0') + return snprintf(buff, len, "[undef]"); + return snprint_str(buff, len, pp->tgt_node_name); +} + +static int +snprint_host_adapter (char * buff, size_t len, struct path * pp) +{ + char adapter[SLOT_NAME_SIZE]; + + if (sysfs_get_host_adapter_name(pp, adapter)) + return snprintf(buff, len, "[undef]"); + return snprint_str(buff, len, adapter); +} + static int snprint_path_checker (char * buff, size_t len, struct path * pp) { @@ -479,6 +593,11 @@ struct path_data pd[] = { {'S', "size", 0, snprint_path_size}, {'z', "serial", 0, snprint_path_serial}, {'m', "multipath", 0, snprint_path_mpp}, + {'N', "host WWNN", 0, snprint_host_wwnn}, + {'n', "target WWNN", 0, snprint_tgt_wwnn}, + {'R', "host WWPN", 0, snprint_host_wwpn}, + {'r', "target WWPN", 0, snprint_tgt_wwpn}, + {'a', "host adapter", 0, snprint_host_adapter}, {0, NULL, 0 , NULL} }; @@ -629,7 +748,7 @@ snprint_multipath_header (char * line, int len, char * format) int snprint_multipath (char * line, int len, char * format, - struct multipath * mpp) + struct multipath * mpp, int pad) { char * c = line; /* line cursor */ char * s = line; /* for padding */ @@ -656,7 +775,8 @@ snprint_multipath (char * line, int len, char * format, data->snprint(buff, MAX_FIELD_LEN, mpp); PRINT(c, TAIL, "%s", buff); - PAD(data->width); + if (pad) + PAD(data->width); buff[0] = '\0'; } while (*f++); @@ -699,7 +819,7 @@ snprint_path_header (char * line, int len, char * format) int snprint_path (char * line, int len, char * format, - struct path * pp) + struct path * pp, int pad) { char * c = line; /* line cursor */ char * s = line; /* for padding */ @@ -726,7 +846,8 @@ snprint_path (char * line, int len, char * format, data->snprint(buff, MAX_FIELD_LEN, pp); PRINT(c, TAIL, "%s", buff); - PAD(data->width); + if (pad) + PAD(data->width); } while (*f++); ENDLINE; @@ -797,6 +918,7 @@ print_multipath_topology (struct multipath * mpp, int verbosity) } } while (resize); printf("%s", buff); + FREE(buff); } extern int @@ -817,7 +939,7 @@ snprint_multipath_topology (char * buff, int len, struct multipath * mpp, reset_multipath_layout(); if (verbosity == 1) - return snprint_multipath(buff, len, "%n", mpp); + return snprint_multipath(buff, len, "%n", mpp, 1); if(isatty(1)) c += sprintf(c, "%c[%dm", 0x1B, 1); /* bold on */ @@ -836,10 +958,11 @@ snprint_multipath_topology (char * buff, int len, struct multipath * mpp, if(isatty(1)) c += sprintf(c, "%c[%dm", 0x1B, 0); /* bold off */ - fwd += snprint_multipath(buff + fwd, len - fwd, style, mpp); + fwd += snprint_multipath(buff + fwd, len - fwd, style, mpp, 1); if (fwd > len) return len; - fwd += snprint_multipath(buff + fwd, len - fwd, PRINT_MAP_PROPS, mpp); + fwd += snprint_multipath(buff + fwd, len - fwd, PRINT_MAP_PROPS, mpp, + 1); if (fwd > len) return len; @@ -866,7 +989,7 @@ snprint_multipath_topology (char * buff, int len, struct multipath * mpp, strcpy(f, " |- " PRINT_PATH_INDENT); else strcpy(f, " `- " PRINT_PATH_INDENT); - fwd += snprint_path(buff + fwd, len - fwd, fmt, pp); + fwd += snprint_path(buff + fwd, len - fwd, fmt, pp, 1); if (fwd > len) return len; } @@ -986,6 +1109,36 @@ snprint_mptable (char * buff, int len, vector mptable) return fwd; } +extern int +snprint_overrides (char * buff, int len, struct hwentry *overrides) +{ + int fwd = 0; + int i; + struct keyword *rootkw; + struct keyword *kw; + + rootkw = find_keyword(NULL, "overrides"); + if (!rootkw) + return 0; + + fwd += snprintf(buff + fwd, len - fwd, "overrides {\n"); + if (fwd > len) + return len; + if (!overrides) + goto out; + iterate_sub_keywords(rootkw, kw, i) { + fwd += snprint_keyword(buff + fwd, len - fwd, "\t%k %v\n", + kw, NULL); + if (fwd > len) + return len; + } +out: + fwd += snprintf(buff + fwd, len - fwd, "}\n"); + if (fwd > len) + return len; + return fwd; +} + extern int snprint_defaults (char * buff, int len) { @@ -1334,8 +1487,10 @@ snprint_devices (char * buff, int len, struct vectors *vecs) if (!(blkdir = opendir("/sys/block"))) return 1; - if ((len - fwd - threshold) <= 0) + if ((len - fwd - threshold) <= 0) { + closedir(blkdir); return len; + } fwd += snprintf(buff + fwd, len - fwd, "available block devices:\n"); strcpy(devpath,"/sys/block/"); @@ -1353,8 +1508,10 @@ snprint_devices (char * buff, int len, struct vectors *vecs) if (S_ISDIR(statbuf.st_mode) == 0) continue; - if ((len - fwd - threshold) <= 0) + if ((len - fwd - threshold) <= 0) { + closedir(blkdir); return len; + } fwd += snprintf(buff + fwd, len - fwd, " %s", devptr); pp = find_path_by_dev(vecs->pathvec, devptr); @@ -1364,7 +1521,7 @@ snprint_devices (char * buff, int len, struct vectors *vecs) if (r > 0) fwd += snprintf(buff + fwd, len - fwd, " devnode blacklisted, unmonitored"); - else if (r < 0) + else if (r <= 0) fwd += snprintf(buff + fwd, len - fwd, " devnode whitelisted, unmonitored"); } else @@ -1394,7 +1551,7 @@ print_path (struct path * pp, char * style) char line[MAX_LINE_LEN]; memset(&line[0], 0, MAX_LINE_LEN); - snprint_path(&line[0], MAX_LINE_LEN, style, pp); + snprint_path(&line[0], MAX_LINE_LEN, style, pp, 1); printf("%s", line); } @@ -1404,7 +1561,7 @@ print_multipath (struct multipath * mpp, char * style) char line[MAX_LINE_LEN]; memset(&line[0], 0, MAX_LINE_LEN); - snprint_multipath(&line[0], MAX_LINE_LEN, style, mpp); + snprint_multipath(&line[0], MAX_LINE_LEN, style, mpp, 1); printf("%s", line); } diff --git a/libmultipath/print.h b/libmultipath/print.h index aef182b..8bd0bbc 100644 --- a/libmultipath/print.h +++ b/libmultipath/print.h @@ -37,8 +37,8 @@ void get_path_layout (vector pathvec, int header); void get_multipath_layout (vector mpvec, int header); int snprint_path_header (char *, int, char *); int snprint_multipath_header (char *, int, char *); -int snprint_path (char *, int, char *, struct path *); -int snprint_multipath (char *, int, char *, struct multipath *); +int snprint_path (char *, int, char *, struct path *, int); +int snprint_multipath (char *, int, char *, struct multipath *, int); int snprint_multipath_topology (char *, int, struct multipath * mpp, int verbosity); int snprint_defaults (char *, int); @@ -50,6 +50,11 @@ int snprint_status (char *, int, struct vectors *); int snprint_devices (char *, int, struct vectors *); int snprint_hwtable (char *, int, vector); int snprint_mptable (char *, int, vector); +int snprint_overrides (char *, int, struct hwentry *); +int snprint_host_wwnn (char *, size_t, struct path *); +int snprint_host_wwpn (char *, size_t, struct path *); +int snprint_tgt_wwnn (char *, size_t, struct path *); +int snprint_tgt_wwpn (char *, size_t, struct path *); void print_multipath_topology (struct multipath * mpp, int verbosity); void print_path (struct path * pp, char * style); diff --git a/libmultipath/prio.c b/libmultipath/prio.c index 05a8cf1..ab8eca9 100644 --- a/libmultipath/prio.c +++ b/libmultipath/prio.c @@ -10,6 +10,13 @@ static LIST_HEAD(prioritizers); +unsigned int get_prio_timeout(unsigned int default_timeout) +{ + if (conf->checker_timeout) + return conf->checker_timeout * 1000; + return default_timeout; +} + int init_prio (void) { if (!add_prio(DEFAULT_PRIO)) @@ -125,7 +132,7 @@ int prio_getprio (struct prio * p, struct path * pp) int prio_selected (struct prio * p) { - if (!p || !p->getprio) + if (!p) return 0; return (p->getprio) ? 1 : 0; } @@ -162,7 +169,7 @@ void prio_put (struct prio * dst) { struct prio * src; - if (!dst) + if (!dst || !dst->getprio) return; src = prio_lookup(dst->name); diff --git a/libmultipath/prio.h b/libmultipath/prio.h index 4eeb216..495688f 100644 --- a/libmultipath/prio.h +++ b/libmultipath/prio.h @@ -51,6 +51,7 @@ struct prio { int (*getprio)(struct path *, char *); }; +unsigned int get_prio_timeout(unsigned int default_timeout); int init_prio (void); void cleanup_prio (void); struct prio * add_prio (char *); diff --git a/libmultipath/prioritizers/alua.c b/libmultipath/prioritizers/alua.c index 4165ec6..cd4aafc 100644 --- a/libmultipath/prioritizers/alua.c +++ b/libmultipath/prioritizers/alua.c @@ -51,44 +51,58 @@ static const char *aas_print_string(int rc) } int -get_alua_info(int fd) +get_alua_info(struct path * pp) { int rc; int tpg; - int aas; - rc = get_target_port_group_support(fd); - if (rc < 0) - return -ALUA_PRIO_TPGS_FAILED; - - if (rc == TPGS_NONE) - return -ALUA_PRIO_NOT_SUPPORTED; - - tpg = get_target_port_group(fd); - if (tpg < 0) + tpg = get_target_port_group(pp); + if (tpg < 0) { + rc = get_target_port_group_support(pp->fd); + if (rc < 0) + return -ALUA_PRIO_TPGS_FAILED; + if (rc == TPGS_NONE) + return -ALUA_PRIO_NOT_SUPPORTED; return -ALUA_PRIO_RTPG_FAILED; - + } condlog(3, "reported target port group is %i", tpg); - rc = get_asymmetric_access_state(fd, tpg); + rc = get_asymmetric_access_state(pp->fd, tpg); if (rc < 0) return -ALUA_PRIO_GETAAS_FAILED; - aas = (rc & 0x0f); condlog(3, "aas = %02x [%s]%s", rc, aas_print_string(rc), (rc & 0x80) ? " [preferred]" : ""); return rc; } +int get_exclusive_perf_arg(char *args) +{ + char *ptr; + + if (args == NULL) + return 0; + ptr = strstr(args, "exclusive_pref_bit"); + if (!ptr) + return 0; + if (ptr[18] != '\0' && ptr[18] != ' ' && ptr[18] != '\t') + return 0; + if (ptr != args && ptr[-1] != ' ' && ptr[-1] != '\t') + return 0; + return 1; +} + int getprio (struct path * pp, char * args) { int rc; int aas; int priopath; + int exclusive_perf; if (pp->fd < 0) return -ALUA_PRIO_NO_INFORMATION; - rc = get_alua_info(pp->fd); + exclusive_perf = get_exclusive_perf_arg(args); + rc = get_alua_info(pp); if (rc >= 0) { aas = (rc & 0x0f); priopath = (rc & 0x80); @@ -108,7 +122,7 @@ int getprio (struct path * pp, char * args) default: rc = 0; } - if (priopath && aas != AAS_OPTIMIZED) + if (priopath && (aas != AAS_OPTIMIZED || exclusive_perf)) rc += 80; } else { switch(-rc) { @@ -119,10 +133,10 @@ int getprio (struct path * pp, char * args) condlog(0, "%s: couldn't get target port group", pp->dev); break; case ALUA_PRIO_GETAAS_FAILED: - condlog(0, "%s: couln't get asymmetric access state", pp->dev); + condlog(0, "%s: couldn't get asymmetric access state", pp->dev); break; case ALUA_PRIO_TPGS_FAILED: - condlog(3, "%s: couln't get supported alua states", pp->dev); + condlog(3, "%s: couldn't get supported alua states", pp->dev); break; } } diff --git a/libmultipath/prioritizers/alua_rtpg.c b/libmultipath/prioritizers/alua_rtpg.c index 981ba06..636aae5 100644 --- a/libmultipath/prioritizers/alua_rtpg.c +++ b/libmultipath/prioritizers/alua_rtpg.c @@ -17,14 +17,18 @@ #include #include #include +#include #define __user #include +#include "../structs.h" +#include "../prio.h" +#include "../discovery.h" #include "alua_rtpg.h" #define SENSE_BUFF_LEN 32 -#define DEF_TIMEOUT 60000 +#define SGIO_TIMEOUT 60000 /* * Macro used to print debug messaged. @@ -134,7 +138,7 @@ do_inquiry(int fd, int evpd, unsigned int codepage, void *resp, int resplen) hdr.dxfer_len = resplen; hdr.sbp = sense; hdr.mx_sb_len = sizeof(sense); - hdr.timeout = DEF_TIMEOUT; + hdr.timeout = get_prio_timeout(SGIO_TIMEOUT); if (ioctl(fd, SG_IO, &hdr) < 0) { PRINT_DEBUG("do_inquiry: IOCTL failed!\n"); @@ -169,8 +173,27 @@ get_target_port_group_support(int fd) return rc; } +static int +get_sysfs_pg83(struct path *pp, unsigned char *buff, int buflen) +{ + struct udev_device *parent = pp->udev; + + while (parent) { + const char *subsys = udev_device_get_subsystem(parent); + if (subsys && !strncmp(subsys, "scsi", 4)) + break; + parent = udev_device_get_parent(parent); + } + + if (!parent || sysfs_get_vpd(parent, 0x83, buff, buflen) <= 0) { + PRINT_DEBUG("failed to read sysfs vpd pg83\n"); + return -1; + } + return 0; +} + int -get_target_port_group(int fd) +get_target_port_group(struct path * pp) { unsigned char *buf; struct vpd83_data * vpd83; @@ -178,7 +201,7 @@ get_target_port_group(int fd) int rc; int buflen, scsi_buflen; - buflen = 128; /* Lets start from 128 */ + buflen = 4096; buf = (unsigned char *)malloc(buflen); if (!buf) { PRINT_DEBUG("malloc failed: could not allocate" @@ -187,24 +210,29 @@ get_target_port_group(int fd) } memset(buf, 0, buflen); - rc = do_inquiry(fd, 1, 0x83, buf, buflen); - if (rc < 0) - goto out; - scsi_buflen = (buf[2] << 8 | buf[3]) + 4; - if (buflen < scsi_buflen) { - free(buf); - buf = (unsigned char *)malloc(scsi_buflen); - if (!buf) { - PRINT_DEBUG("malloc failed: could not allocate" - "%u bytes\n", scsi_buflen); - return -RTPG_RTPG_FAILED; - } - buflen = scsi_buflen; - memset(buf, 0, buflen); - rc = do_inquiry(fd, 1, 0x83, buf, buflen); + rc = get_sysfs_pg83(pp, buf, buflen); + + if (rc < 0) { + rc = do_inquiry(pp->fd, 1, 0x83, buf, buflen); if (rc < 0) goto out; + + scsi_buflen = (buf[2] << 8 | buf[3]) + 4; + if (buflen < scsi_buflen) { + free(buf); + buf = (unsigned char *)malloc(scsi_buflen); + if (!buf) { + PRINT_DEBUG("malloc failed: could not allocate" + "%u bytes\n", scsi_buflen); + return -RTPG_RTPG_FAILED; + } + buflen = scsi_buflen; + memset(buf, 0, buflen); + rc = do_inquiry(pp->fd, 1, 0x83, buf, buflen); + if (rc < 0) + goto out; + } } vpd83 = (struct vpd83_data *) buf; @@ -253,7 +281,7 @@ do_rtpg(int fd, void* resp, long resplen) hdr.dxfer_len = resplen; hdr.mx_sb_len = sizeof(sense); hdr.sbp = sense; - hdr.timeout = DEF_TIMEOUT; + hdr.timeout = get_prio_timeout(SGIO_TIMEOUT); if (ioctl(fd, SG_IO, &hdr) < 0) return -RTPG_RTPG_FAILED; @@ -277,7 +305,7 @@ get_asymmetric_access_state(int fd, unsigned int tpg) int buflen; uint32_t scsi_buflen; - buflen = 128; /* Initial value from old code */ + buflen = 4096; buf = (unsigned char *)malloc(buflen); if (!buf) { PRINT_DEBUG ("malloc failed: could not allocate" diff --git a/libmultipath/prioritizers/alua_rtpg.h b/libmultipath/prioritizers/alua_rtpg.h index c43e0a9..91a15a4 100644 --- a/libmultipath/prioritizers/alua_rtpg.h +++ b/libmultipath/prioritizers/alua_rtpg.h @@ -23,7 +23,7 @@ #define RTPG_TPG_NOT_FOUND 4 int get_target_port_group_support(int fd); -int get_target_port_group(int fd); +int get_target_port_group(struct path * pp); int get_asymmetric_access_state(int fd, unsigned int tpg); #endif /* __RTPG_H__ */ diff --git a/libmultipath/prioritizers/emc.c b/libmultipath/prioritizers/emc.c index 87e9a8d..e49809c 100644 --- a/libmultipath/prioritizers/emc.c +++ b/libmultipath/prioritizers/emc.c @@ -14,15 +14,15 @@ int emc_clariion_prio(const char *dev, int fd) { - unsigned char sense_buffer[256]; + unsigned char sense_buffer[128]; unsigned char sb[128]; unsigned char inqCmdBlk[INQUIRY_CMDLEN] = {INQUIRY_CMD, 1, 0xC0, 0, - sizeof(sb), 0}; + sizeof(sense_buffer), 0}; struct sg_io_hdr io_hdr; int ret = PRIO_UNDEF; memset(&io_hdr, 0, sizeof (struct sg_io_hdr)); - memset(&sense_buffer, 0, 256); + memset(&sense_buffer, 0, 128); io_hdr.interface_id = 'S'; io_hdr.cmd_len = sizeof (inqCmdBlk); io_hdr.mx_sb_len = sizeof (sb); @@ -31,7 +31,7 @@ int emc_clariion_prio(const char *dev, int fd) io_hdr.dxferp = sense_buffer; io_hdr.cmdp = inqCmdBlk; io_hdr.sbp = sb; - io_hdr.timeout = 60000; + io_hdr.timeout = get_prio_timeout(60000); io_hdr.pack_id = 0; if (ioctl(fd, SG_IO, &io_hdr) < 0) { pp_emc_log(0, "sending query command failed"); diff --git a/libmultipath/prioritizers/hds.c b/libmultipath/prioritizers/hds.c index f748707..8043b5b 100644 --- a/libmultipath/prioritizers/hds.c +++ b/libmultipath/prioritizers/hds.c @@ -114,7 +114,7 @@ int hds_modular_prio (const char *dev, int fd) io_hdr.dxferp = inqBuff; io_hdr.cmdp = inqCmdBlk; io_hdr.sbp = sense_buffer; - io_hdr.timeout = 2000; /* TimeOut = 2 seconds */ + io_hdr.timeout = get_prio_timeout(2000); /* TimeOut = 2 seconds */ if (ioctl (fd, SG_IO, &io_hdr) < 0) { pp_hds_log(0, "SG_IO error"); diff --git a/libmultipath/prioritizers/hp_sw.c b/libmultipath/prioritizers/hp_sw.c index c24baad..4950cf7 100644 --- a/libmultipath/prioritizers/hp_sw.c +++ b/libmultipath/prioritizers/hp_sw.c @@ -46,7 +46,7 @@ int hp_sw_prio(const char *dev, int fd) io_hdr.dxfer_direction = SG_DXFER_NONE; io_hdr.cmdp = turCmdBlk; io_hdr.sbp = sb; - io_hdr.timeout = 60000; + io_hdr.timeout = get_prio_timeout(60000); io_hdr.pack_id = 0; retry: if (ioctl(fd, SG_IO, &io_hdr) < 0) { diff --git a/libmultipath/prioritizers/iet.c b/libmultipath/prioritizers/iet.c index 94406df..0bcc48b 100644 --- a/libmultipath/prioritizers/iet.c +++ b/libmultipath/prioritizers/iet.c @@ -109,6 +109,7 @@ int iet_prio(const char *dev, char * args) ssize_t nchars = readlink(path, buffer, sizeof(buffer)-1); if (nchars != -1) { char *device; + buffer[nchars] = '\0'; device = find_regex(buffer,"(sd[a-z]+)"); // if device parsed is the right one if (device!=NULL && strncmp(device, dev, strlen(device)) == 0) { @@ -118,6 +119,7 @@ int iet_prio(const char *dev, char * args) if (ip!=NULL && strncmp(ip, preferredip, strlen(ip)) == 0) { // high prio free(ip); + free(device); closedir(dir_p); return 20; } diff --git a/libmultipath/prioritizers/ontap.c b/libmultipath/prioritizers/ontap.c index 026d45d..5e82a17 100644 --- a/libmultipath/prioritizers/ontap.c +++ b/libmultipath/prioritizers/ontap.c @@ -89,7 +89,7 @@ static int send_gva(const char *dev, int fd, unsigned char pg, io_hdr.dxferp = results; io_hdr.cmdp = cdb; io_hdr.sbp = sb; - io_hdr.timeout = SG_TIMEOUT; + io_hdr.timeout = get_prio_timeout(SG_TIMEOUT); io_hdr.pack_id = 0; if (ioctl(fd, SG_IO, &io_hdr) < 0) { pp_ontap_log(0, "SG_IO ioctl failed, errno=%d", errno); @@ -141,7 +141,7 @@ static int get_proxy(const char *dev, int fd) io_hdr.dxferp = results; io_hdr.cmdp = cdb; io_hdr.sbp = sb; - io_hdr.timeout = SG_TIMEOUT; + io_hdr.timeout = get_prio_timeout(SG_TIMEOUT); io_hdr.pack_id = 0; if (ioctl(fd, SG_IO, &io_hdr) < 0) { pp_ontap_log(0, "ioctl sending inquiry command failed, " diff --git a/libmultipath/prioritizers/rdac.c b/libmultipath/prioritizers/rdac.c index 441b3b0..a210055 100644 --- a/libmultipath/prioritizers/rdac.c +++ b/libmultipath/prioritizers/rdac.c @@ -14,15 +14,15 @@ int rdac_prio(const char *dev, int fd) { - unsigned char sense_buffer[256]; + unsigned char sense_buffer[128]; unsigned char sb[128]; unsigned char inqCmdBlk[INQUIRY_CMDLEN] = {INQUIRY_CMD, 1, 0xC9, 0, - sizeof(sb), 0}; + sizeof(sense_buffer), 0}; struct sg_io_hdr io_hdr; int ret = 0; memset(&io_hdr, 0, sizeof (struct sg_io_hdr)); - memset(sense_buffer, 0, 256); + memset(sense_buffer, 0, 128); io_hdr.interface_id = 'S'; io_hdr.cmd_len = sizeof (inqCmdBlk); io_hdr.mx_sb_len = sizeof (sb); @@ -31,7 +31,7 @@ int rdac_prio(const char *dev, int fd) io_hdr.dxferp = sense_buffer; io_hdr.cmdp = inqCmdBlk; io_hdr.sbp = sb; - io_hdr.timeout = 60000; + io_hdr.timeout = get_prio_timeout(60000); io_hdr.pack_id = 0; if (ioctl(fd, SG_IO, &io_hdr) < 0) { pp_rdac_log(0, "sending inquiry command failed"); diff --git a/libmultipath/prioritizers/weightedpath.c b/libmultipath/prioritizers/weightedpath.c index 54c9039..ba8c555 100644 --- a/libmultipath/prioritizers/weightedpath.c +++ b/libmultipath/prioritizers/weightedpath.c @@ -32,6 +32,8 @@ #include #include #include +#include +#include char *get_next_string(char **temp, char *split_char) { @@ -42,6 +44,36 @@ char *get_next_string(char **temp, char *split_char) return token; } +#define CHECK_LEN \ +do { \ + if ((p - str) >= (len - 1)) { \ + condlog(0, "%s: %s - buffer size too small", pp->dev, pp->prio.name); \ + return -1; \ + } \ +} while(0) + +static int +build_wwn_path(struct path *pp, char *str, int len) +{ + char *p = str; + + p += snprint_host_wwnn(p, str + len - p, pp); + CHECK_LEN; + p += snprintf(p, str + len - p, ":"); + CHECK_LEN; + p += snprint_host_wwpn(p, str + len - p, pp); + CHECK_LEN; + p += snprintf(p, str + len - p, ":"); + CHECK_LEN; + p += snprint_tgt_wwnn(p, str + len - p, pp); + CHECK_LEN; + p += snprintf(p, str + len - p, ":"); + CHECK_LEN; + p += snprint_tgt_wwpn(p, str + len - p, pp); + CHECK_LEN; + return 0; +} + /* main priority routine */ int prio_path_weight(struct path *pp, char *prio_args) { @@ -61,17 +93,25 @@ int prio_path_weight(struct path *pp, char *prio_args) regex = get_next_string(&temp, split_char); /* Return default priority if the argument is not parseable */ - if (!regex) + if (!regex) { + FREE(arg); return priority; + } if (!strcmp(regex, HBTL)) { sprintf(path, "%d:%d:%d:%d", pp->sg_id.host_no, pp->sg_id.channel, pp->sg_id.scsi_id, pp->sg_id.lun); } else if (!strcmp(regex, DEV_NAME)) { strcpy(path, pp->dev); + } else if (!strcmp(regex, WWN)) { + if (build_wwn_path(pp, path, FILE_NAME_SIZE) != 0) { + FREE(arg); + return priority; + } } else { condlog(0, "%s: %s - Invalid arguments", pp->dev, pp->prio.name); + FREE(arg); return priority; } diff --git a/libmultipath/prioritizers/weightedpath.h b/libmultipath/prioritizers/weightedpath.h index 509f215..93d8c43 100644 --- a/libmultipath/prioritizers/weightedpath.h +++ b/libmultipath/prioritizers/weightedpath.h @@ -4,6 +4,7 @@ #define PRIO_WEIGHTED_PATH "weightedpath" #define HBTL "hbtl" #define DEV_NAME "devname" +#define WWN "wwn" #define DEFAULT_PRIORITY 0 int prio_path_weight(struct path *pp, char *prio_args); diff --git a/libmultipath/propsel.c b/libmultipath/propsel.c index 7b7944d..8abe360 100644 --- a/libmultipath/propsel.c +++ b/libmultipath/propsel.c @@ -17,6 +17,7 @@ #include "devmapper.h" #include "prio.h" #include "discovery.h" +#include "dict.h" #include "prioritizers/alua_rtpg.h" #include @@ -29,57 +30,96 @@ pgpolicyfn *pgpolicies[] = { group_by_node_name }; +#define do_set(var, src, dest, msg) \ +do { \ + if (src && src->var) { \ + dest = src->var; \ + origin = msg; \ + goto out; \ + } \ +} while(0) +#define do_default(dest, value) \ +do { \ + dest = value; \ + origin = "(internal default)"; \ +} while(0) + +#define mp_set_mpe(var) \ +do_set(var, mp->mpe, mp->var, "(LUN setting)") +#define mp_set_hwe(var) \ +do_set(var, mp->hwe, mp->var, "(controller setting)") +#define mp_set_ovr(var) \ +do_set(var, conf->overrides, mp->var, "(overrides setting)") +#define mp_set_conf(var) \ +do_set(var, conf, mp->var, "(config file default)") +#define mp_set_default(var, value) \ +do_default(mp->var, value) + +#define pp_set_mpe(var) \ +do_set(var, mpe, pp->var, "(LUN setting)") +#define pp_set_hwe(var) \ +do_set(var, pp->hwe, pp->var, "(controller setting)") +#define pp_set_conf(var) \ +do_set(var, conf, pp->var, "(config file default)") +#define pp_set_ovr(var) \ +do_set(var, conf->overrides, pp->var, "(overrides setting)") +#define pp_set_default(var, value) \ +do_default(pp->var, value) + +#define do_attr_set(var, src, shift, msg) \ +do { \ + if (src && (src->attribute_flags & (1 << shift))) { \ + mp->attribute_flags |= (1 << shift); \ + mp->var = src->var; \ + origin = msg; \ + goto out; \ + } \ +} while(0) + +#define set_attr_mpe(var, shift) \ +do_attr_set(var, mp->mpe, shift, "(LUN setting)") +#define set_attr_conf(var, shift) \ +do_attr_set(var, conf, shift, "(config file default)") + extern int select_mode (struct multipath *mp) { - if (mp->mpe && (mp->mpe->attribute_flags & (1 << ATTR_MODE))) { - mp->attribute_flags |= (1 << ATTR_MODE); - mp->mode = mp->mpe->mode; - condlog(3, "mode = 0%o (LUN setting)", mp->mode); - } - else if (conf->attribute_flags & (1 << ATTR_MODE)) { - mp->attribute_flags |= (1 << ATTR_MODE); - mp->mode = conf->mode; - condlog(3, "mode = 0%o (config file default)", mp->mode); - } - else - mp->attribute_flags &= ~(1 << ATTR_MODE); + char *origin; + + set_attr_mpe(mode, ATTR_MODE); + set_attr_conf(mode, ATTR_MODE); + mp->attribute_flags &= ~(1 << ATTR_MODE); + return 0; +out: + condlog(3, "%s: mode = 0%o %s", mp->alias, mp->mode, origin); return 0; } extern int select_uid (struct multipath *mp) { - if (mp->mpe && (mp->mpe->attribute_flags & (1 << ATTR_UID))) { - mp->attribute_flags |= (1 << ATTR_UID); - mp->uid = mp->mpe->uid; - condlog(3, "uid = %u (LUN setting)", mp->uid); - } - else if (conf->attribute_flags & (1 << ATTR_UID)) { - mp->attribute_flags |= (1 << ATTR_UID); - mp->uid = conf->uid; - condlog(3, "uid = %u (config file default)", mp->uid); - } - else - mp->attribute_flags &= ~(1 << ATTR_UID); + char *origin; + + set_attr_mpe(uid, ATTR_UID); + set_attr_conf(uid, ATTR_UID); + mp->attribute_flags &= ~(1 << ATTR_UID); + return 0; +out: + condlog(3, "%s: uid = 0%o %s", mp->alias, mp->uid, origin); return 0; } extern int select_gid (struct multipath *mp) { - if (mp->mpe && (mp->mpe->attribute_flags & (1 << ATTR_GID))) { - mp->attribute_flags |= (1 << ATTR_GID); - mp->gid = mp->mpe->gid; - condlog(3, "gid = %u (LUN setting)", mp->gid); - } - else if (conf->attribute_flags & (1 << ATTR_GID)) { - mp->attribute_flags |= (1 << ATTR_GID); - mp->gid = conf->gid; - condlog(3, "gid = %u (config file default)", mp->gid); - } - else - mp->attribute_flags &= ~(1 << ATTR_GID); + char *origin; + + set_attr_mpe(gid, ATTR_GID); + set_attr_conf(gid, ATTR_GID); + mp->attribute_flags &= ~(1 << ATTR_GID); + return 0; +out: + condlog(3, "%s: gid = 0%o %s", mp->alias, mp->gid, origin); return 0; } @@ -91,205 +131,165 @@ select_gid (struct multipath *mp) extern int select_rr_weight (struct multipath * mp) { - if (mp->mpe && mp->mpe->rr_weight) { - mp->rr_weight = mp->mpe->rr_weight; - condlog(3, "%s: rr_weight = %i (LUN setting)", - mp->alias, mp->rr_weight); - return 0; - } - if (mp->hwe && mp->hwe->rr_weight) { - mp->rr_weight = mp->hwe->rr_weight; - condlog(3, "%s: rr_weight = %i (controller setting)", - mp->alias, mp->rr_weight); - return 0; - } - if (conf->rr_weight) { - mp->rr_weight = conf->rr_weight; - condlog(3, "%s: rr_weight = %i (config file default)", - mp->alias, mp->rr_weight); - return 0; - } - mp->rr_weight = RR_WEIGHT_NONE; - condlog(3, "%s: rr_weight = %i (internal default)", - mp->alias, mp->rr_weight); + char *origin, buff[13]; + + mp_set_mpe(rr_weight); + mp_set_ovr(rr_weight); + mp_set_hwe(rr_weight); + mp_set_conf(rr_weight); + mp_set_default(rr_weight, RR_WEIGHT_NONE); +out: + print_rr_weight(buff, 13, &mp->rr_weight); + condlog(3, "%s: rr_weight = %s %s", mp->alias, buff, origin); return 0; } extern int select_pgfailback (struct multipath * mp) { - if (mp->mpe && mp->mpe->pgfailback != FAILBACK_UNDEF) { - mp->pgfailback = mp->mpe->pgfailback; - condlog(3, "%s: pgfailback = %i (LUN setting)", - mp->alias, mp->pgfailback); - return 0; - } - if (mp->hwe && mp->hwe->pgfailback != FAILBACK_UNDEF) { - mp->pgfailback = mp->hwe->pgfailback; - condlog(3, "%s: pgfailback = %i (controller setting)", - mp->alias, mp->pgfailback); - return 0; - } - if (conf->pgfailback != FAILBACK_UNDEF) { - mp->pgfailback = conf->pgfailback; - condlog(3, "%s: pgfailback = %i (config file default)", - mp->alias, mp->pgfailback); - return 0; - } - mp->pgfailback = DEFAULT_FAILBACK; - condlog(3, "%s: pgfailover = %i (internal default)", - mp->alias, mp->pgfailback); + char *origin, buff[13]; + + mp_set_mpe(pgfailback); + mp_set_ovr(pgfailback); + mp_set_hwe(pgfailback); + mp_set_conf(pgfailback); + mp_set_default(pgfailback, DEFAULT_FAILBACK); +out: + print_pgfailback(buff, 13, &mp->pgfailback); + condlog(3, "%s: failback = %s %s", mp->alias, buff, origin); return 0; } extern int select_pgpolicy (struct multipath * mp) { - char pgpolicy_name[POLICY_NAME_SIZE]; + char *origin, buff[POLICY_NAME_SIZE]; if (conf->pgpolicy_flag > 0) { mp->pgpolicy = conf->pgpolicy_flag; - mp->pgpolicyfn = pgpolicies[mp->pgpolicy]; - get_pgpolicy_name(pgpolicy_name, POLICY_NAME_SIZE, - mp->pgpolicy); - condlog(3, "%s: pgpolicy = %s (cmd line flag)", - mp->alias, pgpolicy_name); - return 0; - } - if (mp->mpe && mp->mpe->pgpolicy > 0) { - mp->pgpolicy = mp->mpe->pgpolicy; - mp->pgpolicyfn = pgpolicies[mp->pgpolicy]; - get_pgpolicy_name(pgpolicy_name, POLICY_NAME_SIZE, - mp->pgpolicy); - condlog(3, "%s: pgpolicy = %s (LUN setting)", - mp->alias, pgpolicy_name); - return 0; - } - if (mp->hwe && mp->hwe->pgpolicy > 0) { - mp->pgpolicy = mp->hwe->pgpolicy; - mp->pgpolicyfn = pgpolicies[mp->pgpolicy]; - get_pgpolicy_name(pgpolicy_name, POLICY_NAME_SIZE, - mp->pgpolicy); - condlog(3, "%s: pgpolicy = %s (controller setting)", - mp->alias, pgpolicy_name); - return 0; - } - if (conf->pgpolicy > 0) { - mp->pgpolicy = conf->pgpolicy; - mp->pgpolicyfn = pgpolicies[mp->pgpolicy]; - get_pgpolicy_name(pgpolicy_name, POLICY_NAME_SIZE, - mp->pgpolicy); - condlog(3, "%s: pgpolicy = %s (config file default)", - mp->alias, pgpolicy_name); - return 0; + origin = "(cmd line flag)"; + goto out; } - mp->pgpolicy = DEFAULT_PGPOLICY; + mp_set_mpe(pgpolicy); + mp_set_ovr(pgpolicy); + mp_set_hwe(pgpolicy); + mp_set_conf(pgpolicy); + mp_set_default(pgpolicy, DEFAULT_PGPOLICY); +out: mp->pgpolicyfn = pgpolicies[mp->pgpolicy]; - get_pgpolicy_name(pgpolicy_name, POLICY_NAME_SIZE, mp->pgpolicy); - condlog(3, "%s: pgpolicy = %s (internal default)", - mp->alias, pgpolicy_name); + get_pgpolicy_name(buff, POLICY_NAME_SIZE, mp->pgpolicy); + condlog(3, "%s: path_grouping_policy = %s %s", mp->alias, buff, origin); return 0; } extern int select_selector (struct multipath * mp) { - if (mp->mpe && mp->mpe->selector) { - mp->selector = mp->mpe->selector; - condlog(3, "%s: selector = %s (LUN setting)", - mp->alias, mp->selector); - return 0; - } - if (mp->hwe && mp->hwe->selector) { - mp->selector = mp->hwe->selector; - condlog(3, "%s: selector = %s (controller setting)", - mp->alias, mp->selector); - return 0; - } - if (conf->selector) { - mp->selector = conf->selector; - condlog(3, "%s: selector = %s (config file default)", - mp->alias, mp->selector); - return 0; - } - mp->selector = set_default(DEFAULT_SELECTOR); - condlog(3, "%s: selector = %s (internal default)", - mp->alias, mp->selector); + char *origin; + + mp_set_mpe(selector); + mp_set_ovr(selector); + mp_set_hwe(selector); + mp_set_conf(selector); + mp_set_default(selector, DEFAULT_SELECTOR); +out: + mp->selector = STRDUP(mp->selector); + condlog(3, "%s: path_selector = \"%s\" %s", mp->alias, mp->selector, + origin); return 0; } static void select_alias_prefix (struct multipath * mp) { - if (mp->hwe && mp->hwe->alias_prefix) { - mp->alias_prefix = mp->hwe->alias_prefix; - condlog(3, "%s: alias_prefix = %s (controller setting)", - mp->wwid, mp->alias_prefix); - return; - } - if (conf->alias_prefix) { - mp->alias_prefix = conf->alias_prefix; - condlog(3, "%s: alias_prefix = %s (config file default)", - mp->wwid, mp->alias_prefix); - return; - } - mp->alias_prefix = set_default(DEFAULT_ALIAS_PREFIX); - condlog(3, "%s: alias_prefix = %s (internal default)", - mp->wwid, mp->alias_prefix); + char *origin; + + mp_set_ovr(alias_prefix); + mp_set_hwe(alias_prefix); + mp_set_conf(alias_prefix); + mp_set_default(alias_prefix, DEFAULT_ALIAS_PREFIX); +out: + condlog(3, "%s: alias_prefix = %s %s", mp->wwid, mp->alias_prefix, + origin); } static int want_user_friendly_names(struct multipath * mp) { - if (mp->mpe && - mp->mpe->user_friendly_names != USER_FRIENDLY_NAMES_UNDEF) - return (mp->mpe->user_friendly_names == USER_FRIENDLY_NAMES_ON); - if (mp->hwe && - mp->hwe->user_friendly_names != USER_FRIENDLY_NAMES_UNDEF) - return (mp->hwe->user_friendly_names == USER_FRIENDLY_NAMES_ON); - return (conf->user_friendly_names == USER_FRIENDLY_NAMES_ON); + + char *origin; + int user_friendly_names; + + do_set(user_friendly_names, mp->mpe, user_friendly_names, + "(LUN setting)"); + do_set(user_friendly_names, conf->overrides, user_friendly_names, + "(overrides setting)"); + do_set(user_friendly_names, mp->hwe, user_friendly_names, + "(controller setting)"); + do_set(user_friendly_names, conf, user_friendly_names, + "(config file setting)"); + do_default(user_friendly_names, USER_FRIENDLY_NAMES_OFF); +out: + condlog(3, "%s: user_friendly_names = %s %s", mp->wwid, + (user_friendly_names == USER_FRIENDLY_NAMES_ON)? "yes" : "no", + origin); + return (user_friendly_names == USER_FRIENDLY_NAMES_ON); } extern int select_alias (struct multipath * mp) { - if (mp->mpe && mp->mpe->alias) + char *origin = NULL; + + if (mp->mpe && mp->mpe->alias) { mp->alias = STRDUP(mp->mpe->alias); - else { - mp->alias = NULL; - if (want_user_friendly_names(mp)) { - select_alias_prefix(mp); - mp->alias = get_user_friendly_alias(mp->wwid, - conf->bindings_file, mp->alias_prefix, conf->bindings_read_only); - } - if (mp->alias == NULL) - mp->alias = STRDUP(mp->wwid); + origin = "(LUN setting)"; + goto out; + } + + mp->alias = NULL; + if (!want_user_friendly_names(mp)) + goto out; + + select_alias_prefix(mp); + + if (strlen(mp->alias_old) > 0) { + mp->alias = use_existing_alias(mp->wwid, conf->bindings_file, + mp->alias_old, mp->alias_prefix, + conf->bindings_read_only); + memset (mp->alias_old, 0, WWID_SIZE); + origin = "(using existing alias)"; } + if (mp->alias == NULL) { + mp->alias = get_user_friendly_alias(mp->wwid, + conf->bindings_file, mp->alias_prefix, conf->bindings_read_only); + origin = "(user_friendly_name)"; + } +out: + if (mp->alias == NULL) { + mp->alias = STRDUP(mp->wwid); + origin = "(default to wwid)"; + } + if (mp->alias) + condlog(3, "%s: alias = %s %s", mp->wwid, mp->alias, origin); return mp->alias ? 0 : 1; } extern int select_features (struct multipath * mp) { - struct mpentry * mpe; char *origin; - if ((mpe = find_mpe(mp->wwid)) && mpe->features) { - mp->features = STRDUP(mpe->features); - origin = "LUN setting"; - } else if (mp->hwe && mp->hwe->features) { - mp->features = STRDUP(mp->hwe->features); - origin = "controller setting"; - } else if (conf->features) { - mp->features = STRDUP(conf->features); - origin = "config file default"; - } else { - mp->features = set_default(DEFAULT_FEATURES); - origin = "internal default"; - } - condlog(3, "%s: features = %s (%s)", - mp->alias, mp->features, origin); + mp_set_mpe(features); + mp_set_ovr(features); + mp_set_hwe(features); + mp_set_conf(features); + mp_set_default(features, DEFAULT_FEATURES); +out: + mp->features = STRDUP(mp->features); + condlog(3, "%s: features = \"%s\" %s", mp->alias, mp->features, origin); + if (strstr(mp->features, "queue_if_no_path")) { if (mp->no_path_retry == NO_PATH_RETRY_UNDEF) mp->no_path_retry = NO_PATH_RETRY_QUEUE; @@ -305,45 +305,31 @@ select_features (struct multipath * mp) extern int select_hwhandler (struct multipath * mp) { - if (mp->hwe && mp->hwe->hwhandler) { - mp->hwhandler = mp->hwe->hwhandler; - condlog(3, "%s: hwhandler = %s (controller setting)", - mp->alias, mp->hwhandler); - return 0; - } - if (conf->hwhandler) { - mp->hwhandler = conf->hwhandler; - condlog(3, "%s: hwhandler = %s (config file default)", - mp->alias, mp->hwhandler); - return 0; - } - mp->hwhandler = set_default(DEFAULT_HWHANDLER); - condlog(3, "%s: hwhandler = %s (internal default)", - mp->alias, mp->hwhandler); + char *origin; + + mp_set_hwe(hwhandler); + mp_set_conf(hwhandler); + mp_set_default(hwhandler, DEFAULT_HWHANDLER); +out: + mp->hwhandler = STRDUP(mp->hwhandler); + condlog(3, "%s: hardware_handler = \"%s\" %s", mp->alias, mp->hwhandler, + origin); return 0; } extern int select_checker(struct path *pp) { + char *origin, *checker_name; struct checker * c = &pp->checker; - if (pp->hwe && pp->hwe->checker_name) { - checker_get(c, pp->hwe->checker_name); - condlog(3, "%s: path checker = %s (controller setting)", - pp->dev, checker_name(c)); - goto out; - } - if (conf->checker_name) { - checker_get(c, conf->checker_name); - condlog(3, "%s: path checker = %s (config file default)", - pp->dev, checker_name(c)); - goto out; - } - checker_get(c, DEFAULT_CHECKER); - condlog(3, "%s: path checker = %s (internal default)", - pp->dev, checker_name(c)); + do_set(checker_name, conf->overrides, checker_name, "(overrides setting)"); + do_set(checker_name, pp->hwe, checker_name, "(controller setting)"); + do_set(checker_name, conf, checker_name, "(config file setting)"); + do_default(checker_name, DEFAULT_CHECKER); out: + checker_get(c, checker_name); + condlog(3, "%s: path_checker = %s %s", pp->dev, c->name, origin); if (conf->checker_timeout) { c->timeout = conf->checker_timeout; condlog(3, "%s: checker timeout = %u s (config file default)", @@ -363,33 +349,22 @@ out: extern int select_getuid (struct path * pp) { - if (pp->hwe && pp->hwe->uid_attribute) { - pp->uid_attribute = pp->hwe->uid_attribute; - condlog(3, "%s: uid_attribute = %s (controller setting)", - pp->dev, pp->uid_attribute); - return 0; - } - if (pp->hwe && pp->hwe->getuid) { - pp->getuid = pp->hwe->getuid; - condlog(3, "%s: getuid = %s (deprecated) (controller setting)", - pp->dev, pp->getuid); - return 0; - } - if (conf->uid_attribute) { - pp->uid_attribute = conf->uid_attribute; - condlog(3, "%s: uid_attribute = %s (config file default)", - pp->dev, pp->uid_attribute); - return 0; - } - if (conf->getuid) { - pp->getuid = conf->getuid; - condlog(3, "%s: getuid = %s (deprecated) (config file default)", - pp->dev, pp->getuid); - return 0; - } - pp->uid_attribute = STRDUP(DEFAULT_UID_ATTRIBUTE); - condlog(3, "%s: uid_attribute = %s (internal default)", - pp->dev, pp->uid_attribute); + char *origin; + + pp_set_ovr(getuid); + pp_set_ovr(uid_attribute); + pp_set_hwe(getuid); + pp_set_hwe(uid_attribute); + pp_set_conf(getuid); + pp_set_conf(uid_attribute); + pp_set_default(uid_attribute, DEFAULT_UID_ATTRIBUTE); +out: + if (pp->uid_attribute) + condlog(3, "%s: uid_attribute = %s %s", pp->dev, + pp->uid_attribute, origin); + else if (pp->getuid) + condlog(3, "%s: getuid = \"%s\" %s", pp->dev, pp->getuid, + origin); return 0; } @@ -398,10 +373,12 @@ detect_prio(struct path * pp) { int ret; struct prio *p = &pp->prio; + int tpgs = 0; - if (get_target_port_group_support(pp->fd) <= 0) + if ((tpgs = get_target_port_group_support(pp->fd)) <= 0) return; - ret = get_target_port_group(pp->fd); + pp->tpgs = tpgs; + ret = get_target_port_group(pp); if (ret < 0) return; if (get_asymmetric_access_state(pp->fd, ret) < 0) @@ -409,140 +386,107 @@ detect_prio(struct path * pp) prio_get(p, PRIO_ALUA, DEFAULT_PRIO_ARGS); } +#define set_prio(src, msg) \ +do { \ + if (src && src->prio_name) { \ + prio_get(p, src->prio_name, src->prio_args); \ + origin = msg; \ + goto out; \ + } \ +} while(0) + extern int select_prio (struct path * pp) { + char *origin; struct mpentry * mpe; struct prio * p = &pp->prio; if (pp->detect_prio == DETECT_PRIO_ON) { detect_prio(pp); if (prio_selected(p)) { - condlog(3, "%s: prio = %s (detected setting)", - pp->dev, prio_name(p)); - return 0; - } - } - - if ((mpe = find_mpe(pp->wwid))) { - if (mpe->prio_name) { - prio_get(p, mpe->prio_name, mpe->prio_args); - condlog(3, "%s: prio = %s (LUN setting)", - pp->dev, prio_name(p)); - return 0; + origin = "(detected setting)"; + goto out; } } - - if (pp->hwe && pp->hwe->prio_name) { - prio_get(p, pp->hwe->prio_name, pp->hwe->prio_args); - condlog(3, "%s: prio = %s (controller setting)", - pp->dev, pp->hwe->prio_name); - condlog(3, "%s: prio args = %s (controller setting)", - pp->dev, pp->hwe->prio_args); - return 0; - } - if (conf->prio_name) { - prio_get(p, conf->prio_name, conf->prio_args); - condlog(3, "%s: prio = %s (config file default)", - pp->dev, conf->prio_name); - condlog(3, "%s: prio args = %s (config file default)", - pp->dev, conf->prio_args); - return 0; - } + mpe = find_mpe(pp->wwid); + set_prio(mpe, "(LUN setting)"); + set_prio(conf->overrides, "(overrides setting)"); + set_prio(pp->hwe, "controller setting)"); + set_prio(conf, "(config file default)"); prio_get(p, DEFAULT_PRIO, DEFAULT_PRIO_ARGS); - condlog(3, "%s: prio = %s (internal default)", - pp->dev, DEFAULT_PRIO); - condlog(3, "%s: prio args = %s (internal default)", - pp->dev, DEFAULT_PRIO_ARGS); + origin = "(internal default)"; +out: + /* + * fetch tpgs mode for alua, if its not already obtained + */ + if (!strncmp(prio_name(p), PRIO_ALUA, PRIO_NAME_LEN)) { + int tpgs = 0; + if(!pp->tpgs && + (tpgs = get_target_port_group_support(pp->fd)) >= 0) + pp->tpgs = tpgs; + } + condlog(3, "%s: prio = %s %s", pp->dev, prio_name(p), origin); + condlog(3, "%s: prio args = \"%s\" %s", pp->dev, prio_args(p), origin); return 0; } extern int select_no_path_retry(struct multipath *mp) { + char *origin = NULL; + char buff[12]; + if (mp->flush_on_last_del == FLUSH_IN_PROGRESS) { condlog(0, "flush_on_last_del in progress"); mp->no_path_retry = NO_PATH_RETRY_FAIL; return 0; } - if (mp->mpe && mp->mpe->no_path_retry != NO_PATH_RETRY_UNDEF) { - mp->no_path_retry = mp->mpe->no_path_retry; - condlog(3, "%s: no_path_retry = %i (multipath setting)", - mp->alias, mp->no_path_retry); - return 0; - } - if (mp->hwe && mp->hwe->no_path_retry != NO_PATH_RETRY_UNDEF) { - mp->no_path_retry = mp->hwe->no_path_retry; - condlog(3, "%s: no_path_retry = %i (controller setting)", - mp->alias, mp->no_path_retry); - return 0; - } - if (conf->no_path_retry != NO_PATH_RETRY_UNDEF) { - mp->no_path_retry = conf->no_path_retry; - condlog(3, "%s: no_path_retry = %i (config file default)", - mp->alias, mp->no_path_retry); - return 0; - } - if (mp->no_path_retry != NO_PATH_RETRY_UNDEF) - condlog(3, "%s: no_path_retry = %i (inherited setting)", - mp->alias, mp->no_path_retry); + mp_set_mpe(no_path_retry); + mp_set_ovr(no_path_retry); + mp_set_hwe(no_path_retry); + mp_set_conf(no_path_retry); +out: + print_no_path_retry(buff, 12, &mp->no_path_retry); + if (origin) + condlog(3, "%s: no_path_retry = %s %s", mp->alias, buff, + origin); + else if (mp->no_path_retry != NO_PATH_RETRY_UNDEF) + condlog(3, "%s: no_path_retry = %s (inheritied setting)", + mp->alias, buff); else - condlog(3, "%s: no_path_retry = %i (internal default)", - mp->alias, mp->no_path_retry); + condlog(3, "%s: no_path_retry = undef (internal default)", + mp->alias); return 0; } int select_minio_rq (struct multipath * mp) { - if (mp->mpe && mp->mpe->minio_rq) { - mp->minio = mp->mpe->minio_rq; - condlog(3, "%s: minio = %i rq (LUN setting)", - mp->alias, mp->minio); - return 0; - } - if (mp->hwe && mp->hwe->minio_rq) { - mp->minio = mp->hwe->minio_rq; - condlog(3, "%s: minio = %i rq (controller setting)", - mp->alias, mp->minio); - return 0; - } - if (conf->minio) { - mp->minio = conf->minio_rq; - condlog(3, "%s: minio = %i rq (config file default)", - mp->alias, mp->minio); - return 0; - } - mp->minio = DEFAULT_MINIO_RQ; - condlog(3, "%s: minio = %i rq (internal default)", - mp->alias, mp->minio); + char *origin; + + do_set(minio_rq, mp->mpe, mp->minio, "(LUN setting)"); + do_set(minio_rq, conf->overrides, mp->minio, "(overrides setting)"); + do_set(minio_rq, mp->hwe, mp->minio, "(controller setting)"); + do_set(minio_rq, conf, mp->minio, "(config file setting)"); + do_default(mp->minio, DEFAULT_MINIO_RQ); +out: + condlog(3, "%s: minio = %i %s", mp->alias, mp->minio, origin); return 0; } int select_minio_bio (struct multipath * mp) { - if (mp->mpe && mp->mpe->minio) { - mp->minio = mp->mpe->minio; - condlog(3, "%s: minio = %i (LUN setting)", - mp->alias, mp->minio); - return 0; - } - if (mp->hwe && mp->hwe->minio) { - mp->minio = mp->hwe->minio; - condlog(3, "%s: minio = %i (controller setting)", - mp->alias, mp->minio); - return 0; - } - if (conf->minio) { - mp->minio = conf->minio; - condlog(3, "%s: minio = %i (config file default)", - mp->alias, mp->minio); - return 0; - } - mp->minio = DEFAULT_MINIO; - condlog(3, "%s: minio = %i (internal default)", - mp->alias, mp->minio); + char *origin; + + mp_set_mpe(minio); + mp_set_ovr(minio); + mp_set_hwe(minio); + mp_set_conf(minio); + mp_set_default(minio, DEFAULT_MINIO); +out: + condlog(3, "%s: minio = %i %s", mp->alias, mp->minio, origin); return 0; } @@ -560,164 +504,158 @@ select_minio (struct multipath * mp) extern int select_fast_io_fail(struct multipath *mp) { - if (mp->hwe && mp->hwe->fast_io_fail != MP_FAST_IO_FAIL_UNSET) { - mp->fast_io_fail = mp->hwe->fast_io_fail; - if (mp->fast_io_fail == MP_FAST_IO_FAIL_OFF) - condlog(3, "%s: fast_io_fail_tmo = off " - "(controller setting)", mp->alias); - else - condlog(3, "%s: fast_io_fail_tmo = %d " - "(controller setting)", mp->alias, - mp->fast_io_fail == MP_FAST_IO_FAIL_ZERO ? 0 : mp->fast_io_fail); - return 0; - } - if (conf->fast_io_fail != MP_FAST_IO_FAIL_UNSET) { - mp->fast_io_fail = conf->fast_io_fail; - if (mp->fast_io_fail == MP_FAST_IO_FAIL_OFF) - condlog(3, "%s: fast_io_fail_tmo = off " - "(config file default)", mp->alias); - else - condlog(3, "%s: fast_io_fail_tmo = %d " - "(config file default)", mp->alias, - mp->fast_io_fail == MP_FAST_IO_FAIL_ZERO ? 0 : mp->fast_io_fail); - return 0; - } - mp->fast_io_fail = MP_FAST_IO_FAIL_UNSET; + char *origin, buff[12]; + + mp_set_ovr(fast_io_fail); + mp_set_hwe(fast_io_fail); + mp_set_conf(fast_io_fail); + mp_set_default(fast_io_fail, DEFAULT_FAST_IO_FAIL); +out: + print_fast_io_fail(buff, 12, &mp->fast_io_fail); + condlog(3, "%s: fast_io_fail_tmo = %s %s", mp->alias, buff, origin); return 0; } extern int select_dev_loss(struct multipath *mp) { - if (mp->hwe && mp->hwe->dev_loss) { - mp->dev_loss = mp->hwe->dev_loss; - condlog(3, "%s: dev_loss_tmo = %u (controller default)", - mp->alias, mp->dev_loss); - return 0; - } - if (conf->dev_loss) { - mp->dev_loss = conf->dev_loss; - condlog(3, "%s: dev_loss_tmo = %u (config file default)", - mp->alias, mp->dev_loss); - return 0; - } + char *origin, buff[12]; + + mp_set_ovr(dev_loss); + mp_set_hwe(dev_loss); + mp_set_conf(dev_loss); mp->dev_loss = 0; return 0; +out: + print_dev_loss(buff, 12, &mp->dev_loss); + condlog(3, "%s: dev_loss_tmo = %s %s", mp->alias, buff, origin); + return 0; } extern int select_flush_on_last_del(struct multipath *mp) { + char *origin; + if (mp->flush_on_last_del == FLUSH_IN_PROGRESS) return 0; - if (mp->mpe && mp->mpe->flush_on_last_del != FLUSH_UNDEF) { - mp->flush_on_last_del = mp->mpe->flush_on_last_del; - condlog(3, "%s: flush_on_last_del = %i (multipath setting)", - mp->alias, mp->flush_on_last_del); - return 0; - } - if (mp->hwe && mp->hwe->flush_on_last_del != FLUSH_UNDEF) { - mp->flush_on_last_del = mp->hwe->flush_on_last_del; - condlog(3, "%s: flush_on_last_del = %i (controler setting)", - mp->alias, mp->flush_on_last_del); - return 0; - } - if (conf->flush_on_last_del != FLUSH_UNDEF) { - mp->flush_on_last_del = conf->flush_on_last_del; - condlog(3, "%s: flush_on_last_del = %i (config file default)", - mp->alias, mp->flush_on_last_del); - return 0; - } - mp->flush_on_last_del = FLUSH_UNDEF; - condlog(3, "%s: flush_on_last_del = DISABLED (internal default)", - mp->alias); + mp_set_mpe(flush_on_last_del); + mp_set_ovr(flush_on_last_del); + mp_set_hwe(flush_on_last_del); + mp_set_conf(flush_on_last_del); + mp_set_default(flush_on_last_del, FLUSH_DISABLED); +out: + condlog(3, "%s: flush_on_last_del = %s %s", mp->alias, + (mp->flush_on_last_del == FLUSH_ENABLED)? "yes" : "no", origin); return 0; } extern int select_reservation_key (struct multipath * mp) { - int j; - unsigned char *keyp; - uint64_t prkey = 0; + char *origin, buff[12]; + mp_set_mpe(reservation_key); + mp_set_conf(reservation_key); mp->reservation_key = NULL; - - if (mp->mpe && mp->mpe->reservation_key) { - keyp = mp->mpe->reservation_key; - for (j = 0; j < 8; ++j) { - if (j > 0) - prkey <<= 8; - prkey |= *keyp; - ++keyp; - } - - condlog(3, "%s: reservation_key = 0x%" PRIx64 " " - "(multipath setting)", mp->alias, prkey); - - mp->reservation_key = mp->mpe->reservation_key; - return 0; - } - - if (conf->reservation_key) { - keyp = conf->reservation_key; - for (j = 0; j < 8; ++j) { - if (j > 0) - prkey <<= 8; - prkey |= *keyp; - ++keyp; - } - - condlog(3, "%s: reservation_key = 0x%" PRIx64 - " (config file default)", mp->alias, prkey); - - mp->reservation_key = conf->reservation_key; - return 0; - } - + return 0; +out: + print_reservation_key(buff, 12, &mp->reservation_key); + condlog(3, "%s: reservation_key = %s %s", mp->alias, buff, origin); return 0; } extern int select_retain_hwhandler (struct multipath * mp) { + char *origin; unsigned int minv_dm_retain[3] = {1, 5, 0}; if (!VERSION_GE(conf->version, minv_dm_retain)) { mp->retain_hwhandler = RETAIN_HWHANDLER_OFF; - condlog(3, "%s: retain_attached_hw_handler disabled (requires kernel version >= 1.5.0)", mp->alias); - return 0; - } - - if (mp->hwe && mp->hwe->retain_hwhandler) { - mp->retain_hwhandler = mp->hwe->retain_hwhandler; - condlog(3, "%s: retain_attached_hw_handler = %d (controller default)", mp->alias, mp->retain_hwhandler); - return 0; - } - if (conf->retain_hwhandler) { - mp->retain_hwhandler = conf->retain_hwhandler; - condlog(3, "%s: retain_attached_hw_handler = %d (config file default)", mp->alias, mp->retain_hwhandler); - return 0; + origin = "(requires kernel version >= 1.5.0)"; + goto out; } - mp->retain_hwhandler = 0; - condlog(3, "%s: retain_attached_hw_handler = %d (compiled in default)", mp->alias, mp->retain_hwhandler); + mp_set_ovr(retain_hwhandler); + mp_set_hwe(retain_hwhandler); + mp_set_conf(retain_hwhandler); + mp_set_default(retain_hwhandler, DEFAULT_RETAIN_HWHANDLER); +out: + condlog(3, "%s: retain_attached_hw_handler = %s %s", mp->alias, + (mp->retain_hwhandler == RETAIN_HWHANDLER_ON)? "yes" : "no", + origin); return 0; } extern int select_detect_prio (struct path * pp) { - if (pp->hwe && pp->hwe->detect_prio) { - pp->detect_prio = pp->hwe->detect_prio; - condlog(3, "%s: detect_prio = %d (controller default)", pp->dev, pp->detect_prio); - return 0; - } - if (conf->detect_prio) { - pp->detect_prio = conf->detect_prio; - condlog(3, "%s: detect_prio = %d (config file default)", pp->dev, pp->detect_prio); + char *origin; + + pp_set_ovr(detect_prio); + pp_set_hwe(detect_prio); + pp_set_conf(detect_prio); + pp_set_default(detect_prio, DEFAULT_DETECT_PRIO); +out: + condlog(3, "%s: detect_prio = %s %s", pp->dev, + (pp->detect_prio == DETECT_PRIO_ON)? "yes" : "no", origin); + return 0; +} + +extern int +select_deferred_remove (struct multipath *mp) +{ + char *origin; + +#ifndef LIBDM_API_DEFERRED + mp->deferred_remove = DEFERRED_REMOVE_OFF; + origin = "(not compiled with support)"; + goto out; +#endif + if (mp->deferred_remove == DEFERRED_REMOVE_IN_PROGRESS) { + condlog(3, "%s: deferred remove in progress", mp->alias); return 0; } - pp->detect_prio = 0; - condlog(3, "%s: detect_prio = %d (compiled in default)", pp->dev, pp->detect_prio); + mp_set_mpe(deferred_remove); + mp_set_ovr(deferred_remove); + mp_set_hwe(deferred_remove); + mp_set_conf(deferred_remove); + mp_set_default(deferred_remove, DEFAULT_DEFERRED_REMOVE); +out: + condlog(3, "%s: deferred_remove = %s %s", mp->alias, + (mp->deferred_remove == DEFERRED_REMOVE_ON)? "yes" : "no", + origin); + return 0; +} + +extern int +select_delay_watch_checks(struct multipath *mp) +{ + char *origin, buff[12]; + + mp_set_mpe(delay_watch_checks); + mp_set_ovr(delay_watch_checks); + mp_set_hwe(delay_watch_checks); + mp_set_conf(delay_watch_checks); + mp_set_default(delay_watch_checks, DEFAULT_DELAY_CHECKS); +out: + print_delay_checks(buff, 12, &mp->delay_watch_checks); + condlog(3, "%s: delay_watch_checks = %s %s", mp->alias, buff, origin); + return 0; +} + +extern int +select_delay_wait_checks(struct multipath *mp) +{ + char *origin, buff[12]; + + mp_set_mpe(delay_wait_checks); + mp_set_ovr(delay_wait_checks); + mp_set_hwe(delay_wait_checks); + mp_set_conf(delay_wait_checks); + mp_set_default(delay_wait_checks, DEFAULT_DELAY_CHECKS); +out: + print_delay_checks(buff, 12, &mp->delay_wait_checks); + condlog(3, "%s: delay_wait_checks = %s %s", mp->alias, buff, origin); return 0; } diff --git a/libmultipath/propsel.h b/libmultipath/propsel.h index 05c6a4e..f9598e7 100644 --- a/libmultipath/propsel.h +++ b/libmultipath/propsel.h @@ -19,3 +19,6 @@ int select_dev_loss(struct multipath *mp); int select_reservation_key(struct multipath *mp); int select_retain_hwhandler (struct multipath * mp); int select_detect_prio(struct path * pp); +int select_deferred_remove(struct multipath *mp); +int select_delay_watch_checks (struct multipath * mp); +int select_delay_wait_checks (struct multipath * mp); diff --git a/libmultipath/structs.c b/libmultipath/structs.c index 049f17d..0538327 100644 --- a/libmultipath/structs.c +++ b/libmultipath/structs.c @@ -18,6 +18,70 @@ #include "blacklist.h" #include "prio.h" +struct adapter_group * +alloc_adaptergroup(void) +{ + struct adapter_group *agp; + + agp = (struct adapter_group *)MALLOC(sizeof(struct adapter_group)); + + if (!agp) + return NULL; + + agp->host_groups = vector_alloc(); + if (!agp->host_groups) { + FREE(agp); + agp = NULL; + } + return agp; +} + +void free_adaptergroup(vector adapters) +{ + int i; + struct adapter_group *agp; + + vector_foreach_slot(adapters, agp, i) { + free_hostgroup(agp->host_groups); + FREE(agp); + } + vector_free(adapters); +} + +void free_hostgroup(vector hostgroups) +{ + int i; + struct host_group *hgp; + + if (!hostgroups) + return; + + vector_foreach_slot(hostgroups, hgp, i) { + vector_free(hgp->paths); + FREE(hgp); + } + vector_free(hostgroups); +} + +struct host_group * +alloc_hostgroup(void) +{ + struct host_group *hgp; + + hgp = (struct host_group *)MALLOC(sizeof(struct host_group)); + + if (!hgp) + return NULL; + + hgp->paths = vector_alloc(); + + if (!hgp->paths) { + FREE(hgp); + hgp = NULL; + } + return hgp; +} + struct path * alloc_path (void) { @@ -142,10 +206,7 @@ free_multipath_attributes (struct multipath * mpp) if (!mpp) return; - if (mpp->selector && - mpp->selector != conf->selector && - (!mpp->mpe || (mpp->mpe && mpp->selector != mpp->mpe->selector)) && - (!mpp->hwe || (mpp->hwe && mpp->selector != mpp->hwe->selector))) { + if (mpp->selector) { FREE(mpp->selector); mpp->selector = NULL; } @@ -155,9 +216,7 @@ free_multipath_attributes (struct multipath * mpp) mpp->features = NULL; } - if (mpp->hwhandler && - mpp->hwhandler != conf->hwhandler && - (!mpp->hwe || (mpp->hwe && mpp->hwhandler != mpp->hwe->hwhandler))) { + if (mpp->hwhandler) { FREE(mpp->hwhandler); mpp->hwhandler = NULL; } @@ -242,6 +301,26 @@ store_pathgroup (vector pgvec, struct pathgroup * pgp) return 0; } +int +store_hostgroup(vector hostgroupvec, struct host_group * hgp) +{ + if (!vector_alloc_slot(hostgroupvec)) + return 1; + + vector_set_slot(hostgroupvec, hgp); + return 0; +} + +int +store_adaptergroup(vector adapters, struct adapter_group * agp) +{ + if (!vector_alloc_slot(adapters)) + return 1; + + vector_set_slot(adapters, agp); + return 0; +} + struct multipath * find_mp_by_minor (vector mpvec, int minor) { diff --git a/libmultipath/structs.h b/libmultipath/structs.h index 64de06e..ab7dc25 100644 --- a/libmultipath/structs.h +++ b/libmultipath/structs.h @@ -15,7 +15,8 @@ #define BLK_DEV_SIZE 33 #define PATH_SIZE 512 #define NAME_SIZE 512 - +#define HOST_NAME_LEN 16 +#define SLOT_NAME_SIZE 40 #define SCSI_VENDOR_SIZE 9 #define SCSI_PRODUCT_SIZE 17 @@ -66,15 +67,15 @@ enum pgstates { PGSTATE_ACTIVE }; -enum queue_without_daemon_states { - QUE_NO_DAEMON_OFF, - QUE_NO_DAEMON_ON, - QUE_NO_DAEMON_FORCE, +enum yes_no_states { + YN_NO, + YN_YES, }; -enum pgtimeouts { - PGTIMEOUT_UNDEF, - PGTIMEOUT_NONE +enum queue_without_daemon_states { + QUE_NO_DAEMON_OFF = YN_NO, + QUE_NO_DAEMON_ON = YN_YES, + QUE_NO_DAEMON_FORCE, }; enum attribute_bits { @@ -83,10 +84,16 @@ enum attribute_bits { ATTR_MODE, }; +enum yes_no_undef_states { + YNU_UNDEF, + YNU_NO, + YNU_YES, +}; + enum flush_states { - FLUSH_UNDEF, - FLUSH_DISABLED, - FLUSH_ENABLED, + FLUSH_UNDEF = YNU_UNDEF, + FLUSH_DISABLED = YNU_NO, + FLUSH_ENABLED = YNU_YES, FLUSH_IN_PROGRESS, }; @@ -96,21 +103,28 @@ enum log_checker_err_states { }; enum user_friendly_names_states { - USER_FRIENDLY_NAMES_UNDEF, - USER_FRIENDLY_NAMES_OFF, - USER_FRIENDLY_NAMES_ON, + USER_FRIENDLY_NAMES_UNDEF = YNU_UNDEF, + USER_FRIENDLY_NAMES_OFF = YNU_NO, + USER_FRIENDLY_NAMES_ON = YNU_YES, }; enum retain_hwhandler_states { - RETAIN_HWHANDLER_UNDEF, - RETAIN_HWHANDLER_OFF, - RETAIN_HWHANDLER_ON, + RETAIN_HWHANDLER_UNDEF = YNU_UNDEF, + RETAIN_HWHANDLER_OFF = YNU_NO, + RETAIN_HWHANDLER_ON = YNU_YES, }; enum detect_prio_states { - DETECT_PRIO_UNDEF, - DETECT_PRIO_OFF, - DETECT_PRIO_ON, + DETECT_PRIO_UNDEF = YNU_UNDEF, + DETECT_PRIO_OFF = YNU_NO, + DETECT_PRIO_ON = YNU_YES, +}; + +enum deferred_remove_states { + DEFERRED_REMOVE_UNDEF = YNU_UNDEF, + DEFERRED_REMOVE_OFF = YNU_NO, + DEFERRED_REMOVE_ON = YNU_YES, + DEFERRED_REMOVE_IN_PROGRESS, }; enum scsi_protocol { @@ -126,6 +140,18 @@ enum scsi_protocol { SCSI_PROTOCOL_UNSPEC = 0xf, /* No specific protocol */ }; +enum delay_checks_states { + DELAY_CHECKS_OFF = -1, + DELAY_CHECKS_UNDEF = 0, +}; + +enum initialized_states { + INIT_FAILED, + INIT_MISSING_UDEV, + INIT_REQUESTED_UDEV, + INIT_OK, +}; + struct sg_id { int host_no; int channel; @@ -172,6 +198,9 @@ struct path { int priority; int pgindex; int detect_prio; + int watch_checks; + int wait_checks; + int tpgs; char * uid_attribute; char * getuid; struct prio prio; @@ -179,6 +208,8 @@ struct path { struct checker checker; struct multipath * mpp; int fd; + int initialized; + int retriggers; /* configlet pointers */ struct hwentry * hwe; @@ -195,6 +226,8 @@ struct multipath { int bestpg; int queuedio; int action; + int wait_for_udev; + int uev_wait_tick; int pgfailback; int failback_tick; int rr_weight; @@ -206,6 +239,9 @@ struct multipath { int attribute_flags; int fast_io_fail; int retain_hwhandler; + int deferred_remove; + int delay_watch_checks; + int delay_wait_checks; unsigned int dev_loss; uid_t uid; gid_t gid; @@ -251,6 +287,20 @@ struct pathgroup { char * selector; }; +struct adapter_group { + char adapter_name[SLOT_NAME_SIZE]; + struct pathgroup *pgp; + int num_hosts; + vector host_groups; + int next_host_index; +}; + +struct host_group { + int host_no; + int num_paths; + vector paths; +}; + struct path * alloc_path (void); struct pathgroup * alloc_pathgroup (void); struct multipath * alloc_multipath (void); @@ -263,6 +313,14 @@ void free_multipath_attributes (struct multipath *); void drop_multipath (vector mpvec, char * wwid, enum free_path_mode free_paths); void free_multipathvec (vector mpvec, enum free_path_mode free_paths); +struct adapter_group * alloc_adaptergroup(void); +struct host_group * alloc_hostgroup(void); +void free_adaptergroup(vector adapters); +void free_hostgroup(vector hostgroups); + +int store_adaptergroup(vector adapters, struct adapter_group *agp); +int store_hostgroup(vector hostgroupvec, struct host_group *hgp); + int store_path (vector pathvec, struct path * pp); int store_pathgroup (vector pgvec, struct pathgroup * pgp); diff --git a/libmultipath/structs_vec.c b/libmultipath/structs_vec.c index 76c7e02..d9a731a 100644 --- a/libmultipath/structs_vec.c +++ b/libmultipath/structs_vec.c @@ -8,6 +8,7 @@ #include "debug.h" #include "structs.h" #include "structs_vec.h" +#include "sysfs.h" #include "waiter.h" #include "devmapper.h" #include "dmparser.h" @@ -226,6 +227,12 @@ extract_hwe_from_path(struct multipath * mpp) } if (pp) { + if (!strlen(pp->vendor_id) || + !strlen(pp->product_id) || + !strlen(pp->rev)) { + condlog(3, "%s: no device details available", pp->dev); + return NULL; + } condlog(3, "%s: vendor = %s", pp->dev, pp->vendor_id); condlog(3, "%s: product = %s", pp->dev, pp->product_id); condlog(3, "%s: rev = %s", pp->dev, pp->rev); @@ -281,12 +288,38 @@ update_multipath_status (struct multipath *mpp) return 0; } +void sync_paths(struct multipath *mpp, vector pathvec) +{ + struct path *pp; + struct pathgroup *pgp; + int found, i, j; + + vector_foreach_slot (mpp->paths, pp, i) { + found = 0; + vector_foreach_slot(mpp->pg, pgp, j) { + if (find_slot(pgp->paths, (void *)pp) != -1) { + found = 1; + break; + } + } + if (!found) { + condlog(3, "%s dropped path %s", mpp->alias, pp->dev); + vector_del_slot(mpp->paths, i--); + orphan_path(pp, "path removed externally"); + } + } + update_mpp_paths(mpp, pathvec); + vector_foreach_slot (mpp->paths, pp, i) + pp->mpp = mpp; +} + extern int update_multipath_strings (struct multipath *mpp, vector pathvec) { if (!mpp) return 1; + update_mpp_paths(mpp, pathvec); condlog(4, "%s: %s", mpp->alias, __FUNCTION__); free_multipath_attributes(mpp); @@ -295,6 +328,7 @@ update_multipath_strings (struct multipath *mpp, vector pathvec) if (update_multipath_table(mpp, pathvec)) return 1; + sync_paths(mpp, pathvec); if (update_multipath_status(mpp)) return 1; @@ -365,6 +399,8 @@ __setup_multipath (struct vectors * vecs, struct multipath * mpp, int reset) select_pgfailback(mpp); set_no_path_retry(mpp); select_flush_on_last_del(mpp); + if (VECTOR_SIZE(mpp->paths) != 0) + dm_cancel_deferred_remove(mpp); } return 0; @@ -403,12 +439,29 @@ out: return NULL; } +static void +find_existing_alias (struct multipath * mpp, + struct vectors *vecs) +{ + struct multipath * mp; + int i; + + vector_foreach_slot (vecs->mpvec, mp, i) + if (strcmp(mp->wwid, mpp->wwid) == 0) { + strncpy(mpp->alias_old, mp->alias, WWID_SIZE); + return; + } +} + extern struct multipath * add_map_with_path (struct vectors * vecs, struct path * pp, int add_vec) { struct multipath * mpp; + if (!strlen(pp->wwid)) + return NULL; + if (!(mpp = alloc_multipath())) return NULL; @@ -416,6 +469,7 @@ add_map_with_path (struct vectors * vecs, mpp->hwe = pp->hwe; strcpy(mpp->wwid, pp->wwid); + find_existing_alias(mpp, vecs); if (select_alias(mpp)) goto out; mpp->size = pp->size; @@ -438,7 +492,7 @@ out: } extern int -verify_paths(struct multipath * mpp, struct vectors * vecs, vector rpvec) +verify_paths(struct multipath * mpp, struct vectors * vecs) { struct path * pp; int count = 0; @@ -451,7 +505,8 @@ verify_paths(struct multipath * mpp, struct vectors * vecs, vector rpvec) /* * see if path is in sysfs */ - if (sysfs_get_dev(pp->udev, pp->dev_t, BLK_DEV_SIZE) <= 0) { + if (sysfs_attr_get_value(pp->udev, "dev", + pp->dev_t, BLK_DEV_SIZE) < 0) { if (pp->state != PATH_DOWN) { condlog(1, "%s: removing valid path %s in state %d", mpp->alias, pp->dev, pp->state); @@ -463,14 +518,10 @@ verify_paths(struct multipath * mpp, struct vectors * vecs, vector rpvec) vector_del_slot(mpp->paths, i); i--; - if (rpvec) - store_path(rpvec, pp); - else { - if ((j = find_slot(vecs->pathvec, - (void *)pp)) != -1) - vector_del_slot(vecs->pathvec, j); - free_path(pp); - } + if ((j = find_slot(vecs->pathvec, + (void *)pp)) != -1) + vector_del_slot(vecs->pathvec, j); + free_path(pp); } else { condlog(4, "%s: verified path %s dev_t %s", mpp->alias, pp->dev, pp->dev_t); @@ -493,13 +544,9 @@ int update_multipath (struct vectors *vecs, char *mapname, int reset) return 2; } - free_pgvec(mpp->pg, KEEP_PATHS); - mpp->pg = NULL; - if (__setup_multipath(vecs, mpp, reset)) return 1; /* mpp freed in setup_multipath */ - adopt_paths(vecs->pathvec, mpp, 0); /* * compare checkers states with DM states */ @@ -510,7 +557,7 @@ int update_multipath (struct vectors *vecs, char *mapname, int reset) if (pp->state != PATH_DOWN) { int oldstate = pp->state; - condlog(2, "%s: mark as failed", pp->dev_t); + condlog(2, "%s: mark as failed", pp->dev); mpp->stat_path_failures++; pp->state = PATH_DOWN; if (oldstate == PATH_UP || @@ -526,7 +573,6 @@ int update_multipath (struct vectors *vecs, char *mapname, int reset) } } } - return 0; } diff --git a/libmultipath/structs_vec.h b/libmultipath/structs_vec.h index c6278ac..eb8e672 100644 --- a/libmultipath/structs_vec.h +++ b/libmultipath/structs_vec.h @@ -19,7 +19,7 @@ int adopt_paths (vector pathvec, struct multipath * mpp, int get_info); void orphan_paths (vector pathvec, struct multipath * mpp); void orphan_path (struct path * pp, const char *reason); -int verify_paths(struct multipath * mpp, struct vectors * vecs, vector rpvec); +int verify_paths(struct multipath * mpp, struct vectors * vecs); int update_mpp_paths(struct multipath * mpp, vector pathvec); int __setup_multipath (struct vectors * vecs, struct multipath * mpp, int reset); diff --git a/libmultipath/sysfs.c b/libmultipath/sysfs.c index e5834f9..de7df40 100644 --- a/libmultipath/sysfs.c +++ b/libmultipath/sysfs.c @@ -74,6 +74,61 @@ ssize_t sysfs_attr_get_value(struct udev_device *dev, const char *attr_name, return -EPERM; } + /* read attribute value */ + fd = open(devpath, O_RDONLY); + if (fd < 0) { + condlog(4, "attribute '%s' can not be opened: %s", + devpath, strerror(errno)); + return -errno; + } + size = read(fd, value, value_len); + if (size < 0) { + condlog(4, "read from %s failed: %s", devpath, strerror(errno)); + size = -errno; + } else if (size == value_len) { + condlog(4, "overflow while reading from %s", devpath); + size = 0; + } else { + value[size] = '\0'; + } + + close(fd); + if (size > 0) + size = strchop(value); + return size; +} + +ssize_t sysfs_bin_attr_get_value(struct udev_device *dev, const char *attr_name, + unsigned char * value, size_t value_len) +{ + char devpath[PATH_SIZE]; + struct stat statbuf; + int fd; + ssize_t size = -1; + + if (!dev || !attr_name || !value) + return 0; + + snprintf(devpath, PATH_SIZE, "%s/%s", udev_device_get_syspath(dev), + attr_name); + condlog(4, "open '%s'", devpath); + if (stat(devpath, &statbuf) != 0) { + condlog(4, "stat '%s' failed: %s", devpath, strerror(errno)); + return -ENXIO; + } + + /* skip directories */ + if (S_ISDIR(statbuf.st_mode)) { + condlog(4, "%s is a directory", devpath); + return -EISDIR; + } + + /* skip non-writeable files */ + if ((statbuf.st_mode & S_IRUSR) == 0) { + condlog(4, "%s is not readable", devpath); + return -EPERM; + } + /* read attribute value */ fd = open(devpath, O_RDONLY); if (fd < 0) { @@ -156,7 +211,7 @@ sysfs_get_size (struct path *pp, unsigned long long * size) return 1; attr[0] = '\0'; - if (sysfs_attr_get_value(pp->udev, "size", attr, 255) == 0) { + if (sysfs_attr_get_value(pp->udev, "size", attr, 255) <= 0) { condlog(3, "%s: No size attribute in sysfs", pp->dev); return 1; } diff --git a/libmultipath/sysfs.h b/libmultipath/sysfs.h index 34f3e00..2588c24 100644 --- a/libmultipath/sysfs.h +++ b/libmultipath/sysfs.h @@ -9,6 +9,8 @@ ssize_t sysfs_attr_set_value(struct udev_device *dev, const char *attr_name, char * value, size_t value_len); ssize_t sysfs_attr_get_value(struct udev_device *dev, const char *attr_name, char * value, size_t value_len); +ssize_t sysfs_bin_attr_get_value(struct udev_device *dev, const char *attr_name, + unsigned char * value, size_t value_len); int sysfs_get_size (struct path *pp, unsigned long long * size); int sysfs_check_holders(char * check_devt, char * new_devt); #endif diff --git a/libmultipath/uevent.c b/libmultipath/uevent.c index 9ee3ade..478c6ce 100644 --- a/libmultipath/uevent.c +++ b/libmultipath/uevent.c @@ -34,9 +34,11 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -182,6 +184,80 @@ int uevent_dispatch(int (*uev_trigger)(struct uevent *, void * trigger_data), return 0; } +struct uevent *uevent_from_buffer(char *buf, ssize_t buflen) +{ + struct uevent *uev; + char *buffer; + size_t bufpos; + int i; + char *pos; + + uev = alloc_uevent(); + if (!uev) { + condlog(1, "lost uevent, oom"); + return NULL; + } + + if ((size_t)buflen > sizeof(buf)-1) + buflen = sizeof(buf)-1; + + /* + * Copy the shared receive buffer contents to buffer private + * to this uevent so we can immediately reuse the shared buffer. + */ + memcpy(uev->buffer, buf, HOTPLUG_BUFFER_SIZE + OBJECT_SIZE); + buffer = uev->buffer; + buffer[buflen] = '\0'; + + /* save start of payload */ + bufpos = strlen(buffer) + 1; + + /* action string */ + uev->action = buffer; + pos = strchr(buffer, '@'); + if (!pos) { + condlog(3, "bad action string '%s'", buffer); + FREE(uev); + return NULL; + } + pos[0] = '\0'; + + /* sysfs path */ + uev->devpath = &pos[1]; + + /* hotplug events have the environment attached - reconstruct envp[] */ + for (i = 0; (bufpos < (size_t)buflen) && (i < HOTPLUG_NUM_ENVP-1); i++) { + int keylen; + char *key; + + key = &buffer[bufpos]; + keylen = strlen(key); + uev->envp[i] = key; + /* Filter out sequence number */ + if (strncmp(key, "SEQNUM=", 7) == 0) { + char *eptr; + + uev->seqnum = strtoul(key + 7, &eptr, 10); + if (eptr == key + 7) + uev->seqnum = -1; + } + bufpos += keylen + 1; + } + uev->envp[i] = NULL; + + condlog(3, "uevent %ld '%s' from '%s'", uev->seqnum, + uev->action, uev->devpath); + uev->kernel = strrchr(uev->devpath, '/'); + if (uev->kernel) + uev->kernel++; + + /* print payload environment */ + for (i = 0; uev->envp[i] != NULL; i++) + condlog(5, "%s", uev->envp[i]); + + return uev; +} + int failback_listen(void) { int sock; @@ -266,12 +342,9 @@ int failback_listen(void) } while (1) { - int i; - char *pos; size_t bufpos; ssize_t buflen; struct uevent *uev; - char *buffer; struct msghdr smsg; struct iovec iov; char cred_msg[CMSG_SPACE(sizeof(struct ucred))]; @@ -324,69 +397,9 @@ int failback_listen(void) buflen = sizeof(buf)-1; } - uev = alloc_uevent(); - - if (!uev) { - condlog(1, "lost uevent, oom"); + uev = uevent_from_buffer(buf, buflen); + if (!uev) continue; - } - - if ((size_t)buflen > sizeof(buf)-1) - buflen = sizeof(buf)-1; - - /* - * Copy the shared receive buffer contents to buffer private - * to this uevent so we can immediately reuse the shared buffer. - */ - memcpy(uev->buffer, buf, HOTPLUG_BUFFER_SIZE + OBJECT_SIZE); - buffer = uev->buffer; - buffer[buflen] = '\0'; - - /* save start of payload */ - bufpos = strlen(buffer) + 1; - - /* action string */ - uev->action = buffer; - pos = strchr(buffer, '@'); - if (!pos) { - condlog(3, "bad action string '%s'", buffer); - continue; - } - pos[0] = '\0'; - - /* sysfs path */ - uev->devpath = &pos[1]; - - /* hotplug events have the environment attached - reconstruct envp[] */ - for (i = 0; (bufpos < (size_t)buflen) && (i < HOTPLUG_NUM_ENVP-1); i++) { - int keylen; - char *key; - - key = &buffer[bufpos]; - keylen = strlen(key); - uev->envp[i] = key; - /* Filter out sequence number */ - if (strncmp(key, "SEQNUM=", 7) == 0) { - char *eptr; - - uev->seqnum = strtoul(key + 7, &eptr, 10); - if (eptr == key + 7) - uev->seqnum = -1; - } - bufpos += keylen + 1; - } - uev->envp[i] = NULL; - - condlog(3, "uevent %ld '%s' from '%s'", uev->seqnum, - uev->action, uev->devpath); - uev->kernel = strrchr(uev->devpath, '/'); - if (uev->kernel) - uev->kernel++; - - /* print payload environment */ - for (i = 0; uev->envp[i] != NULL; i++) - condlog(5, "%s", uev->envp[i]); - /* * Queue uevent and poke service pthread. */ @@ -401,12 +414,72 @@ exit: return 1; } +struct uevent *uevent_from_udev_device(struct udev_device *dev) +{ + struct uevent *uev; + int i = 0; + char *pos, *end; + struct udev_list_entry *list_entry; + + uev = alloc_uevent(); + if (!uev) { + udev_device_unref(dev); + condlog(1, "lost uevent, oom"); + return NULL; + } + pos = uev->buffer; + end = pos + HOTPLUG_BUFFER_SIZE + OBJECT_SIZE - 1; + udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(dev)) { + const char *name, *value; + int bytes; + + name = udev_list_entry_get_name(list_entry); + if (!name) + name = "(null)"; + value = udev_list_entry_get_value(list_entry); + if (!value) + value = "(null)"; + bytes = snprintf(pos, end - pos, "%s=%s", name, value); + if (pos + bytes >= end) { + condlog(2, "buffer overflow for uevent"); + break; + } + uev->envp[i] = pos; + pos += bytes; + *pos = '\0'; + pos++; + if (strcmp(name, "DEVPATH") == 0) + uev->devpath = uev->envp[i] + 8; + if (strcmp(name, "ACTION") == 0) + uev->action = uev->envp[i] + 7; + i++; + if (i == HOTPLUG_NUM_ENVP - 1) + break; + } + uev->udev = dev; + uev->envp[i] = NULL; + + condlog(3, "uevent '%s' from '%s'", uev->action, uev->devpath); + uev->kernel = strrchr(uev->devpath, '/'); + if (uev->kernel) + uev->kernel++; + + /* print payload environment */ + for (i = 0; uev->envp[i] != NULL; i++) + condlog(5, "%s", uev->envp[i]); + return uev; +} + int uevent_listen(struct udev *udev) { - int err; + int err = 2; struct udev_monitor *monitor = NULL; - int fd, socket_flags; + int fd, fd_ep = -1, socket_flags, events; int need_failback = 1; + int timeout = 30; + sigset_t mask; + LIST_HEAD(uevlisten_tmp); + /* * Queue uevents for service by dedicated thread so that the uevent * listening thread does not block on multipathd locks (vecs->lock) @@ -423,7 +496,6 @@ int uevent_listen(struct udev *udev) monitor = udev_monitor_new_from_netlink(udev, "udev"); if (!monitor) { condlog(2, "failed to create udev monitor"); - err = 2; goto out; } #ifdef LIBUDEV_API_RECVBUF @@ -455,77 +527,63 @@ int uevent_listen(struct udev *udev) condlog(2, "failed to enable receiving : %s", strerror(-err)); goto out; } + + pthread_sigmask(SIG_SETMASK, NULL, &mask); + sigdelset(&mask, SIGHUP); + sigdelset(&mask, SIGUSR1); + events = 0; while (1) { - int i = 0; - char *pos, *end; struct uevent *uev; struct udev_device *dev; - struct udev_list_entry *list_entry; - - dev = udev_monitor_receive_device(monitor); - if (!dev) { - condlog(0, "failed getting udev device"); + struct pollfd ev_poll; + struct timespec poll_timeout; + int fdcount; + + memset(&ev_poll, 0, sizeof(struct pollfd)); + ev_poll.fd = fd; + ev_poll.events = POLLIN; + memset(&poll_timeout, 0, sizeof(struct timespec)); + poll_timeout.tv_sec = timeout; + errno = 0; + fdcount = ppoll(&ev_poll, 1, &poll_timeout, &mask); + if (fdcount && ev_poll.revents & POLLIN) { + timeout = 0; + dev = udev_monitor_receive_device(monitor); + if (!dev) { + condlog(0, "failed getting udev device"); + continue; + } + uev = uevent_from_udev_device(dev); + if (!uev) + continue; + list_add_tail(&uev->node, &uevlisten_tmp); + events++; continue; } - - uev = alloc_uevent(); - if (!uev) { - udev_device_unref(dev); - condlog(1, "lost uevent, oom"); - continue; + if (fdcount < 0) { + if (errno != EINTR) + condlog(0, "error receiving " + "uevent message: %m"); + err = -errno; + break; } - pos = uev->buffer; - end = pos + HOTPLUG_BUFFER_SIZE + OBJECT_SIZE - 1; - udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(dev)) { - const char *name, *value; - int bytes; - - name = udev_list_entry_get_name(list_entry); - if (!name) - name = "(null)"; - value = udev_list_entry_get_value(list_entry); - if (!value) - value = "(null)"; - bytes = snprintf(pos, end - pos, "%s=%s", name, - value); - if (pos + bytes >= end) { - condlog(2, "buffer overflow for uevent"); - break; - } - uev->envp[i] = pos; - pos += bytes; - *pos = '\0'; - pos++; - if (strcmp(name, "DEVPATH") == 0) - uev->devpath = uev->envp[i] + 8; - if (strcmp(name, "ACTION") == 0) - uev->action = uev->envp[i] + 7; - i++; - if (i == HOTPLUG_NUM_ENVP - 1) - break; + if (!list_empty(&uevlisten_tmp)) { + /* + * Queue uevents and poke service pthread. + */ + condlog(3, "Forwarding %d uevents", events); + pthread_mutex_lock(uevq_lockp); + list_splice_tail_init(&uevlisten_tmp, &uevq); + pthread_cond_signal(uev_condp); + pthread_mutex_unlock(uevq_lockp); + events = 0; } - uev->udev = dev; - uev->envp[i] = NULL; - - condlog(3, "uevent '%s' from '%s'", uev->action, uev->devpath); - uev->kernel = strrchr(uev->devpath, '/'); - if (uev->kernel) - uev->kernel++; - - /* print payload environment */ - for (i = 0; uev->envp[i] != NULL; i++) - condlog(5, "%s", uev->envp[i]); - - /* - * Queue uevent and poke service pthread. - */ - pthread_mutex_lock(uevq_lockp); - list_add_tail(&uev->node, &uevq); - pthread_cond_signal(uev_condp); - pthread_mutex_unlock(uevq_lockp); + timeout = 30; } need_failback = 0; out: + if (fd_ep >= 0) + close(fd_ep); if (monitor) udev_monitor_unref(monitor); if (need_failback) diff --git a/libmultipath/util.c b/libmultipath/util.c index 06a6311..ac0d1b2 100644 --- a/libmultipath/util.c +++ b/libmultipath/util.c @@ -112,7 +112,8 @@ size_t strlcpy(char *dst, const char *src, size_t size) bytes++; } - if (bytes == size) + /* If size == 0 there is no space for a final null... */ + if (size) *q = '\0'; return bytes; } @@ -165,7 +166,7 @@ devt2devname (char *devname, int devname_len, char *devt) sprintf(block_path,"/sys/dev/block/%u:%u", major, minor); if (lstat(block_path, &statbuf) == 0) { if (S_ISLNK(statbuf.st_mode) && - readlink(block_path, dev, FILE_NAME_SIZE) > 0) { + readlink(block_path, dev, FILE_NAME_SIZE-1) > 0) { char *p = strrchr(dev, '/'); if (!p) { diff --git a/libmultipath/uxsock.c b/libmultipath/uxsock.c index aff7a62..e91abd9 100644 --- a/libmultipath/uxsock.c +++ b/libmultipath/uxsock.c @@ -19,40 +19,11 @@ #ifdef USE_SYSTEMD #include #endif +#include #include "memory.h" #include "uxsock.h" #include "debug.h" - -/* - * connect to a unix domain socket - */ -int ux_socket_connect(const char *name) -{ - int fd, len; - struct sockaddr_un addr; - - memset(&addr, 0, sizeof(addr)); - addr.sun_family = AF_LOCAL; - addr.sun_path[0] = '\0'; - len = strlen(name) + 1 + sizeof(sa_family_t); - strncpy(&addr.sun_path[1], name, len); - - fd = socket(AF_LOCAL, SOCK_STREAM, 0); - if (fd == -1) { - condlog(3, "Couldn't create ux_socket, error %d", errno); - return -1; - } - - if (connect(fd, (struct sockaddr *)&addr, len) == -1) { - condlog(3, "Couldn't connect to ux_socket, error %d", errno); - close(fd); - return -1; - } - - return fd; -} - /* * create a unix domain socket and start listening on it * return a file descriptor open on the socket @@ -128,7 +99,7 @@ size_t write_all(int fd, const void *buf, size_t len) /* * keep reading until its all read */ -size_t read_all(int fd, void *buf, size_t len) +ssize_t read_all(int fd, void *buf, size_t len, unsigned int timeout) { size_t total = 0; ssize_t n; @@ -138,21 +109,20 @@ size_t read_all(int fd, void *buf, size_t len) while (len) { pfd.fd = fd; pfd.events = POLLIN; - ret = poll(&pfd, 1, 1000); + ret = poll(&pfd, 1, timeout); if (!ret) { - errno = ETIMEDOUT; - return total; + return -ETIMEDOUT; } else if (ret < 0) { if (errno == EINTR) continue; - return total; + return -errno; } else if (!pfd.revents & POLLIN) continue; n = read(fd, buf, len); if (n < 0) { if ((errno == EINTR) || (errno == EAGAIN)) continue; - return total; + return -errno; } if (!n) return total; @@ -166,7 +136,7 @@ size_t read_all(int fd, void *buf, size_t len) /* * send a packet in length prefix format */ -int send_packet(int fd, const char *buf, size_t len) +int send_packet(int fd, const char *buf) { int ret = 0; sigset_t set, old; @@ -176,10 +146,7 @@ int send_packet(int fd, const char *buf, size_t len) sigaddset(&set, SIGPIPE); pthread_sigmask(SIG_BLOCK, &set, &old); - if (write_all(fd, &len, sizeof(len)) != sizeof(len)) - ret = -1; - if (!ret && write_all(fd, buf, len) != len) - ret = -1; + ret = mpath_send_cmd(fd, buf); /* And unblock it again */ pthread_sigmask(SIG_SETMASK, &old, NULL); @@ -190,25 +157,23 @@ int send_packet(int fd, const char *buf, size_t len) /* * receive a packet in length prefix format */ -int recv_packet(int fd, char **buf, size_t *len) +int recv_packet(int fd, char **buf, unsigned int timeout) { - if (read_all(fd, len, sizeof(*len)) != sizeof(*len)) { - (*buf) = NULL; - *len = 0; - return -1; - } - if (len == 0) { - (*buf) = NULL; - return 0; - } - (*buf) = MALLOC(*len); + int err; + ssize_t len; + + *buf = NULL; + len = mpath_recv_reply_len(fd, timeout); + if (len <= 0) + return len; + (*buf) = MALLOC(len); if (!*buf) - return -1; - if (read_all(fd, *buf, *len) != *len) { + return -ENOMEM; + err = mpath_recv_reply_data(fd, *buf, len, timeout); + if (err) { FREE(*buf); (*buf) = NULL; - *len = 0; - return -1; + return err; } return 0; } diff --git a/libmultipath/uxsock.h b/libmultipath/uxsock.h index fd82552..c1cf81f 100644 --- a/libmultipath/uxsock.h +++ b/libmultipath/uxsock.h @@ -1,7 +1,6 @@ /* some prototypes */ -int ux_socket_connect(const char *name); int ux_socket_listen(const char *name); -int send_packet(int fd, const char *buf, size_t len); -int recv_packet(int fd, char **buf, size_t *len); +int send_packet(int fd, const char *buf); +int recv_packet(int fd, char **buf, unsigned int timeout); size_t write_all(int fd, const void *buf, size_t len); -size_t read_all(int fd, void *buf, size_t len); +ssize_t read_all(int fd, void *buf, size_t len, unsigned int timeout); diff --git a/libmultipath/version.h b/libmultipath/version.h index 8519300..d8c5e6e 100644 --- a/libmultipath/version.h +++ b/libmultipath/version.h @@ -20,8 +20,8 @@ #ifndef _VERSION_H #define _VERSION_H -#define VERSION_CODE 0x000500 -#define DATE_CODE 0x0c110d +#define VERSION_CODE 0x000600 +#define DATE_CODE 0x120410 #define PROG "multipath-tools" diff --git a/libmultipath/wwids.c b/libmultipath/wwids.c index eca1799..567c93d 100644 --- a/libmultipath/wwids.c +++ b/libmultipath/wwids.c @@ -260,6 +260,37 @@ out: return ret; } +int +should_multipath(struct path *pp1, vector pathvec) +{ + int i; + struct path *pp2; + + if (!conf->find_multipaths && !conf->ignore_new_devs) + return 1; + + condlog(4, "checking if %s should be multipathed", pp1->dev); + if (!conf->ignore_new_devs) { + vector_foreach_slot(pathvec, pp2, i) { + if (pp1->dev == pp2->dev) + continue; + if (strncmp(pp1->wwid, pp2->wwid, WWID_SIZE) == 0) { + condlog(3, "found multiple paths with wwid %s, " + "multipathing %s", pp1->wwid, pp1->dev); + return 1; + } + } + } + if (check_wwids_file(pp1->wwid, 0) < 0) { + condlog(3, "wwid %s not in wwids file, skipping %s", + pp1->wwid, pp1->dev); + return 0; + } + condlog(3, "found wwid %s in wwids file, multipathing %s", pp1->wwid, + pp1->dev); + return 1; +} + int remember_wwid(char *wwid) { diff --git a/libmultipath/wwids.h b/libmultipath/wwids.h index f3b21fa..9527012 100644 --- a/libmultipath/wwids.h +++ b/libmultipath/wwids.h @@ -12,6 +12,7 @@ "#\n" \ "# Valid WWIDs:\n" +int should_multipath(struct path *pp, vector pathvec); int remember_wwid(char *wwid); int check_wwids_file(char *wwid, int write_wwid); int remove_wwid(char *wwid); diff --git a/mpathpersist/Makefile b/mpathpersist/Makefile index ad8e607..6f7a5cf 100644 --- a/mpathpersist/Makefile +++ b/mpathpersist/Makefile @@ -5,7 +5,7 @@ include ../Makefile.inc OBJS = main.o CFLAGS += -I$(multipathdir) -I$(mpathpersistdir) -LDFLAGS += -lpthread -ldevmapper -L$(mpathpersistdir) -lmpathpersist -L$(multipathdir) -lmultipath -ludev +LDFLAGS += -lpthread -ldevmapper -L$(mpathpersistdir) -lmpathpersist -L$(multipathdir) -L$(mpathcmddir) -lmpathcmd -lmultipath -ludev EXEC = mpathpersist diff --git a/multipath.conf.annotated b/multipath.conf.annotated index 235e130..0be034d 100644 --- a/multipath.conf.annotated +++ b/multipath.conf.annotated @@ -243,8 +243,9 @@ # # # # name : checker_timeout # # scope : multipath & multipathd -# # desc : The timeout to use for path checkers that issue scsi -# # commands with an explicit timeout, in seconds. +# # desc : The timeout to use for path checkers and prioritizers +# # that issue scsi commands with an explicit timeout, in +# # seconds. # # values : n > 0 # # default : taken from /sys/block/sd/device/timeout # checker_timeout 60 @@ -269,6 +270,7 @@ # # default : determined by the OS # dev_loss_tmo 600 # +# # # # name : bindings_file # # scope : multipath # # desc : The location of the bindings file that is used with @@ -277,6 +279,7 @@ # # default : "/var/lib/multipath/bindings" # bindings_file "/etc/multipath/bindings" # +# # # # name : wwids_file # # scope : multipath # # desc : The location of the wwids file multipath uses to @@ -285,6 +288,7 @@ # # default : "/var/lib/multipath/wwids" # wwids_file "/etc/multipath/wwids" # +# # # # name : reservation_key # # scope : multipath # # desc : Service action reservation key used by mpathpersist. @@ -292,6 +296,48 @@ # # default : (null) # reservation_key "mpathkey" # +# # +# # name : force_sync +# # scope : multipathd +# # desc : If set to yes, multipath will run all of the checkers in +# # sync mode, even if the checker has an async mode. +# # values : yes|no +# # default : no +# force_sync yes +# +# # +# # name : config_dir +# # scope : multipath & multipathd +# # desc : If not set to an empty string, multipath will search +# # this directory alphabetically for files ending in ".conf" +# # and it will read configuration information from these +# # files, just as if it was in /etc/multipath.conf +# # values : "" or a fully qualified pathname +# # default : "/etc/multipath/conf.d" +# +# # +# # name : delay_watch_checks +# # scope : multipathd +# # desc : If set to a value greater than 0, multipathd will watch +# # paths that have recently become valid for this many +# # checks. If they fail again while they are being watched, +# # when they next become valid, they will not be used until +# # they have stayed up for delay_wait_checks checks. +# # values : no| > 0 +# # default : no +# delay_watch_checks 12 +# +# # +# # name : delay_wait_checks +# # scope : multipathd +# # desc : If set to a value greater than 0, when a device that has +# # recently come back online fails again within +# # delay_watch_checks checks, the next time it comes back +# # online, it will marked and delayed, and not used until +# # it has passed delay_wait_checks checks. +# # values : no| > 0 +# # default : no +# delay_wait_checks 12 #} # ## @@ -460,6 +506,28 @@ # # default : determined by the process # gid 0 # +# # +# # name : delay_watch_checks +# # scope : multipathd +# # desc : If set to a value greater than 0, multipathd will +# # watch paths that have recently become valid for +# # this many checks. If they fail again while they +# # are being watched, when they next become valid, +# # they will not be used until they have stayed up for +# # delay_wait_checks checks. +# # values : no| > 0 +# delay_watch_checks 12 +# +# # +# # name : delay_wait_checks +# # scope : multipathd +# # desc : If set to a value greater than 0, when a device +# # that has recently come back online fails again +# # within delay_watch_checks checks, the next time it +# # comes online, it will marked and delayed, and not +# # used until it has passed delay_wait_checks checks. +# # values : no| > 0 +# delay_wait_checks 12 # } # multipath { # wwid 1DEC_____321816758474 @@ -473,7 +541,8 @@ ## scope : multipath & multipathd ## desc : list of per storage controller settings ## overrides default settings (device_maps block) -## overriden by per multipath settings (multipaths block) +## overriden by per multipath settings (multipaths block) +## and the overrides settings (overrides block) ## #devices { # # @@ -630,6 +699,30 @@ # # before removing it from the system. # # values : n > 0 # dev_loss_tmo 600 +# +# # +# # name : delay_watch_checks +# # scope : multipathd +# # desc : If set to a value greater than 0, multipathd will +# # watch paths that have recently become valid for +# # this many checks. If they fail again while they +# # are being watched, when they next become valid, +# # they will not be used until they have stayed up for +# # delay_wait_checks checks. +# # values : no| > 0 +# delay_watch_checks 12 +# +# # +# # name : delay_wait_checks +# # scope : multipathd +# # desc : If set to a value greater than 0, when a device +# # that has recently come back online fails again +# # within delay_watch_checks checks, the next time it +# # comes online, it will marked and delayed, and not +# # used until it has passed delay_wait_checks checks. +# # values : no| > 0 +# delay_wait_checks 12 +# # } # device { # vendor "COMPAQ " @@ -639,3 +732,18 @@ # rr_weight priorities # } #} +# +## +## name : devices +## scope : multipath & multipathd +## desc : list of settings to override all hadware settings for all devices +## overrides default settings (device_maps block) +## and per device type settings (devices block) +## overriden by per multipath settings (multipaths block) +## +# attributes and values are identical to the device block +# +#overrides { +# dev_loss_tmo 60 +# no_path_retry fail +#} diff --git a/multipath.conf.defaults b/multipath.conf.defaults index e761902..a4e68b1 100644 --- a/multipath.conf.defaults +++ b/multipath.conf.defaults @@ -26,11 +26,16 @@ # log_checker_err always # retain_attached_hw_handler no # detect_prio no +# config_dir "/etc/multipath/conf.d" +# delay_watch_checks no +# delay_wait_checks no +# missing_uev_wait_timeout 30 #} #blacklist { # devnode "^(ram|raw|loop|fd|md|dm-|sr|scd|st)[0-9]*" -# devnode "^hd[a-z]" +# devnode "^(td|hd|vd)[a-z]" # devnode "^dcssblk[0-9]*" +# devnode "^nvme.*" # device { # vendor "DGC" # product "LUNZ" @@ -69,7 +74,7 @@ # } #} #blacklist_exceptions { -# property "(ID_SCSI_VPD|ID_WWN)" +# property "(SCSI_IDENT_.*|ID_WWN)" #} #devices { # device { @@ -259,6 +264,8 @@ # failback "immediate" # rr_weight "uniform" # no_path_retry 60 +# retain_attached_hw_handler yes +# detect_prio yes # } # device { # vendor "EMC" @@ -284,7 +291,7 @@ # } # device { # vendor "FUJITSU" -# product "ETERNUS_DX(L|400|8000)" +# product "ETERNUS_DX(L|M|400|8000)" # path_grouping_policy "group_by_prio" # path_checker "tur" # features "1 queue_if_no_path" @@ -653,6 +660,32 @@ # no_path_retry 15 # } # device { +# vendor "DELL" +# product "MD36xxi" +# product_blacklist "Universal Xport" +# path_grouping_policy "group_by_prio" +# path_checker "rdac" +# features "2 pg_init_retries 50" +# hardware_handler "1 rdac" +# prio "rdac" +# failback "immediate" +# rr_weight "uniform" +# no_path_retry 15 +# } +# device { +# vendor "DELL" +# product "MD36xxf" +# product_blacklist "Universal Xport" +# path_grouping_policy "group_by_prio" +# path_checker "rdac" +# features "2 pg_init_retries 50" +# hardware_handler "1 rdac" +# prio "rdac" +# failback "immediate" +# rr_weight "uniform" +# no_path_retry 15 +# } +# device { # vendor "NETAPP" # product "LUN.*" # path_grouping_policy "group_by_prio" @@ -908,6 +941,19 @@ # rr_weight "uniform" # no_path_retry "queue" # } +# device { +# vendor "PURE" +# path_selector "queue-length 0" +# path_grouping_policy "multibus" +# path_checker "tur" +# fast_io_fail_tmo 10 +# user_friendly_names "no" +# no_path_retry 0 +# features 0 +# dev_loss_tmo 60 +# } #} #multipaths { #} +#overrides { +#} diff --git a/multipath.conf.synthetic b/multipath.conf.synthetic index bda1b75..f7b9b8a 100644 --- a/multipath.conf.synthetic +++ b/multipath.conf.synthetic @@ -72,3 +72,6 @@ # path_grouping_policy multibus # } #} +#overrides { +# no_path_retry fail +#} diff --git a/multipath/11-dm-mpath.rules b/multipath/11-dm-mpath.rules new file mode 100644 index 0000000..2e7076d --- /dev/null +++ b/multipath/11-dm-mpath.rules @@ -0,0 +1,40 @@ +ACTION!="add|change", GOTO="mpath_end" +ENV{DM_UDEV_RULES_VSN}!="?*", GOTO="mpath_end" +ENV{DM_UUID}!="mpath-?*", GOTO="mpath_end" + +# Do not initiate scanning if no path is available, +# otherwise there would be a hang or IO error on access. +# We'd like to avoid this, especially within udev processing. +ENV{DM_NR_VALID_PATHS}!="?*", IMPORT{db}="DM_NR_VALID_PATHS" +ENV{DM_NR_VALID_PATHS}!="0", GOTO="mpath_blkid_end" +IMPORT{db}="ID_FS_TYPE" +IMPORT{db}="ID_FS_USAGE" +IMPORT{db}="ID_FS_UUID" +IMPORT{db}="ID_FS_UUID_ENC" +IMPORT{db}="ID_FS_VERSION" +LABEL="mpath_blkid_end" + +# Also skip all foreign rules if no path is available. +# Remember the original value of DM_DISABLE_OTHER_RULES_FLAG +# and restore it back once we have at least one path available. +IMPORT{db}="DM_DISABLE_OTHER_RULES_FLAG_OLD" +ENV{DM_ACTION}=="PATH_FAILED",\ + ENV{DM_NR_VALID_PATHS}=="0",\ + ENV{DM_DISABLE_OTHER_RULES_FLAG_OLD}=="",\ + ENV{DM_DISABLE_OTHER_RULES_FLAG_OLD}="$env{DM_UDEV_DISABLE_OTHER_RULES_FLAG}",\ + ENV{DM_UDEV_DISABLE_OTHER_RULES_FLAG}="1" +ENV{DM_ACTION}=="PATH_REINSTATED",\ + ENV{DM_NR_VALID_PATHS}=="1",\ + ENV{DM_UDEV_DISABLE_OTHER_RULES_FLAG}="$env{DM_DISABLE_OTHER_RULES_FLAG_OLD}",\ + ENV{DM_DISABLE_OTHER_RULES_FLAG_OLD}="",\ + ENV{DM_ACTIVATION}="1" + +# DM_SUBSYSTEM_UDEV_FLAG0 is the "RELOAD" flag for multipath subsystem. +# Drop the DM_ACTIVATION flag here as mpath reloads tables if any of its +# paths are lost/recovered. For any stack above the mpath device, this is not +# something that should be reacted upon since it would be useless extra work. +# It's exactly mpath's job to provide *seamless* device access to any of the +# paths that are available underneath. +ENV{DM_SUBSYSTEM_UDEV_FLAG0}=="1", ENV{DM_ACTIVATION}="0" + +LABEL="mpath_end" diff --git a/multipath/Makefile b/multipath/Makefile index 5e5958d..3707235 100644 --- a/multipath/Makefile +++ b/multipath/Makefile @@ -6,8 +6,9 @@ include ../Makefile.inc OBJS = main.o -CFLAGS += -I$(multipathdir) -LDFLAGS += -lpthread -ldevmapper -ldl -L$(multipathdir) -lmultipath -ludev +CFLAGS += -I$(multipathdir) -I$(mpathcmddir) +LDFLAGS += -lpthread -ldevmapper -ldl -L$(multipathdir) -lmultipath -ludev \ + -L$(mpathcmddir) -lmpathcmd EXEC = multipath @@ -21,6 +22,9 @@ $(EXEC): $(OBJS) install: $(INSTALL_PROGRAM) -d $(DESTDIR)$(bindir) $(INSTALL_PROGRAM) -m 755 $(EXEC) $(DESTDIR)$(bindir)/ + $(INSTALL_PROGRAM) -d $(DESTDIR)$(udevrulesdir) + $(INSTALL_PROGRAM) -m 644 11-dm-mpath.rules $(DESTDIR)$(udevrulesdir) + $(INSTALL_PROGRAM) -m 644 $(EXEC).rules $(DESTDIR)$(libudevdir)/rules.d/56-multipath.rules $(INSTALL_PROGRAM) -d $(DESTDIR)$(mandir) $(INSTALL_PROGRAM) -m 644 $(EXEC).8.gz $(DESTDIR)$(mandir) $(INSTALL_PROGRAM) -d $(DESTDIR)$(man5dir) @@ -28,6 +32,8 @@ install: uninstall: rm $(DESTDIR)$(bindir)/$(EXEC) + rm $(DESTDIR)$(udevrulesdir)/11-dm-mpath.rules + rm $(DESTDIR)$(libudevdir)/rules.d/56-multipath.rules rm $(DESTDIR)$(mandir)/$(EXEC).8.gz rm $(DESTDIR)$(man5dir)/$(EXEC).conf.5.gz diff --git a/multipath/dev_t.h b/multipath/dev_t.h deleted file mode 100644 index aa80d5e..0000000 --- a/multipath/dev_t.h +++ /dev/null @@ -1,3 +0,0 @@ -#define MAJOR(dev) ((dev & 0xfff00) >> 8) -#define MINOR(dev) ((dev & 0xff) | ((dev >> 12) & 0xfff00)) -#define MKDEV(ma,mi) ((mi & 0xff) | (ma << 8) | ((mi & ~0xff) << 12)) diff --git a/multipath/main.c b/multipath/main.c index 64c8fc5..d14a913 100644 --- a/multipath/main.c +++ b/multipath/main.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -55,7 +56,8 @@ #include #include #include -#include "dev_t.h" +#include +#include int logsink; @@ -84,7 +86,7 @@ usage (char * progname) { fprintf (stderr, VERSION_STRING); fprintf (stderr, "Usage:\n"); - fprintf (stderr, " %s [-c|-w|-W] [-d] [-r] [-v lvl] [-p pol] [-b fil] [-q] [dev]\n", progname); + fprintf (stderr, " %s [-a|-c|-w|-W] [-d] [-r] [-i] [-v lvl] [-p pol] [-b fil] [-q] [dev]\n", progname); fprintf (stderr, " %s -l|-ll|-f [-v lvl] [-b fil] [dev]\n", progname); fprintf (stderr, " %s -F [-v lvl]\n", progname); fprintf (stderr, " %s -t\n", progname); @@ -97,13 +99,14 @@ usage (char * progname) " -ll show multipath topology (maximum info)\n" \ " -f flush a multipath device map\n" \ " -F flush all multipath device maps\n" \ + " -a add a device wwid to the wwids file\n" \ " -c check if a device should be a path in a multipath device\n" \ " -q allow queue_if_no_path when multipathd is not running\n"\ " -d dry run, do not create or update devmaps\n" \ " -t dump internal hardware table\n" \ " -r force devmap reload\n" \ + " -i ignore wwids file\n" \ " -B treat the bindings file as read only\n" \ - " -p policy failover|multibus|group_by_serial|group_by_prio\n" \ " -b fil bindings file location\n" \ " -w remove a device from the wwids file\n" \ " -W reset the wwids file include only the current devices\n" \ @@ -195,6 +198,9 @@ get_dm_mpvec (vector curmp, vector pathvec, char * refwwid) continue; } + if (conf->cmd == CMD_VALID_PATH) + continue; + dm_get_map(mpp->alias, &mpp->size, params); condlog(3, "params = %s", params); dm_get_status(mpp->alias, status); @@ -207,18 +213,19 @@ get_dm_mpvec (vector curmp, vector pathvec, char * refwwid) * If not in "fast list mode", we need to fetch information * about them */ - if (conf->list != 1) + if (conf->cmd != CMD_LIST_SHORT) update_paths(mpp); - if (conf->list > 1) + if (conf->cmd == CMD_LIST_LONG) mpp->bestpg = select_path_group(mpp); disassemble_status(status, mpp); - if (conf->list) + if (conf->cmd == CMD_LIST_SHORT || + conf->cmd == CMD_LIST_LONG) print_multipath_topology(mpp, conf->verbosity); - if (!conf->dry_run) + if (conf->cmd == CMD_CREATE) reinstate_paths(mpp); } return 0; @@ -260,10 +267,11 @@ configure (void) /* * if we have a blacklisted device parameter, exit early */ - if (dev && conf->dev_type == DEV_DEVNODE && conf->dry_run != 3 && + if (dev && conf->dev_type == DEV_DEVNODE && + conf->cmd != CMD_REMOVE_WWID && (filter_devnode(conf->blist_devnode, conf->elist_devnode, dev) > 0)) { - if (conf->dry_run == 2) + if (conf->cmd == CMD_VALID_PATH) printf("%s is not a valid multipath device path\n", conf->dev); goto out; @@ -276,13 +284,14 @@ configure (void) int failed = get_refwwid(conf->dev, conf->dev_type, pathvec, &refwwid); if (!refwwid) { - if (failed == 2 && conf->dry_run == 2) + condlog(3, "%s: failed to get wwid", conf->dev); + if (failed == 2 && conf->cmd == CMD_VALID_PATH) printf("%s is not a valid multipath device path\n", conf->dev); else condlog(3, "scope is nul"); goto out; } - if (conf->dry_run == 3) { + if (conf->cmd == CMD_REMOVE_WWID) { r = remove_wwid(refwwid); if (r == 0) printf("wwid '%s' removed\n", refwwid); @@ -293,14 +302,29 @@ configure (void) } goto out; } + if (conf->cmd == CMD_ADD_WWID) { + r = remember_wwid(refwwid); + if (r == 0) + printf("wwid '%s' added\n", refwwid); + else + printf("failed adding '%s' to wwids file\n", + refwwid); + goto out; + } condlog(3, "scope limited to %s", refwwid); - if (conf->dry_run == 2) { - if (check_wwids_file(refwwid, 0) == 0){ - printf("%s is a valid multipath device path\n", conf->dev); + /* If you are ignoring the wwids file and find_multipaths is + * set, you need to actually check if there are two available + * paths to determine if this path should be multipathed. To + * do this, we put off the check until after discovering all + * the paths */ + if (conf->cmd == CMD_VALID_PATH && + (!conf->find_multipaths || !conf->ignore_wwids)) { + if (conf->ignore_wwids || + check_wwids_file(refwwid, 0) == 0) r = 0; - } - else - printf("%s is not a valid multipath device path\n", conf->dev); + + printf("%s %s a valid multipath device path\n", + conf->dev, r == 0 ? "is" : "is not"); goto out; } } @@ -311,17 +335,17 @@ configure (void) if (conf->dev) di_flag = DI_WWID; - if (conf->list > 1) + if (conf->cmd == CMD_LIST_LONG) /* extended path info '-ll' */ di_flag |= DI_SYSFS | DI_CHECKER; - else if (conf->list) + else if (conf->cmd == CMD_LIST_SHORT) /* minimum path info '-l' */ di_flag |= DI_SYSFS; else /* maximum info */ di_flag = DI_ALL; - if (path_discovery(pathvec, conf, di_flag)) + if (path_discovery(pathvec, conf, di_flag) < 0) goto out; if (conf->verbosity > 2) @@ -334,7 +358,21 @@ configure (void) filter_pathvec(pathvec, refwwid); - if (conf->list) { + + if (conf->cmd == CMD_VALID_PATH) { + /* This only happens if find_multipaths is and + * ignore_wwids is set. + * If there is currently a multipath device matching + * the refwwid, or there is more than one path matching + * the refwwid, then the path is valid */ + if (VECTOR_SIZE(curmp) != 0 || VECTOR_SIZE(pathvec) > 1) + r = 0; + printf("%s %s a valid multipath device path\n", + conf->dev, r == 0 ? "is" : "is not"); + goto out; + } + + if (conf->cmd != CMD_CREATE && conf->cmd != CMD_DRY_RUN) { r = 0; goto out; } @@ -342,7 +380,7 @@ configure (void) /* * core logic entry point */ - r = coalesce_paths(&vecs, NULL, NULL, conf->force_reload); + r = coalesce_paths(&vecs, NULL, refwwid, conf->force_reload); out: if (refwwid) @@ -357,7 +395,7 @@ out: static int dump_config (void) { - char * c; + char * c, * tmp = NULL; char * reply; unsigned int maxlen = 256; int again = 1; @@ -365,9 +403,12 @@ dump_config (void) reply = MALLOC(maxlen); while (again) { - if (!reply) + if (!reply) { + if (tmp) + free(tmp); return 1; - c = reply; + } + c = tmp = reply; c += snprint_defaults(c, reply + maxlen - c); again = ((c - reply) == maxlen); if (again) { @@ -392,10 +433,19 @@ dump_config (void) reply = REALLOC(reply, maxlen *= 2); continue; } - c += snprint_mptable(c, reply + maxlen - c, conf->mptable); + c += snprint_overrides(c, reply + maxlen - c, conf->overrides); again = ((c - reply) == maxlen); - if (again) + if (again) { reply = REALLOC(reply, maxlen *= 2); + continue; + } + if (VECTOR_SIZE(conf->mptable) > 0) { + c += snprint_mptable(c, reply + maxlen - c, + conf->mptable); + again = ((c - reply) == maxlen); + if (again) + reply = REALLOC(reply, maxlen *= 2); + } } printf("%s", reply); @@ -409,7 +459,7 @@ get_dev_type(char *dev) { int i; if (stat(dev, &buf) == 0 && S_ISBLK(buf.st_mode)) { - if (dm_is_dm_major(MAJOR(buf.st_rdev))) + if (dm_is_dm_major(major(buf.st_rdev))) return DEV_DEVMAP; return DEV_DEVNODE; } @@ -429,11 +479,11 @@ main (int argc, char *argv[]) int r = 1; udev = udev_new(); - + logsink = 0; if (load_config(DEFAULT_CONFIGFILE, udev)) exit(1); - while ((arg = getopt(argc, argv, ":dchl::FfM:v:p:b:BrtqwW")) != EOF ) { + while ((arg = getopt(argc, argv, ":adchl::FfM:v:p:b:BritquwW")) != EOF ) { switch(arg) { case 1: printf("optarg : %s\n",optarg); break; @@ -456,11 +506,11 @@ main (int argc, char *argv[]) conf->allow_queueing = 1; break; case 'c': - conf->dry_run = 2; + conf->cmd = CMD_VALID_PATH; break; case 'd': - if (!conf->dry_run) - conf->dry_run = 1; + if (conf->cmd == CMD_CREATE) + conf->cmd = CMD_DRY_RUN; break; case 'f': conf->remove = FLUSH_ONE; @@ -469,11 +519,10 @@ main (int argc, char *argv[]) conf->remove = FLUSH_ALL; break; case 'l': - conf->list = 1; - conf->dry_run = 1; - if (optarg && !strncmp(optarg, "l", 1)) - conf->list++; + conf->cmd = CMD_LIST_LONG; + else + conf->cmd = CMD_LIST_SHORT; break; case 'M': @@ -492,17 +541,27 @@ main (int argc, char *argv[]) case 'r': conf->force_reload = 1; break; + case 'i': + conf->ignore_wwids = 1; + break; case 't': r = dump_config(); goto out_free_config; case 'h': usage(argv[0]); exit(0); + case 'u': + conf->cmd = CMD_VALID_PATH; + conf->dev_type = DEV_UEVENT; + break; case 'w': - conf->dry_run = 3; + conf->cmd = CMD_REMOVE_WWID; break; case 'W': - conf->dry_run = 4; + conf->cmd = CMD_RESET_WWIDS; + break; + case 'a': + conf->cmd = CMD_ADD_WWID; break; case ':': fprintf(stderr, "Missing option argument\n"); @@ -526,6 +585,7 @@ main (int argc, char *argv[]) if (dm_prereq()) exit(1); dm_drv_version(conf->version, TGT_MPATH); + dm_udev_set_sync_support(1); if (optind < argc) { conf->dev = MALLOC(FILE_NAME_SIZE); @@ -534,9 +594,15 @@ main (int argc, char *argv[]) goto out; strncpy(conf->dev, argv[optind], FILE_NAME_SIZE); - conf->dev_type = get_dev_type(conf->dev); + if (conf->dev_type != DEV_UEVENT) + conf->dev_type = get_dev_type(conf->dev); } conf->daemon = 0; + if (conf->dev_type == DEV_UEVENT) { + openlog("multipath", 0, LOG_DAEMON); + setlogmask(LOG_UPTO(conf->verbosity + 3)); + logsink = 1; + } if (conf->max_fds) { struct rlimit fd_limit; @@ -558,16 +624,28 @@ main (int argc, char *argv[]) } dm_init(); - if (conf->dry_run == 2 && + if (conf->cmd == CMD_VALID_PATH && (!conf->dev || conf->dev_type == DEV_DEVMAP)) { condlog(0, "the -c option requires a path to check"); goto out; } - if (conf->dry_run == 3 && !conf->dev) { + if (conf->cmd == CMD_VALID_PATH && + conf->dev_type == DEV_UEVENT) { + int fd; + + fd = mpath_connect(); + if (fd == -1) { + printf("%s is not a valid multipath device path\n", + conf->dev); + goto out; + } + mpath_disconnect(fd); + } + if (conf->cmd == CMD_REMOVE_WWID && !conf->dev) { condlog(0, "the -w option requires a device"); goto out; } - if (conf->dry_run == 4) { + if (conf->cmd == CMD_RESET_WWIDS) { struct multipath * mpp; int i; vector curmp; @@ -604,14 +682,15 @@ main (int argc, char *argv[]) condlog(3, "restart multipath configuration process"); out: - udev_wait(conf->cookie); - dm_lib_release(); dm_lib_exit(); cleanup_prio(); cleanup_checkers(); + if (conf->dev_type == DEV_UEVENT) + closelog(); + out_free_config: /* * Freeing config must be done after dm_lib_exit(), because diff --git a/multipath/multipath.8 b/multipath/multipath.8 index a2262ac..966139e 100644 --- a/multipath/multipath.8 +++ b/multipath/multipath.8 @@ -8,7 +8,7 @@ multipath \- Device mapper target autoconfig .RB [\| \-b\ \c .IR bindings_file \|] .RB [\| \-d \|] -.RB [\| \-h | \-l | \-ll | \-f | \-t | \-F | \-B | \-c | \-q | \|-r | \-w | \-W \|] +.RB [\| \-h | \-l | \-ll | \-f | \-t | \-F | \-B | \-c | \-q | \|-r | \|-i | \-a | \|-u | \-w | \-W \|] .RB [\| \-p\ \c .BR failover | multibus | group_by_serial | group_by_prio | group_by_node_name \|] .RB [\| device \|] @@ -55,6 +55,9 @@ print internal hardware table to stdout .B \-r force devmap reload .TP +.B \-i +ignore wwids file when processing devices +.TP .B \-B treat the bindings file as read only .TP @@ -68,6 +71,13 @@ check if a block device should be a path in a multipath device .B \-q allow device tables with queue_if_no_path when multipathd is not running .TP +.B \-a +add the wwid for the specified device to the wwids file +.TP +.B \-u +check if the device specified in the program environment should be +a path in a multipath device. +.TP .B \-w remove the wwid for the specified device from the wwids file .TP diff --git a/multipath/multipath.conf.5 b/multipath/multipath.conf.5 index cf5bec0..0d4df0f 100644 --- a/multipath/multipath.conf.5 +++ b/multipath/multipath.conf.5 @@ -55,12 +55,15 @@ section. .TP .B multipaths This section defines the multipath topologies. They are indexed by a -\fIWorld Wide Identifier\fR(wwid), which is taken to be the value of -the udev attribute given by the -\fIuid_attribute\fR keyword. +\fIWorld Wide Identifier\fR(wwid). For details on the wwid generation +see section \fBWWID generation\fR below. .TP .B devices This section defines the device-specific settings. +.TP +.B overrides +This section defines values for attributes that should override the +device-specific settings for all devices. .RE .LP .SH "defaults section" @@ -87,6 +90,28 @@ directory where the dynamic shared objects are stored; default is system dependent, commonly .I /lib/multipath .TP +.B find_multipaths +If set to +.I yes +, instead of trying to create a multipath device for every non-blacklisted +path, multipath will only create a device if one of three condidions are +met. +.I 1 +There are at least two non-blacklisted paths with the same wwid, +.I 2 +the user manually forces the creation, by specifying a device with the multipath +command, or +.I 3 +a path has the same WWID as a multipath device that was previously created +while find_multipaths was set (even if that multipath device doesn't currently +exist). +Whenever a multipath device is created with find_multipaths set, multipath will +remeber the WWID of the device, so that it will automatically create the +device again, as soon as it sees a path with that WWID. This should allow most +users to have multipath automatically choose the correct paths to make into +multipath devices, without having to edit the blacklist; Default is +.I no +.TP .B verbosity default verbosity. Higher values increase the verbosity level. Valid levels are between 0 and 6; default is @@ -150,7 +175,7 @@ identifier. Default value is .B getuid_callout The default program and args to callout to obtain a unique path identifier. Should be specified with an absolute path. -This parameter is deprecated; \fIuid_attribute\fR should be used instead. +This parameter is deprecated. .TP .B prio The name of the path priority routine. The specified routine @@ -168,7 +193,9 @@ Return a constant priority of \fI1\fR. Generate the path priority for EMC arrays. .TP .B alua -Generate the path priority based on the SCSI-3 ALUA settings. +Generate the path priority based on the SCSI-3 ALUA settings. This prioritizer +accepts the optional prio_arg +.I exclusive_pref_bit .TP .B ontap Generate the path priority for NetApp arrays. @@ -194,13 +221,29 @@ Default value is \fBnone\fR. .RE .TP .B prio_args -Arguments to pass to to the prio function. Currently only used with -.I weighted, which needs a value of the form -.I " ..." +Arguments to pass to to the prio function. This only applies to certain +prioritizers +.RS +.TP 12 +.B weighted +Needs a value of the form +.I " ..." .I hbtl regex can be of SCSI H:B:T:L format Ex: 1:0:.:. , *:0:0:. .I devname regex can be of device name format Ex: sda , sd.e +.I wwn +regex can be of the form +.I "host_wwnn:host_wwpn:target_wwnn:target_wwpn" +these values can be looked up through sysfs or by running +.I mulitpathd show paths format "%N:%R:%n:%r" Ex: 0x200100e08ba0aea0:0x210100e08ba0aea0:.*:.* , .*:.*:iqn.2009-10.com.redhat.msp.lab.ask-06:.* +.TP +.B alua +If +.I exclusive_pref_bit +is set, paths with the TPGS pref bit set will always be in their own path +group. +.RE .TP .B features Specify any device-mapper features to be used. Syntax is @@ -330,8 +373,8 @@ maximum number of open fds is taken from the calling process. It is usually if that number is greated than 1024. .TP .B checker_timeout -Specify the timeout to user for path checkers that issue scsi commands with an -explicit timeout, in seconds; default taken from +Specify the timeout to use for path checkers and prioritizers that issue scsi +commands with an explicit timeout, in seconds; default taken from .I /sys/block/sd/device/timeout .TP .B fast_io_fail_tmo @@ -364,7 +407,7 @@ multipathd running, access to the paths cannot be restored, and the kernel cannot be told to stop queueing IO. Setting queue_without_daemon to .I no , avoids this problem. Default is -.I yes +.I no .TP .B bindings_file The full pathname of the binding file to be used when the user_friendly_names option is set. Defaults to @@ -409,6 +452,68 @@ will automatically use the .I alua prioritizer. If not, the prioritizer will be selected as usual. Default is .I no +.TP +.B force_sync +If set to +.I yes +, multipathd will call the path checkers in sync mode only. This means that +only one checker will run at a time. This is useful in the case where many +multipathd checkers running in parallel causes significant CPU pressure. The +Default is +.I no +.TP +.B deferred_remove +If set to +.I yes +, multipathd will do a deferred remove instead of a regular remove when the +last path device has been deleted. This means that if the multipath device is +still in use, it will be freed when the last user closes it. If path is added +to the multipath device before the last user closes it, the deferred remove +will be canceled. Default is +.I no +.TP +.B config_dir +If set to anything other than "", multipath will search this directory +alphabetically for file ending in ".conf" and it will read configuration +information from them, just as if it was in /etc/multipath.conf. config_dir +must either be "" or a fully qualified directory name. Default is +.I "/etc/multipath/conf.d" +.TP +.B delay_watch_checks +If set to a value greater than 0, multipathd will watch paths that have +recently become valid for this many checks. If they fail again while they are +being watched, when they next become valid, they will not be used until they +have stayed up for +.I delay_wait_checks +checks. Default is +.I no +.TP +.B delay_wait_checks +If set to a value greater than 0, when a device that has recently come back +online fails again within +.I delay_watch_checks +checks, the next time it comes back online, it will marked and delayed, and not +used until it has passed +.I delay_wait_checks +checks. Default is +.I no +.TP +.B uxsock_timeout +CLI receive timeout in milliseconds. For larger systems CLI commands +might timeout before the multipathd lock is released and the CLI command +can be processed. This will result in errors like +'timeout receiving packet' to be returned from CLI commands. +In these cases it is recommended to increase the CLI timeout to avoid +those issues. The default is +.I 1000 +.TP +.B missing_uev_wait_timeout +Controls how many seconds multipathd will wait, after a new multipath device +is created, to receive a change event from udev for the device, before +automatically enabling device reloads. Usually multipathd will delay reloads +on a device until it receives a change uevent from the initial table load. The +default is +.I 30 . .SH "blacklist section" The @@ -426,7 +531,7 @@ The \fIWorld Wide Identification\fR of a device. Regular expression of the device nodes to be excluded. .TP .B property -Regular expresion of the udev property to be excluded. +Regular expression of the udev property to be excluded. .TP .B device Subsection for the device description. This subsection recognizes the @@ -453,8 +558,8 @@ The following keywords are recognized: The \fIWorld Wide Identification\fR of a device. .TP .B property -Regular expresion of the udev property to be whitelisted. Defaults to -.I (ID_WWN|ID_SCSI_VPD) +Regular expression of the udev property to be whitelisted. Defaults to +.I (ID_WWN|SCSI_IDENT_.*) .TP .B devnode Regular expression of the device nodes to be whitelisted. @@ -527,6 +632,12 @@ section: .B features .TP .B reservation_key +.TP +.B deferred_remove +.TP +.B delay_watch_checks +.TP +.B delay_wait_checks .RE .PD .LP @@ -617,9 +728,98 @@ section: .B retain_attached_hw_handler .TP .B detect_prio +.TP +.B deferred_remove +.TP +.B delay_watch_checks +.TP +.B delay_wait_checks .RE .PD .LP +.SH "overrides section" +The overrides section recognizes the following optional attributes; if not set +the values are taken from the +.I devices +or +.I defaults +sections: +.sp 1 +.PD .1v +.RS +.TP 18 +.B path_grouping_policy +.TP +.B uid_attribute +.TP +.B getuid_callout +.TP +.B path_selector +.TP +.B path_checker +.TP +.B alias_prefix +.TP +.B features +.TP +.B prio +.TP +.B prio_args +.TP +.B failback +.TP +.B rr_weight +.TP +.B no_path_retry +.TP +.B rr_min_io +.TP +.B rr_min_io_rq +.TP +.B flush_on_last_del +.TP +.B fast_io_fail_tmo +.TP +.B dev_loss_tmo +.TP +.B user_friendly_names +.TP +.B retain_attached_hw_handler +.TP +.B detect_prio +.TP +.B deferred_remove +.TP +.B delay_watch_checks +.TP +.B delay_wait_checks +.RE +.PD +.LP +.SH "WWID generation" +Multipath uses a \fIWorld Wide Identification\fR (wwid) to determine +which paths belong to the same device. Each path presenting the same +wwid is assumed to point to the same device. +.LP +The wwid is generated by three methods (in the order of preference): +.TP 17 +.B getuid_callout +Use the specified external program; cf \fIgetuid_callout\fR above. +Care should be taken when using this method; the external program +needs to be loaded from disk for execution, which might lead to +deadlock situations in an all-paths-down scenario. +.TP +.B uid_attribute +Use the value of the specified udev attribute; cf \fIuid_attribute\fR +above. This method is preferred to \fIgetuid_callout\fR as multipath +does not need to call any external programs here. However, under +certain circumstances udev might not be able to generate the requested +variable. +.TP +.B vpd_pg83 +If none of the \fIgetuid_callout\fR or \fIuid_attribute\fR parameters +are present multipath will try to use the sysfs attribute +\fIvpd_pg83\fR to generate the wwid. .SH "KNOWN ISSUES" The usage of .B queue_if_no_path diff --git a/multipath/multipath.rules b/multipath/multipath.rules new file mode 100644 index 0000000..c8fb7e6 --- /dev/null +++ b/multipath/multipath.rules @@ -0,0 +1,22 @@ +# Set DM_MULTIPATH_DEVICE_PATH if the device should be handled by multipath +SUBSYSTEM!="block", GOTO="end_mpath" +ACTION!="add|change", GOTO="end_mpath" +KERNEL!="sd*|dasd*", GOTO="end_mpath" + +ENV{DEVTYPE}!="partition", GOTO="test_dev" +IMPORT{parent}="DM_MULTIPATH_DEVICE_PATH" +ENV{DM_MULTIPATH_DEVICE_PATH}=="1", ENV{ID_FS_TYPE}="none", \ + ENV{SYSTEMD_READY}="0" +GOTO="end_mpath" + +LABEL="test_dev" + +ENV{MPATH_SBIN_PATH}="/sbin" +TEST!="$env{MPATH_SBIN_PATH}/multipath", ENV{MPATH_SBIN_PATH}="/usr/sbin" + +ENV{DM_MULTIPATH_DEVICE_PATH}!="1", \ + PROGRAM=="$env{MPATH_SBIN_PATH}/multipath -u %k", \ + ENV{DM_MULTIPATH_DEVICE_PATH}="1", ENV{ID_FS_TYPE}="none", \ + ENV{SYSTEMD_READY}="0" + +LABEL="end_mpath" diff --git a/multipathd/Makefile b/multipathd/Makefile index 781122a..9b0210f 100644 --- a/multipathd/Makefile +++ b/multipathd/Makefile @@ -5,16 +5,21 @@ include ../Makefile.inc # # basic flags setting # -CFLAGS += -I$(multipathdir) -I$(mpathpersistdir) +CFLAGS += -I$(multipathdir) -I$(mpathpersistdir) -I$(mpathcmddir) ifdef SYSTEMD CFLAGS += -DUSE_SYSTEMD=$(SYSTEMD) endif LDFLAGS += -lpthread -ldevmapper -lreadline ifdef SYSTEMD - LDFLAGS += -lsystemd-daemon + ifeq ($(shell test $(SYSTEMD) -gt 209 && echo 1), 1) + LDFLAGS += -lsystemd + else + LDFLAGS += -lsystemd-daemon + endif endif LDFLAGS += -ludev -ldl \ - -L$(multipathdir) -lmultipath -L$(mpathpersistdir) -lmpathpersist + -L$(multipathdir) -lmultipath -L$(mpathpersistdir) -lmpathpersist \ + -L$(mpathcmddir) -lmpathcmd # # debuging stuff diff --git a/multipathd/cli.c b/multipathd/cli.c index 2a5edfa..6a5c6db 100644 --- a/multipathd/cli.c +++ b/multipathd/cli.c @@ -26,7 +26,7 @@ alloc_handler (void) } static int -add_key (vector vec, char * str, unsigned long code, int has_param) +add_key (vector vec, char * str, uint64_t code, int has_param) { struct key * kw; @@ -57,7 +57,7 @@ out: } int -add_handler (unsigned long fp, int (*fn)(void *, char **, int *, void *)) +add_handler (uint64_t fp, int (*fn)(void *, char **, int *, void *)) { struct handler * h; @@ -79,7 +79,7 @@ add_handler (unsigned long fp, int (*fn)(void *, char **, int *, void *)) } static struct handler * -find_handler (unsigned long fp) +find_handler (uint64_t fp) { int i; struct handler *h; @@ -92,7 +92,7 @@ find_handler (unsigned long fp) } int -set_handler_callback (unsigned long fp, int (*fn)(void *, char **, int *, void *)) +set_handler_callback (uint64_t fp, int (*fn)(void *, char **, int *, void *)) { struct handler * h = find_handler(fp); @@ -180,7 +180,7 @@ load_keys (void) r += add_key(keys, "config", CONFIG, 0); r += add_key(keys, "blacklist", BLACKLIST, 0); r += add_key(keys, "devices", DEVICES, 0); - r += add_key(keys, "format", FMT, 1); + r += add_key(keys, "raw", RAW, 0); r += add_key(keys, "wildcards", WILDCARDS, 0); r += add_key(keys, "quit", QUIT, 0); r += add_key(keys, "exit", QUIT, 0); @@ -188,6 +188,7 @@ load_keys (void) r += add_key(keys, "getprstatus", GETPRSTATUS, 0); r += add_key(keys, "setprstatus", SETPRSTATUS, 0); r += add_key(keys, "unsetprstatus", UNSETPRSTATUS, 0); + r += add_key(keys, "format", FMT, 1); if (r) { free_keys(keys); @@ -292,11 +293,11 @@ out: return r; } -static unsigned long +static uint64_t fingerprint(vector vec) { int i; - unsigned long fp = 0; + uint64_t fp = 0; struct key * kw; if (!vec) @@ -320,52 +321,90 @@ alloc_handlers (void) } static int -genhelp_sprint_aliases (char * reply, vector keys, struct key * refkw) +genhelp_sprint_aliases (char * reply, int maxlen, vector keys, + struct key * refkw) { - int i, fwd = 0; + int i, len = 0; struct key * kw; - vector_foreach_slot (keys, kw, i) - if (kw->code == refkw->code && kw != refkw) - fwd += sprintf(reply, "|%s", kw->str); + vector_foreach_slot (keys, kw, i) { + if (kw->code == refkw->code && kw != refkw) { + len += snprintf(reply + len, maxlen - len, + "|%s", kw->str); + if (len >= maxlen) + return len; + } + } - return fwd; + return len; } -static char * -genhelp_handler (void) -{ +static int +do_genhelp(char *reply, int maxlen) { + int len = 0; int i, j; - unsigned long fp; + uint64_t fp; struct handler * h; struct key * kw; - char * reply; - char * p; - reply = MALLOC(INITIAL_REPLY_LEN); - - if (!reply) - return NULL; - - p = reply; - p += sprintf(p, VERSION_STRING); - p += sprintf(p, "CLI commands reference:\n"); + len += snprintf(reply + len, maxlen - len, VERSION_STRING); + if (len >= maxlen) + goto out; + len += snprintf(reply + len, maxlen - len, "CLI commands reference:\n"); + if (len >= maxlen) + goto out; vector_foreach_slot (handlers, h, i) { fp = h->fingerprint; vector_foreach_slot (keys, kw, j) { if ((kw->code & fp)) { fp -= kw->code; - p += sprintf(p, " %s", kw->str); - p += genhelp_sprint_aliases(p, keys, kw); - - if (kw->has_param) - p += sprintf(p, " $%s", kw->str); + len += snprintf(reply + len , maxlen - len, + " %s", kw->str); + if (len >= maxlen) + goto out; + len += genhelp_sprint_aliases(reply + len, + maxlen - len, + keys, kw); + if (len >= maxlen) + goto out; + + if (kw->has_param) { + len += snprintf(reply + len, + maxlen - len, + " $%s", kw->str); + if (len >= maxlen) + goto out; + } } } - p += sprintf(p, "\n"); + len += snprintf(reply + len, maxlen - len, "\n"); + if (len >= maxlen) + goto out; } +out: + return len; +} + +static char * +genhelp_handler (void) +{ + char * reply; + char * p = NULL; + int maxlen = INITIAL_REPLY_LEN; + int again = 1; + + reply = MALLOC(maxlen); + + while (again) { + if (!reply) + return NULL; + p = reply; + p += do_genhelp(reply, maxlen); + again = ((p - reply) >= maxlen); + REALLOC_REPLY(reply, again, maxlen); + } return reply; } @@ -403,7 +442,7 @@ parse_cmd (char * cmd, char ** reply, int * len, void * data) } char * -get_keyparam (vector v, unsigned long code) +get_keyparam (vector v, uint64_t code) { struct key * kw; int i; @@ -425,12 +464,15 @@ cli_init (void) { add_handler(LIST+PATHS, NULL); add_handler(LIST+PATHS+FMT, NULL); + add_handler(LIST+PATHS+RAW+FMT, NULL); + add_handler(LIST+PATH, NULL); add_handler(LIST+STATUS, NULL); add_handler(LIST+DAEMON, NULL); add_handler(LIST+MAPS, NULL); add_handler(LIST+MAPS+STATUS, NULL); add_handler(LIST+MAPS+STATS, NULL); add_handler(LIST+MAPS+FMT, NULL); + add_handler(LIST+MAPS+RAW+FMT, NULL); add_handler(LIST+MAPS+TOPOLOGY, NULL); add_handler(LIST+TOPOLOGY, NULL); add_handler(LIST+MAP+TOPOLOGY, NULL); @@ -474,7 +516,7 @@ void cli_exit(void) } static int -key_match_fingerprint (struct key * kw, unsigned long fp) +key_match_fingerprint (struct key * kw, uint64_t fp) { if (!fp) return 0; @@ -489,7 +531,7 @@ char * key_generator (const char * str, int state) { static int index, len, has_param; - static unsigned long rlfp; + static uint64_t rlfp; struct key * kw; int i; struct handler *h; @@ -559,7 +601,7 @@ key_generator (const char * str, int state) * nfp is the candidate fingerprint we try to * validate against all known command fingerprints. */ - unsigned long nfp = rlfp | kw->code; + uint64_t nfp = rlfp | kw->code; vector_foreach_slot(handlers, h, i) { if (!rlfp || ((h->fingerprint & nfp) == nfp)) { /* diff --git a/multipathd/cli.h b/multipathd/cli.h index 09fdc68..2aa19d5 100644 --- a/multipathd/cli.h +++ b/multipathd/cli.h @@ -1,3 +1,5 @@ +#include + enum { __LIST, __ADD, @@ -26,13 +28,14 @@ enum { __CONFIG, __BLACKLIST, __DEVICES, - __FMT, + __RAW, __WILDCARDS, __QUIT, __SHUTDOWN, __GETPRSTATUS, __SETPRSTATUS, __UNSETPRSTATUS, + __FMT, }; #define LIST (1 << __LIST) @@ -62,35 +65,50 @@ enum { #define CONFIG (1 << __CONFIG) #define BLACKLIST (1 << __BLACKLIST) #define DEVICES (1 << __DEVICES) -#define FMT (1 << __FMT) +#define RAW (1 << __RAW) #define COUNT (1 << __COUNT) #define WILDCARDS (1 << __WILDCARDS) #define QUIT (1 << __QUIT) #define SHUTDOWN (1 << __SHUTDOWN) -#define GETPRSTATUS (1UL << __GETPRSTATUS) -#define SETPRSTATUS (1UL << __SETPRSTATUS) -#define UNSETPRSTATUS (1UL << __UNSETPRSTATUS) +#define GETPRSTATUS (1ULL << __GETPRSTATUS) +#define SETPRSTATUS (1ULL << __SETPRSTATUS) +#define UNSETPRSTATUS (1ULL << __UNSETPRSTATUS) +#define FMT (1ULL << __FMT) + +#define INITIAL_REPLY_LEN 1200 -#define INITIAL_REPLY_LEN 1100 +#define REALLOC_REPLY(r, a, m) \ + do { \ + if ((a)) { \ + char *tmp = (r); \ + (r) = REALLOC((r), (m) * 2); \ + if ((r)) { \ + memset((r) + (m), 0, (m)); \ + (m) *= 2; \ + } \ + else \ + free(tmp); \ + } \ + } while (0) struct key { char * str; char * param; - unsigned long code; + uint64_t code; int has_param; }; struct handler { - unsigned long fingerprint; + uint64_t fingerprint; int (*fn)(void *, char **, int *, void *); }; int alloc_handlers (void); -int add_handler (unsigned long fp, int (*fn)(void *, char **, int *, void *)); -int set_handler_callback (unsigned long fp, int (*fn)(void *, char **, int *, void *)); +int add_handler (uint64_t fp, int (*fn)(void *, char **, int *, void *)); +int set_handler_callback (uint64_t fp, int (*fn)(void *, char **, int *, void *)); int parse_cmd (char * cmd, char ** reply, int * len, void *); int load_keys (void); -char * get_keyparam (vector v, unsigned long code); +char * get_keyparam (vector v, uint64_t code); void free_keys (vector vec); void free_handlers (void); int cli_init (void); diff --git a/multipathd/cli_handlers.c b/multipathd/cli_handlers.c index f7fc522..168b872 100644 --- a/multipathd/cli_handlers.c +++ b/multipathd/cli_handlers.c @@ -23,19 +23,9 @@ #include "cli.h" #include "uevent.h" -#define REALLOC_REPLY(r, a, m) \ - do { \ - if ((a)) { \ - (r) = REALLOC((r), (m) * 2); \ - if ((r)) { \ - memset((r) + (m), 0, (m)); \ - (m) *= 2; \ - } \ - } \ - } while (0) - int -show_paths (char ** r, int * len, struct vectors * vecs, char * style) +show_paths (char ** r, int * len, struct vectors * vecs, char * style, + int pretty) { int i; struct path * pp; @@ -53,13 +43,42 @@ show_paths (char ** r, int * len, struct vectors * vecs, char * style) c = reply; - if (VECTOR_SIZE(vecs->pathvec) > 0) + if (pretty && VECTOR_SIZE(vecs->pathvec) > 0) c += snprint_path_header(c, reply + maxlen - c, style); vector_foreach_slot(vecs->pathvec, pp, i) c += snprint_path(c, reply + maxlen - c, - style, pp); + style, pp, pretty); + + again = ((c - reply) == (maxlen - 1)); + + REALLOC_REPLY(reply, again, maxlen); + } + *r = reply; + *len = (int)(c - reply + 1); + return 0; +} + +int +show_path (char ** r, int * len, struct vectors * vecs, struct path *pp, + char * style) +{ + char * c; + char * reply; + unsigned int maxlen = INITIAL_REPLY_LEN; + int again = 1; + + get_path_layout(vecs->pathvec, 1); + reply = MALLOC(maxlen); + + while (again) { + if (!reply) + return 1; + + c = reply; + + c += snprint_path(c, reply + maxlen - c, style, pp, 0); again = ((c - reply) == (maxlen - 1)); @@ -144,7 +163,7 @@ show_config (char ** r, int * len) unsigned int maxlen = INITIAL_REPLY_LEN; int again = 1; - reply = MALLOC(maxlen); + c = reply = MALLOC(maxlen); while (again) { if (!reply) @@ -152,47 +171,35 @@ show_config (char ** r, int * len) c = reply; c += snprint_defaults(c, reply + maxlen - c); again = ((c - reply) == maxlen); - if (again) { - reply = REALLOC(reply, maxlen * 2); - if (!reply) - return 1; - memset(reply + maxlen, 0, maxlen); - maxlen *= 2; + REALLOC_REPLY(reply, again, maxlen); + if (again) continue; - } c += snprint_blacklist(c, reply + maxlen - c); again = ((c - reply) == maxlen); - if (again) { - reply = REALLOC(reply, maxlen * 2); - if (!reply) - return 1; - memset(reply + maxlen, 0, maxlen); - maxlen *= 2; + REALLOC_REPLY(reply, again, maxlen); + if (again) continue; - } c += snprint_blacklist_except(c, reply + maxlen - c); again = ((c - reply) == maxlen); - if (again) { - reply = REALLOC(reply, maxlen * 2); - if (!reply) - return 1; - memset(reply + maxlen, 0, maxlen); - maxlen *= 2; + REALLOC_REPLY(reply, again, maxlen); + if (again) continue; - } c += snprint_hwtable(c, reply + maxlen - c, conf->hwtable); again = ((c - reply) == maxlen); - if (again) { - reply = REALLOC(reply, maxlen * 2); - if (!reply) - return 1; - memset(reply + maxlen, 0, maxlen); - maxlen *= 2; + REALLOC_REPLY(reply, again, maxlen); + if (again) continue; - } - c += snprint_mptable(c, reply + maxlen - c, conf->mptable); + c += snprint_overrides(c, reply + maxlen - c, conf->overrides); again = ((c - reply) == maxlen); REALLOC_REPLY(reply, again, maxlen); + if (again) + continue; + if (VECTOR_SIZE(conf->mptable) > 0) { + c += snprint_mptable(c, reply + maxlen - c, + conf->mptable); + again = ((c - reply) == maxlen); + REALLOC_REPLY(reply, again, maxlen); + } } *r = reply; *len = (int)(c - reply + 1); @@ -214,7 +221,7 @@ cli_list_paths (void * v, char ** reply, int * len, void * data) condlog(3, "list paths (operator)"); - return show_paths(reply, len, vecs, PRINT_PATH_CHECKER); + return show_paths(reply, len, vecs, PRINT_PATH_CHECKER, 1); } int @@ -225,7 +232,33 @@ cli_list_paths_fmt (void * v, char ** reply, int * len, void * data) condlog(3, "list paths (operator)"); - return show_paths(reply, len, vecs, fmt); + return show_paths(reply, len, vecs, fmt, 1); +} + +int +cli_list_paths_raw (void * v, char ** reply, int * len, void * data) +{ + struct vectors * vecs = (struct vectors *)data; + char * fmt = get_keyparam(v, FMT); + + condlog(3, "list paths (operator)"); + + return show_paths(reply, len, vecs, fmt, 0); +} + +int +cli_list_path (void * v, char ** reply, int * len, void * data) +{ + struct vectors * vecs = (struct vectors *)data; + char * param = get_keyparam(v, PATH); + struct path *pp; + + param = convert_dev(param, 1); + condlog(3, "%s: list path (operator)", param); + + pp = find_path_by_dev(vecs->pathvec, param); + + return show_path(reply, len, vecs, pp, "%o"); } int @@ -316,7 +349,8 @@ show_daemon (char ** r, int *len) } int -show_maps (char ** r, int *len, struct vectors * vecs, char * style) +show_maps (char ** r, int *len, struct vectors * vecs, char * style, + int pretty) { int i; struct multipath * mpp; @@ -333,13 +367,13 @@ show_maps (char ** r, int *len, struct vectors * vecs, char * style) return 1; c = reply; - if (VECTOR_SIZE(vecs->mpvec) > 0) + if (pretty && VECTOR_SIZE(vecs->mpvec) > 0) c += snprint_multipath_header(c, reply + maxlen - c, style); vector_foreach_slot(vecs->mpvec, mpp, i) c += snprint_multipath(c, reply + maxlen - c, - style, mpp); + style, mpp, pretty); again = ((c - reply) == (maxlen - 1)); @@ -358,7 +392,18 @@ cli_list_maps_fmt (void * v, char ** reply, int * len, void * data) condlog(3, "list maps (operator)"); - return show_maps(reply, len, vecs, fmt); + return show_maps(reply, len, vecs, fmt, 1); +} + +int +cli_list_maps_raw (void * v, char ** reply, int * len, void * data) +{ + struct vectors * vecs = (struct vectors *)data; + char * fmt = get_keyparam(v, FMT); + + condlog(3, "list maps (operator)"); + + return show_maps(reply, len, vecs, fmt, 0); } int @@ -368,7 +413,7 @@ cli_list_maps (void * v, char ** reply, int * len, void * data) condlog(3, "list maps (operator)"); - return show_maps(reply, len, vecs, PRINT_MAP_NAMES); + return show_maps(reply, len, vecs, PRINT_MAP_NAMES, 1); } int @@ -388,7 +433,7 @@ cli_list_maps_status (void * v, char ** reply, int * len, void * data) condlog(3, "list maps status (operator)"); - return show_maps(reply, len, vecs, PRINT_MAP_STATUS); + return show_maps(reply, len, vecs, PRINT_MAP_STATUS, 1); } int @@ -398,7 +443,7 @@ cli_list_maps_stats (void * v, char ** reply, int * len, void * data) condlog(3, "list maps stats (operator)"); - return show_maps(reply, len, vecs, PRINT_MAP_STATS); + return show_maps(reply, len, vecs, PRINT_MAP_STATS, 1); } int @@ -466,7 +511,7 @@ cli_del_path (void * v, char ** reply, int * len, void * data) pp = find_path_by_dev(vecs->pathvec, param); if (!pp) { condlog(0, "%s: path already removed", param); - return 0; + return 1; } return ev_remove_path(pp, vecs); } @@ -478,37 +523,50 @@ cli_add_map (void * v, char ** reply, int * len, void * data) char * param = get_keyparam(v, MAP); int major, minor; char dev_path[PATH_SIZE]; - char *alias; - int rc; + char *alias, *refwwid; + int rc, count = 0; param = convert_dev(param, 0); condlog(2, "%s: add map (operator)", param); - if (filter_wwid(conf->blist_wwid, conf->elist_wwid, param) > 0) { + if (filter_wwid(conf->blist_wwid, conf->elist_wwid, param, NULL) > 0) { *reply = strdup("blacklisted\n"); *len = strlen(*reply) + 1; condlog(2, "%s: map blacklisted", param); - return 0; - } - minor = dm_get_minor(param); - if (minor < 0) { - condlog(2, "%s: not a device mapper table", param); - return 0; - } - major = dm_get_major(param); - if (major < 0) { - condlog(2, "%s: not a device mapper table", param); - return 0; + return 1; } - sprintf(dev_path,"dm-%d", minor); - alias = dm_mapname(major, minor); + do { + minor = dm_get_minor(param); + if (minor < 0) + condlog(2, "%s: not a device mapper table", param); + major = dm_get_major(param); + if (major < 0) + condlog(2, "%s: not a device mapper table", param); + sprintf(dev_path, "dm-%d", minor); + alias = dm_mapname(major, minor); + /*if there is no mapname found, we first create the device*/ + if (!alias && !count) { + condlog(2, "%s: mapname not found for %d:%d", + param, major, minor); + rc = get_refwwid(param, DEV_DEVMAP, vecs->pathvec, + &refwwid); + if (refwwid) { + if (coalesce_paths(vecs, NULL, refwwid, 0)) + condlog(2, "%s: coalesce_paths failed", + param); + dm_lib_release(); + } + } /*we attempt to create device only once*/ + count++; + } while (!alias && (count < 2)); + if (!alias) { - condlog(2, "%s: mapname not found for %d:%d", - param, major, minor); - return 0; + condlog(2, "%s: add map failed", param); + return 1; } rc = ev_add_map(dev_path, alias, vecs); FREE(alias); + FREE(refwwid); return rc; } @@ -527,19 +585,19 @@ cli_del_map (void * v, char ** reply, int * len, void * data) minor = dm_get_minor(param); if (minor < 0) { condlog(2, "%s: not a device mapper table", param); - return 0; + return 1; } major = dm_get_major(param); if (major < 0) { condlog(2, "%s: not a device mapper table", param); - return 0; + return 1; } sprintf(dev_path,"dm-%d", minor); alias = dm_mapname(major, minor); if (!alias) { condlog(2, "%s: mapname not found for %d:%d", param, major, minor); - return 0; + return 1; } rc = ev_remove_map(param, alias, minor, vecs); FREE(alias); @@ -565,6 +623,11 @@ cli_reload(void *v, char **reply, int *len, void *data) condlog(0, "%s: invalid map name. cannot reload", mapname); return 1; } + if (mpp->wait_for_udev) { + condlog(2, "%s: device not fully created, failing reload", + mpp->alias); + return 1; + } return reload_map(vecs, mpp, 0); } @@ -611,6 +674,12 @@ cli_resize(void *v, char **reply, int *len, void *data) return 1; } + if (mpp->wait_for_udev) { + condlog(2, "%s: device not fully created, failing resize", + mpp->alias); + return 1; + } + pgp = VECTOR_SLOT(mpp->pg, 0); if (!pgp){ @@ -775,6 +844,12 @@ cli_reconfigure(void * v, char ** reply, int * len, void * data) { struct vectors * vecs = (struct vectors *)data; + if (need_to_delay_reconfig(vecs)) { + conf->delayed_reconfig = 1; + condlog(2, "delaying reconfigure (operator)"); + return 0; + } + condlog(2, "reconfigure (operator)"); return reconfigure(vecs); @@ -785,17 +860,25 @@ cli_suspend(void * v, char ** reply, int * len, void * data) { struct vectors * vecs = (struct vectors *)data; char * param = get_keyparam(v, MAP); - int r = dm_simplecmd_noflush(DM_DEVICE_SUSPEND, param); + int r; + struct multipath * mpp; param = convert_dev(param, 0); - condlog(2, "%s: suspend (operator)", param); + mpp = find_mp_by_alias(vecs->mpvec, param); + if (!mpp) + return 1; - if (!r) /* error */ + if (mpp->wait_for_udev) { + condlog(2, "%s: device not fully created, failing suspend", + mpp->alias); return 1; + } - struct multipath * mpp = find_mp_by_alias(vecs->mpvec, param); + r = dm_simplecmd_noflush(DM_DEVICE_SUSPEND, param, 0, 0); - if (!mpp) + condlog(2, "%s: suspend (operator)", param); + + if (!r) /* error */ return 1; dm_get_info(param, &mpp->dmi); @@ -807,17 +890,25 @@ cli_resume(void * v, char ** reply, int * len, void * data) { struct vectors * vecs = (struct vectors *)data; char * param = get_keyparam(v, MAP); - int r = dm_simplecmd_noflush(DM_DEVICE_RESUME, param); + int r; + struct multipath * mpp; param = convert_dev(param, 0); - condlog(2, "%s: resume (operator)", param); + mpp = find_mp_by_alias(vecs->mpvec, param); + if (!mpp) + return 1; - if (!r) /* error */ + if (mpp->wait_for_udev) { + condlog(2, "%s: device not fully created, failing resume", + mpp->alias); return 1; + } - struct multipath * mpp = find_mp_by_alias(vecs->mpvec, param); + r = dm_simplecmd_noflush(DM_DEVICE_RESUME, param, 0, 0); - if (!mpp) + condlog(2, "%s: resume (operator)", param); + + if (!r) /* error */ return 1; dm_get_info(param, &mpp->dmi); @@ -850,9 +941,21 @@ cli_reinstate(void * v, char ** reply, int * len, void * data) int cli_reassign (void * v, char ** reply, int * len, void * data) { + struct vectors * vecs = (struct vectors *)data; char * param = get_keyparam(v, MAP); + struct multipath *mpp; param = convert_dev(param, 0); + mpp = find_mp_by_alias(vecs->mpvec, param); + if (!mpp) + return 1; + + if (mpp->wait_for_udev) { + condlog(2, "%s: device not fully created, failing reassign", + mpp->alias); + return 1; + } + condlog(3, "%s: reset devices (operator)", param); dm_reassign(param); diff --git a/multipathd/cli_handlers.h b/multipathd/cli_handlers.h index de51961..799f8da 100644 --- a/multipathd/cli_handlers.h +++ b/multipathd/cli_handlers.h @@ -1,9 +1,12 @@ int cli_list_paths (void * v, char ** reply, int * len, void * data); int cli_list_paths_fmt (void * v, char ** reply, int * len, void * data); +int cli_list_paths_raw (void * v, char ** reply, int * len, void * data); +int cli_list_path (void * v, char ** reply, int * len, void * data); int cli_list_status (void * v, char ** reply, int * len, void * data); int cli_list_daemon (void * v, char ** reply, int * len, void * data); int cli_list_maps (void * v, char ** reply, int * len, void * data); int cli_list_maps_fmt (void * v, char ** reply, int * len, void * data); +int cli_list_maps_raw (void * v, char ** reply, int * len, void * data); int cli_list_maps_status (void * v, char ** reply, int * len, void * data); int cli_list_maps_stats (void * v, char ** reply, int * len, void * data); int cli_list_map_topology (void * v, char ** reply, int * len, void * data); diff --git a/multipathd/main.c b/multipathd/main.c index af93f32..bbcfe0d 100644 --- a/multipathd/main.c +++ b/multipathd/main.c @@ -21,6 +21,7 @@ #include #endif #include +#include #include #include @@ -29,6 +30,10 @@ */ #include +#ifdef USE_SYSTEMD +static int use_watchdog; +#endif + /* * libmultipath */ @@ -54,9 +59,11 @@ #include #include #include +#include #include #include #include +#include "prioritizers/alua_rtpg.h" #include "main.h" #include "pidfile.h" @@ -192,7 +199,8 @@ sync_map_state(struct multipath *mpp) vector_foreach_slot (mpp->pg, pgp, i){ vector_foreach_slot (pgp->paths, pp, j){ if (pp->state == PATH_UNCHECKED || - pp->state == PATH_WILD) + pp->state == PATH_WILD || + pp->state == PATH_DELAYED) continue; if ((pp->dmstate == PSTATE_FAILED || pp->dmstate == PSTATE_UNDEF) && @@ -218,19 +226,30 @@ sync_maps_state(vector mpvec) } static int -flush_map(struct multipath * mpp, struct vectors * vecs) +flush_map(struct multipath * mpp, struct vectors * vecs, int nopaths) { + int r; + + if (nopaths) + r = dm_flush_map_nopaths(mpp->alias, mpp->deferred_remove); + else + r = dm_flush_map(mpp->alias); /* * clear references to this map before flushing so we can ignore * the spurious uevent we may generate with the dm_flush_map call below */ - if (dm_flush_map(mpp->alias)) { + if (r) { /* * May not really be an error -- if the map was already flushed * from the device mapper by dmsetup(8) for instance. */ - condlog(0, "%s: can't flush", mpp->alias); - return 1; + if (r == 1) + condlog(0, "%s: can't flush", mpp->alias); + else { + condlog(2, "%s: devmap deferred remove", mpp->alias); + mpp->deferred_remove = DEFERRED_REMOVE_IN_PROGRESS; + } + return r; } else { dm_lib_release(); @@ -243,6 +262,47 @@ flush_map(struct multipath * mpp, struct vectors * vecs) return 0; } +int +update_map (struct multipath *mpp, struct vectors *vecs) +{ + int retries = 3; + char params[PARAMS_SIZE] = {0}; + +retry: + condlog(4, "%s: updating new map", mpp->alias); + if (adopt_paths(vecs->pathvec, mpp, 1)) { + condlog(0, "%s: failed to adopt paths for new map update", + mpp->alias); + retries = -1; + goto fail; + } + verify_paths(mpp, vecs); + mpp->flush_on_last_del = FLUSH_UNDEF; + mpp->action = ACT_RELOAD; + + if (setup_map(mpp, params, PARAMS_SIZE)) { + condlog(0, "%s: failed to setup new map in update", mpp->alias); + retries = -1; + goto fail; + } + if (domap(mpp, params) <= 0 && retries-- > 0) { + condlog(0, "%s: map_udate sleep", mpp->alias); + sleep(1); + goto retry; + } + dm_lib_release(); + +fail: + if (setup_multipath(vecs, mpp)) + return 1; + + sync_map_state(mpp); + + if (retries < 0) + condlog(0, "%s: failed reload in new map update", mpp->alias); + return 0; +} + static int uev_add_map (struct uevent * uev, struct vectors * vecs) { @@ -277,7 +337,7 @@ ev_add_map (char * dev, char * alias, struct vectors * vecs) map_present = dm_map_present(alias); - if (map_present && dm_type(alias, TGT_MPATH) <= 0) { + if (map_present && !dm_is_mpath(alias)) { condlog(4, "%s: not a multipath map", alias); return 0; } @@ -285,6 +345,20 @@ ev_add_map (char * dev, char * alias, struct vectors * vecs) mpp = find_mp_by_alias(vecs->mpvec, alias); if (mpp) { + if (mpp->wait_for_udev > 1) { + if (update_map(mpp, vecs)) + /* setup multipathd removed the map */ + return 1; + } + if (mpp->wait_for_udev) { + mpp->wait_for_udev = 0; + if (conf->delayed_reconfig && + !need_to_delay_reconfig(vecs)) { + condlog(2, "reconfigure (delayed)"); + reconfigure(vecs); + return 0; + } + } /* * Not really an error -- we generate our own uevent * if we create a multipath mapped device as a result @@ -302,10 +376,15 @@ ev_add_map (char * dev, char * alias, struct vectors * vecs) /* * now we can register the map */ - if (map_present && (mpp = add_map_without_path(vecs, alias))) { - sync_map_state(mpp); - condlog(2, "%s: devmap %s registered", alias, dev); - return 0; + if (map_present) { + if ((mpp = add_map_without_path(vecs, alias))) { + sync_map_state(mpp); + condlog(2, "%s: devmap %s registered", alias, dev); + return 0; + } else { + condlog(2, "%s: uev_add_map failed", dev); + return 1; + } } r = get_refwwid(dev, DEV_DEVMAP, vecs->pathvec, &refwwid); @@ -369,21 +448,21 @@ ev_remove_map (char * devname, char * alias, int minor, struct vectors * vecs) if (!mpp) { condlog(2, "%s: devmap not registered, can't remove", devname); - return 0; + return 1; } if (strcmp(mpp->alias, alias)) { condlog(2, "%s: minor number mismatch (map %d, event %d)", mpp->alias, mpp->dmi->minor, minor); - return 0; + return 1; } - return flush_map(mpp, vecs); + return flush_map(mpp, vecs, 0); } static int uev_add_path (struct uevent *uev, struct vectors * vecs) { struct path *pp; - int ret, i; + int ret = 0, i; condlog(2, "%s: add path (uevent)", uev->kernel); if (strstr(uev->kernel, "..") != NULL) { @@ -396,44 +475,58 @@ uev_add_path (struct uevent *uev, struct vectors * vecs) pp = find_path_by_dev(vecs->pathvec, uev->kernel); if (pp) { + int r; + condlog(0, "%s: spurious uevent, path already in pathvec", uev->kernel); - if (pp->mpp) - return 0; - if (!strlen(pp->wwid)) { + if (!pp->mpp && !strlen(pp->wwid)) { + condlog(3, "%s: reinitialize path", uev->kernel); udev_device_unref(pp->udev); pp->udev = udev_device_ref(uev->udev); - ret = pathinfo(pp, conf->hwtable, - DI_ALL | DI_BLACKLIST); - if (ret == 2) { + r = pathinfo(pp, conf->hwtable, + DI_ALL | DI_BLACKLIST); + if (r == PATHINFO_OK) + ret = ev_add_path(pp, vecs); + else if (r == PATHINFO_SKIPPED) { + condlog(3, "%s: remove blacklisted path", + uev->kernel); i = find_slot(vecs->pathvec, (void *)pp); if (i != -1) vector_del_slot(vecs->pathvec, i); free_path(pp); - return 0; - } else if (ret == 1) { + } else { condlog(0, "%s: failed to reinitialize path", uev->kernel); - return 1; + ret = 1; } } - } else { - /* - * get path vital state - */ - ret = store_pathinfo(vecs->pathvec, conf->hwtable, - uev->udev, DI_ALL, &pp); - if (!pp) { - if (ret == 2) - return 0; - condlog(0, "%s: failed to store path info", - uev->kernel); - return 1; - } + return ret; + } + + /* + * get path vital state + */ + ret = alloc_path_with_pathinfo(conf->hwtable, uev->udev, + DI_ALL, &pp); + if (!pp) { + if (ret == PATHINFO_SKIPPED) + return 0; + condlog(3, "%s: failed to get path info", uev->kernel); + return 1; + } + ret = store_path(vecs->pathvec, pp); + if (!ret) { pp->checkint = conf->checkint; + ret = ev_add_path(pp, vecs); + } else { + condlog(0, "%s: failed to store path info, " + "dropping event", + uev->kernel); + free_path(pp); + ret = 1; } - return ev_add_path(pp, vecs); + return ret; } /* @@ -445,30 +538,32 @@ int ev_add_path (struct path * pp, struct vectors * vecs) { struct multipath * mpp; - char empty_buff[WWID_SIZE] = {0}; char params[PARAMS_SIZE] = {0}; int retries = 3; int start_waiter = 0; + int ret; /* * need path UID to go any further */ - if (memcmp(empty_buff, pp->wwid, WWID_SIZE) == 0) { + if (strlen(pp->wwid) == 0) { condlog(0, "%s: failed to get path uid", pp->dev); goto fail; /* leave path added to pathvec */ } - mpp = pp->mpp = find_mp_by_wwid(vecs->mpvec, pp->wwid); + mpp = find_mp_by_wwid(vecs->mpvec, pp->wwid); + if (mpp && mpp->wait_for_udev) { + mpp->wait_for_udev = 2; + orphan_path(pp, "waiting for create to complete"); + return 0; + } + + pp->mpp = mpp; rescan: if (mpp) { - if ((!pp->size) || (mpp->size != pp->size)) { - if (!pp->size) - condlog(0, "%s: failed to add new path %s, " - "device size is 0", - mpp->alias, pp->dev); - else - condlog(0, "%s: failed to add new path %s, " - "device size mismatch", - mpp->alias, pp->dev); + if (mpp->size != pp->size) { + condlog(0, "%s: failed to add new path %s, " + "device size mismatch", + mpp->alias, pp->dev); int i = find_slot(vecs->pathvec, (void *)pp); if (i != -1) vector_del_slot(vecs->pathvec, i); @@ -481,21 +576,14 @@ rescan: if (adopt_paths(vecs->pathvec, mpp, 1)) goto fail; /* leave path added to pathvec */ - verify_paths(mpp, vecs, NULL); + verify_paths(mpp, vecs); mpp->flush_on_last_del = FLUSH_UNDEF; mpp->action = ACT_RELOAD; - } - else { - if (!pp->size) { - condlog(0, "%s: failed to create new map," - " device size is 0 ", pp->dev); - int i = find_slot(vecs->pathvec, (void *)pp); - if (i != -1) - vector_del_slot(vecs->pathvec, i); - free_path(pp); - return 1; + } else { + if (!should_multipath(pp, vecs->pathvec)) { + orphan_path(pp, "only one path"); + return 0; } - condlog(4,"%s: creating new map", pp->dev); if ((mpp = add_map_with_path(vecs, pp, 1))) { mpp->action = ACT_CREATE; @@ -523,7 +611,15 @@ rescan: /* * reload the map for the multipath mapped device */ - if (domap(mpp, params) <= 0) { +retry: + ret = domap(mpp, params); + if (ret <= 0) { + if (ret < 0 && retries-- > 0) { + condlog(0, "%s: retry domap for addition of new " + "path %s", mpp->alias, pp->dev); + sleep(1); + goto retry; + } condlog(0, "%s: failed in domap for addition of new " "path %s", mpp->alias, pp->dev); /* @@ -561,7 +657,7 @@ rescan: return 0; } else - return 1; + goto fail; fail_map: remove_map(mpp, vecs, 1); @@ -627,7 +723,7 @@ ev_remove_path (struct path *pp, struct vectors * vecs) mpp->flush_on_last_del = FLUSH_IN_PROGRESS; dm_queue_if_no_path(mpp->alias, 0); } - if (!flush_map(mpp, vecs)) { + if (!flush_map(mpp, vecs, 1)) { condlog(2, "%s: removed map after" " removing all paths", alias); @@ -644,6 +740,12 @@ ev_remove_path (struct path *pp, struct vectors * vecs) " removal of path %s", mpp->alias, pp->dev); goto fail; } + + if (mpp->wait_for_udev) { + mpp->wait_for_udev = 2; + goto out; + } + /* * reload the map */ @@ -657,9 +759,8 @@ ev_remove_path (struct path *pp, struct vectors * vecs) /* * update our state from kernel */ - if (setup_multipath(vecs, mpp)) { - goto fail; - } + if (setup_multipath(vecs, mpp)) + return 1; sync_map_state(mpp); condlog(2, "%s [%s]: path removed from map %s", @@ -684,21 +785,29 @@ static int uev_update_path (struct uevent *uev, struct vectors * vecs) { int ro, retval = 0; + struct path * pp; + + pp = find_path_by_dev(vecs->pathvec, uev->kernel); + if (!pp) { + condlog(0, "%s: spurious uevent, path not found", + uev->kernel); + return 1; + } + + if (pp->initialized == INIT_REQUESTED_UDEV) + return uev_add_path(uev, vecs); ro = uevent_get_disk_ro(uev); if (ro >= 0) { - struct path * pp; - condlog(2, "%s: update path write_protect to '%d' (uevent)", uev->kernel, ro); - pp = find_path_by_dev(vecs->pathvec, uev->kernel); - if (!pp) { - condlog(0, "%s: spurious uevent, path not found", - uev->kernel); - return 1; - } if (pp->mpp) { + if (pp->mpp->wait_for_udev) { + pp->mpp->wait_for_udev = 2; + return 0; + } + retval = reload_map(vecs, pp->mpp, 0); condlog(2, "%s: map %s reloaded (retval %d)", @@ -864,12 +973,15 @@ uxlsnrloop (void * ap) set_handler_callback(LIST+PATHS, cli_list_paths); set_handler_callback(LIST+PATHS+FMT, cli_list_paths_fmt); + set_handler_callback(LIST+PATHS+RAW+FMT, cli_list_paths_raw); + set_handler_callback(LIST+PATH, cli_list_path); set_handler_callback(LIST+MAPS, cli_list_maps); set_handler_callback(LIST+STATUS, cli_list_status); set_handler_callback(LIST+DAEMON, cli_list_daemon); set_handler_callback(LIST+MAPS+STATUS, cli_list_maps_status); set_handler_callback(LIST+MAPS+STATS, cli_list_maps_stats); set_handler_callback(LIST+MAPS+FMT, cli_list_maps_fmt); + set_handler_callback(LIST+MAPS+RAW+FMT, cli_list_maps_raw); set_handler_callback(LIST+MAPS+TOPOLOGY, cli_list_maps_topology); set_handler_callback(LIST+TOPOLOGY, cli_list_maps_topology); set_handler_callback(LIST+MAP+TOPOLOGY, cli_list_map_topology); @@ -949,19 +1061,23 @@ fail_path (struct path * pp, int del_active) /* * caller must have locked the path list before calling that function */ -static void +static int reinstate_path (struct path * pp, int add_active) { + int ret = 0; + if (!pp->mpp) - return; + return 0; - if (dm_reinstate_path(pp->mpp->alias, pp->dev_t)) + if (dm_reinstate_path(pp->mpp->alias, pp->dev_t)) { condlog(0, "%s: reinstate failed", pp->dev_t); - else { + ret = 1; + } else { condlog(2, "%s: reinstated", pp->dev_t); if (add_active) update_queue_mode_add_path(pp->mpp); } + return ret; } static void @@ -1031,6 +1147,33 @@ followover_should_failback(struct path * pp) return 1; } +static void +missing_uev_wait_tick(struct vectors *vecs) +{ + struct multipath * mpp; + unsigned int i; + int timed_out = 0; + + vector_foreach_slot (vecs->mpvec, mpp, i) { + if (mpp->wait_for_udev && --mpp->uev_wait_tick <= 0) { + timed_out = 1; + condlog(0, "%s: timeout waiting on creation uevent. enabling reloads", mpp->alias); + if (mpp->wait_for_udev > 1 && update_map(mpp, vecs)) { + /* update_map removed map */ + i--; + continue; + } + mpp->wait_for_udev = 0; + } + } + + if (timed_out && conf->delayed_reconfig && + !need_to_delay_reconfig(vecs)) { + condlog(2, "reconfigure (delayed)"); + reconfigure(vecs); + } +} + static void defered_failback_tick (vector mpvec) { @@ -1116,14 +1259,28 @@ check_path (struct vectors * vecs, struct path * pp) int newstate; int new_path_up = 0; int chkr_new_path_up = 0; + int add_active; + int disable_reinstate = 0; int oldchkrstate = pp->chkrstate; - if (!pp->mpp) + if ((pp->initialized == INIT_OK || + pp->initialized == INIT_REQUESTED_UDEV) && !pp->mpp) return 0; if (pp->tick && --pp->tick) return 0; /* don't check this path yet */ + if (!pp->mpp && pp->initialized == INIT_MISSING_UDEV && + pp->retriggers < conf->retrigger_tries) { + condlog(2, "%s: triggering change event to reinitialize", + pp->dev); + pp->initialized = INIT_REQUESTED_UDEV; + pp->retriggers++; + sysfs_attr_set_value(pp->udev, "uevent", "change", + strlen("change")); + return 0; + } + /* * provision a next check soonest, * in case we exit abnormaly from here @@ -1131,11 +1288,14 @@ check_path (struct vectors * vecs, struct path * pp) pp->tick = conf->checkint; newstate = path_offline(pp); - if (newstate == PATH_REMOVED) { - condlog(2, "%s: remove path (checker)", pp->dev); - ev_remove_path(pp, vecs); - return 0; - } + /* + * Wait for uevent for removed paths; + * some LLDDs like zfcp keep paths unavailable + * without sending uevents. + */ + if (newstate == PATH_REMOVED) + newstate = PATH_DOWN; + if (newstate == PATH_UP) newstate = get_state(pp, 1); else @@ -1147,7 +1307,7 @@ check_path (struct vectors * vecs, struct path * pp) return 1; } if (!pp->mpp) { - if (!strlen(pp->wwid) && + if (!strlen(pp->wwid) && pp->initialized != INIT_MISSING_UDEV && (newstate == PATH_UP || newstate == PATH_GHOST)) { condlog(2, "%s: add missing path", pp->dev); if (pathinfo(pp, conf->hwtable, DI_ALL) == 0) { @@ -1173,6 +1333,30 @@ check_path (struct vectors * vecs, struct path * pp) pp->dev); pp->dmstate = PSTATE_UNDEF; } + /* if update_multipath_strings orphaned the path, quit early */ + if (!pp->mpp) + return 0; + + if ((newstate == PATH_UP || newstate == PATH_GHOST) && + pp->wait_checks > 0) { + if (pp->mpp && pp->mpp->nr_active > 0) { + pp->state = PATH_DELAYED; + pp->wait_checks--; + return 1; + } else + pp->wait_checks = 0; + } + + /* + * don't reinstate failed path, if its in stand-by + * and if target supports only implicit tpgs mode. + * this will prevent unnecessary i/o by dm on stand-by + * paths if there are no other active paths in map. + */ + disable_reinstate = (newstate == PATH_GHOST && + pp->mpp->nr_active == 0 && + pp->tpgs == TPGS_IMPLICIT) ? 1 : 0; + pp->chkrstate = newstate; if (newstate != pp->state) { int oldstate = pp->state; @@ -1192,9 +1376,14 @@ check_path (struct vectors * vecs, struct path * pp) * proactively fail path in the DM */ if (oldstate == PATH_UP || - oldstate == PATH_GHOST) + oldstate == PATH_GHOST) { fail_path(pp, 1); - else + if (pp->mpp->delay_wait_checks > 0 && + pp->watch_checks > 0) { + pp->wait_checks = pp->mpp->delay_wait_checks; + pp->watch_checks = 0; + } + }else fail_path(pp, 0); /* @@ -1221,11 +1410,21 @@ check_path (struct vectors * vecs, struct path * pp) * reinstate this path */ if (oldstate != PATH_UP && - oldstate != PATH_GHOST) - reinstate_path(pp, 1); - else - reinstate_path(pp, 0); - + oldstate != PATH_GHOST) { + if (pp->mpp->delay_watch_checks > 0) + pp->watch_checks = pp->mpp->delay_watch_checks; + add_active = 1; + } else { + if (pp->watch_checks > 0) + pp->watch_checks--; + add_active = 0; + } + if (!disable_reinstate && reinstate_path(pp, add_active)) { + condlog(3, "%s: reload map", pp->dev); + ev_add_path(pp, vecs); + pp->tick = 1; + return 0; + } new_path_up = 1; if (oldchkrstate != PATH_UP && oldchkrstate != PATH_GHOST) @@ -1239,10 +1438,16 @@ check_path (struct vectors * vecs, struct path * pp) enable_group(pp); } else if (newstate == PATH_UP || newstate == PATH_GHOST) { - if (pp->dmstate == PSTATE_FAILED || - pp->dmstate == PSTATE_UNDEF) { + if ((pp->dmstate == PSTATE_FAILED || + pp->dmstate == PSTATE_UNDEF) && + !disable_reinstate) { /* Clear IO errors */ - reinstate_path(pp, 0); + if (reinstate_path(pp, 0)) { + condlog(3, "%s: reload map", pp->dev); + ev_add_path(pp, vecs); + pp->tick = 1; + return 0; + } } else { LOG_MSG(4, checker_message(&pp->checker)); if (pp->checkint != conf->max_checkint) { @@ -1258,6 +1463,8 @@ check_path (struct vectors * vecs, struct path * pp) condlog(4, "%s: delay next check %is", pp->dev_t, pp->checkint); } + if (pp->watch_checks > 0) + pp->watch_checks--; pp->tick = pp->checkint; } } @@ -1271,6 +1478,9 @@ check_path (struct vectors * vecs, struct path * pp) pp->state = newstate; + + if (pp->mpp->wait_for_udev) + return 1; /* * path prio refreshing */ @@ -1322,7 +1532,7 @@ checkerloop (void *ap) pthread_testcancel(); condlog(4, "tick"); #ifdef USE_SYSTEMD - if (conf->watchdog) + if (use_watchdog) sd_notify(0, "WATCHDOG=1"); #endif if (vecs->pathvec) { @@ -1333,6 +1543,7 @@ checkerloop (void *ap) if (vecs->mpvec) { defered_failback_tick(vecs->mpvec); retry_count_tick(vecs->mpvec); + missing_uev_wait_tick(vecs); } if (count) count--; @@ -1362,7 +1573,7 @@ configure (struct vectors * vecs, int start_waiters) struct multipath * mpp; struct path * pp; vector mpvec; - int i; + int i, ret; if (!vecs->pathvec && !(vecs->pathvec = vector_alloc())) return 1; @@ -1376,7 +1587,9 @@ configure (struct vectors * vecs, int start_waiters) /* * probe for current path (from sysfs) and map (from dm) sets */ - path_discovery(vecs->pathvec, conf, DI_ALL); + ret = path_discovery(vecs->pathvec, conf, DI_ALL); + if (ret < 0) + return 1; vector_foreach_slot (vecs->pathvec, pp, i){ if (filter_path(conf, pp) > 0){ @@ -1435,12 +1648,30 @@ configure (struct vectors * vecs, int start_waiters) return 0; } +int +need_to_delay_reconfig(struct vectors * vecs) +{ + struct multipath *mpp; + int i; + + if (!VECTOR_SIZE(vecs->mpvec)) + return 0; + + vector_foreach_slot(vecs->mpvec, mpp, i) { + if (mpp->wait_for_udev) + return 1; + } + return 0; +} + int reconfigure (struct vectors * vecs) { struct config * old = conf; int retval = 1; + running_state = DAEMON_CONFIGURE; + /* * free old map and path vectors ... they use old conf state */ @@ -1459,12 +1690,16 @@ reconfigure (struct vectors * vecs) if (!load_config(DEFAULT_CONFIGFILE, udev)) { dm_drv_version(conf->version, TGT_MPATH); conf->verbosity = old->verbosity; + conf->bindings_read_only = old->bindings_read_only; + conf->ignore_new_devs = old->ignore_new_devs; conf->daemon = 1; configure(vecs, 1); free_config(old); retval = 0; } + running_state = DAEMON_RUNNING; + return retval; } @@ -1518,12 +1753,18 @@ void handle_signals(void) { if (reconfig_sig && running_state == DAEMON_RUNNING) { - condlog(2, "reconfigure (signal)"); pthread_cleanup_push(cleanup_lock, &gvecs->lock); lock(gvecs->lock); pthread_testcancel(); - reconfigure(gvecs); + if (need_to_delay_reconfig(gvecs)) { + conf->delayed_reconfig = 1; + condlog(2, "delaying reconfigure (signal)"); + } + else { + condlog(2, "reconfigure (signal)"); + reconfigure(gvecs); + } lock_cleanup_pop(gvecs->lock); } if (log_reset_sig) { @@ -1653,7 +1894,7 @@ child (void * param) #ifdef USE_SYSTEMD unsigned long checkint; #endif - int rc, pid_rc; + int rc; char *envp; mlockall(MCL_CURRENT | MCL_FUTURE); @@ -1663,7 +1904,7 @@ child (void * param) udev = udev_new(); setup_thread_attr(&misc_attr, 64 * 1024, 1); - setup_thread_attr(&uevent_attr, 128 * 1024, 1); + setup_thread_attr(&uevent_attr, DEFAULT_UEVENT_STACKSIZE * 1024, 1); setup_thread_attr(&waiter_attr, 32 * 1024, 1); if (logsink == 1) { @@ -1671,6 +1912,12 @@ child (void * param) log_thread_start(&log_attr); pthread_attr_destroy(&log_attr); } + if (pidfile_create(DEFAULT_PIDFILE, daemon_pid)) { + condlog(1, "failed to create pidfile"); + if (logsink == 1) + log_thread_stop(); + exit(1); + } running_state = DAEMON_START; @@ -1733,7 +1980,7 @@ child (void * param) set_oom_adj(); conf->daemon = 1; - udev_set_sync_support(0); + dm_udev_set_sync_support(0); #ifdef USE_SYSTEMD envp = getenv("WATCHDOG_USEC"); if (envp && sscanf(envp, "%lu", &checkint) == 1) { @@ -1746,7 +1993,7 @@ child (void * param) conf->checkint = conf->max_checkint / 4; condlog(3, "enabling watchdog, interval %d max %d", conf->checkint, conf->max_checkint); - conf->watchdog = conf->checkint; + use_watchdog = conf->checkint; } #endif /* @@ -1790,10 +2037,6 @@ child (void * param) } pthread_attr_destroy(&misc_attr); - /* Startup complete, create logfile */ - pid_rc = pidfile_create(DEFAULT_PIDFILE, daemon_pid); - /* Ignore errors, we can live without */ - running_state = DAEMON_RUNNING; #ifdef USE_SYSTEMD sd_notify(0, "READY=1\nSTATUS=running"); @@ -1844,10 +2087,8 @@ child (void * param) dm_lib_exit(); /* We're done here */ - if (!pid_rc) { - condlog(3, "unlink pidfile"); - unlink(DEFAULT_PIDFILE); - } + condlog(3, "unlink pidfile"); + unlink(DEFAULT_PIDFILE); condlog(2, "--------shut down-------"); @@ -1939,6 +2180,7 @@ main (int argc, char *argv[]) extern int optind; int arg; int err; + int foreground = 0; logsink = 1; running_state = DAEMON_INIT; @@ -1960,10 +2202,12 @@ main (int argc, char *argv[]) if (!conf) exit(1); - while ((arg = getopt(argc, argv, ":dsv:k::")) != EOF ) { + while ((arg = getopt(argc, argv, ":dsv:k::Bn")) != EOF ) { switch(arg) { case 'd': - logsink = 0; + foreground = 1; + if (logsink > 0) + logsink = 0; //debug=1; /* ### comment me out ### */ break; case 'v': @@ -1977,8 +2221,16 @@ main (int argc, char *argv[]) logsink = -1; break; case 'k': - uxclnt(optarg); + if (load_config(DEFAULT_CONFIGFILE, udev_new())) + exit(1); + uxclnt(optarg, conf->uxsock_timeout); exit(0); + case 'B': + conf->bindings_read_only = 1; + break; + case 'n': + conf->ignore_new_devs = 1; + break; default: ; } @@ -1988,6 +2240,8 @@ main (int argc, char *argv[]) char * s = cmd; char * c = s; + if (load_config(DEFAULT_CONFIGFILE, udev_new())) + exit(1); while (optind < argc) { if (strchr(argv[optind], ' ')) c += snprintf(c, s + CMDSIZE - c, "\"%s\" ", argv[optind]); @@ -1996,13 +2250,16 @@ main (int argc, char *argv[]) optind++; } c += snprintf(c, s + CMDSIZE - c, "\n"); - uxclnt(s); + uxclnt(s, conf->uxsock_timeout); exit(0); } - if (logsink < 1) + if (foreground) { + if (!isatty(fileno(stdout))) + setbuf(stdout, NULL); err = 0; - else + daemon_pid = getpid(); + } else err = daemonize(); if (err < 0) diff --git a/multipathd/main.h b/multipathd/main.h index 10378ef..2f706d2 100644 --- a/multipathd/main.h +++ b/multipathd/main.h @@ -18,6 +18,7 @@ extern pid_t daemon_pid; void exit_daemon(void); const char * daemon_status(void); +int need_to_delay_reconfig (struct vectors *); int reconfigure (struct vectors *); int ev_add_path (struct path *, struct vectors *); int ev_remove_path (struct path *, struct vectors *); diff --git a/multipathd/multipathd.8 b/multipathd/multipathd.8 index 6da9d2b..77f6e72 100644 --- a/multipathd/multipathd.8 +++ b/multipathd/multipathd.8 @@ -28,8 +28,17 @@ Suppress timestamps. Do not prefix logging messages with a timestamp. .B -v "level" Verbosity level. Print additional information while running multipathd. A level of 0 means only print errors. A level of 3 or greater prints debugging information as well. .TP +.B -B +Read-only bindings file. Multipathd will not write to the user_friendly_names +bindings file. If a user_friendly_name doesn't already exist for a device, it +will use its WWID as its alias. +.TP .B -k multipathd will enter interactive mode. From this mode, the available commands can be viewed by entering "help". When you are finished entering commands, press CTRL-D to quit. +.TP +.B -n +ignore new devices. Multipathd will not create a multipath device unless the +wwid for the device is already listed in the wwids file. .SH COMMANDS .TP @@ -80,6 +89,9 @@ Show all available block devices by name including the information if they are b .B list|show status Show the number of path checkers in each possible state, the number of monitored paths, and whether multipathd is currently handling a uevent. .TP +.B list|show daemon +Show the current state of the multipathd daemon +.TP .B add path $path Add a path to the list of monitored paths. $path is as listed in /sys/block (e.g. sda). .TP @@ -107,6 +119,13 @@ Sets map $map into suspend state. .B resume map|multipath $map Resumes map $map from suspend state. .TP +.B reset map|multipath $map +Reassign existing device-mapper table(s) use use the multipath device, instead +of its path devices. +.TP +.B reload map|multipath $map +Reload a multipath device. +.TP .B fail path $path Sets path $path into failed state. .TP @@ -125,8 +144,27 @@ Disable queuing on multipathed map $map .B restorequeueing map|multipath $map Restore queuing on multipahted map $map .TP +.B forcequeueing daemon +Forces multipathd into queue_without_daemon mode, so that no_path_retry queueing +will not be disabled when the daemon stops +.TP +.B restorequeueing daemon +Restores configured queue_without_daemon mode +.TP +.B map|multipath $map setprstatus +Enable persistent reservation management on $map +.TP +.B map|multipath $map unsetprstatus +Disable persistent reservation management on $map +.TP +.B map|multipath $map getprstatus +Get the current persistent reservation management status of $map +.TP .B quit|exit End interactive session. +.TP +.B shutdown +Stop multipathd. .SH "SYSTEMD INTEGRATION" When compiled with systemd support two systemd service files are diff --git a/multipathd/multipathd.init.suse b/multipathd/multipathd.init.suse index d1319b1..ed699fa 100644 --- a/multipathd/multipathd.init.suse +++ b/multipathd/multipathd.init.suse @@ -17,7 +17,7 @@ PATH=/bin:/usr/bin:/sbin:/usr/sbin DAEMON=/sbin/multipathd -PIDFILE=/var/run/multipathd.pid +PIDFILE=/run/multipathd.pid MPATH_INIT_TIMEOUT=10 ARGS="" diff --git a/multipathd/multipathd.service b/multipathd/multipathd.service index be3ba3f..d5f8606 100644 --- a/multipathd/multipathd.service +++ b/multipathd/multipathd.service @@ -1,8 +1,10 @@ [Unit] Description=Device-Mapper Multipath Device Controller Before=iscsi.service iscsid.service lvm2-activation-early.service -After=syslog.target +Before=local-fs-pre.target +After=multipathd.socket DefaultDependencies=no +Wants=local-fs-pre.target multipathd.socket blk-availability.service Conflicts=shutdown.target [Service] diff --git a/multipathd/multipathd.socket b/multipathd/multipathd.socket index 3d4b6da..921706d 100644 --- a/multipathd/multipathd.socket +++ b/multipathd/multipathd.socket @@ -1,5 +1,7 @@ +[Unit] +Description=multipathd control socket +DefaultDependencies=no +Before=sockets.target + [Socket] ListenStream=@/org/kernel/linux/storage/multipathd - -[Install] -WantedBy=sockets.target diff --git a/multipathd/uxclnt.c b/multipathd/uxclnt.c index e86be21..06c1bf8 100644 --- a/multipathd/uxclnt.c +++ b/multipathd/uxclnt.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -17,6 +18,7 @@ #include #include +#include #include #include #include @@ -63,16 +65,16 @@ static int need_quit(char *str, size_t len) /* * process the client */ -static void process(int fd) +static void process(int fd, unsigned int timeout) { char *line; char *reply; + int ret; cli_init(); rl_readline_name = "multipathd"; rl_completion_entry_function = key_generator; while ((line = readline("multipathd> "))) { - size_t len; size_t llen = strlen(line); if (!llen) { @@ -83,8 +85,9 @@ static void process(int fd) if (need_quit(line, llen)) break; - if (send_packet(fd, line, llen + 1) != 0) break; - if (recv_packet(fd, &reply, &len) != 0) break; + if (send_packet(fd, line) != 0) break; + ret = recv_packet(fd, &reply, timeout); + if (ret != 0) break; print_reply(reply); @@ -96,18 +99,22 @@ static void process(int fd) } } -static void process_req(int fd, char * inbuf) +static void process_req(int fd, char * inbuf, unsigned int timeout) { char *reply; - size_t len; + int ret; - if (send_packet(fd, inbuf, strlen(inbuf) + 1) != 0) { + if (send_packet(fd, inbuf) != 0) { printf("cannot send packet\n"); return; } - if (recv_packet(fd, &reply, &len) != 0) - printf("error receiving packet\n"); - else { + ret = recv_packet(fd, &reply, timeout); + if (ret < 0) { + if (ret == -ETIMEDOUT) + printf("timeout receiving packet\n"); + else + printf("error %d receiving packet\n", ret); + } else { printf("%s", reply); FREE(reply); } @@ -116,18 +123,18 @@ static void process_req(int fd, char * inbuf) /* * entry point */ -int uxclnt(char * inbuf) +int uxclnt(char * inbuf, unsigned int timeout) { int fd; - fd = ux_socket_connect(DEFAULT_SOCKET); + fd = mpath_connect(); if (fd == -1) exit(1); if (inbuf) - process_req(fd, inbuf); + process_req(fd, inbuf, timeout); else - process(fd); + process(fd, timeout); return 0; } diff --git a/multipathd/uxclnt.h b/multipathd/uxclnt.h index 0667a24..8e2cdce 100644 --- a/multipathd/uxclnt.h +++ b/multipathd/uxclnt.h @@ -1 +1 @@ -int uxclnt(char * inbuf); +int uxclnt(char * inbuf, unsigned int timeout); diff --git a/multipathd/uxlsnr.c b/multipathd/uxlsnr.c index ed8e012..e5c5d90 100644 --- a/multipathd/uxlsnr.c +++ b/multipathd/uxlsnr.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -29,6 +30,8 @@ #include #include #include +#include +#include #include "main.h" #include "cli.h" @@ -37,12 +40,12 @@ struct timespec sleep_time = {5, 0}; struct client { + struct list_head node; int fd; - struct client *next, *prev; }; -static struct client *clients; -static unsigned num_clients; +LIST_HEAD(clients); +pthread_mutex_t client_lock = PTHREAD_MUTEX_INITIALIZER; struct pollfd *polls; volatile sig_atomic_t reconfig_sig = 0; volatile sig_atomic_t log_reset_sig = 0; @@ -62,14 +65,19 @@ static void new_client(int ux_sock) if (fd == -1) return; - /* put it in our linked list */ c = (struct client *)MALLOC(sizeof(*c)); + if (!c) { + close(fd); + return; + } memset(c, 0, sizeof(*c)); + INIT_LIST_HEAD(&c->node); c->fd = fd; - c->next = clients; - if (c->next) c->next->prev = c; - clients = c; - num_clients++; + + /* put it in our linked list */ + pthread_mutex_lock(&client_lock); + list_add_tail(&c->node, &clients); + pthread_mutex_unlock(&client_lock); } /* @@ -77,12 +85,12 @@ static void new_client(int ux_sock) */ static void dead_client(struct client *c) { + pthread_mutex_lock(&client_lock); + list_del_init(&c->node); + pthread_mutex_unlock(&client_lock); close(c->fd); - if (c->prev) c->prev->next = c->next; - if (c->next) c->next->prev = c->prev; - if (c == clients) clients = c->next; + c->fd = -1; FREE(c); - num_clients--; } void free_polls (void) @@ -91,6 +99,24 @@ void free_polls (void) FREE(polls); } +void check_timeout(struct timeval start_time, char *inbuf, + unsigned int timeout) +{ + struct timeval diff_time, end_time; + + if (start_time.tv_sec && gettimeofday(&end_time, NULL) == 0) { + timersub(&end_time, &start_time, &diff_time); + unsigned long msecs; + + msecs = diff_time.tv_sec * 1000 + + diff_time.tv_usec / 1000; + if (msecs > timeout) + condlog(2, "cli cmd '%s' timeout reached " + "after %lu.%06lu secs", inbuf, + diff_time.tv_sec, diff_time.tv_usec); + } +} + void uxsock_cleanup(void *arg) { cli_exit(); @@ -100,20 +126,27 @@ void uxsock_cleanup(void *arg) /* * entry point */ -void * uxsock_listen(int (*uxsock_trigger)(char *, char **, int *, void *), - void * trigger_data) +void * uxsock_listen(uxsock_trigger_fn uxsock_trigger, void * trigger_data) { int ux_sock; - size_t len; - int rlen; + int rlen, timeout; char *inbuf; char *reply; sigset_t mask; ux_sock = ux_socket_listen(DEFAULT_SOCKET); - if (ux_sock == -1) - exit(1); + if (ux_sock == -1) { + condlog(1, "could not create uxsock: %d", errno); + return NULL; + } + + if (!conf) { + condlog(1, "configuration changed"); + return NULL; + } + + timeout = conf->uxsock_timeout; pthread_cleanup_push(uxsock_cleanup, NULL); @@ -122,19 +155,48 @@ void * uxsock_listen(int (*uxsock_trigger)(char *, char **, int *, void *), sigdelset(&mask, SIGHUP); sigdelset(&mask, SIGUSR1); while (1) { - struct client *c; - int i, poll_count; + struct pollfd *new; + struct client *c, *tmp; + int i, poll_count, num_clients; + + /* + * Store configuration timeout; + * configuration might change during + * the call to 'reconfigure'. + */ + if (conf) + timeout = conf->uxsock_timeout; /* setup for a poll */ - polls = REALLOC(polls, (1+num_clients) * sizeof(*polls)); + pthread_mutex_lock(&client_lock); + num_clients = 0; + list_for_each_entry(c, &clients, node) { + num_clients++; + } + new = REALLOC(polls, (1+num_clients) * sizeof(*polls)); + /* If we can't allocate poliing space for the new client, + * close it */ + if (!new) { + if (!num_clients) { + condlog(1, "can't listen for new clients"); + return NULL; + } + dead_client(list_entry(clients.prev, + typeof(struct client), node)); + } + else + polls = new; polls[0].fd = ux_sock; polls[0].events = POLLIN; /* setup the clients */ - for (i=1, c = clients; c; i++, c = c->next) { + i = 1; + list_for_each_entry(c, &clients, node) { polls[i].fd = c->fd; polls[i].events = POLLIN; + i++; } + pthread_mutex_unlock(&client_lock); /* most of our life is spent in this call */ poll_count = ppoll(polls, i, &sleep_time, &mask); @@ -154,20 +216,35 @@ void * uxsock_listen(int (*uxsock_trigger)(char *, char **, int *, void *), continue; /* see if a client wants to speak to us */ - for (i=1, c = clients; c; i++) { - struct client *next = c->next; - + for (i = 1; i < num_clients + 1; i++) { if (polls[i].revents & POLLIN) { - if (recv_packet(c->fd, &inbuf, &len) != 0) { + struct timeval start_time; + + c = NULL; + pthread_mutex_lock(&client_lock); + list_for_each_entry(tmp, &clients, node) { + if (tmp->fd == polls[i].fd) { + c = tmp; + break; + } + } + pthread_mutex_unlock(&client_lock); + if (!c) { + condlog(3, "cli%d: invalid fd %d", + i, polls[i].fd); + continue; + } + if (gettimeofday(&start_time, NULL) != 0) + start_time.tv_sec = 0; + if (recv_packet(c->fd, &inbuf, timeout) != 0) { dead_client(c); } else { - inbuf[len - 1] = 0; condlog(4, "Got request [%s]", inbuf); uxsock_trigger(inbuf, &reply, &rlen, trigger_data); if (reply) { - if (send_packet(c->fd, reply, - rlen) != 0) { + if (send_packet(c->fd, + reply) != 0) { dead_client(c); } condlog(4, "Reply [%d bytes]", @@ -175,10 +252,11 @@ void * uxsock_listen(int (*uxsock_trigger)(char *, char **, int *, void *), FREE(reply); reply = NULL; } + check_timeout(start_time, inbuf, + timeout); FREE(inbuf); } } - c = next; } /* see if we got a new client */ diff --git a/multipathd/uxlsnr.h b/multipathd/uxlsnr.h index 8b5a525..d274b04 100644 --- a/multipathd/uxlsnr.h +++ b/multipathd/uxlsnr.h @@ -1,11 +1,13 @@ #ifndef _UXLSNR_H #define _UXLSNR_H -void * uxsock_listen(int (*uxsock_trigger) - (char *, char **, int *, void *), - void * trigger_data); +typedef int (uxsock_trigger_fn)(char *, char **, int *, void *); + +void * uxsock_listen(uxsock_trigger_fn uxsock_trigger, + void * trigger_data); extern volatile sig_atomic_t reconfig_sig; extern volatile sig_atomic_t log_reset_sig; + #endif -- 2.34.1