Change logs are at :
- pre-0.4.5
- http://christophe.varoqui.free.fr/wiki/wakka.php?wiki=ChangeLog
+ http://web.archive.org/web/20070309224034/http://christophe.varoqui.free.fr/wiki/wakka.php?wiki=ChangeLog
- post-0.4.5
- http://www.kernel.org/git/gitweb.cgi?p=linux/storage/multipath-tools/.git;a=log
+ http://git.opensvc.com/?p=multipath-tools/.git;a=log
-Things to read
+multipath-tools for Linux <http://christophe.varoqui.free.fr/>
+
+This package provides the following binaries to drive the
+Device Mapper multipathing driver:
+
+multipath - Device mapper target autoconfig.
+multipathd - Multipath daemon.
+mpathpersist - Manages SCSI persistent reservations on dm multipath devices.
+kpartxrtx - Create device maps from partition tables.
+
+To get latest code: git clone http://git.opensvc.com/multipath-tools/.git
+
+Mailing list: http://www.redhat.com/mailman/listinfo/dm-devel
+Gitweb: http://git.opensvc.com/?p=multipath-tools/.git
+++ /dev/null
-Things to do
$(INSTALL_PROGRAM) -m 755 $(EXEC) $(DESTDIR)$(bindir)
$(INSTALL_PROGRAM) -d $(DESTDIR)$(libudevdir)
$(INSTALL_PROGRAM) -m 755 kpartx_id $(DESTDIR)$(libudevdir)
- $(INSTALL_PROGRAM) -d $(DESTDIR)/etc/udev/rules.d
- $(INSTALL_PROGRAM) -m 644 kpartx.rules $(DESTDIR)/etc/udev/rules.d/
+ $(INSTALL_PROGRAM) -d $(DESTDIR)$(libudevdir)/rules.d
+ $(INSTALL_PROGRAM) -m 644 kpartx.rules $(DESTDIR)$(libudevdir)/rules.d/66-kpartx.rules
$(INSTALL_PROGRAM) -d $(DESTDIR)$(mandir)
$(INSTALL_PROGRAM) -m 644 $(EXEC).8.gz $(DESTDIR)$(mandir)
rm -f $(DESTDIR)$(bindir)/$(EXEC)
rm -f $(DESTDIR)$(mandir)/$(EXEC).8.gz
rm -f $(DESTDIR)$(libudevdir)/kpartx_id
+ rm -f $(DESTDIR)$(libudevdir)/rules.d/66-kpartx.rules
+ rm -f $(DESTDIR)$(libudevdir)/rules.d/67-kpartx-compat.rules
clean:
rm -f core *.o $(EXEC) *.gz
return sectors * (blocksize >> 9);
}
+/*
+ * Magic records per track calculation, copied from fdasd.c
+ */
+static unsigned int ceil_quot(unsigned int d1, unsigned int d2)
+{
+ return (d1 + (d2 - 1)) / d2;
+}
+
+unsigned int recs_per_track(unsigned int dl)
+{
+ int dn = ceil_quot(dl + 6, 232) + 1;
+ return 1729 / (10 + 9 + ceil_quot(dl + 6 * dn, 34));
+}
+
+
typedef unsigned int __attribute__((__may_alias__)) label_ints_t;
/*
}
if (ioctl(fd_dasd, BIODASDINFO, (unsigned long)&info) != 0) {
- goto out;
+ info.label_block = 2;
+ info.FBA_layout = 0;
+ memcpy(info.type, "ECKD", sizeof(info.type));
}
- if (ioctl(fd_dasd, HDIO_GETGEO, (unsigned long)&geo) != 0) {
+ if (ioctl(fd_dasd, BLKSSZGET, &blocksize) != 0)
goto out;
- }
if (ioctl(fd_dasd, BLKGETSIZE64, &disksize) != 0)
goto out;
- disksize >>= 9;
- if (ioctl(fd_dasd, BLKSSZGET, &blocksize) != 0)
- goto out;
+ if (ioctl(fd_dasd, HDIO_GETGEO, (unsigned long)&geo) != 0) {
+ unsigned int cyl;
+
+ geo.heads = 15;
+ geo.sectors = recs_per_track(blocksize);
+ cyl = disksize / (blocksize * geo.heads * geo.sectors);
+ if (cyl < LV_COMPAT_CYL)
+ geo.cylinders = cyl;
+ else
+ geo.cylinders = LV_COMPAT_CYL;
+ geo.start = 0;
+ }
+
+ disksize >>= 9;
if (blocksize < 512 || blocksize > 4096)
goto out;
#define BLKGETSIZE _IO(0x12,96)
#define BLKSSZGET _IO(0x12,104)
#define BLKGETSIZE64 _IOR(0x12,114,size_t) /* device size in bytes (u64 *arg)*/
+#define LV_COMPAT_CYL 0xFFFE
/*
* Only compile this on S/390. Doesn't make any sense
OPTIONS="link_priority=50"
-# Create persistent links for multipath tables
-ENV{DM_UUID}=="mpath-*", \
+ENV{DM_UUID}=="?*", ENV{DM_TYPE}=="?*" \
SYMLINK+="disk/by-id/$env{DM_TYPE}-$env{DM_NAME}"
-ENV{DM_MPATH}=="?*", ENV{DM_PART}!="?*", \
- SYMLINK+="disk/by-id/$env{DM_TYPE}-$env{DM_MPATH}"
+
+# Create persistent links for multipath tables
ENV{DM_WWN}=="?*", ENV{DM_PART}!="?*", \
SYMLINK+="disk/by-id/wwn-$env{DM_WWN}"
# Create persistent links for partitions
-ENV{DM_PART}=="?*", \
- SYMLINK+="disk/by-id/$env{DM_TYPE}-$env{DM_NAME}-part$env{DM_PART}"
-ENV{DM_MPATH}=="?*", ENV{DM_PART}=="?*", \
- SYMLINK+="disk/by-id/$env{DM_TYPE}-$env{DM_MPATH}-part$env{DM_PART}"
ENV{DM_WWN}=="?*", ENV{DM_PART}=="?*", \
SYMLINK+="disk/by-id/wwn-$env{DM_WWN}-part$env{DM_PART}"
# Set the name of the table. We're only interested in dmraid,
# multipath, and kpartx tables; everything else is ignored.
if [ "$dmtbl" = "part" ] ; then
- # The name of the kpartx table is the name of the parent table
dmname=$($DMSETUP info -c --noheadings -o name -u $dmuuid)
- echo "DM_NAME=$dmname"
- if [ "$dmname" != ${dmuuid#mpath-} ] ; then
- echo "DM_MPATH=${dmuuid#mpath-}"
- fi
+ echo "DM_MPATH=$dmname"
# We need the dependencies of the parent table to figure out
# the type if the parent is a multipath table
case "$dmuuid" in
;;
esac
elif [ "$dmtbl" = "mpath" ] ; then
- if [ -n "$DM_NAME" -a "$DM_NAME" != "$dmuuid" ] ; then
- echo "DM_MPATH=$dmuuid"
- fi
dmname="$dmuuid"
# We need the dependencies of the table to figure out the type
dmdeps=$($DMSETUP deps -u $UUID)
-elif [ "$dmtbl" = "dmraid" ] ; then
- dmname=$tblname
fi
[ -n "$dmpart" ] && echo "DM_PART=$dmpart"
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/mman.h>
+#include <sys/types.h>
+#include <dirent.h>
#include <sysmacros.h>
-#include <asm/posix_types.h>
#include <linux/loop.h>
#include "lopart.h"
extern char *
find_loop_by_file (const char * filename)
{
- char dev[64];
- char *loop_formats[] = { "/dev/loop%d", "/dev/loop/%d" };
- int i, j, fd;
+ DIR *dir;
+ struct dirent *dent;
+ char dev[64], *found = NULL;
+ int fd;
struct stat statbuf;
struct loop_info loopinfo;
- dev_t file_dev;
- ino_t file_ino;
- if (stat (filename, &statbuf) != 0) {
+ dir = opendir("/dev");
+ if (!dir)
return NULL;
- }
- file_dev = statbuf.st_dev;
- file_ino = statbuf.st_ino;
-
- for (j = 0; j < SIZE(loop_formats); j++) {
-
- for (i = 0; i < 256; i++) {
- sprintf (dev, loop_formats[j], i);
-
- if (stat (dev, &statbuf) != 0 ||
- !S_ISBLK(statbuf.st_mode))
- continue;
-
- fd = open (dev, O_RDONLY);
- if (fd < 0)
- break;
+ while ((dent = readdir(dir)) != NULL) {
+ if (strncmp(dent->d_name,"loop",4))
+ continue;
+ if (!strcmp(dent->d_name, "loop-control"))
+ continue;
+ sprintf(dev, "/dev/%s", dent->d_name);
- if (ioctl (fd, LOOP_GET_STATUS, &loopinfo) != 0) {
- close (fd);
- continue;
- }
+ if (stat (dev, &statbuf) != 0 ||
+ !S_ISBLK(statbuf.st_mode))
+ continue;
- if (loopinfo.lo_device == file_dev && loopinfo.lo_inode == file_ino) {
- close (fd);
- return xstrdup(dev); /*found */
- }
+ fd = open (dev, O_RDONLY);
+ if (fd < 0)
+ break;
+ if (ioctl (fd, LOOP_GET_STATUS, &loopinfo) != 0) {
close (fd);
continue;
}
+
+ if (0 == strcmp(filename, loopinfo.lo_name)) {
+ close (fd);
+ found = xstrdup(dev);
+ break;
+ }
+
+ close (fd);
}
- return NULL;
+ closedir(dir);
+ return found;
}
extern char *
find_unused_loop_device (void)
{
- /* Just creating a device, say in /tmp, is probably a bad idea -
- people might have problems with backup or so.
- So, we just try /dev/loop[0-7]. */
-
- char dev[20];
- char *loop_formats[] = { "/dev/loop%d", "/dev/loop/%d" };
- int i, j, fd, first = 0, somedev = 0, someloop = 0, loop_known = 0;
+ char dev[20], *next_loop_dev = NULL;;
+ int fd, next_loop = 0, somedev = 0, someloop = 0, loop_known = 0;
struct stat statbuf;
struct loop_info loopinfo;
FILE *procdev;
- if (stat("/dev/loop-control", &statbuf) == 0 &&
- S_ISCHR(statbuf.st_mode)) {
- fd = open("/dev/loop-control", O_RDWR);
- if (fd >= 0)
- first = ioctl(fd, LOOP_CTL_GET_FREE);
- close(fd);
- if (first < 0)
- first = 0;
- }
- for (j = 0; j < SIZE(loop_formats); j++) {
+ while (next_loop_dev == NULL) {
+ if (stat("/dev/loop-control", &statbuf) == 0 &&
+ S_ISCHR(statbuf.st_mode)) {
+ fd = open("/dev/loop-control", O_RDWR);
+ if (fd < 0)
+ return NULL;
+ next_loop = ioctl(fd, LOOP_CTL_GET_FREE);
+ if (next_loop < 0)
+ return NULL;
+ close(fd);
+ }
- for(i = first; i < 256; i++) {
- sprintf(dev, loop_formats[j], i);
+ sprintf(dev, "/dev/loop%d", next_loop);
if (stat (dev, &statbuf) == 0 && S_ISBLK(statbuf.st_mode)) {
somedev++;
else if (errno == ENXIO) {
close (fd);
- return xstrdup(dev);/* probably free */
+ next_loop_dev = xstrdup(dev);
}
close (fd);
}
-
+
/* continue trying as long as devices exist */
continue;
}
break;
- }
}
+ if (next_loop_dev)
+ return next_loop_dev;
/* Nothing found. Why not? */
if ((procdev = fopen(PROC_DEVICES, "r")) != NULL) {
fprintf(stderr, "mount: could not find any device /dev/loop#");
else if (!someloop) {
-
if (loop_known == 1)
fprintf(stderr,
"mount: Could not find any loop device.\n"
" Maybe /dev/loop# has a wrong major number?");
-
else if (loop_known == -1)
fprintf(stderr,
"mount: Could not find any loop device, and, according to %s,\n"
} else
fprintf(stderr, "mount: could not find any free loop device");
-
return 0;
}
uninstall:
rm -f $(DESTDIR)$(syslibdir)/$(LIBS)
- rm $(DESTDIR)$(man3dir)/mpath_persistent_reserve_in.3.gz
- rm $(DESTDIR)$(man3dir)/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
}
pos++;
- if (!*pos)
+ if (!*pos) {
+ condlog(3, "formatted callout = %s", dst);
return 0;
+ }
len = strlen(pos) + 1;
myfree -= len;
return 1;
snprintf(p, len, "%s", pos);
- condlog(3, "reformated callout = %s", dst);
+ condlog(3, "reformatted callout = %s", dst);
return 0;
}
int delay_watch_checks;
int delay_wait_checks;
int uxsock_timeout;
+ int strict_timing;
int retrigger_tries;
int retrigger_delay;
int ignore_new_devs;
strncpy(mpp->alias_old, cmpp->alias, WWID_SIZE);
mpp->action = ACT_RENAME;
if (force_reload)
- mpp->action = ACT_RENAME2;
+ mpp->action = ACT_FORCERENAME;
return;
}
mpp->action = ACT_CREATE;
if (!pgp->paths)
continue;
vector_foreach_slot(pgp->paths, pp, j) {
- if (lock && flock(pp->fd, LOCK_EX | LOCK_NB) &&
+ if (lock && flock(pp->fd, LOCK_SH | LOCK_NB) &&
errno == EWOULDBLOCK)
goto fail;
else if (!lock)
break;
case ACT_RELOAD:
- r = dm_addmap_reload(mpp, params);
- if (r)
- r = dm_simplecmd_noflush(DM_DEVICE_RESUME, mpp->alias,
- 0, MPATH_UDEV_RELOAD_FLAG);
+ r = dm_addmap_reload(mpp, params, 0);
break;
case ACT_RESIZE:
- r = dm_addmap_reload(mpp, params);
- if (r)
- r = dm_simplecmd_flush(DM_DEVICE_RESUME, mpp->alias, 1, 0);
+ r = dm_addmap_reload(mpp, params, 1);
break;
case ACT_RENAME:
r = dm_rename(mpp->alias_old, mpp->alias);
break;
- case ACT_RENAME2:
+ case ACT_FORCERENAME:
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);
- }
+ if (r)
+ r = dm_addmap_reload(mpp, params, 0);
break;
default:
return ret;
}
}
- if (pp->udev && filter_property(conf, pp->udev) > 0)
+ if (pp->udev && pp->uid_attribute &&
+ filter_property(conf, pp->udev) > 0)
return 2;
refwwid = pp->wwid;
if (dev_type == DEV_DEVT) {
strchop(dev);
- pp = find_path_by_devt(pathvec, dev);
+ if (devt2devname(buff, FILE_NAME_SIZE, dev)) {
+ condlog(0, "%s: cannot find block device\n", dev);
+ return 1;
+ }
+ pp = find_path_by_dev(pathvec, buff);
if (!pp) {
struct udev_device *udevice = udev_device_new_from_devnum(conf->udev, 'b', parse_devt(dev));
return ret;
}
}
- if (pp->udev && filter_property(conf, pp->udev) > 0)
+ if (pp->udev && pp->uid_attribute &&
+ filter_property(conf, pp->udev) > 0)
return 2;
refwwid = pp->wwid;
dev);
return ret;
}
- if (pp->udev && filter_property(conf, pp->udev) > 0)
+ if (pp->udev && pp->uid_attribute &&
+ filter_property(conf, pp->udev) > 0)
return 2;
refwwid = pp->wwid;
ACT_RENAME,
ACT_CREATE,
ACT_RESIZE,
- ACT_RENAME2,
+ ACT_FORCERENAME,
};
#define FLUSH_ONE 1
#define DEFAULT_REASSIGN_MAPS 0
#define DEFAULT_FIND_MULTIPATHS 0
#define DEFAULT_FAST_IO_FAIL 5
+#define DEFAULT_DEV_LOSS_TMO 600
#define DEFAULT_RETAIN_HWHANDLER RETAIN_HWHANDLER_OFF
#define DEFAULT_DETECT_PRIO DETECT_PRIO_OFF
#define DEFAULT_DEFERRED_REMOVE DEFERRED_REMOVE_OFF
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);
+ if (udev_wait_flag &&
+ !dm_task_set_cookie(dmt, &cookie,
+ DM_UDEV_DISABLE_LIBRARY_FALLBACK | udev_flags))
goto out;
- }
+
r = dm_task_run (dmt);
if (udev_wait_flag) {
}
extern int
-dm_simplecmd_flush (int task, const char *name, int needsync, uint16_t udev_flags) {
- return dm_simplecmd(task, name, 0, needsync, udev_flags, 0);
+dm_simplecmd_flush (int task, const char *name, uint16_t udev_flags) {
+ return dm_simplecmd(task, name, 0, 1, udev_flags, 0);
}
extern int
-dm_simplecmd_noflush (int task, const char *name, int needsync, uint16_t udev_flags) {
- return dm_simplecmd(task, name, 1, needsync, udev_flags, 0);
+dm_simplecmd_noflush (int task, const char *name, uint16_t udev_flags) {
+ return dm_simplecmd(task, name, 1, 1, udev_flags, 0);
}
static int
deferred_remove);
}
-extern int
-dm_addmap (int task, const char *target, struct multipath *mpp, char * params,
- int use_uuid, int ro) {
+static int
+dm_addmap (int task, const char *target, struct multipath *mpp,
+ char * params, int ro) {
int r = 0;
struct dm_task *dmt;
char *prefixed_uuid = NULL;
if (ro)
dm_task_set_ro(dmt);
- if (use_uuid && strlen(mpp->wwid) > 0){
- prefixed_uuid = MALLOC(UUID_PREFIX_LEN + strlen(mpp->wwid) + 1);
- if (!prefixed_uuid) {
- condlog(0, "cannot create prefixed uuid : %s",
- strerror(errno));
- goto addout;
+ if (task == DM_DEVICE_CREATE) {
+ if (strlen(mpp->wwid) > 0) {
+ prefixed_uuid = MALLOC(UUID_PREFIX_LEN +
+ strlen(mpp->wwid) + 1);
+ if (!prefixed_uuid) {
+ condlog(0, "cannot create prefixed uuid : %s",
+ strerror(errno));
+ goto addout;
+ }
+ sprintf(prefixed_uuid, UUID_PREFIX "%s", mpp->wwid);
+ if (!dm_task_set_uuid(dmt, prefixed_uuid))
+ goto freeout;
}
- sprintf(prefixed_uuid, UUID_PREFIX "%s", mpp->wwid);
- if (!dm_task_set_uuid(dmt, prefixed_uuid))
- goto freeout;
+ dm_task_skip_lockfs(dmt);
+#ifdef LIBDM_API_FLUSH
+ dm_task_no_flush(dmt);
+#endif
}
if (mpp->attribute_flags & (1 << ATTR_MODE) &&
if (mpp->attribute_flags & (1 << ATTR_GID) &&
!dm_task_set_gid(dmt, mpp->gid))
goto freeout;
- condlog(4, "%s: addmap [0 %llu %s %s]", mpp->alias, mpp->size,
+ condlog(4, "%s: %s [0 %llu %s %s]", mpp->alias,
+ task == DM_DEVICE_RELOAD ? "reload" : "addmap", mpp->size,
target, params);
dm_task_no_open_count(dmt);
if (task == DM_DEVICE_CREATE &&
- !dm_task_set_cookie(dmt, &cookie, (conf->daemon)? DM_UDEV_DISABLE_LIBRARY_FALLBACK : 0)) {
- dm_udev_complete(cookie);
+ !dm_task_set_cookie(dmt, &cookie,
+ DM_UDEV_DISABLE_LIBRARY_FALLBACK))
goto freeout;
- }
+
r = dm_task_run (dmt);
if (task == DM_DEVICE_CREATE) {
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, ro))
return 1;
/*
* DM_DEVICE_CREATE is actually DM_DEV_CREATE + DM_TABLE_LOAD.
#define ADDMAP_RO 1
extern int
-dm_addmap_reload (struct multipath *mpp, char *params) {
- if (dm_addmap(DM_DEVICE_RELOAD, TGT_MPATH, mpp, params, 0, ADDMAP_RW))
- return 1;
- if (errno != EROFS)
- return 0;
- return dm_addmap(DM_DEVICE_RELOAD, TGT_MPATH, mpp, params, 0, ADDMAP_RO);
+dm_addmap_reload (struct multipath *mpp, char *params, int flush)
+{
+ int r;
+ uint16_t udev_flags = flush ? 0 : MPATH_UDEV_RELOAD_FLAG;
+
+ /*
+ * DM_DEVICE_RELOAD cannot wait on a cookie, as
+ * the cookie will only ever be released after an
+ * DM_DEVICE_RESUME. So call DM_DEVICE_RESUME
+ * after each successful call to DM_DEVICE_RELOAD.
+ */
+ r = dm_addmap(DM_DEVICE_RELOAD, TGT_MPATH, mpp, params, ADDMAP_RW);
+ if (!r) {
+ if (errno != EROFS)
+ return 0;
+ r = dm_addmap(DM_DEVICE_RELOAD, TGT_MPATH, mpp,
+ params, ADDMAP_RO);
+ }
+ if (r)
+ r = dm_simplecmd(DM_DEVICE_RESUME, mpp->alias, flush,
+ 1, udev_flags, 0);
+ return r;
}
extern int
if (s)
queue_if_no_path = 0;
else
- s = dm_simplecmd_flush(DM_DEVICE_SUSPEND, mapname, 1, 0);
+ s = dm_simplecmd_flush(DM_DEVICE_SUSPEND, mapname, 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, 1, 0);
+ dm_simplecmd_noflush(DM_DEVICE_RESUME, mapname, 0);
if (queue_if_no_path)
s = dm_queue_if_no_path((char *)mapname, 1);
return 1;
dm_task_no_open_count(dmt);
- if (!dm_task_set_cookie(dmt, &cookie, (conf->daemon)? DM_UDEV_DISABLE_LIBRARY_FALLBACK : 0))
+ if (!dm_task_set_cookie(dmt, &cookie,
+ DM_UDEV_DISABLE_LIBRARY_FALLBACK))
goto out;
r = dm_task_run(dmt);
condlog(3, "%s: failed to reassign targets", name);
goto out_reload;
}
- dm_simplecmd_noflush(DM_DEVICE_RESUME, name, 1, MPATH_UDEV_RELOAD_FLAG);
+ dm_simplecmd_noflush(DM_DEVICE_RESUME, name,
+ MPATH_UDEV_RELOAD_FLAG);
}
r = 1;
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, uint16_t);
-int dm_simplecmd_noflush (int, const char *, int, uint16_t);
+int dm_simplecmd_flush (int, const char *, uint16_t);
+int dm_simplecmd_noflush (int, const char *, uint16_t);
int dm_addmap_create (struct multipath *mpp, char *params);
-int dm_addmap_reload (struct multipath *mpp, char *params);
+int dm_addmap_reload (struct multipath *mpp, char *params, int flush);
int dm_map_present (const char *);
int dm_get_map(const char *, unsigned long long *, char *);
int dm_get_status(char *, char *);
declare_def_handler(uev_wait_timeout, set_int)
declare_def_snprint(uev_wait_timeout, print_int)
+declare_def_handler(strict_timing, set_yes_no)
+declare_def_snprint(strict_timing, print_yes_no)
+
static int
def_config_dir_handler(vector strvec)
{
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("strict_timing", &def_strict_timing_handler, &snprint_def_strict_timing);
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);
#include <stdio.h>
#include <ctype.h>
#include <unistd.h>
+#include <limits.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
return -ENOMEM;
udev_enumerate_add_match_subsystem(udev_iter, "block");
+ udev_enumerate_add_match_is_initialized(udev_iter);
udev_enumerate_scan_devices(udev_iter);
udev_list_entry_foreach(entry,
const char *attr = NULL;
const char *subsys;
struct udev_device *parent;
- int r;
- unsigned int t;
+ char *eptr;
+ unsigned long t;
if (!pp->udev || pp->bus != SYSFS_BUS_SCSI)
return -ENOSYS;
return -ENXIO;
}
- r = sscanf(attr, "%u\n", &t);
-
- if (r != 1) {
+ t = strtoul(attr, &eptr, 0);
+ if (attr == eptr || t == ULONG_MAX) {
condlog(3, "%s: Cannot parse timeout attribute '%s'",
pp->dev, attr);
return -EINVAL;
}
-
+ if (t > UINT_MAX) {
+ condlog(3, "%s: Overflow in timeout value '%s'",
+ pp->dev, attr);
+ return -ERANGE;
+ }
*timeout = t;
return 0;
sysfs_set_rport_tmo(struct multipath *mpp, struct path *pp)
{
struct udev_device *rport_dev = NULL;
- char value[16];
+ char value[16], *eptr;
char rport_id[32];
unsigned long long tmo = 0;
int ret;
pp->sg_id.channel, pp->sg_id.scsi_id, rport_id);
/*
+ * read the current dev_loss_tmo value from sysfs
+ */
+ ret = sysfs_attr_get_value(rport_dev, "dev_loss_tmo", value, 16);
+ if (ret <= 0) {
+ condlog(0, "%s: failed to read dev_loss_tmo value, "
+ "error %d", rport_id, -ret);
+ goto out;
+ }
+ tmo = strtoull(value, &eptr, 0);
+ if (value == eptr || tmo == ULLONG_MAX) {
+ condlog(0, "%s: Cannot parse dev_loss_tmo "
+ "attribute '%s'", rport_id, value);
+ goto out;
+ }
+
+ /*
* This is tricky.
* dev_loss_tmo will be limited to 600 if fast_io_fail
* is _not_ set.
* then set fast_io_fail, and _then_ set dev_loss_tmo
* to the correct value.
*/
- memset(value, 0, 16);
if (mpp->fast_io_fail != MP_FAST_IO_FAIL_UNSET &&
mpp->fast_io_fail != MP_FAST_IO_FAIL_ZERO &&
mpp->fast_io_fail != MP_FAST_IO_FAIL_OFF) {
/* Check if we need to temporarily increase dev_loss_tmo */
- ret = sysfs_attr_get_value(rport_dev, "dev_loss_tmo",
- value, 16);
- if (ret <= 0) {
- condlog(0, "%s: failed to read dev_loss_tmo value, "
- "error %d", rport_id, -ret);
- goto out;
- }
- if (sscanf(value, "%llu\n", &tmo) != 1) {
- condlog(0, "%s: Cannot parse dev_loss_tmo "
- "attribute '%s'", rport_id, value);
- goto out;
- }
if (mpp->fast_io_fail >= tmo) {
+ /* Increase dev_loss_tmo temporarily */
snprintf(value, 16, "%u", mpp->fast_io_fail + 1);
+ ret = sysfs_attr_set_value(rport_dev, "dev_loss_tmo",
+ value, strlen(value));
+ if (ret <= 0) {
+ if (ret == -EBUSY)
+ condlog(3, "%s: rport blocked",
+ rport_id);
+ else
+ condlog(0, "%s: failed to set "
+ "dev_loss_tmo to %s, error %d",
+ rport_id, value, -ret);
+ goto out;
+ }
}
- } else if (mpp->dev_loss > 600) {
- condlog(3, "%s: limiting dev_loss_tmo to 600, since "
- "fast_io_fail is not set", rport_id);
- snprintf(value, 16, "%u", 600);
- } else {
- snprintf(value, 16, "%u", mpp->dev_loss);
- }
- if (strlen(value)) {
- ret = sysfs_attr_set_value(rport_dev, "dev_loss_tmo",
- value, strlen(value));
- if (ret <= 0) {
- if (ret == -EBUSY)
- condlog(3, "%s: rport blocked", rport_id);
- else
- condlog(0, "%s: failed to set dev_loss_tmo to %s, error %d",
- rport_id, value, -ret);
- goto out;
- }
+ } else if (mpp->dev_loss > DEFAULT_DEV_LOSS_TMO) {
+ condlog(3, "%s: limiting dev_loss_tmo to %d, since "
+ "fast_io_fail is not set",
+ rport_id, DEFAULT_DEV_LOSS_TMO);
+ mpp->dev_loss = DEFAULT_DEV_LOSS_TMO;
}
if (mpp->fast_io_fail != MP_FAST_IO_FAIL_UNSET) {
if (mpp->fast_io_fail == MP_FAST_IO_FAIL_OFF)
rport_id, value, -ret);
}
}
- if (tmo > 0) {
+ if (mpp->dev_loss > 0) {
snprintf(value, 16, "%u", mpp->dev_loss);
ret = sysfs_attr_set_value(rport_dev, "dev_loss_tmo",
value, strlen(value));
no_path_retry_tmo = MAX_DEV_LOSS_TMO;
if (no_path_retry_tmo > dev_loss_tmo)
dev_loss_tmo = no_path_retry_tmo;
- condlog(3, "%s: update dev_loss_tmo to %d",
+ condlog(3, "%s: update dev_loss_tmo to %u",
mpp->alias, dev_loss_tmo);
} else if (mpp->no_path_retry == NO_PATH_RETRY_QUEUE) {
dev_loss_tmo = MAX_DEV_LOSS_TMO;
- condlog(3, "%s: update dev_loss_tmo to %d",
+ condlog(3, "%s: update dev_loss_tmo to %u",
mpp->alias, dev_loss_tmo);
}
mpp->dev_loss = dev_loss_tmo;
condlog(3, "%s: product = %s", pp->dev, pp->product_id);
- if (sysfs_get_rev(parent, pp->rev, SCSI_REV_SIZE) <= 0)
+ if (sysfs_get_rev(parent, pp->rev, SCSI_REV_SIZE) < 0)
return 1;
condlog(3, "%s: rev = %s", pp->dev, pp->rev);
}
static int
-get_uid (struct path * pp)
+get_uid (struct path * pp, int path_state)
{
char *c;
const char *origin = "unknown";
/* Use 'getuid' callout, deprecated */
condlog(1, "%s: using deprecated getuid callout", pp->dev);
- if (apply_format(pp->getuid, &buff[0], pp)) {
+ if (path_state != PATH_UP) {
+ condlog(3, "%s: path inaccessible", pp->dev);
+ len = -EWOULDBLOCK;
+ } else 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);
len = get_vpd_uid(pp);
origin = "sysfs";
pp->uid_attribute = NULL;
- if (len < 0) {
+ if (len < 0 && path_state == PATH_UP) {
condlog(1, "%s: failed to get sysfs uid: %s",
pp->dev, strerror(-len));
len = get_vpd_sgio(pp->fd, 0x83, pp->wwid,
condlog(3, "%s: mask = 0x%x", pp->dev, mask);
/*
+ * Sanity check: we need the device number to
+ * avoid inconsistent information in
+ * find_path_by_dev()/find_path_by_devt()
+ */
+ if (!strlen(pp->dev_t) && !(mask & DI_SYSFS)) {
+ condlog(1, "%s: empty device number", pp->dev);
+ mask |= DI_SYSFS;
+ }
+
+ /*
* fetch info available in sysfs
*/
if (mask & DI_SYSFS && sysfs_pathinfo(pp))
}
if ((mask & DI_WWID) && !strlen(pp->wwid)) {
- get_uid(pp);
+ get_uid(pp, path_state);
if (!strlen(pp->wwid)) {
pp->initialized = INIT_MISSING_UDEV;
pp->tick = conf->retrigger_delay;
FREE(word);
for (j = 0; j < num_paths; j++) {
+ char devname[FILE_NAME_SIZE];
+
pp = NULL;
p += get_word(p, &word);
if (!word)
goto out;
+ if (devt2devname(devname, FILE_NAME_SIZE, word)) {
+ condlog(2, "%s: cannot find block device",
+ word);
+ FREE(word);
+ continue;
+ }
+
if (pathvec)
- pp = find_path_by_devt(pathvec, word);
+ pp = find_path_by_dev(pathvec, devname);
if (!pp) {
pp = alloc_path();
goto out1;
strncpy(pp->dev_t, word, BLK_DEV_SIZE);
-
+ strncpy(pp->dev, devname, FILE_NAME_SIZE);
+ if (strlen(mpp->wwid)) {
+ strncpy(pp->wwid, mpp->wwid, WWID_SIZE);
+ }
/* Only call this in multipath client mode */
if (!conf->daemon && store_path(pathvec, pp))
goto out1;
+ } else {
+ if (!strlen(pp->wwid) &&
+ strlen(mpp->wwid))
+ strncpy(pp->wwid, mpp->wwid, WWID_SIZE);
}
FREE(word);
.prio_name = PRIO_ALUA,
.prio_args = NULL,
},
+ {
+ /* HP MSA 1040/2040 product family */
+ .vendor = "HP",
+ .product = "MSA (1|2)040 SA(N|S)",
+ .features = DEFAULT_FEATURES,
+ .hwhandler = DEFAULT_HWHANDLER,
+ .pgpolicy = GROUP_BY_PRIO,
+ .pgfailback = -FAILBACK_IMMEDIATE,
+ .rr_weight = RR_WEIGHT_NONE,
+ .no_path_retry = 18,
+ .minio = 100,
+ .checker_name = TUR,
+ .prio_name = PRIO_ALUA,
+ .prio_args = NULL,
+ },
{
/* HP SVSP */
.prio_args = NULL,
},
{
+ .vendor = "(LIO-ORG|SUSE)",
+ .product = "RBD",
+ .features = DEFAULT_FEATURES,
+ .hwhandler = "1 alua",
+ .pgpolicy = GROUP_BY_PRIO,
+ .pgfailback = -FAILBACK_IMMEDIATE,
+ .rr_weight = RR_WEIGHT_NONE,
+ .no_path_retry = 12,
+ .checker_name = TUR,
+ .minio = 100,
+ .prio_name = PRIO_ALUA,
+ .prio_args = NULL,
+ },
+ {
.vendor = "DataCore",
.product = "SANmelody",
.features = DEFAULT_FEATURES,
a.depth--; pthread_mutex_unlock(a.mutex)
#define lock_cleanup_pop(a) \
fprintf(stderr, "%s:%s(%i) unlock %p depth: %d (%ld)\n", __FILE__, __FUNCTION__, __LINE__, a.mutex, a.depth, pthread_self()); \
- pthread_cleanup_pop(1);
+ pthread_cleanup_pop(1)
#else
#define lock(a) a.depth++; pthread_mutex_lock(a.mutex)
#define unlock(a) a.depth--; pthread_mutex_unlock(a.mutex)
-#define lock_cleanup_pop(a) pthread_cleanup_pop(1);
+#define lock_cleanup_pop(a) pthread_cleanup_pop(1)
#endif
void cleanup_lock (void * data);
else if (mpp->no_path_retry == NO_PATH_RETRY_UNDEF)
return snprintf(buff, len, "-");
else if (mpp->no_path_retry > 0) {
- if (mpp->retry_tick)
+ if (mpp->retry_tick > 0)
return snprintf(buff, len, "%i sec",
mpp->retry_tick);
- else
+ else if (mpp->retry_tick == 0 && mpp->nr_active > 0)
return snprintf(buff, len, "%i chk",
mpp->no_path_retry);
+ else
+ return snprintf(buff, len, "off");
}
return 0;
}
pp->sg_id.channel = -1;
pp->sg_id.scsi_id = -1;
pp->sg_id.lun = -1;
+ pp->sg_id.proto_id = SCSI_PROTOCOL_UNSPEC;
pp->fd = -1;
pp->priority = PRIO_UNDEF;
}
int
store_path (vector pathvec, struct path * pp)
{
+ int err = 0;
+
+ if (!strlen(pp->dev_t)) {
+ condlog(2, "%s: Empty device number", pp->dev);
+ err++;
+ }
+ if (!strlen(pp->dev)) {
+ condlog(2, "%s: Empty device name", pp->dev_t);
+ err++;
+ }
+
+ if (err > 1)
+ return 1;
+
if (!vector_alloc_slot(pathvec))
return 1;
if (!strcmp(pp->dev, dev))
return pp;
- condlog(3, "%s: not found in pathvec", dev);
+ condlog(4, "%s: dev not found in pathvec", dev);
return NULL;
}
if (!strcmp(pp->dev_t, dev_t))
return pp;
- condlog(3, "%s: not found in pathvec", dev_t);
+ condlog(4, "%s: dev_t not found in pathvec", dev_t);
return NULL;
}
}
extern int
-adopt_paths (vector pathvec, struct multipath * mpp, int get_info)
+adopt_paths (vector pathvec, struct multipath * mpp)
{
int i;
struct path * pp;
if (!find_path_by_dev(mpp->paths, pp->dev) &&
store_path(mpp->paths, pp))
return 1;
- if (get_info && pathinfo(pp, conf->hwtable,
- DI_PRIO | DI_CHECKER))
+ if (pathinfo(pp, conf->hwtable,
+ DI_PRIO | DI_CHECKER))
return 1;
}
}
if (setup_multipath(vecs, mpp))
return NULL; /* mpp freed in setup_multipath */
- if (adopt_paths(vecs->pathvec, mpp, 1))
+ if (adopt_paths(vecs->pathvec, mpp))
goto out;
if (!vector_alloc_slot(vecs->mpvec))
goto out;
mpp->size = pp->size;
- if (adopt_paths(vecs->pathvec, mpp, 1))
+ if (adopt_paths(vecs->pathvec, mpp))
goto out;
if (add_vec) {
void set_no_path_retry(struct multipath *mpp);
-int adopt_paths (vector pathvec, struct multipath * mpp, int get_info);
+int adopt_paths (vector pathvec, struct multipath * mpp);
void orphan_paths (vector pathvec, struct multipath * mpp);
void orphan_path (struct path * pp, const char *reason);
}
pthread_sigmask(SIG_SETMASK, NULL, &mask);
- sigdelset(&mask, SIGHUP);
- sigdelset(&mask, SIGUSR1);
events = 0;
while (1) {
struct uevent *uev;
continue;
}
if (fdcount < 0) {
- if (errno != EINTR)
- condlog(0, "error receiving "
- "uevent message: %m");
+ if (errno == EINTR)
+ continue;
+
+ condlog(0, "error receiving "
+ "uevent message: %m");
err = -errno;
break;
}
#ifndef _VERSION_H
#define _VERSION_H
-#define VERSION_CODE 0x000600
-#define DATE_CODE 0x120410
+#define VERSION_CODE 0x000601
+#define DATE_CODE 0x050a10
#define PROG "multipath-tools"
+++ /dev/null
-##
-## This is a template multipath-tools configuration file
-## Uncomment the lines relevent to your environment
-##
-#
-##
-## name : defaults
-## desc : multipath-tools default settings
-##
-#defaults {
-# #
-# # name : polling_interval
-# # scope : multipathd
-# # desc : interval between two path checks in seconds. For
-# # properly functioning paths, the interval between checks
-# # will gradually increase to (4 * polling_interval).
-# # values : n > 0
-# # default : 5
-# #
-# polling_interval 10
-#
-# #
-# # name : path_selector
-# # scope : multipath & multipathd
-# # desc : the default path selector algorithm to use
-# # these algorithms are offered by the kernel multipath target
-# # values : "round-robin 0" = Loop through every path in the path group,
-# # sending the same amount of IO to each.
-# # "queue-length 0" = Send the next bunch of IO down the path
-# # with the least amount of outstanding IO.
-# # "service-time 0" = Choose the path for the next bunch of IO
-# # based on the amount of outstanding IO to
-# # the path and its relative throughput.
-# # default : "service-time 0"
-# #
-# path_selector "service-time 0"
-#
-# #
-# # name : path_grouping_policy
-# # scope : multipath & multipathd
-# # desc : the default path grouping policy to apply to unspecified
-# # multipaths
-# # values : failover = 1 path per priority group
-# # multibus = all valid paths in 1 priority group
-# # group_by_serial = 1 priority group per detected serial
-# # number
-# # group_by_prio = 1 priority group per path priority
-# # value
-# # group_by_node_name = 1 priority group per target node name
-# # default : failover
-# #
-# path_grouping_policy multibus
-#
-# #
-# # name : uid_attribute
-# # scope : multipath & multipathd
-# # desc : the default udev attribute from which the path
-# # identifier should be generated.
-# # default : ID_SERIAL
-# #
-# uid_attribute "ID_SERIAL"
-#
-# #
-# # name : getuid_callout
-# # scope : multipath & multipathd
-# # desc : the default program and args to callout to obtain a unique
-# # path identifier. This parameter is deprecated.
-# # This parameter is deprecated, superseded by uid_attribute
-# # default : /lib/udev/scsi_id --whitelisted --device=/dev/%n
-# #
-# getuid_callout "/lib/udev/scsi_id --whitelisted --device=/dev/%n"
-#
-# #
-# # name : prio
-# # scope : multipath & multipathd
-# # desc : the default function to call to obtain a path
-# # priority value. The ALUA bits in SPC-3 provide an
-# # exploitable prio value for example.
-# # default : const
-# #
-# prio "alua"
-#
-# #
-# # name : prio_args
-# # scope : multipath & multipathd
-# # desc : The arguments string passed to the prio function
-# # Most prio functions do not need arguments. The
-# # datacore prioritizer need one.
-# # default : (null)
-# #
-# prio_args "timeout=1000 preferredsds=foo"
-#
-# #
-# # name : features
-# # scope : multipath & multipathd
-# # desc : The default extra features of multipath devices.
-# # Syntax is "num[ feature_0 feature_1 ...]", where `num' is the
-# # number of features in the following (possibly empty) list of
-# # features.
-# # values : queue_if_no_path = Queue IO if no path is active; consider
-# # using the `no_path_retry' keyword instead.
-# # no_partitions = Disable automatic partitions generation via
-# # kpartx.
-# # default : "0"
-# #
-# features "0"
-# features "1 queue_if_no_path"
-# features "1 no_partitions"
-# features "2 queue_if_no_path no_partitions"
-#
-# #
-# # name : path_checker, checker
-# # scope : multipath & multipathd
-# # desc : the default method used to determine the paths' state
-# # values : readsector0|tur|emc_clariion|hp_sw|directio|rdac|cciss_tur
-# # default : directio
-# #
-# path_checker directio
-#
-# #
-# # name : rr_min_io
-# # scope : multipath & multipathd
-# # desc : the number of IO to route to a path before switching
-# # to the next in the same path group for the bio-based
-# # multipath implementation. This parameter is used for
-# # kernels version up to 2.6.31; newer kernel version
-# # use the parameter rr_min_io_rq
-# # default : 1000
-# #
-# rr_min_io 1000
-#
-# #
-# # name : rr_min_io_rq
-# # scope : multipath & multipathd
-# # desc : the number of IO to route to a path before switching
-# # to the next in the same path group for the request-based
-# # multipath implementation. This parameter is used for
-# # kernels versions later than 2.6.31.
-# # default : 1
-# #
-# rr_min_io_rq 1
-#
-# #
-# # name : flush_on_last_del
-# # scope : multipathd
-# # desc : If set to "yes", multipathd will disable queueing when the
-# # last path to a device has been deleted.
-# # values : yes|no
-# # default : no
-# #
-# flush_on_last_del yes
-#
-# #
-# # name : max_fds
-# # scope : multipathd
-# # desc : Sets the maximum number of open file descriptors for the
-# # multipathd process.
-# # values : max|n > 0
-# # default : None
-# #
-# max_fds 8192
-#
-# #
-# # name : rr_weight
-# # scope : multipath & multipathd
-# # desc : if set to priorities the multipath configurator will assign
-# # path weights as "path prio * rr_min_io"
-# # values : priorities|uniform
-# # default : uniform
-# #
-# rr_weight priorities
-#
-# #
-# # name : failback
-# # scope : multipathd
-# # desc : tell the daemon to manage path group failback, or not to.
-# # 0 means immediate failback, values >0 means deffered
-# # failback expressed in seconds.
-# # values : manual|immediate|n > 0
-# # default : manual
-# #
-# failback immediate
-#
-# #
-# # name : no_path_retry
-# # scope : multipath & multipathd
-# # desc : tell the number of retries until disable queueing, or
-# # "fail" means immediate failure (no queueing),
-# # "queue" means never stop queueing
-# # values : queue|fail|n (>0)
-# # default : (null)
-# #
-# no_path_retry queue
-#
-# #
-# # name : queue_without_daemon
-# # scope : multipathd
-# # desc : If set to "no", multipathd will disable queueing for all
-# # devices when it is shut down.
-# # values : yes|no
-# # default : yes
-# queue_without_daemon no
-#
-# #
-# # name : user_friendly_names
-# # scope : multipath & multipathd
-# # desc : If set to "yes", using the bindings file
-# # /etc/multipath/bindings to assign a persistent and
-# # unique alias to the multipath, in the form of mpath<n>.
-# # If set to "no" use the WWID as the alias. In either case
-# # this be will be overriden by any specific aliases in this
-# # file.
-# # values : yes|no
-# # default : no
-# user_friendly_names no
-#
-# #
-# # name : mode
-# # scope : multipath & multipathd
-# # desc : The mode to use for the multipath device nodes, in octal.
-# # values : 0000 - 0777
-# # default : determined by the process
-# mode 0644
-#
-# #
-# # name : uid
-# # scope : multipath & multipathd
-# # desc : The user id to use for the multipath device nodes. You
-# # may use either the numeric or symbolic uid
-# # values : <user_id>
-# # default : determined by the process
-# uid 0
-#
-# #
-# # name : gid
-# # scope : multipath & multipathd
-# # desc : The group id to user for the multipath device nodes. You
-# # may use either the numeric or symbolic gid
-# # values : <group_id>
-# # default : determined by the process
-# gid disk
-#
-# #
-# # name : checker_timeout
-# # scope : multipath & multipathd
-# # 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<x>/device/timeout
-# checker_timeout 60
-#
-# #
-# # name : fast_io_fail_tmo
-# # scope : multipath & multipathd
-# # desc : The number of seconds the scsi layer will wait after a
-# # problem has been detected on a FC remote port before failing
-# # IO to devices on that remote port.
-# # values : off | n >= 0 (smaller than dev_loss_tmo)
-# # default : determined by the OS
-# fast_io_fail_tmo 5
-#
-# #
-# # name : dev_loss_tmo
-# # scope : multipath & multipathd
-# # desc : The number of seconds the scsi layer will wait after a
-# # problem has been detected on a FC remote port before
-# # removing it from the system.
-# # values : infinity | n > 0
-# # 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
-# # the user_friendly_names option.
-# # values : <full_pathname>
-# # 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
-# # keep track of the created multipath devices.
-# # values : <full_pathname>
-# # default : "/var/lib/multipath/wwids"
-# wwids_file "/etc/multipath/wwids"
-#
-# #
-# # name : reservation_key
-# # scope : multipath
-# # desc : Service action reservation key used by mpathpersist.
-# # values : <key>
-# # 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|<n> > 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|<n> > 0
-# # default : no
-# delay_wait_checks 12
-#}
-#
-##
-## name : blacklist
-## scope : multipath & multipathd
-## desc : list of device names to discard as not multipath candidates
-## Devices can be identified by their device node name "devnode",
-## their WWID "wwid", or their vender and product strings
-## "device"
-## default : fd, hd, md, dm, sr, scd, st, ram, raw, loop, dcssblk
-##
-#blacklist {
-# wwid 26353900f02796769
-# devnode "^(ram|raw|loop|fd|md|dm-|sr|scd|st)[0-9]*"
-# devnode "^hd[a-z]"
-# devnode "^dcssblk[0-9]*"
-# device {
-# vendor DEC.*
-# product MSA[15]00
-# }
-#}
-##
-## name : blacklist_exceptions
-## scope : multipath & multipathd
-## desc : list of device names to be treated as multipath candidates
-## even if they are on the blacklist.
-## Note: blacklist exceptions are only valid in the same class.
-## It is not possible to blacklist devices using the devnode keyword
-## and to exclude some devices of them using the wwid keyword.
-## default : -
-##
-#blacklist_exceptions {
-# devnode "^dasd[c-d]+[0-9]*"
-# wwid "IBM.75000000092461.4d00.34"
-# wwid "IBM.75000000092461.4d00.35"
-# wwid "IBM.75000000092461.4d00.36"
-#}
-#
-##
-## name : multipaths
-## scope : multipath & multipathd
-## desc : list of multipaths finest-grained settings
-##
-#multipaths {
-# #
-# # name : multipath
-# # scope : multipath & multipathd
-# # desc : container for settings that apply to one specific multipath
-# #
-# multipath {
-# #
-# # name : wwid
-# # scope : multipath & multipathd
-# # desc : index of the container
-# #
-# wwid 3600508b4000156d700012000000b0000
-#
-# #
-# # name : alias
-# # scope : multipath & multipathd
-# # desc : symbolic name for the multipath. If you are using
-# # user_friendly_names, do not set the alias to
-# # mpath<n>. This may conflict with an automatically
-# # assigned user friendly name, and give you
-# # incorrect device node names.
-# #
-# alias yellow
-#
-# #
-# # name : path_grouping_policy
-# # scope : multipath & multipathd
-# # desc : path grouping policy to apply to this multipath
-# # values : failover, multibus, group_by_serial
-# # values : failover = 1 path per priority group
-# # multibus = all valid paths in 1 priority
-# # group
-# # group_by_serial = 1 priority group per detected
-# # serial number
-# # group_by_prio = 1 priority group per path
-# # priority value
-# # group_by_node_name = 1 priority group per target
-# # node name
-# #
-# path_grouping_policy failover
-#
-# #
-# # name : path_selector
-# # scope : multipath & multipathd
-# # desc : the path selector algorithm to use for this mpath
-# # these algo are offered by the kernel mpath target
-# # values : "round-robin 0"
-# #
-# path_selector "round-robin 0"
-#
-# #
-# # name : failback
-# # scope : multipathd
-# # desc : tell the daemon to manage path group failback, or
-# # not to. 0 means immediate failback, values >0 means
-# # deffered failback expressed in seconds.
-# # values : manual|immediate|n > 0
-# #
-# failback manual
-#
-# #
-# # name : rr_weight
-# # scope : multipath & multipathd
-# # desc : if set to priorities the multipath configurator will
-# # assign path weights as "path prio * rr_min_io"
-# # values : priorities|uniform
-# #
-# rr_weight priorities
-#
-# #
-# # name : no_path_retry
-# # scope : multipath & multipathd
-# # desc : tell the number of retries until disable queueing,
-# # or "fail" means immediate failure (no queueing),
-# # "queue" means never stop queueing
-# # values : queue|fail|n (>0)
-# #
-# no_path_retry queue
-#
-# #
-# # name : rr_min_io
-# # scope : multipath & multipathd
-# # desc : the number of IO to route to a path before switching
-# # to the next in the same path group
-# #
-# rr_min_io 100
-#
-# #
-# # name : flush_on_last_del
-# # scope : multipathd
-# # desc : If set to "yes", multipathd will disable queueing
-# # when the last path to a device has been deleted.
-# # values : yes|no
-# # default : no
-# #
-# flush_on_last_del yes
-#
-# #
-# # name : mode
-# # scope : multipath & multipathd
-# # desc : The mode to use for the multipath device nodes, in
-# # octal.
-# # values : 0000 - 0777
-# # default : determined by the process
-# mode 0644
-#
-# #
-# # name : uid
-# # scope : multipath & multipathd
-# # desc : The user id to use for the multipath device nodes.
-# # You may use either the numeric or symbolic uid
-# # values : <user_id>
-# # default : determined by the process
-# uid 0
-#
-# #
-# # name : gid
-# # scope : multipath & multipathd
-# # desc : The group id to user for the multipath device nodes.
-# # You may use either the numeric or symbolic gid
-# # values : <group_id>
-# # 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|<n> > 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|<n> > 0
-# delay_wait_checks 12
-# }
-# multipath {
-# wwid 1DEC_____321816758474
-# alias red
-# rr_weight priorities
-# }
-#}
-#
-##
-## name : devices
-## scope : multipath & multipathd
-## desc : list of per storage controller settings
-## overrides default settings (device_maps block)
-## overriden by per multipath settings (multipaths block)
-## and the overrides settings (overrides block)
-##
-#devices {
-# #
-# # name : device
-# # scope : multipath & multipathd
-# # desc : settings for this specific storage controller
-# #
-# device {
-# #
-# # name : vendor, product
-# # scope : multipath & multipathd
-# # desc : index for the block
-# #
-# vendor "COMPAQ "
-# product "HSV110 (C)COMPAQ"
-#
-# #
-# # name : path_grouping_policy
-# # scope : multipath & multipathd
-# # desc : path grouping policy to apply to this multipath
-# # values : failover, multibus, group_by_serial
-# # values : failover = 1 path per priority group
-# # multibus = all valid paths in 1 priority
-# # group
-# # group_by_serial = 1 priority group per detected
-# # serial number
-# # group_by_prio = 1 priority group per path
-# # priority value
-# # group_by_node_name = 1 priority group per target
-# # node name
-# #
-# path_grouping_policy failover
-#
-# #
-# # name : getuid_callout
-# # scope : multipath & multipathd
-# # desc : the program and args to callout to obtain a unique
-# # path identifier. Absolute path required
-# #
-# getuid_callout "/lib/udev/scsi_id --whitelisted --device=/dev/%n"
-#
-# #
-# # name : prio
-# # scope : multipath & multipathd
-# # desc : the function to call to obtain a path
-# # weight. Weights are summed for each path group to
-# # determine the next PG to use case of failure.
-# # default : no callout, all paths equals
-# #
-# prio "hp_sw"
-#
-# #
-# # name : prio_args
-# # scope : multipath & multipathd
-# # desc : The arguments string passed to the prio function
-# # Most prio functions do not need arguments. The
-# # datacore prioritizer need one.
-# # default : (null)
-# #
-# prio_args "timeout=1000 preferredsds=foo"
-#
-# # name : path_checker, checker
-# # scope : multipathd & multipathd
-# # desc : path checking algorithm to use to check path state
-# # values : readsector0|tur|emc_clariion|hp_sw|directio|rdac|
-# # cciss_tur
-# #
-# path_checker directio
-#
-# # as already described
-# path_selector "service-time 0"
-#
-# # as already described
-# features "0"
-#
-# #
-# # name : hardware_handler
-# # scope : multipath & multipathd
-# # desc : If set, it specifies a module that will be used to
-# # perform hardware specific actions when switching
-# # path groups or handling IO errors
-# # values : "0"|"1 emc"
-# # default : "0"
-# #
-# hardware_handler "1 emc"
-#
-# #
-# # name : failback
-# # scope : multipathd
-# # desc : tell the daemon to manage path group failback, or
-# # not to. 0 means immediate failback, values >0 means
-# # deffered failback expressed in seconds.
-# # values : manual|immediate|n > 0
-# #
-# failback 30
-#
-# #
-# # name : rr_weight
-# # scope : multipath & multipathd
-# # desc : if set to priorities the multipath configurator will
-# # assign path weights as "path prio * rr_min_io"
-# # values : priorities|uniform
-# #
-# rr_weight priorities
-#
-# #
-# # name : no_path_retry
-# # scope : multipath & multipathd
-# # desc : tell the number of retries until disable queueing,
-# # or "fail" means immediate failure (no queueing),
-# # "queue" means never stop queueing
-# # values : queue|fail|n (>0)
-# #
-# no_path_retry queue
-#
-# #
-# # name : rr_min_io
-# # scope : multipath & multipathd
-# # desc : the number of IO to route to a path before switching
-# # to the next in the same path group
-# #
-# rr_min_io 100
-#
-# #
-# # name : flush_on_last_del
-# # scope : multipathd
-# # desc : If set to "yes", multipathd will disable queueing
-# # when the last path to a device has been deleted.
-# # values : yes|no
-# #
-# flush_on_last_del yes
-#
-# #
-# # name : product_blacklist
-# # scope : multipath & multipathd
-# # desc : product strings to blacklist for this vendor
-# #
-# product_blacklist LUN_Z
-#
-# #
-# # name : fast_io_fail_tmo
-# # scope : multipath & multipathd
-# # desc : The number of seconds the scsi layer will wait after
-# # a problem has been detected on a FC remote port
-# # before failing IO to devices on that remote port.
-# # values : off | n >= 0 (smaller than dev_loss_tmo)
-# fast_io_fail_tmo 5
-#
-# #
-# # name : dev_loss_tmo
-# # scope : multipath & multipathd
-# # desc : The number of seconds the scsi layer will wait after
-# # a problem has been detected on a FC remote port
-# # 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|<n> > 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|<n> > 0
-# delay_wait_checks 12
-#
-# }
-# device {
-# vendor "COMPAQ "
-# product "MSA1000 "
-# path_grouping_policy multibus
-# path_checker tur
-# 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
-#}
+++ /dev/null
-# These are the compiled in default settings. They will be used unless you
-# overwrite these values in your config file.
-
-#defaults {
-# verbosity 2
-# polling_interval 5
-# path_selector "service-time 0"
-# path_grouping_policy "failover"
-# uid_attribute "ID_SERIAL"
-# prio "const"
-# prio_args ""
-# features "0"
-# path_checker "directio"
-# alias_prefix "mpath"
-# failback "manual"
-# rr_min_io 1000
-# rr_min_io_rq 1
-# max_fds "max"
-# rr_weight "uniform"
-# queue_without_daemon "yes"
-# flush_on_last_del "no"
-# user_friendly_names "no"
-# fast_io_fail_tmo 5
-# bindings_file "/etc/multipath/bindings"
-# wwids_file /etc/multipath/wwids
-# 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 "^(td|hd|vd)[a-z]"
-# devnode "^dcssblk[0-9]*"
-# devnode "^nvme.*"
-# device {
-# vendor "DGC"
-# product "LUNZ"
-# }
-# device {
-# vendor "EMC"
-# product "LUNZ"
-# }
-# device {
-# vendor "IBM"
-# product "Universal Xport"
-# }
-# device {
-# vendor "IBM"
-# product "S/390.*"
-# }
-# device {
-# vendor "DELL"
-# product "Universal Xport"
-# }
-# device {
-# vendor "SGI"
-# product "Universal Xport"
-# }
-# device {
-# vendor "STK"
-# product "Universal Xport"
-# }
-# device {
-# vendor "SUN"
-# product "Universal Xport"
-# }
-# device {
-# vendor "(LSI|ENGENIO)"
-# product "Universal Xport"
-# }
-#}
-#blacklist_exceptions {
-# property "(SCSI_IDENT_.*|ID_WWN)"
-#}
-#devices {
-# device {
-# vendor "COMPELNT"
-# product "Compellent Vol"
-# path_grouping_policy "multibus"
-# path_checker "tur"
-# features "0"
-# hardware_handler "0"
-# prio "const"
-# failback "immediate"
-# rr_weight "uniform"
-# no_path_retry "queue"
-# }
-# device {
-# vendor "APPLE*"
-# product "Xserve RAID "
-# path_grouping_policy "multibus"
-# path_checker "directio"
-# features "0"
-# hardware_handler "0"
-# prio "const"
-# rr_weight "uniform"
-# }
-# device {
-# vendor "3PARdata"
-# product "VV"
-# path_grouping_policy "multibus"
-# path_checker "directio"
-# features "0"
-# hardware_handler "0"
-# prio "const"
-# rr_weight "uniform"
-# }
-# device {
-# vendor "DEC"
-# product "HSG80"
-# path_grouping_policy "group_by_prio"
-# path_checker "hp_sw"
-# features "1 queue_if_no_path"
-# hardware_handler "1 hp_sw"
-# prio "hp_sw"
-# rr_weight "uniform"
-# }
-# device {
-# vendor "HP"
-# product "A6189A"
-# path_grouping_policy "multibus"
-# path_checker "directio"
-# features "0"
-# hardware_handler "0"
-# prio "const"
-# rr_weight "uniform"
-# no_path_retry 12
-# }
-# device {
-# vendor "(COMPAQ|HP)"
-# product "(MSA|HSV)1.0.*"
-# path_grouping_policy "group_by_prio"
-# path_checker "hp_sw"
-# features "1 queue_if_no_path"
-# hardware_handler "1 hp_sw"
-# prio "hp_sw"
-# rr_weight "uniform"
-# no_path_retry 12
-# rr_min_io 100
-# }
-# device {
-# vendor "(COMPAQ|HP)"
-# product "MSA VOLUME"
-# path_grouping_policy "group_by_prio"
-# path_checker "tur"
-# features "0"
-# hardware_handler "0"
-# prio "alua"
-# failback "immediate"
-# rr_weight "uniform"
-# no_path_retry 12
-# rr_min_io 100
-# }
-# device {
-# vendor "(COMPAQ|HP)"
-# product "HSV1[01]1|HSV2[01]0|HSV3[046]0|HSV4[05]0"
-# path_grouping_policy "group_by_prio"
-# path_checker "tur"
-# features "0"
-# hardware_handler "0"
-# prio "alua"
-# failback "immediate"
-# rr_weight "uniform"
-# no_path_retry 12
-# rr_min_io 100
-# }
-# device {
-# vendor "HP"
-# product "MSA2[02]12fc|MSA2012i"
-# path_grouping_policy "multibus"
-# path_checker "tur"
-# features "0"
-# hardware_handler "0"
-# prio "const"
-# failback "immediate"
-# rr_weight "uniform"
-# no_path_retry 18
-# rr_min_io 100
-# }
-# device {
-# vendor "HP"
-# product "MSA2012sa|MSA23(12|24)(fc|i|sa)|MSA2000s VOLUME"
-# path_grouping_policy "group_by_prio"
-# path_checker "tur"
-# features "0"
-# hardware_handler "0"
-# prio "alua"
-# failback "immediate"
-# rr_weight "uniform"
-# no_path_retry 18
-# rr_min_io 100
-# }
-# device {
-# vendor "HP"
-# product "HSVX700"
-# path_grouping_policy "group_by_prio"
-# path_checker "tur"
-# features "0"
-# hardware_handler "1 alua"
-# prio "alua"
-# failback "immediate"
-# rr_weight "uniform"
-# no_path_retry 12
-# rr_min_io 100
-# }
-# device {
-# vendor "HP"
-# product "LOGICAL VOLUME.*"
-# path_grouping_policy "multibus"
-# path_checker "tur"
-# features "0"
-# hardware_handler "0"
-# prio "const"
-# rr_weight "uniform"
-# no_path_retry 12
-# }
-# device {
-# vendor "HP"
-# product "P2000 G3 FC|P2000G3 FC/iSCSI|P2000 G3 SAS|P2000 G3 iSCSI"
-# path_grouping_policy "group_by_prio"
-# path_checker "tur"
-# features "0"
-# hardware_handler "0"
-# prio "alua"
-# failback "immediate"
-# rr_weight "uniform"
-# no_path_retry 18
-# rr_min_io 100
-# }
-# device {
-# vendor "DDN"
-# product "SAN DataDirector"
-# path_grouping_policy "multibus"
-# path_checker "directio"
-# features "0"
-# hardware_handler "0"
-# prio "const"
-# rr_weight "uniform"
-# }
-# device {
-# vendor "EMC"
-# product "SYMMETRIX"
-# path_grouping_policy "multibus"
-# path_checker "tur"
-# features "0"
-# hardware_handler "0"
-# prio "const"
-# rr_weight "uniform"
-# no_path_retry 6
-# }
-# device {
-# vendor "DGC"
-# product ".*"
-# product_blacklist "LUNZ"
-# path_grouping_policy "group_by_prio"
-# path_checker "emc_clariion"
-# features "1 queue_if_no_path"
-# hardware_handler "1 emc"
-# prio "emc"
-# failback "immediate"
-# rr_weight "uniform"
-# no_path_retry 60
-# retain_attached_hw_handler yes
-# detect_prio yes
-# }
-# device {
-# vendor "EMC"
-# product "Invista"
-# product_blacklist "LUNZ"
-# path_grouping_policy "multibus"
-# path_checker "tur"
-# features "0"
-# hardware_handler "0"
-# prio "const"
-# rr_weight "uniform"
-# no_path_retry 5
-# }
-# device {
-# vendor "FSC"
-# product "CentricStor"
-# path_grouping_policy "group_by_serial"
-# path_checker "directio"
-# features "0"
-# hardware_handler "0"
-# prio "const"
-# rr_weight "uniform"
-# }
-# device {
-# vendor "FUJITSU"
-# product "ETERNUS_DX(L|M|400|8000)"
-# path_grouping_policy "group_by_prio"
-# path_checker "tur"
-# features "1 queue_if_no_path"
-# hardware_handler "0"
-# prio "alua"
-# failback "immediate"
-# rr_weight "uniform"
-# no_path_retry 10
-# }
-# device {
-# vendor "(HITACHI|HP)"
-# product "OPEN-.*"
-# path_grouping_policy "multibus"
-# path_checker "tur"
-# features "0"
-# hardware_handler "0"
-# prio "const"
-# rr_weight "uniform"
-# }
-# device {
-# vendor "HITACHI"
-# product "DF.*"
-# path_grouping_policy "group_by_prio"
-# path_checker "tur"
-# features "1 queue_if_no_path"
-# hardware_handler "0"
-# prio "hds"
-# failback "immediate"
-# rr_weight "uniform"
-# }
-# device {
-# vendor "IBM"
-# product "ProFibre 4000R"
-# path_grouping_policy "multibus"
-# path_checker "directio"
-# features "0"
-# hardware_handler "0"
-# prio "const"
-# rr_weight "uniform"
-# }
-# device {
-# vendor "IBM"
-# product "^1722-600"
-# product_blacklist "Universal Xport"
-# path_grouping_policy "group_by_prio"
-# path_checker "rdac"
-# features "1 queue_if_no_path"
-# hardware_handler "1 rdac"
-# prio "rdac"
-# failback "immediate"
-# rr_weight "uniform"
-# no_path_retry 300
-# }
-# device {
-# vendor "IBM"
-# product "^1724"
-# product_blacklist "Universal Xport"
-# path_grouping_policy "group_by_prio"
-# path_checker "rdac"
-# features "1 queue_if_no_path"
-# hardware_handler "1 rdac"
-# prio "rdac"
-# failback "immediate"
-# rr_weight "uniform"
-# no_path_retry 300
-# }
-# device {
-# vendor "IBM"
-# product "^1726"
-# product_blacklist "Universal Xport"
-# path_grouping_policy "group_by_prio"
-# path_checker "rdac"
-# features "1 queue_if_no_path"
-# hardware_handler "1 rdac"
-# prio "rdac"
-# failback "immediate"
-# rr_weight "uniform"
-# no_path_retry 300
-# }
-# device {
-# vendor "IBM"
-# product "^1742"
-# product_blacklist "Universal Xport"
-# path_grouping_policy "group_by_prio"
-# path_checker "rdac"
-# features "0"
-# hardware_handler "1 rdac"
-# prio "rdac"
-# failback "immediate"
-# rr_weight "uniform"
-# no_path_retry "queue"
-# }
-# device {
-# vendor "IBM"
-# product "^1745|^1746"
-# 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 "IBM"
-# product "^1814"
-# product_blacklist "Universal Xport"
-# path_grouping_policy "group_by_prio"
-# path_checker "rdac"
-# features "0"
-# hardware_handler "1 rdac"
-# prio "rdac"
-# failback "immediate"
-# rr_weight "uniform"
-# no_path_retry "queue"
-# }
-# device {
-# vendor "IBM"
-# product "^1815"
-# product_blacklist "Universal Xport"
-# path_grouping_policy "group_by_prio"
-# path_checker "rdac"
-# features "0"
-# hardware_handler "1 rdac"
-# prio "rdac"
-# failback "immediate"
-# rr_weight "uniform"
-# no_path_retry "queue"
-# }
-# device {
-# vendor "IBM"
-# product "^1818"
-# product_blacklist "Universal Xport"
-# path_grouping_policy "group_by_prio"
-# path_checker "rdac"
-# features "0"
-# hardware_handler "1 rdac"
-# prio "rdac"
-# failback "immediate"
-# rr_weight "uniform"
-# no_path_retry "queue"
-# }
-# device {
-# vendor "IBM"
-# product "^3526"
-# product_blacklist "Universal Xport"
-# path_grouping_policy "group_by_prio"
-# path_checker "rdac"
-# features "0"
-# hardware_handler "1 rdac"
-# prio "rdac"
-# failback "immediate"
-# rr_weight "uniform"
-# no_path_retry "queue"
-# }
-# device {
-# vendor "IBM"
-# product "^3542"
-# path_grouping_policy "group_by_serial"
-# path_checker "tur"
-# features "0"
-# hardware_handler "0"
-# prio "const"
-# rr_weight "uniform"
-# }
-# device {
-# vendor "IBM"
-# product "^2105800"
-# path_grouping_policy "group_by_serial"
-# path_checker "tur"
-# features "1 queue_if_no_path"
-# hardware_handler "0"
-# prio "const"
-# rr_weight "uniform"
-# }
-# device {
-# vendor "IBM"
-# product "^2105F20"
-# path_grouping_policy "group_by_serial"
-# path_checker "tur"
-# features "1 queue_if_no_path"
-# hardware_handler "0"
-# prio "const"
-# rr_weight "uniform"
-# }
-# device {
-# vendor "IBM"
-# product "^1750500"
-# path_grouping_policy "group_by_prio"
-# path_checker "tur"
-# features "1 queue_if_no_path"
-# hardware_handler "0"
-# prio "alua"
-# failback "immediate"
-# rr_weight "uniform"
-# }
-# device {
-# vendor "IBM"
-# product "^2107900"
-# path_grouping_policy "multibus"
-# path_checker "tur"
-# features "1 queue_if_no_path"
-# hardware_handler "0"
-# prio "const"
-# rr_weight "uniform"
-# }
-# device {
-# vendor "IBM"
-# product "^2145"
-# path_grouping_policy "group_by_prio"
-# path_checker "tur"
-# features "1 queue_if_no_path"
-# hardware_handler "0"
-# prio "alua"
-# failback "immediate"
-# rr_weight "uniform"
-# }
-# device {
-# vendor "IBM"
-# product "S/390 DASD ECKD"
-# product_blacklist "S/390.*"
-# path_grouping_policy "multibus"
-# uid_attribute "ID_UID"
-# path_checker "directio"
-# features "1 queue_if_no_path"
-# hardware_handler "0"
-# prio "const"
-# rr_weight "uniform"
-# }
-# device {
-# vendor "IBM"
-# product "S/390 DASD FBA"
-# product_blacklist "S/390.*"
-# path_grouping_policy "multibus"
-# uid_attribute "ID_UID"
-# path_checker "directio"
-# features "1 queue_if_no_path"
-# hardware_handler "0"
-# prio "const"
-# rr_weight "uniform"
-# }
-# device {
-# vendor "IBM"
-# product "^IPR.*"
-# path_grouping_policy "group_by_prio"
-# path_checker "tur"
-# features "1 queue_if_no_path"
-# hardware_handler "1 alua"
-# prio "alua"
-# failback "immediate"
-# rr_weight "uniform"
-# }
-# device {
-# vendor "IBM"
-# product "1820N00"
-# path_grouping_policy "group_by_prio"
-# path_checker "tur"
-# features "0"
-# hardware_handler "0"
-# prio "alua"
-# failback "immediate"
-# rr_weight "uniform"
-# no_path_retry "queue"
-# rr_min_io 100
-# }
-# device {
-# vendor "IBM"
-# product "2810XIV"
-# path_grouping_policy "multibus"
-# path_checker "tur"
-# features "1 queue_if_no_path"
-# hardware_handler "0"
-# prio "const"
-# failback 15
-# rr_weight "uniform"
-# rr_min_io 15
-# }
-# device {
-# vendor "AIX"
-# product "VDASD"
-# path_grouping_policy "multibus"
-# path_checker "directio"
-# features "0"
-# hardware_handler "0"
-# prio "const"
-# failback "immediate"
-# rr_weight "uniform"
-# no_path_retry 60
-# }
-# device {
-# vendor "IBM"
-# product "3303 NVDISK"
-# path_grouping_policy "failover"
-# path_checker "tur"
-# features "0"
-# hardware_handler "0"
-# prio "const"
-# failback "immediate"
-# rr_weight "uniform"
-# no_path_retry 60
-# }
-# device {
-# vendor "AIX"
-# product "NVDISK"
-# path_grouping_policy "group_by_prio"
-# path_checker "tur"
-# features "0"
-# hardware_handler "1 alua"
-# prio "alua"
-# failback "immediate"
-# rr_weight "uniform"
-# no_path_retry 60
-# }
-# device {
-# vendor "DELL"
-# product "MD3000"
-# 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 "MD3000i"
-# 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 "MD32xx"
-# 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 "MD32xxi"
-# 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 "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"
-# path_checker "tur"
-# features "3 queue_if_no_path pg_init_retries 50"
-# hardware_handler "0"
-# prio "ontap"
-# failback "immediate"
-# rr_weight "uniform"
-# rr_min_io 128
-# flush_on_last_del "yes"
-# dev_loss_tmo "infinity"
-# retain_attached_hw_handler "yes"
-# detect_prio "yes"
-# }
-# device {
-# vendor "NEXENTA"
-# product "COMSTAR"
-# path_grouping_policy "group_by_serial"
-# path_checker "directio"
-# features "1 queue_if_no_path"
-# hardware_handler "0"
-# prio "const"
-# failback "immediate"
-# rr_weight "uniform"
-# no_path_retry 30
-# rr_min_io 128
-# }
-# device {
-# vendor "IBM"
-# product "Nseries.*"
-# path_grouping_policy "group_by_prio"
-# path_checker "directio"
-# features "1 queue_if_no_path"
-# hardware_handler "0"
-# prio "ontap"
-# failback "immediate"
-# rr_weight "uniform"
-# rr_min_io 128
-# }
-# device {
-# vendor "Pillar"
-# product "Axiom.*"
-# path_grouping_policy "group_by_prio"
-# path_checker "tur"
-# features "0"
-# hardware_handler "0"
-# prio "alua"
-# rr_weight "uniform"
-# }
-# device {
-# vendor "SGI"
-# product "TP9[13]00"
-# path_grouping_policy "multibus"
-# path_checker "directio"
-# features "0"
-# hardware_handler "0"
-# prio "const"
-# rr_weight "uniform"
-# }
-# device {
-# vendor "SGI"
-# product "TP9[45]00"
-# product_blacklist "Universal Xport"
-# path_grouping_policy "group_by_prio"
-# path_checker "rdac"
-# features "0"
-# hardware_handler "1 rdac"
-# prio "rdac"
-# failback "immediate"
-# rr_weight "uniform"
-# no_path_retry "queue"
-# }
-# device {
-# vendor "SGI"
-# product "IS.*"
-# 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 "NEC"
-# product "DISK ARRAY"
-# path_grouping_policy "group_by_prio"
-# path_checker "tur"
-# features "0"
-# hardware_handler "1 alua"
-# prio "alua"
-# failback "immediate"
-# rr_weight "uniform"
-# }
-# device {
-# vendor "STK"
-# product "OPENstorage D280"
-# product_blacklist "Universal Xport"
-# path_grouping_policy "group_by_prio"
-# path_checker "tur"
-# features "0"
-# hardware_handler "1 rdac"
-# prio "rdac"
-# failback "immediate"
-# rr_weight "uniform"
-# }
-# device {
-# vendor "SUN"
-# product "(StorEdge 3510|T4)"
-# path_grouping_policy "multibus"
-# path_checker "directio"
-# features "0"
-# hardware_handler "0"
-# prio "const"
-# rr_weight "uniform"
-# }
-# device {
-# vendor "SUN"
-# product "STK6580_6780"
-# path_grouping_policy "group_by_prio"
-# path_checker "tur"
-# features "0"
-# hardware_handler "1 rdac"
-# prio "rdac"
-# failback "immediate"
-# rr_weight "uniform"
-# }
-# device {
-# vendor "EUROLOGC"
-# product "FC2502"
-# path_grouping_policy "group_by_prio"
-# path_checker "directio"
-# features "0"
-# hardware_handler "0"
-# prio "const"
-# rr_weight "uniform"
-# }
-# device {
-# vendor "PIVOT3"
-# product "RAIGE VOLUME"
-# path_grouping_policy "multibus"
-# path_checker "tur"
-# features "1 queue_if_no_path"
-# hardware_handler "0"
-# prio "const"
-# rr_weight "uniform"
-# rr_min_io 100
-# }
-# device {
-# vendor "SUN"
-# product "CSM200_R"
-# product_blacklist "Universal Xport"
-# path_grouping_policy "group_by_prio"
-# path_checker "rdac"
-# features "0"
-# hardware_handler "1 rdac"
-# prio "rdac"
-# failback "immediate"
-# rr_weight "uniform"
-# no_path_retry "queue"
-# }
-# device {
-# vendor "SUN"
-# product "LCSM100_[IEFS]"
-# product_blacklist "Universal Xport"
-# path_grouping_policy "group_by_prio"
-# path_checker "rdac"
-# features "0"
-# hardware_handler "1 rdac"
-# prio "rdac"
-# failback "immediate"
-# rr_weight "uniform"
-# no_path_retry "queue"
-# }
-# device {
-# vendor "SUN"
-# product "SUN_6180"
-# path_grouping_policy "group_by_prio"
-# path_checker "rdac"
-# features "0"
-# hardware_handler "1 rdac"
-# prio "rdac"
-# failback "immediate"
-# rr_weight "uniform"
-# no_path_retry "queue"
-# rr_min_io 1000
-# rr_min_io_rq 1
-# }
-# device {
-# vendor "(NETAPP|LSI|ENGENIO)"
-# product "INF-01-00"
-# 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 30
-# detect_prio "yes"
-# retain_attached_hw_handler "yes"
-# }
-# device {
-# vendor "STK"
-# product "FLEXLINE 380"
-# product_blacklist "Universal Xport"
-# path_grouping_policy "group_by_prio"
-# path_checker "rdac"
-# features "0"
-# hardware_handler "1 rdac"
-# prio "rdac"
-# failback "immediate"
-# rr_weight "uniform"
-# no_path_retry "queue"
-# }
-# device {
-# vendor "Intel"
-# product "Multi-Flex"
-# path_grouping_policy "group_by_prio"
-# path_checker "tur"
-# features "0"
-# hardware_handler "1 alua"
-# prio "alua"
-# failback "immediate"
-# rr_weight "uniform"
-# no_path_retry "queue"
-# }
-# device {
-# vendor "DataCore"
-# product "SANmelody"
-# path_grouping_policy "group_by_prio"
-# path_checker "tur"
-# features "0"
-# hardware_handler "0"
-# prio "alua"
-# failback "immediate"
-# rr_weight "uniform"
-# no_path_retry "queue"
-# }
-# device {
-# vendor "DataCore"
-# product "Virtual Disk"
-# path_grouping_policy "group_by_prio"
-# path_checker "tur"
-# features "0"
-# hardware_handler "0"
-# prio "alua"
-# failback "immediate"
-# 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 {
-#}
+++ /dev/null
-##
-## This is a template multipath-tools configuration file
-## Uncomment the lines relevent to your environment
-##
-#defaults {
-# udev_dir /dev
-# polling_interval 10
-# path_selector "round-robin 0"
-# path_grouping_policy multibus
-# getuid_callout "/lib/udev/scsi_id --whitelisted --device=/dev/%n"
-# prio const
-# path_checker directio
-# rr_min_io 100
-# flush_on_last_del no
-# max_fds 8192
-# rr_weight priorities
-# failback immediate
-# no_path_retry fail
-# queue_without_daemon no
-# user_friendly_names no
-# mode 644
-# uid 0
-# gid disk
-#}
-#blacklist {
-# wwid 26353900f02796769
-# devnode "^(ram|raw|loop|fd|md|dm-|sr|scd|st)[0-9]*"
-# devnode "^hd[a-z][[0-9]*]"
-# device {
-# vendor DEC.*
-# product MSA[15]00
-# }
-#}
-#blacklist_exceptions {
-# devnode "^dasd[c-d]+[0-9]*"
-# wwid "IBM.75000000092461.4d00.34"
-#}
-#multipaths {
-# multipath {
-# wwid 3600508b4000156d700012000000b0000
-# alias yellow
-# path_grouping_policy multibus
-# path_selector "round-robin 0"
-# failback manual
-# rr_weight priorities
-# no_path_retry 5
-# rr_min_io 100
-# }
-# multipath {
-# wwid 1DEC_____321816758474
-# alias red
-# }
-#}
-#devices {
-# device {
-# vendor "COMPAQ "
-# product "HSV110 (C)COMPAQ"
-# path_grouping_policy multibus
-# getuid_callout "/lib/udev/scsi_id --whitelisted --device=/dev/%n"
-# path_checker directio
-# path_selector "round-robin 0"
-# hardware_handler "0"
-# failback 15
-# rr_weight priorities
-# no_path_retry queue
-# rr_min_io 100
-# product_blacklist LUNZ
-# }
-# device {
-# vendor "COMPAQ "
-# product "MSA1000 "
-# path_grouping_policy multibus
-# }
-#}
-#overrides {
-# no_path_retry fail
-#}
# 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"
+ENV{ID_FS_TYPE}!="?*", IMPORT{db}="ID_FS_TYPE"
+ENV{ID_FS_USAGE}!="?*", IMPORT{db}="ID_FS_USAGE"
+ENV{ID_FS_UUID}!="?*", IMPORT{db}="ID_FS_UUID"
+ENV{ID_FS_ENC}!="?*", IMPORT{db}="ID_FS_UUID_ENC"
+ENV{ID_FS_VERSION}!="?*", IMPORT{db}="ID_FS_VERSION"
LABEL="mpath_blkid_end"
# Also skip all foreign rules if no path is available.
int failed = get_refwwid(conf->dev, conf->dev_type, pathvec,
&refwwid);
if (!refwwid) {
- condlog(3, "%s: failed to get wwid", conf->dev);
+ condlog(4, "%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
if a number of retries is given with \fIno_path_retry\fR and the
overall retry interval is longer than the specified \fIdev_loss_tmo\fR value.
The linux kernel will cap this value to \fI300\fR if \fBfast_io_fail_tmo\fR
-is not set.
+is not set. Default is 600.
.TP
.B queue_without_daemon
If set to
checks. Default is
.I no
.TP
+.B strict_timinig
+If set to
+.I yes
+, multipathd will start a new path checker loop after exactly one second,
+so that each path check will occur at exactly
+.I polling_interval
+seconds. On busy systems path checks might take longer than one second;
+here the missing ticks will be accounted for on the next round.
+A warning will be printed if path checks take longer than
+.I polling_interval
+seconds.
+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
/*
* Copyright (c) 2005 Christophe Varoqui
*/
+#include <sys/time.h>
+#include <errno.h>
+#include <pthread.h>
#include <memory.h>
#include <vector.h>
+#include <structs.h>
+#include <structs_vec.h>
#include <parser.h>
#include <util.h>
#include <version.h>
if (!h)
return 1;
h->fn = fn;
+ h->locked = 1;
+ return 0;
+}
+
+int
+set_unlocked_handler_callback (unsigned long fp,int (*fn)(void *, char **, int *, void *))
+{
+ struct handler * h = find_handler(fp);
+
+ if (!h)
+ return 1;
+ h->fn = fn;
+ h->locked = 0;
return 0;
}
return foundkw;
}
-#define E_SYNTAX 1
-#define E_NOPARM 2
-#define E_NOMEM 3
-
+/*
+ * get_cmdvec
+ *
+ * returns:
+ * ENOMEM: not enough memory to allocate command
+ * EAGAIN: command not found
+ * EINVAL: argument missing for command
+ */
static int
get_cmdvec (char * cmd, vector *v)
{
strvec = alloc_strvec(cmd);
if (!strvec)
- return E_NOMEM;
+ return ENOMEM;
cmdvec = vector_alloc();
if (!cmdvec) {
free_strvec(strvec);
- return E_NOMEM;
+ return ENOMEM;
}
vector_foreach_slot(strvec, buff, i) {
}
kw = find_key(buff);
if (!kw) {
- r = E_SYNTAX;
+ r = EAGAIN;
goto out;
}
cmdkw = alloc_key();
if (!cmdkw) {
- r = E_NOMEM;
+ r = ENOMEM;
goto out;
}
if (!vector_alloc_slot(cmdvec)) {
FREE(cmdkw);
- r = E_NOMEM;
+ r = ENOMEM;
goto out;
}
vector_set_slot(cmdvec, cmdkw);
get_param = 1;
}
if (get_param) {
- r = E_NOPARM;
+ r = EINVAL;
goto out;
}
*v = cmdvec;
}
static int
-do_genhelp(char *reply, int maxlen) {
+do_genhelp(char *reply, int maxlen, const char *cmd, int error) {
int len = 0;
int i, j;
uint64_t fp;
struct handler * h;
struct key * kw;
+ switch(error) {
+ case ENOMEM:
+ len += snprintf(reply + len, maxlen - len,
+ "%s: Not enough memory\n", cmd);
+ break;
+ case EAGAIN:
+ len += snprintf(reply + len, maxlen - len,
+ "%s: not found\n", cmd);
+ break;
+ case EINVAL:
+ len += snprintf(reply + len, maxlen - len,
+ "%s: Missing argument\n", cmd);
+ break;
+ }
+ if (len >= maxlen)
+ goto out;
len += snprintf(reply + len, maxlen - len, VERSION_STRING);
if (len >= maxlen)
goto out;
static char *
-genhelp_handler (void)
+genhelp_handler (const char *cmd, int error)
{
char * reply;
char * p = NULL;
if (!reply)
return NULL;
p = reply;
- p += do_genhelp(reply, maxlen);
+ p += do_genhelp(reply, maxlen, cmd, error);
again = ((p - reply) >= maxlen);
REALLOC_REPLY(reply, again, maxlen);
}
}
int
-parse_cmd (char * cmd, char ** reply, int * len, void * data)
+parse_cmd (char * cmd, char ** reply, int * len, void * data, int timeout )
{
int r;
struct handler * h;
vector cmdvec = NULL;
+ struct timespec tmo;
+ struct timeval now;
r = get_cmdvec(cmd, &cmdvec);
if (r) {
- *reply = genhelp_handler();
+ *reply = genhelp_handler(cmd, r);
*len = strlen(*reply) + 1;
return 0;
}
h = find_handler(fingerprint(cmdvec));
if (!h || !h->fn) {
- *reply = genhelp_handler();
+ *reply = genhelp_handler(cmd, EINVAL);
*len = strlen(*reply) + 1;
free_keys(cmdvec);
return 0;
/*
* execute handler
*/
- r = h->fn(cmdvec, reply, len, data);
+ if (gettimeofday(&now, NULL) == 0) {
+ tmo.tv_sec = now.tv_sec + timeout;
+ tmo.tv_nsec = now.tv_usec * 1000;
+ } else {
+ tmo.tv_sec = 0;
+ }
+ if (h->locked) {
+ struct vectors * vecs = (struct vectors *)data;
+
+ pthread_cleanup_push(cleanup_lock, &vecs->lock);
+ if (tmo.tv_sec) {
+ vecs->lock.depth++;
+ r = pthread_mutex_timedlock(vecs->lock.mutex, &tmo);
+ } else {
+ lock(vecs->lock);
+ r = 0;
+ }
+ if (r == 0) {
+ pthread_testcancel();
+ r = h->fn(cmdvec, reply, len, data);
+ }
+ lock_cleanup_pop(vecs->lock);
+ } else
+ r = h->fn(cmdvec, reply, len, data);
free_keys(cmdvec);
return r;
add_handler(LIST+MAPS+TOPOLOGY, NULL);
add_handler(LIST+TOPOLOGY, NULL);
add_handler(LIST+MAP+TOPOLOGY, NULL);
+ add_handler(LIST+MAP+FMT, NULL);
+ add_handler(LIST+MAP+RAW+FMT, NULL);
add_handler(LIST+CONFIG, NULL);
add_handler(LIST+BLACKLIST, NULL);
add_handler(LIST+DEVICES, NULL);
/*
* If last keyword takes a param, don't even try to guess
*/
- if (r == E_NOPARM) {
+ if (r == EINVAL) {
has_param = 1;
return (strdup("(value)"));
}
struct handler {
uint64_t fingerprint;
+ int locked;
int (*fn)(void *, char **, int *, void *);
};
int alloc_handlers (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 set_unlocked_handler_callback (uint64_t fp, int (*fn)(void *, char **, int *, void *));
+int parse_cmd (char * cmd, char ** reply, int * len, void *, int);
int load_keys (void);
char * get_keyparam (vector v, uint64_t code);
void free_keys (vector vec);
}
int
+show_map (char ** r, int *len, struct multipath * mpp, char * style,
+ int pretty)
+{
+ char * c;
+ char * reply;
+ unsigned int maxlen = INITIAL_REPLY_LEN;
+ int again = 1;
+
+ reply = MALLOC(maxlen);
+ while (again) {
+ if (!reply)
+ return 1;
+
+ c = reply;
+ c += snprint_multipath(c, reply + maxlen - c, style,
+ mpp, pretty);
+
+ again = ((c - reply) == (maxlen - 1));
+
+ REALLOC_REPLY(reply, again, maxlen);
+ }
+ *r = reply;
+ *len = (int)(c - reply + 1);
+ return 0;
+}
+
+int
show_maps (char ** r, int *len, struct vectors * vecs, char * style,
int pretty)
{
}
int
+cli_list_map_fmt (void * v, char ** reply, int * len, void * data)
+{
+ struct multipath * mpp;
+ struct vectors * vecs = (struct vectors *)data;
+ char * param = get_keyparam(v, MAP);
+ char * fmt = get_keyparam(v, FMT);
+
+ param = convert_dev(param, 0);
+ get_path_layout(vecs->pathvec, 0);
+ get_multipath_layout(vecs->mpvec, 1);
+ mpp = find_mp_by_str(vecs->mpvec, param);
+ if (!mpp)
+ return 1;
+
+ condlog(3, "list map %s fmt %s (operator)", param, fmt);
+
+ return show_map(reply, len, mpp, fmt, 1);
+}
+
+int
+cli_list_map_raw (void * v, char ** reply, int * len, void * data)
+{
+ struct multipath * mpp;
+ struct vectors * vecs = (struct vectors *)data;
+ char * param = get_keyparam(v, MAP);
+ char * fmt = get_keyparam(v, FMT);
+
+ param = convert_dev(param, 0);
+ get_path_layout(vecs->pathvec, 0);
+ get_multipath_layout(vecs->mpvec, 1);
+ mpp = find_mp_by_str(vecs->mpvec, param);
+ if (!mpp)
+ return 1;
+
+ condlog(3, "list map %s fmt %s (operator)", param, fmt);
+
+ return show_map(reply, len, mpp, fmt, 0);
+}
+
+int
cli_list_maps (void * v, char ** reply, int * len, void * data)
{
struct vectors * vecs = (struct vectors *)data;
return 1;
}
- mpp->retry_tick = 0;
+ mpp->retry_tick = -1;
dm_queue_if_no_path(mpp->alias, 0);
return 0;
}
condlog(2, "disable queueing (operator)");
vector_foreach_slot(vecs->mpvec, mpp, i) {
- mpp->retry_tick = 0;
+ mpp->retry_tick = -1;
dm_queue_if_no_path(mpp->alias, 0);
}
return 0;
int
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);
+ if (set_config_state(DAEMON_CONFIGURE) == ETIMEDOUT) {
+ condlog(2, "timeout starting reconfiguration");
+ return 1;
+ }
+ return 0;
}
int
return 1;
}
- r = dm_simplecmd_noflush(DM_DEVICE_SUSPEND, param, 0, 0);
+ r = dm_simplecmd_noflush(DM_DEVICE_SUSPEND, param, 0);
condlog(2, "%s: suspend (operator)", param);
return 1;
}
- r = dm_simplecmd_noflush(DM_DEVICE_RESUME, param, 0, 0);
+ r = dm_simplecmd_noflush(DM_DEVICE_RESUME, param, 0);
condlog(2, "%s: resume (operator)", param);
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_map_fmt (void * v, char ** reply, int * len, void * data);
+int cli_list_map_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);
static int use_watchdog;
#endif
+int uxsock_timeout;
+
/*
* libmultipath
*/
unsigned int mpath_mx_alloc_len;
int logsink;
-enum daemon_status running_state;
+enum daemon_status running_state = DAEMON_INIT;
pid_t daemon_pid;
+pthread_mutex_t config_lock = PTHREAD_MUTEX_INITIALIZER;
+pthread_cond_t config_cond = PTHREAD_COND_INITIALIZER;
-static sem_t exit_sem;
/*
* global copy of vecs for use in sig handlers
*/
struct udev * udev;
+const char *
+daemon_status(void)
+{
+ switch (running_state) {
+ case DAEMON_INIT:
+ return "init";
+ case DAEMON_START:
+ return "startup";
+ case DAEMON_CONFIGURE:
+ return "configure";
+ case DAEMON_IDLE:
+ return "idle";
+ case DAEMON_RUNNING:
+ return "running";
+ case DAEMON_SHUTDOWN:
+ return "shutdown";
+ }
+ return NULL;
+}
+
+/*
+ * I love you too, systemd ...
+ */
+const char *
+sd_notify_status(void)
+{
+ switch (running_state) {
+ case DAEMON_INIT:
+ return "STATUS=init";
+ case DAEMON_START:
+ return "STATUS=startup";
+ case DAEMON_CONFIGURE:
+ return "STATUS=configure";
+ case DAEMON_IDLE:
+ return "STATUS=idle";
+ case DAEMON_RUNNING:
+ return "STATUS=running";
+ case DAEMON_SHUTDOWN:
+ return "STATUS=shutdown";
+ }
+ return NULL;
+}
+
+static void config_cleanup(void *arg)
+{
+ pthread_mutex_unlock(&config_lock);
+}
+
+void post_config_state(enum daemon_status state)
+{
+ pthread_mutex_lock(&config_lock);
+ if (state != running_state) {
+ running_state = state;
+ pthread_cond_broadcast(&config_cond);
+#ifdef USE_SYSTEMD
+ sd_notify(0, sd_notify_status());
+#endif
+ }
+ pthread_mutex_unlock(&config_lock);
+}
+
+int set_config_state(enum daemon_status state)
+{
+ int rc = 0;
+
+ pthread_cleanup_push(config_cleanup, NULL);
+ pthread_mutex_lock(&config_lock);
+ if (running_state != state) {
+ if (running_state != DAEMON_IDLE) {
+ struct timespec ts;
+
+ clock_gettime(CLOCK_REALTIME, &ts);
+ ts.tv_sec += 1;
+ rc = pthread_cond_timedwait(&config_cond,
+ &config_lock, &ts);
+ }
+ if (!rc) {
+ running_state = state;
+ pthread_cond_broadcast(&config_cond);
+#ifdef USE_SYSTEMD
+ sd_notify(0, sd_notify_status());
+#endif
+ }
+ }
+ pthread_cleanup_pop(1);
+ return rc;
+}
+
static int
need_switch_pathgroup (struct multipath * mpp, int refresh)
{
vector_foreach_slot (pgp->paths, pp, j)
pathinfo(pp, conf->hwtable, DI_PRIO);
+ if (!mpp->pg || VECTOR_SIZE(mpp->paths) == 0)
+ return 0;
+
mpp->bestpg = select_path_group(mpp);
if (mpp->bestpg != mpp->nextpg)
retry:
condlog(4, "%s: updating new map", mpp->alias);
- if (adopt_paths(vecs->pathvec, mpp, 1)) {
+ if (adopt_paths(vecs->pathvec, mpp)) {
condlog(0, "%s: failed to adopt paths for new map update",
mpp->alias);
retries = -1;
return 1;
}
}
+ pthread_cleanup_push(cleanup_lock, &vecs->lock);
+ lock(vecs->lock);
+ pthread_testcancel();
rc = ev_add_map(uev->kernel, alias, vecs);
+ lock_cleanup_pop(vecs->lock);
FREE(alias);
return rc;
}
if (mpp) {
if (mpp->wait_for_udev > 1) {
if (update_map(mpp, vecs))
- /* setup multipathd removed the map */
+ /* setup multipathd removed the map */
return 1;
}
if (mpp->wait_for_udev) {
if (conf->delayed_reconfig &&
!need_to_delay_reconfig(vecs)) {
condlog(2, "reconfigure (delayed)");
- reconfigure(vecs);
+ set_config_state(DAEMON_CONFIGURE);
return 0;
}
}
return 0;
}
minor = uevent_get_minor(uev);
+
+ pthread_cleanup_push(cleanup_lock, &vecs->lock);
+ lock(vecs->lock);
+ pthread_testcancel();
mpp = find_mp_by_minor(vecs->mpvec, minor);
if (!mpp) {
orphan_paths(vecs->pathvec, mpp);
remove_map_and_stop_waiter(mpp, vecs, 1);
out:
+ lock_cleanup_pop(vecs->lock);
FREE(alias);
return 0;
}
+/* Called from CLI handler */
int
ev_remove_map (char * devname, char * alias, int minor, struct vectors * vecs)
{
return 1;
}
+ pthread_cleanup_push(cleanup_lock, &vecs->lock);
+ lock(vecs->lock);
+ pthread_testcancel();
pp = find_path_by_dev(vecs->pathvec, uev->kernel);
if (pp) {
int r;
ret = 1;
}
}
- return ret;
}
+ lock_cleanup_pop(vecs->lock);
+ if (pp)
+ return ret;
/*
* get path vital state
condlog(3, "%s: failed to get path info", uev->kernel);
return 1;
}
+ pthread_cleanup_push(cleanup_lock, &vecs->lock);
+ lock(vecs->lock);
+ pthread_testcancel();
ret = store_path(vecs->pathvec, pp);
if (!ret) {
pp->checkint = conf->checkint;
free_path(pp);
ret = 1;
}
-
+ lock_cleanup_pop(vecs->lock);
return ret;
}
pp->mpp = mpp;
rescan:
if (mpp) {
- if (mpp->size != pp->size) {
+ if (pp->size && mpp->size != pp->size) {
condlog(0, "%s: failed to add new path %s, "
"device size mismatch",
mpp->alias, pp->dev);
condlog(4,"%s: adopting all paths for path %s",
mpp->alias, pp->dev);
- if (adopt_paths(vecs->pathvec, mpp, 1))
+ if (adopt_paths(vecs->pathvec, mpp))
goto fail; /* leave path added to pathvec */
verify_paths(mpp, vecs);
*/
start_waiter = 1;
}
- else
+ if (!start_waiter)
goto fail; /* leave path added to pathvec */
}
- /* persistent reseravtion check*/
- mpath_pr_event_handle(pp);
+ /* persistent reservation check*/
+ mpath_pr_event_handle(pp);
/*
* push the map to the device-mapper
* deal with asynchronous uevents :((
*/
if (mpp->action == ACT_RELOAD && retries-- > 0) {
- condlog(0, "%s: uev_add_path sleep", mpp->alias);
+ condlog(0, "%s: ev_add_path sleep", mpp->alias);
sleep(1);
update_mpp_paths(mpp, vecs->pathvec);
goto rescan;
condlog(2, "%s [%s]: path added to devmap %s",
pp->dev, pp->dev_t, mpp->alias);
return 0;
- }
- else
+ } else
goto fail;
fail_map:
uev_remove_path (struct uevent *uev, struct vectors * vecs)
{
struct path *pp;
+ int ret;
condlog(2, "%s: remove path (uevent)", uev->kernel);
+ pthread_cleanup_push(cleanup_lock, &vecs->lock);
+ lock(vecs->lock);
+ pthread_testcancel();
pp = find_path_by_dev(vecs->pathvec, uev->kernel);
-
+ if (pp)
+ ret = ev_remove_path(pp, vecs);
+ lock_cleanup_pop(vecs->lock);
if (!pp) {
/* Not an error; path might have been purged earlier */
condlog(0, "%s: path already removed", uev->kernel);
return 0;
}
-
- return ev_remove_path(pp, vecs);
+ return ret;
}
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;
+ struct multipath *mpp = NULL;
+
condlog(2, "%s: update path write_protect to '%d' (uevent)",
uev->kernel, ro);
- if (pp->mpp) {
- if (pp->mpp->wait_for_udev) {
- pp->mpp->wait_for_udev = 2;
- return 0;
+ pthread_cleanup_push(cleanup_lock, &vecs->lock);
+ lock(vecs->lock);
+ pthread_testcancel();
+ /*
+ * pthread_mutex_lock() and pthread_mutex_unlock()
+ * need to be at the same indentation level, hence
+ * this slightly convoluted codepath.
+ */
+ pp = find_path_by_dev(vecs->pathvec, uev->kernel);
+ if (pp) {
+ if (pp->initialized == INIT_REQUESTED_UDEV) {
+ retval = 2;
+ } else {
+ mpp = pp->mpp;
+ if (mpp && mpp->wait_for_udev) {
+ mpp->wait_for_udev = 2;
+ mpp = NULL;
+ retval = 0;
+ }
}
+ if (mpp) {
+ retval = reload_map(vecs, mpp, 0);
- retval = reload_map(vecs, pp->mpp, 0);
-
- condlog(2, "%s: map %s reloaded (retval %d)",
- uev->kernel, pp->mpp->alias, retval);
+ condlog(2, "%s: map %s reloaded (retval %d)",
+ uev->kernel, mpp->alias, retval);
+ }
}
-
+ lock_cleanup_pop(vecs->lock);
+ if (!pp) {
+ condlog(0, "%s: spurious uevent, path not found",
+ uev->kernel);
+ return 1;
+ }
+ if (retval == 2)
+ return uev_add_path(uev, vecs);
}
return retval;
*len = 0;
vecs = (struct vectors *)trigger_data;
- pthread_cleanup_push(cleanup_lock, &vecs->lock);
- lock(vecs->lock);
- pthread_testcancel();
-
- r = parse_cmd(str, reply, len, vecs);
+ r = parse_cmd(str, reply, len, vecs, uxsock_timeout / 1000);
if (r > 0) {
- *reply = STRDUP("fail\n");
+ if (r == ETIMEDOUT)
+ *reply = STRDUP("timeout\n");
+ else
+ *reply = STRDUP("fail\n");
*len = strlen(*reply) + 1;
r = 1;
}
}
/* else if (r < 0) leave *reply alone */
- lock_cleanup_pop(vecs->lock);
return r;
}
if (uev_discard(uev->devpath))
return 0;
- pthread_cleanup_push(cleanup_lock, &vecs->lock);
- lock(vecs->lock);
- pthread_testcancel();
+ pthread_cleanup_push(config_cleanup, NULL);
+ pthread_mutex_lock(&config_lock);
+ if (running_state != DAEMON_IDLE &&
+ running_state != DAEMON_RUNNING)
+ pthread_cond_wait(&config_cond, &config_lock);
+ pthread_cleanup_pop(1);
+
+ if (running_state == DAEMON_SHUTDOWN)
+ return 0;
/*
* device map event
}
out:
- lock_cleanup_pop(vecs->lock);
return r;
}
static void *
uxlsnrloop (void * ap)
{
- if (cli_init())
+ if (cli_init()) {
+ condlog(1, "Failed to init uxsock listener");
return NULL;
+ }
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_unlocked_handler_callback(LIST+STATUS, cli_list_status);
+ set_unlocked_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+TOPOLOGY, cli_list_maps_topology);
set_handler_callback(LIST+TOPOLOGY, cli_list_maps_topology);
set_handler_callback(LIST+MAP+TOPOLOGY, cli_list_map_topology);
- set_handler_callback(LIST+CONFIG, cli_list_config);
- set_handler_callback(LIST+BLACKLIST, cli_list_blacklist);
+ set_handler_callback(LIST+MAP+FMT, cli_list_map_fmt);
+ set_handler_callback(LIST+MAP+RAW+FMT, cli_list_map_fmt);
+ set_unlocked_handler_callback(LIST+CONFIG, cli_list_config);
+ set_unlocked_handler_callback(LIST+BLACKLIST, cli_list_blacklist);
set_handler_callback(LIST+DEVICES, cli_list_devices);
set_handler_callback(LIST+WILDCARDS, cli_list_wildcards);
set_handler_callback(ADD+PATH, cli_add_path);
set_handler_callback(ADD+MAP, cli_add_map);
set_handler_callback(DEL+MAP, cli_del_map);
set_handler_callback(SWITCH+MAP+GROUP, cli_switch_group);
- set_handler_callback(RECONFIGURE, cli_reconfigure);
+ set_unlocked_handler_callback(RECONFIGURE, cli_reconfigure);
set_handler_callback(SUSPEND+MAP, cli_suspend);
set_handler_callback(RESUME+MAP, cli_resume);
set_handler_callback(RESIZE+MAP, cli_resize);
set_handler_callback(RESTOREQ+MAP, cli_restore_queueing);
set_handler_callback(DISABLEQ+MAPS, cli_disable_all_queueing);
set_handler_callback(RESTOREQ+MAPS, cli_restore_all_queueing);
- set_handler_callback(QUIT, cli_quit);
- set_handler_callback(SHUTDOWN, cli_shutdown);
+ set_unlocked_handler_callback(QUIT, cli_quit);
+ set_unlocked_handler_callback(SHUTDOWN, cli_shutdown);
set_handler_callback(GETPRSTATUS+MAP, cli_getprstatus);
set_handler_callback(SETPRSTATUS+MAP, cli_setprstatus);
set_handler_callback(UNSETPRSTATUS+MAP, cli_unsetprstatus);
void
exit_daemon (void)
{
- sem_post(&exit_sem);
-}
-
-const char *
-daemon_status(void)
-{
- switch (running_state) {
- case DAEMON_INIT:
- return "init";
- case DAEMON_START:
- return "startup";
- case DAEMON_CONFIGURE:
- return "configure";
- case DAEMON_RUNNING:
- return "running";
- case DAEMON_SHUTDOWN:
- return "shutdown";
- }
- return NULL;
+ post_config_state(DAEMON_SHUTDOWN);
}
static void
if (timed_out && conf->delayed_reconfig &&
!need_to_delay_reconfig(vecs)) {
condlog(2, "reconfigure (delayed)");
- reconfigure(vecs);
+ set_config_state(DAEMON_CONFIGURE);
}
}
unsigned int i;
vector_foreach_slot (mpvec, mpp, i) {
- if (mpp->retry_tick) {
+ if (mpp->retry_tick > 0) {
mpp->stat_total_queueing_time++;
condlog(4, "%s: Retrying.. No active path", mpp->alias);
if(--mpp->retry_tick == 0) {
* Returns '1' if the path has been checked, '0' otherwise
*/
int
-check_path (struct vectors * vecs, struct path * pp)
+check_path (struct vectors * vecs, struct path * pp, int ticks)
{
int newstate;
int new_path_up = 0;
pp->initialized == INIT_REQUESTED_UDEV) && !pp->mpp)
return 0;
- if (pp->tick && --pp->tick)
+ if (pp->tick)
+ pp->tick -= (pp->tick > ticks) ? ticks : pp->tick;
+ if (pp->tick)
return 0; /* don't check this path yet */
if (!pp->mpp && pp->initialized == INIT_MISSING_UDEV &&
sysfs_attr_set_value(pp->udev, "uevent", "change",
strlen("change"));
return 0;
- }
+ }
/*
* provision a next check soonest,
struct path *pp;
int count = 0;
unsigned int i;
+ struct itimerval timer_tick_it;
+ struct timeval last_time;
mlockall(MCL_CURRENT | MCL_FUTURE);
vecs = (struct vectors *)ap;
pp->checkint = conf->checkint;
}
+ /* Tweak start time for initial path check */
+ if (gettimeofday(&last_time, NULL) != 0)
+ last_time.tv_sec = 0;
+ else
+ last_time.tv_sec -= 1;
+
while (1) {
struct timeval diff_time, start_time, end_time;
- int num_paths = 0;
+ int num_paths = 0, ticks = 0, signo, strict_timing, rc = 0;
+ sigset_t mask;
if (gettimeofday(&start_time, NULL) != 0)
start_time.tv_sec = 0;
- pthread_cleanup_push(cleanup_lock, &vecs->lock);
- lock(vecs->lock);
- pthread_testcancel();
- condlog(4, "tick");
+ if (start_time.tv_sec && last_time.tv_sec) {
+ timersub(&start_time, &last_time, &diff_time);
+ condlog(4, "tick (%lu.%06lu secs)",
+ diff_time.tv_sec, diff_time.tv_usec);
+ last_time.tv_sec = start_time.tv_sec;
+ last_time.tv_usec = start_time.tv_usec;
+ ticks = diff_time.tv_sec;
+ } else {
+ ticks = 1;
+ condlog(4, "tick (%d ticks)", ticks);
+ }
#ifdef USE_SYSTEMD
if (use_watchdog)
sd_notify(0, "WATCHDOG=1");
#endif
+ rc = set_config_state(DAEMON_RUNNING);
+ if (rc == ETIMEDOUT) {
+ condlog(4, "timeout waiting for DAEMON_IDLE");
+ continue;
+ }
+ strict_timing = conf->strict_timing;
if (vecs->pathvec) {
+ pthread_cleanup_push(cleanup_lock, &vecs->lock);
+ lock(vecs->lock);
+ pthread_testcancel();
vector_foreach_slot (vecs->pathvec, pp, i) {
- num_paths += check_path(vecs, pp);
+ num_paths += check_path(vecs, pp, ticks);
}
+ lock_cleanup_pop(vecs->lock);
}
if (vecs->mpvec) {
+ pthread_cleanup_push(cleanup_lock, &vecs->lock);
+ lock(vecs->lock);
+ pthread_testcancel();
defered_failback_tick(vecs->mpvec);
retry_count_tick(vecs->mpvec);
missing_uev_wait_tick(vecs);
+ lock_cleanup_pop(vecs->lock);
}
if (count)
count--;
else {
+ pthread_cleanup_push(cleanup_lock, &vecs->lock);
+ lock(vecs->lock);
+ pthread_testcancel();
condlog(4, "map garbage collection");
mpvec_garbage_collector(vecs);
count = MAPGCINT;
+ lock_cleanup_pop(vecs->lock);
}
- lock_cleanup_pop(vecs->lock);
+ diff_time.tv_usec = 0;
if (start_time.tv_sec &&
- gettimeofday(&end_time, NULL) == 0 &&
- num_paths) {
+ gettimeofday(&end_time, NULL) == 0) {
timersub(&end_time, &start_time, &diff_time);
- condlog(3, "checked %d path%s in %lu.%06lu secs",
- num_paths, num_paths > 1 ? "s" : "",
- diff_time.tv_sec, diff_time.tv_usec);
+ if (num_paths) {
+ condlog(3, "checked %d path%s in %lu.%06lu secs",
+ num_paths, num_paths > 1 ? "s" : "",
+ diff_time.tv_sec, diff_time.tv_usec);
+ if (diff_time.tv_sec > conf->max_checkint)
+ condlog(1, "path checkers took longer "
+ "than %lu seconds, consider "
+ "increasing max_polling_interval",
+ diff_time.tv_sec);
+ }
+ }
+
+ post_config_state(DAEMON_IDLE);
+ if (!strict_timing)
+ sleep(1);
+ else {
+ timer_tick_it.it_interval.tv_sec = 0;
+ timer_tick_it.it_interval.tv_usec = 0;
+ if (diff_time.tv_usec) {
+ timer_tick_it.it_value.tv_sec = 0;
+ timer_tick_it.it_value.tv_usec =
+ (unsigned long)1000000 - diff_time.tv_usec;
+ } else {
+ timer_tick_it.it_value.tv_sec = 1;
+ timer_tick_it.it_value.tv_usec = 0;
+ }
+ setitimer(ITIMER_REAL, &timer_tick_it, NULL);
+
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGALRM);
+ condlog(3, "waiting for %lu.%06lu secs",
+ timer_tick_it.it_value.tv_sec,
+ timer_tick_it.it_value.tv_usec);
+ if (sigwait(&mask, &signo) != 0) {
+ condlog(3, "sigwait failed with error %d",
+ errno);
+ conf->strict_timing = 0;
+ break;
+ }
}
- sleep(1);
}
return NULL;
}
struct config * old = conf;
int retval = 1;
- running_state = DAEMON_CONFIGURE;
-
/*
* free old map and path vectors ... they use old conf state
*/
configure(vecs, 1);
free_config(old);
retval = 0;
+ } else {
+ conf = old;
}
-
- running_state = DAEMON_RUNNING;
+ uxsock_timeout = conf->uxsock_timeout;
return retval;
}
void
handle_signals(void)
{
- if (reconfig_sig && running_state == DAEMON_RUNNING) {
- pthread_cleanup_push(cleanup_lock,
- &gvecs->lock);
- lock(gvecs->lock);
- pthread_testcancel();
- 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 (reconfig_sig) {
+ condlog(2, "reconfigure (signal)");
+ set_config_state(DAEMON_CONFIGURE);
}
if (log_reset_sig) {
condlog(2, "reset log (signal)");
sigaddset(&set, SIGHUP);
sigaddset(&set, SIGUSR1);
sigaddset(&set, SIGUSR2);
+ sigaddset(&set, SIGALRM);
pthread_sigmask(SIG_BLOCK, &set, NULL);
signal_set(SIGHUP, sighup);
char *envp;
mlockall(MCL_CURRENT | MCL_FUTURE);
- sem_init(&exit_sem, 0, 0);
signal_init();
udev = udev_new();
exit(1);
}
- running_state = DAEMON_START;
+ post_config_state(DAEMON_START);
-#ifdef USE_SYSTEMD
- sd_notify(0, "STATUS=startup");
-#endif
condlog(2, "--------start up--------");
condlog(2, "read " DEFAULT_CONFIGFILE);
if (load_config(DEFAULT_CONFIGFILE, udev))
goto failed;
+ uxsock_timeout = conf->uxsock_timeout;
+
dm_drv_version(conf->version, TGT_MPATH);
if (init_checkers()) {
condlog(0, "failed to initialize checkers");
}
#endif
/*
+ * Signal start of configuration
+ */
+ post_config_state(DAEMON_CONFIGURE);
+
+ /*
* Start uevent listener early to catch events
*/
if ((rc = pthread_create(&uevent_thr, &uevent_attr, ueventloop, udev))) {
condlog(0, "failed to create cli listener: %d", rc);
goto failed;
}
- /*
- * fetch and configure both paths and multipaths
- */
-#ifdef USE_SYSTEMD
- sd_notify(0, "STATUS=configure");
-#endif
- running_state = DAEMON_CONFIGURE;
-
- lock(vecs->lock);
- if (configure(vecs, 1)) {
- unlock(vecs->lock);
- condlog(0, "failure during configuration");
- goto failed;
- }
- unlock(vecs->lock);
/*
* start threads
}
pthread_attr_destroy(&misc_attr);
- running_state = DAEMON_RUNNING;
#ifdef USE_SYSTEMD
- sd_notify(0, "READY=1\nSTATUS=running");
+ sd_notify(0, "READY=1");
#endif
- /*
- * exit path
- */
- while(sem_wait(&exit_sem) != 0); /* Do nothing */
+ while (running_state != DAEMON_SHUTDOWN) {
+ pthread_cleanup_push(config_cleanup, NULL);
+ pthread_mutex_lock(&config_lock);
+ if (running_state != DAEMON_CONFIGURE &&
+ running_state != DAEMON_SHUTDOWN) {
+ pthread_cond_wait(&config_cond, &config_lock);
+ }
+ pthread_cleanup_pop(1);
+ if (running_state == DAEMON_CONFIGURE) {
+ pthread_cleanup_push(cleanup_lock, &vecs->lock);
+ lock(vecs->lock);
+ pthread_testcancel();
+ if (!need_to_delay_reconfig(vecs)) {
+ reconfigure(vecs);
+ } else {
+ conf->delayed_reconfig = 1;
+ }
+ lock_cleanup_pop(vecs->lock);
+ post_config_state(DAEMON_IDLE);
+ }
+ }
-#ifdef USE_SYSTEMD
- sd_notify(0, "STATUS=shutdown");
-#endif
- running_state = DAEMON_SHUTDOWN;
lock(vecs->lock);
if (conf->queue_without_daemon == QUE_NO_DAEMON_OFF)
vector_foreach_slot(vecs->mpvec, mpp, i)
int foreground = 0;
logsink = 1;
- running_state = DAEMON_INIT;
dm_init();
if (getuid() != 0) {
case 'k':
if (load_config(DEFAULT_CONFIGFILE, udev_new()))
exit(1);
- uxclnt(optarg, conf->uxsock_timeout);
+ uxclnt(optarg, uxsock_timeout + 100);
exit(0);
case 'B':
conf->bindings_read_only = 1;
conf->ignore_new_devs = 1;
break;
default:
- ;
+ fprintf(stderr, "Invalid argument '-%c'\n",
+ optopt);
+ exit(1);
}
}
if (optind < argc) {
if (load_config(DEFAULT_CONFIGFILE, udev_new()))
exit(1);
+ memset(cmd, 0x0, CMDSIZE);
while (optind < argc) {
if (strchr(argv[optind], ' '))
c += snprintf(c, s + CMDSIZE - c, "\"%s\" ", argv[optind]);
optind++;
}
c += snprintf(c, s + CMDSIZE - c, "\n");
- uxclnt(s, conf->uxsock_timeout);
+ uxclnt(s, uxsock_timeout + 100);
exit(0);
}
DAEMON_INIT,
DAEMON_START,
DAEMON_CONFIGURE,
+ DAEMON_IDLE,
DAEMON_RUNNING,
DAEMON_SHUTDOWN,
};
struct prin_resp;
extern pid_t daemon_pid;
+extern int uxsock_timeout;
void exit_daemon(void);
const char * daemon_status(void);
int ev_add_map (char *, char *, struct vectors *);
int ev_remove_map (char *, char *, int, struct vectors *);
void sync_map_state (struct multipath *);
+int set_config_state(enum daemon_status);
void * mpath_alloc_prin_response(int prin_sa);
int prin_do_scsi_ioctl(char *, int rq_servact, struct prin_resp * resp,
int noisy);
[Unit]
Description=Device-Mapper Multipath Device Controller
Before=iscsi.service iscsid.service lvm2-activation-early.service
-Before=local-fs-pre.target
-After=multipathd.socket
+Before=local-fs-pre.target systemd-udev-trigger.service
+After=multipathd.socket systemd-udevd.service
DefaultDependencies=no
Wants=local-fs-pre.target multipathd.socket blk-availability.service
Conflicts=shutdown.target
Type=notify
NotifyAccess=main
LimitCORE=infinity
-ExecStartPre=/sbin/modprobe dm-multipath
+ExecStartPre=/sbin/modprobe -a scsi_dh_alua scsi_dh_emc scsi_dh_rdac dm-multipath
ExecStart=/sbin/multipathd -d -s
ExecReload=/sbin/multipathd reconfigure
int fd;
};
+#define MIN_POLLS 1023
+
LIST_HEAD(clients);
pthread_mutex_t client_lock = PTHREAD_MUTEX_INITIALIZER;
struct pollfd *polls;
void * uxsock_listen(uxsock_trigger_fn uxsock_trigger, void * trigger_data)
{
int ux_sock;
- int rlen, timeout;
+ int rlen;
char *inbuf;
char *reply;
sigset_t mask;
+ int old_clients = MIN_POLLS;
ux_sock = ux_socket_listen(DEFAULT_SOCKET);
}
if (!conf) {
- condlog(1, "configuration changed");
+ condlog(1, "uxsock: configuration changed");
return NULL;
}
- timeout = conf->uxsock_timeout;
-
pthread_cleanup_push(uxsock_cleanup, NULL);
- polls = (struct pollfd *)MALLOC(0);
+ condlog(3, "uxsock: startup listener");
+ polls = (struct pollfd *)MALLOC(MIN_POLLS + 1);
+ if (!polls) {
+ condlog(0, "uxsock: failed to allocate poll fds");
+ return NULL;
+ }
pthread_sigmask(SIG_SETMASK, NULL, &mask);
sigdelset(&mask, SIGHUP);
sigdelset(&mask, SIGUSR1);
while (1) {
- 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 */
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;
+ if (num_clients != old_clients) {
+ struct pollfd *new;
+ if (num_clients < MIN_POLLS) {
+ new = REALLOC(polls, (1 + MIN_POLLS) *
+ sizeof(struct pollfd));
+ } else {
+ new = REALLOC(polls, (1+num_clients) *
+ sizeof(struct pollfd));
}
- dead_client(list_entry(clients.prev,
- typeof(struct client), node));
- }
- else
+ if (!new) {
+ pthread_mutex_unlock(&client_lock);
+ condlog(0, "%s: failed to realloc %d poll fds",
+ "uxsock", 1 + num_clients);
+ pthread_yield();
+ continue;
+ }
+ num_clients = old_clients;
polls = new;
+ }
polls[0].fd = ux_sock;
polls[0].events = POLLIN;
}
/* something went badly wrong! */
- condlog(0, "poll");
- pthread_exit(NULL);
+ condlog(0, "uxsock: poll failed with %d", errno);
+ break;
}
if (poll_count == 0)
}
pthread_mutex_unlock(&client_lock);
if (!c) {
- condlog(3, "cli%d: invalid fd %d",
+ condlog(4, "cli%d: new 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) {
+ if (recv_packet(c->fd, &inbuf,
+ uxsock_timeout) != 0) {
dead_client(c);
- } else {
- condlog(4, "Got request [%s]", inbuf);
- uxsock_trigger(inbuf, &reply, &rlen,
- trigger_data);
- if (reply) {
- if (send_packet(c->fd,
- reply) != 0) {
- dead_client(c);
- }
- condlog(4, "Reply [%d bytes]",
- rlen);
- FREE(reply);
- reply = NULL;
+ continue;
+ }
+ condlog(4, "cli[%d]: Got request [%s]",
+ i, inbuf);
+ uxsock_trigger(inbuf, &reply, &rlen,
+ trigger_data);
+ if (reply) {
+ if (send_packet(c->fd,
+ reply) != 0) {
+ dead_client(c);
+ } else {
+ condlog(4, "cli[%d]: "
+ "Reply [%d bytes]",
+ i, rlen);
}
- check_timeout(start_time, inbuf,
- timeout);
- FREE(inbuf);
+ FREE(reply);
+ reply = NULL;
}
+ check_timeout(start_time, inbuf,
+ uxsock_timeout);
+ FREE(inbuf);
}
}