Gitweb: https://git.opensvc.com/?p=multipath-tools/.git
+Add storage devices
+===================
+Follow the instructions in the libmultipath/hwtable.c header.
+
+
Mailing list (subscribers-only)
============
To subscribe and archives: https://www.redhat.com/mailman/listinfo/dm-devel
$(INSTALL_PROGRAM) -d $(DESTDIR)$(libudevdir)
$(INSTALL_PROGRAM) -m 755 kpartx_id $(DESTDIR)$(libudevdir)
$(INSTALL_PROGRAM) -d $(DESTDIR)$(libudevdir)/rules.d
+ $(INSTALL_PROGRAM) -m 644 dm-parts.rules $(DESTDIR)$(libudevdir)/rules.d/11-dm-parts.rules
$(INSTALL_PROGRAM) -m 644 kpartx.rules $(DESTDIR)$(libudevdir)/rules.d/66-kpartx.rules
+ $(INSTALL_PROGRAM) -m 644 del-part-nodes.rules $(DESTDIR)$(libudevdir)/rules.d/68-del-part-nodes.rules
$(INSTALL_PROGRAM) -d $(DESTDIR)$(man8dir)
$(INSTALL_PROGRAM) -m 644 $(EXEC).8.gz $(DESTDIR)$(man8dir)
$(RM) $(DESTDIR)$(bindir)/$(EXEC)
$(RM) $(DESTDIR)$(man8dir)/$(EXEC).8.gz
$(RM) $(DESTDIR)$(libudevdir)/kpartx_id
+ $(RM) $(DESTDIR)$(libudevdir)/rules.d/11-dm-parts.rules
$(RM) $(DESTDIR)$(libudevdir)/rules.d/66-kpartx.rules
$(RM) $(DESTDIR)$(libudevdir)/rules.d/67-kpartx-compat.rules
+ $(RM) $(DESTDIR)$(libudevdir)/rules.d/68-del-part-nodes.rules
clean:
$(RM) core *.o $(EXEC) *.gz
/* Couldn't open the device */
return -1;
}
+ } else if ((unsigned int)major(sbuf.st_rdev) != 94) {
+ /* Not a DASD */
+ return -1;
} else {
fd_dasd = fd;
}
--- /dev/null
+# These rules can delete partitions devnodes for slave devices
+# for certain aggregate devices such as multipath.
+# This is desirable to avoid confusion and keep the number
+# of device nodes and symlinks within limits.
+#
+# This is done only once on the first "add" or "change" event for
+# any given device.
+#
+# To suppress this, use the kernel parameter "dont_del_part_nodes",
+# or create an udev rule file that sets ENV{DONT_DEL_PART_NODES}="1".
+
+SUBSYSTEM!="block", GOTO="end_del_part_nodes"
+KERNEL!="sd*|dasd*|rbd*", GOTO="end_del_part_nodes"
+ACTION!="add|change", GOTO="end_del_part_nodes"
+
+IMPORT{cmdline}="dont_del_part_nodes"
+ENV{dont_del_part_nodes}=="1", GOTO="end_del_part_nodes"
+ENV{DONT_DEL_PART_NODES}=="1", GOTO="end_del_part_nodes"
+
+# dm-multipath
+ENV{DM_MULTIPATH_DEVICE_PATH}=="1", GOTO="del_part_nodes"
+
+# Other aggregate device types can be added here.
+
+GOTO="end_del_part_nodes"
+
+LABEL="del_part_nodes"
+IMPORT{db}="DM_DEL_PART_NODES"
+ENV{DM_DEL_PART_NODES}!="1", ENV{DM_DEL_PART_NODES}="1", \
+ RUN+="/usr/sbin/partx -d --nr 1-1024 $env{DEVNAME}"
+
+LABEL="end_del_part_nodes"
return r;
}
+static void
+strip_slash (char * device)
+{
+ char * p = device;
+
+ while (*(p++) != 0x0) {
+
+ if (*p == '/')
+ *p = '!';
+ }
+}
+
+static int format_partname(char *buf, size_t bufsiz,
+ const char *mapname, const char *delim, int part)
+{
+ if (snprintf(buf, bufsiz, "%s%s%d", mapname, delim, part) >= bufsiz)
+ return 0;
+ strip_slash(buf);
+ return 1;
+}
+
+static char *make_prefixed_uuid(int part, const char *uuid)
+{
+ char *prefixed_uuid;
+ int len = MAX_PREFIX_LEN + strlen(uuid) + 1;
+
+ prefixed_uuid = malloc(len);
+ if (!prefixed_uuid) {
+ fprintf(stderr, "cannot create prefixed uuid : %s\n",
+ strerror(errno));
+ return NULL;
+ }
+ snprintf(prefixed_uuid, len, UUID_PREFIX "%s", part, uuid);
+ return prefixed_uuid;
+}
+
int dm_addmap(int task, const char *name, const char *target,
const char *params, uint64_t size, int ro, const char *uuid,
int part, mode_t mode, uid_t uid, gid_t gid)
goto addout;
if (task == DM_DEVICE_CREATE && uuid) {
- prefixed_uuid = malloc(MAX_PREFIX_LEN + strlen(uuid) + 1);
- if (!prefixed_uuid) {
- fprintf(stderr, "cannot create prefixed uuid : %s\n",
- strerror(errno));
+ prefixed_uuid = make_prefixed_uuid(part, uuid);
+ if (prefixed_uuid == NULL)
goto addout;
- }
- sprintf(prefixed_uuid, UUID_PREFIX "%s", part, uuid);
if (!dm_task_set_uuid(dmt, prefixed_uuid))
goto addout;
}
return r;
}
-int dm_map_present(char * str, char **uuid)
+static int dm_map_present(char *str, char **uuid)
{
int r = 0;
struct dm_task *dmt;
return r;
}
+static int dm_rename (const char *old, const char *new)
+{
+ int r = 0;
+ struct dm_task *dmt;
+ uint16_t udev_flags = DM_UDEV_DISABLE_LIBRARY_FALLBACK;
+ uint32_t cookie = 0;
+
+ dmt = dm_task_create(DM_DEVICE_RENAME);
+ if (!dmt)
+ return r;
+
+ if (!dm_task_set_name(dmt, old) ||
+ !dm_task_set_newname(dmt, new) ||
+ !dm_task_no_open_count(dmt) ||
+ !dm_task_set_cookie(dmt, &cookie, udev_flags))
+ goto out;
+
+ r = dm_task_run(dmt);
+ dm_udev_wait(cookie);
+
+out:
+ dm_task_destroy(dmt);
+ return r;
+}
+
+static const char *dm_find_uuid(const char *uuid)
+{
+ struct dm_task *dmt;
+ const char *name = NULL, *tmp;
+
+ if ((dmt = dm_task_create(DM_DEVICE_INFO)) == NULL)
+ return NULL;
+
+ if (!dm_task_set_uuid(dmt, uuid) ||
+ !dm_task_run(dmt))
+ goto out;
+
+ tmp = dm_task_get_name(dmt);
+ if (tmp != NULL && *tmp != '\0')
+ name = strdup(tmp);
+
+out:
+ dm_task_destroy(dmt);
+ return name;
+}
char *
dm_mapname(int major, int minor)
}
static int
-dm_get_map(char *mapname, char * outparams)
+dm_get_map(const char *mapname, char * outparams)
{
int r = 1;
struct dm_task *dmt;
struct remove_data rd = { verbose };
return do_foreach_partmaps(mapname, uuid, devt, remove_partmap, &rd);
}
+
+int dm_find_part(const char *parent, const char *delim, int part,
+ const char *parent_uuid,
+ char *name, size_t namesiz, char **part_uuid, int verbose)
+{
+ int r;
+ char params[PARAMS_SIZE];
+ const char *tmp;
+ char *uuid;
+ int major, minor;
+ char dev_t[32];
+
+ if (!format_partname(name, namesiz, parent, delim, part)) {
+ if (verbose)
+ fprintf(stderr, "partname too small\n");
+ return 0;
+ }
+
+ r = dm_map_present(name, part_uuid);
+ if (r == 1 || parent_uuid == NULL || *parent_uuid == '\0')
+ return r;
+
+ uuid = make_prefixed_uuid(part, parent_uuid);
+ if (!uuid)
+ return 0;
+
+ tmp = dm_find_uuid(uuid);
+ if (tmp == NULL)
+ return r;
+
+ /* Sanity check on partition, see dm_foreach_partmaps */
+ if (dm_type(tmp, "linear") != 1)
+ goto out;
+
+ /*
+ * Try nondm uuid first. That way we avoid confusing
+ * a device name with a device mapper name.
+ */
+ if (!nondm_parse_uuid(parent_uuid, &major, &minor) &&
+ dm_devn(parent, &major, &minor))
+ goto out;
+ snprintf(dev_t, sizeof(dev_t), "%d:%d", major, minor);
+
+ if (dm_get_map(tmp, params))
+ goto out;
+
+ if (!strstr(params, dev_t))
+ goto out;
+
+ if (verbose)
+ fprintf(stderr, "found map %s for uuid %s, renaming to %s\n",
+ tmp, uuid, name);
+
+ r = dm_rename(tmp, name);
+ if (r == 0) {
+ free(uuid);
+ if (verbose)
+ fprintf(stderr, "renaming %s->%s failed\n", tmp, name);
+ } else
+ *part_uuid = uuid;
+out:
+ free((void*)tmp);
+ return r;
+}
+
+char *nondm_create_uuid(dev_t devt)
+{
+#define NONDM_UUID_BUFLEN (34 + sizeof(NONDM_UUID_PREFIX) + \
+ sizeof(NONDM_UUID_SUFFIX))
+ static char uuid_buf[NONDM_UUID_BUFLEN];
+ snprintf(uuid_buf, sizeof(uuid_buf), "%s_%u:%u_%s",
+ NONDM_UUID_PREFIX, major(devt), minor(devt),
+ NONDM_UUID_SUFFIX);
+ uuid_buf[NONDM_UUID_BUFLEN-1] = '\0';
+ return uuid_buf;
+}
+
+int nondm_parse_uuid(const char *uuid, int *major, int *minor)
+{
+ const char *p;
+ char *e;
+ int ma, mi;
+
+ if (strncmp(uuid, NONDM_UUID_PREFIX "_", sizeof(NONDM_UUID_PREFIX)))
+ return 0;
+ p = uuid + sizeof(NONDM_UUID_PREFIX);
+ ma = strtoul(p, &e, 10);
+ if (e == p || *e != ':')
+ return 0;
+ p = e + 1;
+ mi = strtoul(p, &e, 10);
+ if (e == p || *e != '_')
+ return 0;
+ p = e + 1;
+ if (strcmp(p, NONDM_UUID_SUFFIX))
+ return 0;
+
+ *major = ma;
+ *minor = mi;
+ return 1;
+}
int dm_simplecmd (int, const char *, int, uint16_t);
int dm_addmap (int, const char *, const char *, const char *, uint64_t,
int, const char *, int, mode_t, uid_t, gid_t);
-int dm_map_present (char *, char **);
char * dm_mapname(int major, int minor);
dev_t dm_get_first_dep(char *devname);
char * dm_mapuuid(const char *mapname);
int dm_devn (const char * mapname, int *major, int *minor);
int dm_remove_partmaps (char * mapname, char *uuid, dev_t devt, int verbose);
-int dm_no_partitions(char * mapname);
+int dm_find_part(const char *parent, const char *delim, int part,
+ const char *parent_uuid,
+ char *name, size_t namesiz, char **part_uuid, int verbose);
+/*
+ * UUID format for partitions created on non-DM devices
+ * ${UUID_PREFIX}devnode_${MAJOR}:${MINOR}_${NONDM_UUID_SUFFIX}"
+ * where ${UUID_PREFIX} is "part${PARTNO}-" (see devmapper.c).
+ *
+ * The suffix should be sufficiently unique to avoid incidental conflicts;
+ * the value below is a base64-encoded random number.
+ * The UUID format shouldn't be changed between kpartx releases.
+ */
+#define NONDM_UUID_PREFIX "devnode"
+#define NONDM_UUID_SUFFIX "Wh5pYvM"
+char *nondm_create_uuid(dev_t devt);
+int nondm_parse_uuid(const char *uuid, int *major, int *minor);
#endif /* _KPARTX_DEVMAPPER_H */
--- /dev/null
+# Rules for partitions created by kpartx
+
+KERNEL!="dm-*", GOTO="dm_parts_end"
+ACTION!="add|change", GOTO="dm_parts_end"
+ENV{DM_UUID}!="part[0-9]*", GOTO="dm_parts_end"
+
+# We must take care that symlinks don't get lost,
+# even if blkid fails in 13-dm-disk.rules later.
+#
+# Fixme: we have currently no way to avoid calling blkid on
+# partitions of broken mpath maps such as DM_NOSCAN.
+# But when partition devices appear, kpartx has likely read
+# the partition table shortly before, so odds are not bad
+# that blkid will also succeed.
+
+IMPORT{db}="ID_FS_USAGE"
+IMPORT{db}="ID_FS_UUID_ENC"
+IMPORT{db}="ID_FS_LABEL_ENC"
+IMPORT{db}="ID_PART_ENTRY_NAME"
+IMPORT{db}="ID_PART_ENTRY_UUID"
+IMPORT{db}="ID_PART_ENTRY_SCHEME"
+
+# Maps should take precedence over their members.
+ENV{DM_UDEV_LOW_PRIORITY_FLAG}!="1", OPTIONS+="link_priority=50"
+
+# Set some additional symlinks that typically exist for mpath
+# path members, too, and should be overridden.
+#
+# kpartx_id is very robust, it works for suspended maps and maps
+# with 0 dependencies. It sets DM_TYPE, DM_PART, DM_WWN
+IMPORT{program}=="kpartx_id %M %m $env{DM_UUID}"
+
+# DM_TYPE only has a reasonable value for partitions on multipath.
+ENV{DM_UUID}=="*-mpath-*", ENV{DM_TYPE}=="?*" \
+ SYMLINK+="disk/by-id/$env{DM_TYPE}-$env{DM_NAME}"
+ENV{DM_WWN}=="?*", ENV{DM_PART}=="?*", \
+ SYMLINK+="disk/by-id/wwn-$env{DM_WWN}-part$env{DM_PART}"
+
+LABEL="dm_parts_end"
int ptct = 0;
int udev_sync = 1;
-/*
- * UUID format for partitions created on non-DM devices
- * ${UUID_PREFIX}devnode_${MAJOR}:${MINOR}_${NONDM_UUID_SUFFIX}"
- * where ${UUID_PREFIX} is "part${PARTNO}-" (see devmapper.c).
- *
- * The suffix should be sufficiently unique to avoid incidental conflicts;
- * the value below is a base64-encoded random number.
- * The UUID format shouldn't be changed between kpartx releases.
- */
-#define NONDM_UUID_PREFIX "devnode"
-#define NONDM_UUID_SUFFIX "Wh5pYvM"
-
-static char *
-nondm_create_uuid(dev_t devt)
-{
-#define NONDM_UUID_BUFLEN (34 + sizeof(NONDM_UUID_PREFIX) + \
- sizeof(NONDM_UUID_SUFFIX))
- static char uuid_buf[NONDM_UUID_BUFLEN];
- snprintf(uuid_buf, sizeof(uuid_buf), "%s_%u:%u_%s",
- NONDM_UUID_PREFIX, major(devt), minor(devt),
- NONDM_UUID_SUFFIX);
- uuid_buf[NONDM_UUID_BUFLEN-1] = '\0';
- return uuid_buf;
-}
-
static void
addpts(char *t, ptreader f)
{
*delimiter = 'p';
}
-static void
-strip_slash (char * device)
-{
- char * p = device;
-
- while (*(p++) != 0x0) {
-
- if (*p == '/')
- *p = '!';
- }
-}
-
static int
find_devname_offset (char * device)
{
/* add/remove partitions to the kernel devmapper tables */
int r = 0;
+
+ if (what == DELETE) {
+ r = dm_remove_partmaps(mapname, uuid, buf.st_rdev,
+ verbose);
+ if (loopdev) {
+ if (del_loop(loopdev)) {
+ if (verbose)
+ fprintf(stderr, "can't del loop : %s\n",
+ loopdev);
+ r = 1;
+ } else
+ fprintf(stderr, "loop deleted : %s\n", loopdev);
+ }
+ goto end;
+ }
+
for (i = 0; i < ptct; i++) {
ptp = &pts[i];
break;
- case DELETE:
- r = dm_remove_partmaps(mapname, uuid, buf.st_rdev,
- verbose);
- if (loopdev) {
- if (del_loop(loopdev)) {
- if (verbose)
- printf("can't del loop : %s\n",
- loopdev);
- exit(1);
- }
- printf("loop deleted : %s\n", loopdev);
- }
- break;
-
case ADD:
case UPDATE:
/* ADD and UPDATE share the same code that adds new partitions. */
continue;
}
- if (safe_sprintf(partname, "%s%s%d",
- mapname, delim, j+1)) {
- fprintf(stderr, "partname too small\n");
- exit(1);
- }
- strip_slash(partname);
-
if (safe_sprintf(params, "%d:%d %" PRIu64 ,
major(buf.st_rdev), minor(buf.st_rdev), slices[j].start)) {
fprintf(stderr, "params too small\n");
exit(1);
}
- op = (dm_map_present(partname, &part_uuid) ?
- DM_DEVICE_RELOAD : DM_DEVICE_CREATE);
+ op = (dm_find_part(mapname, delim, j + 1, uuid,
+ partname, sizeof(partname),
+ &part_uuid, verbose) ?
+ DM_DEVICE_RELOAD : DM_DEVICE_CREATE);
if (part_uuid && uuid) {
if (check_uuid(uuid, part_uuid, &reason) != 0) {
fprintf(stderr, "Invalid slice %d\n",
k);
- if (safe_sprintf(partname, "%s%s%d",
- mapname, delim, j+1)) {
- fprintf(stderr, "partname too small\n");
- exit(1);
- }
- strip_slash(partname);
-
if (safe_sprintf(params, "%d:%d %" PRIu64,
major(buf.st_rdev), minor(buf.st_rdev),
slices[j].start)) {
exit(1);
}
- op = (dm_map_present(partname,
- &part_uuid) ?
+ op = (dm_find_part(mapname, delim, j + 1, uuid,
+ partname,
+ sizeof(partname),
+ &part_uuid, verbose) ?
DM_DEVICE_RELOAD : DM_DEVICE_CREATE);
if (part_uuid && uuid) {
for (j = MAXSLICES-1; j >= 0; j--) {
char *part_uuid, *reason;
- if (safe_sprintf(partname, "%s%s%d",
- mapname, delim, j+1)) {
- fprintf(stderr, "partname too small\n");
- exit(1);
- }
- strip_slash(partname);
-
if (slices[j].size ||
- !dm_map_present(partname, &part_uuid))
+ !dm_find_part(mapname, delim, j + 1, uuid,
+ partname, sizeof(partname),
+ &part_uuid, verbose))
continue;
if (part_uuid && uuid) {
printf("loop deleted : %s\n", device);
}
+end:
dm_lib_release();
dm_lib_exit();
#
KERNEL!="dm-*", GOTO="kpartx_end"
-ACTION=="remove", GOTO="kpartx_end"
+ACTION!="add|change", GOTO="kpartx_end"
+ENV{DM_UUID}!="?*", GOTO="kpartx_end"
-ENV{DM_TABLE_STATE}!="LIVE", GOTO="kpartx_end"
-ENV{DM_DEPS}=="0", GOTO="kpartx_end"
+# Create dm tables for partitions on multipath devices.
+ENV{DM_UUID}!="mpath-?*", GOTO="mpath_kpartx_end"
-ENV{DM_UUID}=="?*", IMPORT{program}=="kpartx_id %M %m $env{DM_UUID}"
+# DM_SUBSYSTEM_UDEV_FLAG1 is the "skip_kpartx" flag.
+# For events not generated by libdevmapper, we need to fetch it from db.
+ENV{DM_UDEV_PRIMARY_SOURCE_FLAG}!="1", IMPORT{db}="DM_SUBSYSTEM_UDEV_FLAG1"
+ENV{DM_SUBSYSTEM_UDEV_FLAG1}=="1", GOTO="mpath_kpartx_end"
-OPTIONS="link_priority=50"
+# 11-dm-mpath.rules sets MPATH_UNCHANGED for events that can be ignored.
+ENV{MPATH_UNCHANGED}=="1", GOTO="mpath_kpartx_end"
-ENV{DM_UUID}=="?*", ENV{DM_TYPE}=="?*" \
- SYMLINK+="disk/by-id/$env{DM_TYPE}-$env{DM_NAME}"
+# Don't run kpartx now if we know it will fail or hang.
+ENV{DM_SUSPENDED}=="1", GOTO="mpath_kpartx_end"
+ENV{DM_NOSCAN}=="1", GOTO="mpath_kpartx_end"
-# Create persistent links for multipath tables
-ENV{DM_WWN}=="?*", ENV{DM_PART}!="?*", \
- SYMLINK+="disk/by-id/wwn-$env{DM_WWN}"
+# Run kpartx
+GOTO="run_kpartx"
+LABEL="mpath_kpartx_end"
-# Create persistent links for partitions
-ENV{DM_WWN}=="?*", ENV{DM_PART}=="?*", \
- SYMLINK+="disk/by-id/wwn-$env{DM_WWN}-part$env{DM_PART}"
+## Code for other subsystems (non-multipath) could be placed here ##
-# Create persistent by-label/by-uuid links
-ENV{ID_FS_USAGE}=="?*", IMPORT{db}="ID_FS_USAGE"
-ENV{ID_FS_UUID_ENC}=="?*", IMPORT{db}="ID_FS_UUID_ENC"
-ENV{ID_FS_USAGE}=="filesystem|other|crypto", ENV{ID_FS_UUID_ENC}=="?*", \
- SYMLINK+="disk/by-uuid/$env{ID_FS_UUID_ENC}"
-ENV{ID_FS_LABEL_ENC}=="?*", IMPORT{db}="ID_FS_LABEL_ENC"
-ENV{ID_FS_USAGE}=="filesystem|other", ENV{ID_FS_LABEL_ENC}=="?*", \
- SYMLINK+="disk/by-label/$env{ID_FS_LABEL_ENC}"
+GOTO="kpartx_end"
-# Create dm tables for partitions
-ENV{DM_ACTION}=="PATH_FAILED|PATH_REINSTATED", GOTO="kpartx_end"
-ENV{DM_NR_VALID_PATHS}=="0", GOTO="kpartx_end"
-ENV{DM_UDEV_PRIMARY_SOURCE_FLAG}!="1", IMPORT{db}="DM_SUBSYSTEM_UDEV_FLAG1"
-ENV{DM_SUBSYSTEM_UDEV_FLAG1}=="1", GOTO="kpartx_end"
-ENV{DM_STATE}!="SUSPENDED", ENV{DM_UUID}=="mpath-*", \
- RUN+="/sbin/kpartx -un -p -part /dev/$name"
+LABEL="run_kpartx"
+RUN+="/sbin/kpartx -un -p -part /dev/$name"
LABEL="kpartx_end"
parted -s -- $1 mkpart prim ext2 1MiB -1s
}
+wipe_ptable() {
+ dd if=/dev/zero of=$1 bs=1b count=1
+}
+
step preparation
[[ $UID -eq 0 ]]
# Test invocation of kpartx with regular file here
LO2P1=/dev/mapper/$(basename $LO2)-foo1
$KPARTX $KPARTX_OPTS -a -p -foo $FILE2
+[[ -b $LO2P1 ]]
push_cleanup 'dmsetup remove -f $(basename $LO2P1)'
+step "remove partitions with deleted ptable"
+wipe_ptable $LO2
+$KPARTX $KPARTX_OPTS -d $LO2
+[[ ! -b $LO2P1 ]]
+
+mk_partitions $LO2
+$KPARTX $KPARTX_OPTS -a -p -foo $FILE2
+[[ -b $LO2P1 ]]
+
LO1P1=/dev/mapper/$(basename $LO1)-eggs1
$KPARTX $KPARTX_OPTS -a -p -eggs $LO1
push_cleanup 'dmsetup remove -f $(basename $LO1P1)'
[[ -b $SPAN2P1 ]]
[[ -b $SPAN1P1 ]]
+step "rename partitions on DM device to default"
+$KPARTX $KPARTX_OPTS -u /dev/mapper/$SPAN1
+[[ ! -b ${SPAN1P1} ]]
+# This assumes that $SPAN1 ends in a non-digit
+[[ -b ${SPAN1P1//-spam/} ]]
+
+step "rename partitions on DM device back from default"
+$KPARTX $KPARTX_OPTS -u -p -spam /dev/mapper/$SPAN1
+[[ -b ${SPAN1P1} ]]
+[[ ! -b ${SPANP1//-foo/} ]]
+
step "delete partitions on DM devices"
$KPARTX $KPARTX_OPTS -d /dev/mapper/$SPAN1 >&2
usleep $WAIT_US
[[ -b $LO2P1 ]]
[[ ! -b $SPAN2P1 ]]
+step "rename partitions on loop device"
+$KPARTX $KPARTX_OPTS -u -p -spam $LO2
+[[ ! -b ${LO2P1} ]]
+[[ -b ${LO2P1//-foo/-spam} ]]
+
+step "rename partitions on loop device back"
+$KPARTX $KPARTX_OPTS -u -p -foo $LO2
+[[ -b ${LO2P1} ]]
+[[ ! -b ${LO2P1//-foo/-spam} ]]
+
+step "rename partitions on loop device to default"
+$KPARTX $KPARTX_OPTS -u $LO2
+#read a
+[[ ! -b ${LO2P1} ]]
+# $LO1 ends in a digit
+[[ -b ${LO2P1//-foo/p} ]]
+
+step "rename partitions on loop device back from default"
+$KPARTX $KPARTX_OPTS -u -p -foo $LO2
+[[ -b ${LO2P1} ]]
+[[ ! -b ${LO2P1//-foo/p} ]]
+
+step "rename partitions on loop devices"
+$KPARTX $KPARTX_OPTS -u -p spam $LO2
+
step "delete partitions on loop devices"
$KPARTX $KPARTX_OPTS -d $LO3
/*
- * Copyright (C) 2015 - 2016 Red Hat, Inc.
+ * Copyright (C) 2015 - 2017 Red Hat, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
*/
#include <stdint.h>
+#include <stdbool.h>
#include <string.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <unistd.h>
#include <assert.h>
#include <json.h>
+#include <time.h>
#include <mpath_cmd.h>
#include "libdmmp/libdmmp.h"
#define _DMMP_JSON_MAJOR_VERSION 0
#define _DMMP_JSON_MAPS_KEY "maps"
#define _ERRNO_STR_BUFF_SIZE 256
+#define _IPC_MAX_CMD_LEN 512
+/* ^ Was _MAX_CMD_LEN in ./libmultipath/uxsock.h */
struct dmmp_context {
void (*log_func)(struct dmmp_context *ctx, int priority,
unsigned int tmo;
};
+/*
+ * The multipathd daemon are using "uxsock_timeout" to define timeout value,
+ * if timeout at daemon side, we will get message "timeout\n".
+ * To unify this timeout with `dmmp_context_timeout_set()`, this function
+ * will keep retry mpath_process_cmd() tile meet the time of
+ * dmmp_context_timeout_get().
+ * Need to free `*output` string manually.
+ */
+static int _process_cmd(struct dmmp_context *ctx, int fd, const char *cmd,
+ char **output);
+
+static int _ipc_connect(struct dmmp_context *ctx, int *fd);
+
_dmmp_getter_func_gen(dmmp_context_log_priority_get,
struct dmmp_context, ctx, log_priority,
int);
uint32_t i = 0;
int cur_json_major_version = -1;
int ar_maps_len = -1;
- int socket_fd = -1;
- int errno_save = 0;
- char errno_str_buff[_ERRNO_STR_BUFF_SIZE];
+ int ipc_fd = -1;
assert(ctx != NULL);
assert(dmmp_mps != NULL);
*dmmp_mps = NULL;
*dmmp_mp_count = 0;
- socket_fd = mpath_connect();
- if (socket_fd == -1) {
- errno_save = errno;
- memset(errno_str_buff, 0, _ERRNO_STR_BUFF_SIZE);
- strerror_r(errno_save, errno_str_buff, _ERRNO_STR_BUFF_SIZE);
- if (errno_save == ECONNREFUSED) {
- rc = DMMP_ERR_NO_DAEMON;
- _error(ctx, "Socket connection refuse. "
- "Maybe multipathd daemon is not running");
- } else {
- _error(ctx, "IPC failed with error %d(%s)", errno_save,
- errno_str_buff);
- rc = DMMP_ERR_IPC_ERROR;
- }
- goto out;
- }
+ _good(_ipc_connect(ctx, &ipc_fd), rc, out);
- if (mpath_process_cmd(socket_fd, _DMMP_IPC_SHOW_JSON_CMD,
- &j_str, ctx->tmo) != 0) {
- errno_save = errno;
- memset(errno_str_buff, 0, _ERRNO_STR_BUFF_SIZE);
- strerror_r(errno_save, errno_str_buff, _ERRNO_STR_BUFF_SIZE);
- if (errno_save == ETIMEDOUT) {
- rc = DMMP_ERR_IPC_TIMEOUT;
- _error(ctx, "IPC communication timeout, try to "
- "increase it via dmmp_context_timeout_set()");
- goto out;
- }
- _error(ctx, "IPC failed when process command '%s' with "
- "error %d(%s)", _DMMP_IPC_SHOW_JSON_CMD, errno_save,
- errno_str_buff);
- rc = DMMP_ERR_IPC_ERROR;
- goto out;
- }
-
- if ((j_str == NULL) || (strlen(j_str) == 0)) {
- _error(ctx, "IPC return empty reply for command %s",
- _DMMP_IPC_SHOW_JSON_CMD);
- rc = DMMP_ERR_IPC_ERROR;
- goto out;
- }
+ _good(_process_cmd(ctx, ipc_fd, _DMMP_IPC_SHOW_JSON_CMD, &j_str),
+ rc, out);
_debug(ctx, "Got json output from multipathd: '%s'", j_str);
+
j_token = json_tokener_new();
if (j_token == NULL) {
rc = DMMP_ERR_BUG;
}
out:
- if (socket_fd >= 0)
- mpath_disconnect(socket_fd);
+ if (ipc_fd >= 0)
+ mpath_disconnect(ipc_fd);
free(j_str);
if (j_token != NULL)
json_tokener_free(j_token);
return rc;
}
+
+static int _process_cmd(struct dmmp_context *ctx, int fd, const char *cmd,
+ char **output)
+{
+ int errno_save = 0;
+ int rc = DMMP_OK;
+ char errno_str_buff[_ERRNO_STR_BUFF_SIZE];
+ struct timespec start_ts;
+ struct timespec cur_ts;
+ unsigned int ipc_tmo = 0;
+ bool flag_check_tmo = false;
+ unsigned int elapsed = 0;
+
+ assert(output != NULL);
+ assert(ctx != NULL);
+ assert(cmd != NULL);
+
+ *output = NULL;
+
+ if (clock_gettime(CLOCK_MONOTONIC, &start_ts) != 0) {
+ _error(ctx, "BUG: Failed to get CLOCK_MONOTONIC time "
+ "via clock_gettime(), error %d", errno);
+ return DMMP_ERR_BUG;
+ }
+
+ ipc_tmo = ctx->tmo;
+ if (ctx->tmo == 0)
+ ipc_tmo = _DEFAULT_UXSOCK_TIMEOUT;
+
+invoke:
+ _debug(ctx, "Invoking IPC command '%s' with IPC tmo %u miliseconds",
+ cmd, ipc_tmo);
+ flag_check_tmo = false;
+ if (mpath_process_cmd(fd, cmd, output, ipc_tmo) != 0) {
+ errno_save = errno;
+ memset(errno_str_buff, 0, _ERRNO_STR_BUFF_SIZE);
+ strerror_r(errno_save, errno_str_buff, _ERRNO_STR_BUFF_SIZE);
+ if (errno_save == ETIMEDOUT) {
+ flag_check_tmo = true;
+ } else {
+ _error(ctx, "IPC failed when process command '%s' with "
+ "error %d(%s)", cmd, errno_save, errno_str_buff);
+ _debug(ctx, "%s", *output);
+ rc = DMMP_ERR_IPC_ERROR;
+ goto out;
+ }
+ }
+ if ((*output != NULL) &&
+ (strncmp(*output, "timeout", strlen("timeout")) == 0))
+ flag_check_tmo = true;
+
+ if (flag_check_tmo == true) {
+ free(*output);
+ *output = NULL;
+ if (ctx->tmo == 0) {
+ _debug(ctx, "IPC timeout, but user requested infinite "
+ "timeout");
+ goto invoke;
+ }
+
+ if (clock_gettime(CLOCK_MONOTONIC, &cur_ts) != 0) {
+ _error(ctx, "BUG: Failed to get CLOCK_MONOTONIC time "
+ "via clock_gettime(), error %d", errno);
+ rc = DMMP_ERR_BUG;
+ goto out;
+ }
+ elapsed = (cur_ts.tv_sec - start_ts.tv_sec) * 1000 +
+ (cur_ts.tv_nsec - start_ts.tv_nsec) / 1000000;
+
+ if (elapsed >= ctx->tmo) {
+ rc = DMMP_ERR_IPC_TIMEOUT;
+ _error(ctx, "Timeout, try to increase it via "
+ "dmmp_context_timeout_set()");
+ goto out;
+ }
+ if (ctx->tmo != 0)
+ ipc_tmo = ctx->tmo - elapsed;
+
+ _debug(ctx, "IPC timeout, but user requested timeout has not "
+ "reached yet, still have %u milliseconds", ipc_tmo);
+ goto invoke;
+ } else {
+ if ((*output == NULL) || (strlen(*output) == 0)) {
+ _error(ctx, "IPC return empty reply for command %s",
+ cmd);
+ rc = DMMP_ERR_IPC_ERROR;
+ goto out;
+ }
+ }
+
+out:
+ if (rc != DMMP_OK) {
+ free(*output);
+ *output = NULL;
+ }
+ return rc;
+}
+
+static int _ipc_connect(struct dmmp_context *ctx, int *fd)
+{
+ int rc = DMMP_OK;
+ int errno_save = 0;
+ char errno_str_buff[_ERRNO_STR_BUFF_SIZE];
+
+ assert(ctx != NULL);
+ assert(fd != NULL);
+
+ *fd = -1;
+
+ *fd = mpath_connect();
+ if (*fd == -1) {
+ errno_save = errno;
+ memset(errno_str_buff, 0, _ERRNO_STR_BUFF_SIZE);
+ strerror_r(errno_save, errno_str_buff, _ERRNO_STR_BUFF_SIZE);
+ if (errno_save == ECONNREFUSED) {
+ rc = DMMP_ERR_NO_DAEMON;
+ _error(ctx, "Socket connection refuse. "
+ "Maybe multipathd daemon is not running");
+ } else {
+ _error(ctx, "IPC failed with error %d(%s)", errno_save,
+ errno_str_buff);
+ rc = DMMP_ERR_IPC_ERROR;
+ }
+ }
+ return rc;
+}
+
+int dmmp_flush_mpath(struct dmmp_context *ctx, const char *mpath_name)
+{
+ int rc = DMMP_OK;
+ struct dmmp_mpath **dmmp_mps = NULL;
+ uint32_t dmmp_mp_count = 0;
+ uint32_t i = 0;
+ bool found = false;
+ int ipc_fd = -1;
+ char cmd[_IPC_MAX_CMD_LEN];
+ char *output = NULL;
+
+ assert(ctx != NULL);
+ assert(mpath_name != NULL);
+
+ snprintf(cmd, _IPC_MAX_CMD_LEN, "del map %s", mpath_name);
+ if (strlen(cmd) == _IPC_MAX_CMD_LEN - 1) {
+ rc = DMMP_ERR_INVALID_ARGUMENT;
+ _error(ctx, "Invalid mpath name %s", mpath_name);
+ goto out;
+ }
+
+ _good(_ipc_connect(ctx, &ipc_fd), rc, out);
+ _good(_process_cmd(ctx, ipc_fd, cmd, &output), rc, out);
+
+ /* _process_cmd() already make sure output is not NULL */
+
+ if (strncmp(output, "fail", strlen("fail")) == 0) {
+ /* Check whether specified mpath exits */
+ _good(dmmp_mpath_array_get(ctx, &dmmp_mps, &dmmp_mp_count),
+ rc, out);
+
+ for (i = 0; i < dmmp_mp_count; ++i) {
+ if (strcmp(dmmp_mpath_name_get(dmmp_mps[i]),
+ mpath_name) == 0) {
+ found = true;
+ break;
+ }
+ }
+
+ if (found == false) {
+ rc = DMMP_ERR_MPATH_NOT_FOUND;
+ _error(ctx, "Specified mpath %s not found", mpath_name);
+ goto out;
+ }
+
+ rc = DMMP_ERR_MPATH_BUSY;
+ _error(ctx, "Specified mpath is in use");
+ } else if (strncmp(output, "ok", strlen("ok")) != 0) {
+ rc = DMMP_ERR_BUG;
+ _error(ctx, "Got unexpected output for cmd '%s': '%s'",
+ cmd, output);
+ }
+
+out:
+ if (ipc_fd >= 0)
+ mpath_disconnect(ipc_fd);
+ dmmp_mpath_array_free(dmmp_mps, dmmp_mp_count);
+ free(output);
+ return rc;
+}
+
+int dmmp_reconfig(struct dmmp_context *ctx)
+{
+ int rc = DMMP_OK;
+ int ipc_fd = -1;
+ char *output = NULL;
+ char cmd[_IPC_MAX_CMD_LEN];
+
+ snprintf(cmd, _IPC_MAX_CMD_LEN, "%s", "reconfigure");
+
+ _good(_ipc_connect(ctx, &ipc_fd), rc, out);
+ _good(_process_cmd(ctx, ipc_fd, cmd, &output), rc, out);
+
+out:
+ if (ipc_fd >= 0)
+ mpath_disconnect(ipc_fd);
+ free(output);
+ return rc;
+}
#define DMMP_ERR_IPC_ERROR 4
#define DMMP_ERR_NO_DAEMON 5
#define DMMP_ERR_INCOMPATIBLE 6
+#define DMMP_ERR_MPATH_BUSY 7
+#define DMMP_ERR_MPATH_NOT_FOUND 8
+#define DMMP_ERR_INVALID_ARGUMENT 9
/*
* Use the syslog severity level as log priority
*
* @tmo:
* Timeout in milliseconds(1 seconds equal 1000 milliseconds).
+ * 0 means infinite, function only return when error or pass.
*
* Return:
* void
*/
DMMP_DLL_EXPORT const char *dmmp_path_status_str(uint32_t path_status);
+/**
+ * dmmp_flush_mpath() - Flush specified multipath device map if unused.
+ *
+ * Flush a multipath device map specified as parameter, if unused.
+ *
+ * @ctx:
+ * Pointer of 'struct dmmp_context'.
+ * If this pointer is NULL, your program will be terminated by assert.
+ * @mpath_name:
+ * const char *. The name of multipath device map.
+ *
+ * Return:
+ * int. Valid error codes are:
+ *
+ * * DMMP_OK
+ *
+ * * DMMP_ERR_BUG
+ *
+ * * DMMP_ERR_NO_MEMORY
+ *
+ * * DMMP_ERR_NO_DAEMON
+ *
+ * * DMMP_ERR_MPATH_BUSY
+ *
+ * * DMMP_ERR_MPATH_NOT_FOUND
+ *
+ * * DMMP_ERR_INVALID_ARGUMENT
+ *
+ * Error number could be converted to string by dmmp_strerror().
+ */
+DMMP_DLL_EXPORT int dmmp_flush_mpath(struct dmmp_context *ctx,
+ const char *mpath_name);
+
+/**
+ * dmmp_reconfig() - Instruct multipathd daemon to do reconfiguration.
+ *
+ * Instruct multipathd daemon to do reconfiguration.
+ *
+ * @ctx:
+ * Pointer of 'struct dmmp_context'.
+ * If this pointer is NULL, your program will be terminated by assert.
+ *
+ * Return:
+ * int. Valid error codes are:
+ *
+ * * DMMP_OK
+ *
+ * * DMMP_ERR_BUG
+ *
+ * * DMMP_ERR_NO_MEMORY
+ *
+ * * DMMP_ERR_NO_DAEMON
+ *
+ * Error number could be converted to string by dmmp_strerror().
+ */
+DMMP_DLL_EXPORT int dmmp_reconfig(struct dmmp_context *ctx);
+
#ifdef __cplusplus
} /* End of extern "C" */
#endif
/*
- * Copyright (C) 2015 - 2016 Red Hat, Inc.
+ * Copyright (C) 2015 - 2017 Red Hat, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
{DMMP_ERR_IPC_ERROR, "Error when communicate with multipathd daemon"},
{DMMP_ERR_NO_DAEMON, "The multipathd daemon not started"},
{DMMP_ERR_INCOMPATIBLE, "Incompatible multipathd daemon version"},
+ {DMMP_ERR_MPATH_BUSY, "Specified multipath device map is in use"},
+ {DMMP_ERR_MPATH_NOT_FOUND, "Specified multipath not found"},
+ {DMMP_ERR_INVALID_ARGUMENT, "Invalid argument"},
};
_dmmp_str_func_gen(dmmp_strerror, int, rc, _DMMP_RC_MSG_CONV);
/*
- * Copyright (C) 2015-2016 Red Hat, Inc.
+ * Copyright (C) 2015-2017 Red Hat, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
#include <string.h>
#include <pthread.h>
#include <unistd.h>
+#include <stdbool.h>
#include <libdmmp/libdmmp.h>
} while(0)
#define PASS(...) fprintf(stdout, "PASS: "__VA_ARGS__ );
#define FILE_NAME_SIZE 256
-#define TMO 10000 /* Forcing timeout to 10 seconds */
+#define TMO 60000 /* Forcing timeout to 60 seconds */
int test_paths(struct dmmp_path_group *mp_pg)
{
struct dmmp_context *ctx = NULL;
struct dmmp_mpath **dmmp_mps = NULL;
uint32_t dmmp_mp_count = 0;
+ uint32_t old_dmmp_mp_count = 0;
const char *name = NULL;
const char *wwid = NULL;
const char *kdev = NULL;
uint32_t i = 0;
int rc = EXIT_SUCCESS;
+ const char *old_name = NULL;
+ bool found = false;
ctx = dmmp_context_new();
dmmp_context_log_priority_set(ctx, DMMP_LOG_PRIORITY_DEBUG);
dmmp_context_timeout_set(ctx, TMO);
if (dmmp_context_timeout_get(ctx) != TMO)
FAIL(rc, out, "dmmp_context_timeout_set(): Failed to set "
- "timeout to %u", TMO);
+ "timeout to %u\n", TMO);
if (dmmp_mpath_array_get(ctx, &dmmp_mps, &dmmp_mp_count) != 0)
FAIL(rc, out, "dmmp_mpath_array_get(): rc != 0\n");
if (rc != 0)
goto out;
}
+
+ old_name = strdup(name);
+ if (old_name == NULL)
+ FAIL(rc, out, "strdup(): no memory\n");
+
+ old_dmmp_mp_count = dmmp_mp_count;
+
dmmp_mpath_array_free(dmmp_mps, dmmp_mp_count);
+
+ if (dmmp_flush_mpath(ctx, old_name) != DMMP_OK)
+ FAIL(rc, out, "dmmp_flush_mpath(): Failed\n");
+
+ PASS("dmmp_flush_mpath(): OK\n");
+
+ if (dmmp_reconfig(ctx) != DMMP_OK)
+ FAIL(rc, out, "dmmp_reconfig(): Failed\n");
+
+ PASS("dmmp_reconfig(): OK\n");
+
+ if (dmmp_mpath_array_get(ctx, &dmmp_mps, &dmmp_mp_count) != 0)
+ FAIL(rc, out, "dmmp_mpath_array_get(): rc != 0\n");
+ if (dmmp_mp_count == 0)
+ FAIL(rc, out, "dmmp_mpath_array_get(): "
+ "Got no multipath devices\n");
+
+ if (dmmp_mp_count != old_dmmp_mp_count)
+ FAIL(rc, out, "Got different mpath count after reconfig: "
+ "old %" PRIu32 ", new %" PRIu32 "\n", old_dmmp_mp_count,
+ dmmp_mp_count);
+
+ for (i = 0; i < dmmp_mp_count; ++i) {
+ if (strcmp(old_name, dmmp_mpath_name_get(dmmp_mps[i])) == 0) {
+ found = true;
+ break;
+ }
+ }
+ if (found == false)
+ FAIL(rc, out, "dmmp_reconfig() does not recreate deleted "
+ "mpath %s\n", old_name);
+
out:
dmmp_context_free(ctx);
exit(rc);
len = mpath_recv_reply_len(fd, timeout);
if (len <= 0)
return len;
- if (len > MAX_REPLY_LEN) {
- errno = EINVAL;
- return -1;
- }
*reply = malloc(len);
if (!*reply)
return -1;
#define DEFAULT_SOCKET "/org/kernel/linux/storage/multipathd"
#define DEFAULT_REPLY_TIMEOUT 4000
-#define MAX_REPLY_LEN 65536
/*
int map_present;
int major, minor;
int ret;
- int j;
- unsigned char *keyp;
uint64_t prkey;
struct config *conf;
select_reservation_key(conf, mpp);
put_multipath_config(conf);
+ memcpy(&prkey, paramp->sa_key, 8);
+ if (mpp->prkey_source == PRKEY_SOURCE_FILE && prkey &&
+ ((!get_be64(mpp->reservation_key) && MPATH_PROUT_REG_SA) ||
+ MPATH_PROUT_REG_IGN_SA)) {
+ memcpy(&mpp->reservation_key, paramp->sa_key, 8);
+ if (update_prkey(alias, get_be64(mpp->reservation_key))) {
+ condlog(0, "%s: failed to set prkey for multipathd.",
+ alias);
+ ret = MPATH_PR_DMMP_ERROR;
+ goto out1;
+ }
+ }
+
+ if (memcmp(paramp->key, &mpp->reservation_key, 8) &&
+ memcmp(paramp->sa_key, &mpp->reservation_key, 8)) {
+ condlog(0, "%s: configured reservation key doesn't match: 0x%" PRIx64, alias, get_be64(mpp->reservation_key));
+ ret = MPATH_PR_SYNTAX_ERROR;
+ goto out1;
+ }
+
switch(rq_servact)
{
case MPATH_PROUT_REG_SA:
if ((ret == MPATH_PR_SUCCESS) && ((rq_servact == MPATH_PROUT_REG_SA) ||
(rq_servact == MPATH_PROUT_REG_IGN_SA)))
{
- keyp=paramp->sa_key;
- prkey = 0;
- for (j = 0; j < 8; ++j) {
- if (j > 0)
- prkey <<= 8;
- prkey |= *keyp;
- ++keyp;
- }
- if (prkey == 0)
- update_prflag(alias, "unset", noisy);
- else
- update_prflag(alias, "set", noisy);
- } else {
- if ((ret == MPATH_PR_SUCCESS) && ((rq_servact == MPATH_PROUT_CLEAR_SA) ||
- (rq_servact == MPATH_PROUT_PREE_AB_SA ))){
- update_prflag(alias, "unset", noisy);
- }
+ if (prkey == 0) {
+ update_prflag(alias, 0);
+ update_prkey(alias, 0);
+ } else
+ update_prflag(alias, 1);
+ } else if ((ret == MPATH_PR_SUCCESS) && (rq_servact == MPATH_PROUT_CLEAR_SA)) {
+ update_prflag(alias, 0);
+ update_prkey(alias, 0);
}
out1:
free_multipathvec(curmp, KEEP_PATHS);
goto out1;
}
- if (mpp->reservation_key ){
- memcpy (pamp->key, mpp->reservation_key, 8);
+ if (get_be64(mpp->reservation_key)){
+ memcpy (pamp->key, &mpp->reservation_key, 8);
condlog (3, "%s: reservation key set.", mpp->wwid);
}
pptr=pamp->trnptid_list[0];
for (i = 0; i < num; i++){
- if (mpp->reservation_key &&
+ if (get_be64(mpp->reservation_key) &&
memcmp(pr_buff->prin_descriptor.prin_readfd.descriptors[i]->key,
- mpp->reservation_key, 8)){
+ &mpp->reservation_key, 8)){
/*register with tarnsport id*/
memset(pamp, 0, length);
pamp->trnptid_list[0] = pptr;
}
else
{
- if (mpp->reservation_key)
+ if (get_be64(mpp->reservation_key))
found = 1;
}
if (found){
memset (pamp, 0, length);
- memcpy (pamp->sa_key, mpp->reservation_key, 8);
+ memcpy (pamp->sa_key, &mpp->reservation_key, 8);
memset (pamp->key, 0, 8);
status = mpath_prout_reg(mpp, MPATH_PROUT_REG_SA, rq_scope, rq_type, pamp, noisy);
}
{
int noisy=0;
struct prin_resp *resp;
- int i,j, ret, isFound;
- unsigned char *keyp;
- uint64_t prkey;
+ int i, ret, isFound;
- if (!mpp->reservation_key)
+ if (!get_be64(mpp->reservation_key))
{
/* Nothing to do. Assuming pr mgmt feature is disabled*/
condlog(3, "%s: reservation_key not set in multipath.conf", mpp->alias);
return MPATH_PR_SUCCESS;
}
- prkey = 0;
- keyp = mpp->reservation_key;
- for (j = 0; j < 8; ++j) {
- if (j > 0)
- prkey <<= 8;
- prkey |= *keyp;
- ++keyp;
- }
- condlog(2, "%s: Multipath reservation_key: 0x%" PRIx64 " ", mpp->alias, prkey);
+ condlog(2, "%s: Multipath reservation_key: 0x%" PRIx64 " ", mpp->alias,
+ get_be64(mpp->reservation_key));
isFound =0;
for (i = 0; i < resp->prin_descriptor.prin_readkeys.additional_length/8; i++ )
condlog(2, "%s: PR IN READKEYS[%d] reservation key:", mpp->alias, i);
dumpHex((char *)&resp->prin_descriptor.prin_readkeys.key_list[i*8], 8 , 1);
- if (!memcmp(mpp->reservation_key, &resp->prin_descriptor.prin_readkeys.key_list[i*8], 8))
+ if (!memcmp(&mpp->reservation_key, &resp->prin_descriptor.prin_readkeys.key_list[i*8], 8))
{
condlog(2, "%s: reservation key found in pr in readkeys response", mpp->alias);
isFound =1;
#include "mpathpr.h"
-int update_prflag(char * arg1, char * arg2, int noisy)
+static int do_update_pr(char *alias, char *arg)
{
int fd;
- char str[64];
+ char str[256];
char *reply;
int ret = 0;
fd = mpath_connect();
if (fd == -1) {
condlog (0, "ux socket connect error");
- return 1 ;
+ return -1;
}
- snprintf(str,sizeof(str),"map %s %s", arg1, arg2);
- condlog (2, "%s: pr flag message=%s", arg1, str);
+ snprintf(str,sizeof(str),"map %s %s", alias, arg);
+ condlog (2, "%s: pr message=%s", alias, str);
if (send_packet(fd, str) != 0) {
- condlog(2, "%s: message=%s send error=%d", arg1, str, errno);
+ condlog(2, "%s: message=%s send error=%d", alias, str, errno);
mpath_disconnect(fd);
- return -2;
+ return -1;
}
ret = recv_packet(fd, &reply, DEFAULT_REPLY_TIMEOUT);
if (ret < 0) {
- condlog(2, "%s: message=%s recv error=%d", arg1, str, errno);
- ret = -2;
+ condlog(2, "%s: message=%s recv error=%d", alias, str, errno);
+ ret = -1;
} else {
- condlog (2, "%s: message=%s reply=%s", arg1, str, reply);
- if (!reply || strncmp(reply,"ok", 2) == 0)
+ condlog (2, "%s: message=%s reply=%s", alias, str, reply);
+ if (reply && strncmp(reply,"ok", 2) == 0)
+ ret = 0;
+ else
ret = -1;
- else if (strncmp(reply, "fail", 4) == 0)
- ret = -2;
- else{
- ret = atoi(reply);
- }
}
free(reply);
mpath_disconnect(fd);
return ret;
}
+
+int update_prflag(char *mapname, int set) {
+ return do_update_pr(mapname, (set)? "setprstatus" : "unsetprstatus");
+}
+
+int update_prkey(char *mapname, uint64_t prkey) {
+ char str[256];
+
+ if (prkey)
+ sprintf(str, "setprkey key %" PRIx64, prkey);
+ else
+ sprintf(str, "unsetprkey");
+ return do_update_pr(mapname, str);
+}
int send_prout_activepath(char * dev, int rq_servact, int rq_scope,
unsigned int rq_type, struct prout_param_descriptor * paramp, int noisy);
-int update_prflag(char * arg1, char * arg2, int noisy);
+int update_prflag(char *mapname, int set);
+int update_prkey(char *mapname, uint64_t prkey);
void * mpath_alloc_prin_response(int prin_sa);
int update_map_pr(struct multipath *mpp);
pgpolicies.o debug.o defaults.o uevent.o time-util.o \
switchgroup.o uxsock.o print.o alias.o log_pthread.o \
log.o configure.o structs_vec.o sysfs.o prio.o checkers.o \
- lock.o waiter.o file.o wwids.o prioritizers/alua_rtpg.o
+ lock.o waiter.o file.o wwids.o prioritizers/alua_rtpg.o prkey.o
all: $(LIBS)
--- /dev/null
+#ifndef BYTEORDER_H_INCLUDED
+#define BYTEORDER_H_INCLUDED
+
+#ifdef __linux__
+# include <endian.h>
+# include <byteswap.h>
+#else
+# error unsupported
+#endif
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+# define le16_to_cpu(x) (x)
+# define be16_to_cpu(x) bswap_16(x)
+# define le32_to_cpu(x) (x)
+# define le64_to_cpu(x) (x)
+# define be32_to_cpu(x) bswap_32(x)
+# define be64_to_cpu(x) bswap_64(x)
+#elif BYTE_ORDER == BIG_ENDIAN
+# define le16_to_cpu(x) bswap_16(x)
+# define be16_to_cpu(x) (x)
+# define le32_to_cpu(x) bswap_32(x)
+# define le64_to_cpu(x) bswap_64(x)
+# define be32_to_cpu(x) (x)
+# define be64_to_cpu(x) (x)
+#else
+# error unsupported
+#endif
+
+#define cpu_to_le16(x) le16_to_cpu(x)
+#define cpu_to_be16(x) be16_to_cpu(x)
+#define cpu_to_le32(x) le32_to_cpu(x)
+#define cpu_to_be32(x) be32_to_cpu(x)
+#define cpu_to_le64(x) le64_to_cpu(x)
+#define cpu_to_be64(x) be64_to_cpu(x)
+
+struct be64 {
+ uint64_t _v;
+};
+
+#define get_be64(x) be64_to_cpu((x)._v)
+#define put_be64(x, y) do { (x)._v = cpu_to_be64(y); } while (0)
+
+
+#endif /* BYTEORDER_H_INCLUDED */
"timeout",
"removed",
"delayed",
+ "none",
};
static LIST_HEAD(checkers);
if (!c)
return NULL;
snprintf(c->name, CHECKER_NAME_LEN, "%s", name);
+ if (!strncmp(c->name, NONE, 4))
+ goto done;
snprintf(libname, LIB_CHECKER_NAMELEN, "%s/libcheck%s.so",
multipath_dir, name);
if (stat(libname,&stbuf) < 0) {
condlog(0, "A dynamic linking error occurred: (%s)", errstr);
if (!c->repair)
goto out;
-
- c->fd = 0;
+done:
+ c->fd = -1;
c->sync = 1;
list_add(&c->node, &checkers);
return c;
if (!c)
return 1;
c->mpcontext = mpctxt_addr;
- return c->init(c);
+ if (c->init)
+ return c->init(c);
+ return 0;
}
void checker_put (struct checker * dst)
{
struct checker * src;
- if (!dst || !dst->check)
+ if (!dst || !strlen(dst->name))
return;
src = checker_lookup(dst->name);
if (dst->free)
MSG(c, "checker disabled");
return;
}
-
- c->repair(c);
+ if (c->repair)
+ c->repair(c);
}
-int checker_check (struct checker * c)
+int checker_check (struct checker * c, int path_state)
{
int r;
MSG(c, "checker disabled");
return PATH_UNCHECKED;
}
- if (c->fd <= 0) {
+ if (!strncmp(c->name, NONE, 4))
+ return path_state;
+
+ if (c->fd < 0) {
MSG(c, "no usable fd");
return PATH_WILD;
}
{
if (!c)
return 0;
+ if (!strncmp(c->name, NONE, 4))
+ return 1;
return (c->check) ? 1 : 0;
}
#define EMC_CLARIION "emc_clariion"
#define READSECTOR0 "readsector0"
#define CCISS_TUR "cciss_tur"
+#define NONE "none"
#define RBD "rbd"
#define ASYNC_TIMEOUT_SEC 30
void checker_enable (struct checker *);
void checker_disable (struct checker *);
void checker_repair (struct checker *);
-int checker_check (struct checker *);
+int checker_check (struct checker *, int);
int checker_selected (struct checker *);
char * checker_name (struct checker *);
char * checker_message (struct checker *);
#include "../libmultipath/debug.h"
#include "../libmultipath/util.h"
#include "../libmultipath/time-util.h"
+#include "../libmultipath/util.h"
struct rbd_checker_context;
typedef int (thread_fn)(struct rbd_checker_context *ct, char *msg);
return PATH_UP;
}
-static int safe_write(int fd, const void *buf, size_t count)
-{
- while (count > 0) {
- ssize_t r = write(fd, buf, count);
- if (r < 0) {
- if (errno == EINTR)
- continue;
- return -errno;
- }
- count -= r;
- buf = (char *)buf + r;
- }
- return 0;
-}
-
static int sysfs_write_rbd_bus(const char *which, const char *buf,
size_t buf_len)
{
if (conf->wwids_file)
FREE(conf->wwids_file);
+ if (conf->prkeys_file)
+ FREE(conf->prkeys_file);
+
if (conf->prio_name)
FREE(conf->prio_name);
if (conf->config_dir)
FREE(conf->config_dir);
- if (conf->reservation_key)
- FREE(conf->reservation_key);
-
free_blacklist(conf->blist_devnode);
free_blacklist(conf->blist_wwid);
free_blacklist(conf->blist_property);
get_sys_max_fds(&conf->max_fds);
conf->bindings_file = set_default(DEFAULT_BINDINGS_FILE);
conf->wwids_file = set_default(DEFAULT_WWIDS_FILE);
+ conf->prkeys_file = set_default(DEFAULT_PRKEYS_FILE);
conf->multipath_dir = set_default(DEFAULT_MULTIPATHDIR);
conf->attribute_flags = 0;
conf->reassign_maps = DEFAULT_REASSIGN_MAPS;
conf->bindings_file = set_default(DEFAULT_BINDINGS_FILE);
if (!conf->multipath_dir || !conf->bindings_file ||
- !conf->wwids_file)
+ !conf->wwids_file || !conf->prkeys_file)
goto out;
return conf;
#include <sys/types.h>
#include <stdint.h>
#include <urcu.h>
+#include <inttypes.h>
+#include "byteorder.h"
#define ORIGIN_DEFAULT 0
#define ORIGIN_CONFIG 1
CMD_REMOVE_WWID,
CMD_RESET_WWIDS,
CMD_ADD_WWID,
+ CMD_USABLE_PATHS,
};
enum force_reload_types {
char * prio_name;
char * prio_args;
- unsigned char * reservation_key;
+ int prkey_source;
+ struct be64 reservation_key;
int pgpolicy;
int pgfailback;
int rr_weight;
char * hwhandler;
char * bindings_file;
char * wwids_file;
+ char * prkeys_file;
char * prio_name;
char * prio_args;
char * checker_name;
char * alias_prefix;
char * partition_delim;
char * config_dir;
- unsigned char * reservation_key;
+ int prkey_source;
+ struct be64 reservation_key;
vector keywords;
vector mptable;
return 0;
}
+struct udev_device *get_udev_device(const char *dev, enum devtypes dev_type)
+{
+ struct udev_device *ud = NULL;
+ const char *base;
+
+ if (dev == NULL || *dev == '\0')
+ return NULL;
+
+ switch (dev_type) {
+ case DEV_DEVNODE:
+ case DEV_DEVMAP:
+ /* This should be GNU basename, compiler will warn if not */
+ base = basename(dev);
+ if (*base == '\0')
+ break;
+ ud = udev_device_new_from_subsystem_sysname(udev, "block",
+ base);
+ break;
+ case DEV_DEVT:
+ ud = udev_device_new_from_devnum(udev, 'b', parse_devt(dev));
+ break;
+ case DEV_UEVENT:
+ ud = udev_device_new_from_environment(udev);
+ break;
+ default:
+ condlog(0, "Internal error: get_udev_device called with invalid type %d\n",
+ dev_type);
+ break;
+ }
+ if (ud == NULL)
+ condlog(2, "get_udev_device: failed to look up %s with type %d",
+ dev, dev_type);
+ return ud;
+}
+
/*
* returns:
* 0 - success
pp = find_path_by_dev(pathvec, buff);
if (!pp) {
- struct udev_device *udevice = udev_device_new_from_subsystem_sysname(udev, "block", buff);
+ struct udev_device *udevice =
+ get_udev_device(buff, dev_type);
- if (!udevice) {
- condlog(2, "%s: can't get udev device", buff);
+ if (!udevice)
return 1;
- }
+
conf = get_multipath_config();
ret = store_pathinfo(pathvec, conf, udevice,
flags, &pp);
}
pp = find_path_by_dev(pathvec, buff);
if (!pp) {
- struct udev_device *udevice = udev_device_new_from_devnum(udev, 'b', parse_devt(dev));
+ struct udev_device *udevice =
+ get_udev_device(dev, dev_type);
- if (!udevice) {
- condlog(2, "%s: can't get udev device", dev);
+ if (!udevice)
return 1;
- }
+
conf = get_multipath_config();
ret = store_pathinfo(pathvec, conf, udevice,
flags, &pp);
}
if (dev_type == DEV_UEVENT) {
- struct udev_device *udevice = udev_device_new_from_environment(udev);
+ struct udev_device *udevice = get_udev_device(dev, dev_type);
- if (!udevice) {
- condlog(2, "%s: can't get udev device", dev);
+ if (!udevice)
return 1;
- }
+
conf = get_multipath_config();
ret = store_pathinfo(pathvec, conf, udevice,
flags, &pp);
vector pathvec, char **wwid);
int reload_map(struct vectors *vecs, struct multipath *mpp, int refresh, int is_daemon);
int sysfs_get_host_adapter_name(struct path *pp, char *adapter_name);
+struct udev_device *get_udev_device(const char *dev, enum devtypes dev_type);
#define DEFAULT_CONFIGFILE "/etc/multipath.conf"
#define DEFAULT_BINDINGS_FILE "/etc/multipath/bindings"
#define DEFAULT_WWIDS_FILE "/etc/multipath/wwids"
+#define DEFAULT_PRKEYS_FILE "/etc/multipath/prkeys"
#define DEFAULT_CONFIG_DIR "/etc/multipath/conf.d"
char * set_default (char * str);
static int
dm_addmap (int task, const char *target, struct multipath *mpp,
- char * params, int ro, int skip_kpartx) {
+ char * params, int ro, uint16_t udev_flags) {
int r = 0;
struct dm_task *dmt;
char *prefixed_uuid = NULL;
uint32_t cookie = 0;
- uint16_t udev_flags = DM_UDEV_DISABLE_LIBRARY_FALLBACK | ((skip_kpartx == SKIP_KPARTX_ON)? MPATH_UDEV_NO_KPARTX_FLAG : 0);
+
+ /* Need to add this here to allow 0 to be passed in udev_flags */
+ udev_flags |= DM_UDEV_DISABLE_LIBRARY_FALLBACK;
if (!(dmt = libmp_dm_task_create (task)))
return 0;
return r;
}
+static uint16_t build_udev_flags(const struct multipath *mpp, int reload)
+{
+ /* DM_UDEV_DISABLE_LIBRARY_FALLBACK is added in dm_addmap */
+ return (mpp->skip_kpartx == SKIP_KPARTX_ON ?
+ MPATH_UDEV_NO_KPARTX_FLAG : 0) |
+ (mpp->nr_active == 0 ?
+ MPATH_UDEV_NO_PATHS_FLAG : 0) |
+ (reload && !mpp->force_udev_reload ?
+ MPATH_UDEV_RELOAD_FLAG : 0);
+}
+
int dm_addmap_create (struct multipath *mpp, char * params)
{
int ro;
+ uint16_t udev_flags = build_udev_flags(mpp, 0);
for (ro = 0; ro <= 1; ro++) {
int err;
if (dm_addmap(DM_DEVICE_CREATE, TGT_MPATH, mpp, params, ro,
- mpp->skip_kpartx))
+ udev_flags))
return 1;
/*
* DM_DEVICE_CREATE is actually DM_DEV_CREATE + DM_TABLE_LOAD.
int dm_addmap_reload(struct multipath *mpp, char *params, int flush)
{
int r = 0;
- uint16_t udev_flags = ((mpp->force_udev_reload)?
- 0 : MPATH_UDEV_RELOAD_FLAG) |
- ((mpp->skip_kpartx == SKIP_KPARTX_ON)?
- MPATH_UDEV_NO_KPARTX_FLAG : 0) |
- ((mpp->nr_active)? 0 : MPATH_UDEV_NO_PATHS_FLAG);
+ uint16_t udev_flags = build_udev_flags(mpp, 1);
/*
* DM_DEVICE_RELOAD cannot wait on a cookie, as
*/
if (!mpp->force_readonly)
r = dm_addmap(DM_DEVICE_RELOAD, TGT_MPATH, mpp, params,
- ADDMAP_RW, SKIP_KPARTX_OFF);
+ ADDMAP_RW, 0);
if (!r) {
if (!mpp->force_readonly && errno != EROFS)
return 0;
r = dm_addmap(DM_DEVICE_RELOAD, TGT_MPATH, mpp,
- params, ADDMAP_RO, SKIP_KPARTX_OFF);
+ params, ADDMAP_RO, 0);
}
if (r)
r = dm_simplecmd(DM_DEVICE_RESUME, mpp->alias, !flush,
return r;
}
-int dm_get_uuid(char *name, char *uuid)
+int dm_get_uuid(const char *name, char *uuid)
{
if (dm_get_prefixed_uuid(name, uuid))
return 1;
return dm_groupmsg("disable", mapname, index);
}
+struct multipath *dm_get_multipath(const char *name)
+{
+ struct multipath *mpp = NULL;
+
+ mpp = alloc_multipath();
+ if (!mpp)
+ return NULL;
+
+ mpp->alias = STRDUP(name);
+
+ if (!mpp->alias)
+ goto out;
+
+ if (dm_get_map(name, &mpp->size, NULL))
+ goto out;
+
+ dm_get_uuid(name, mpp->wwid);
+ dm_get_info(name, &mpp->dmi);
+
+ return mpp;
+out:
+ free_multipath(mpp, KEEP_PATHS);
+ return NULL;
+}
+
int
dm_get_maps (vector mp)
{
if (!dm_is_mpath(names->name))
goto next;
- mpp = alloc_multipath();
-
+ mpp = dm_get_multipath(names->name);
if (!mpp)
goto out;
- mpp->alias = STRDUP(names->name);
-
- if (!mpp->alias)
- goto out1;
-
- if (dm_get_map(names->name, &mpp->size, NULL))
- goto out1;
-
- dm_get_uuid(names->name, mpp->wwid);
- dm_get_info(names->name, &mpp->dmi);
-
if (!vector_alloc_slot(mp))
- goto out1;
+ goto out;
vector_set_slot(mp, mpp);
mpp = NULL;
r = 0;
goto out;
-out1:
- free_multipath(mpp, KEEP_PATHS);
out:
dm_task_destroy (dmt);
return r;
}
int
-dm_get_info (char * mapname, struct dm_info ** dmi)
+dm_get_info (const char * mapname, struct dm_info ** dmi)
{
if (!mapname)
return 1;
if (delim)
rd.delim = delim;
- if (isdigit(new[strlen(new)-1]))
- rd.delim = "p";
- else
- rd.delim = "";
+ else {
+ if (isdigit(new[strlen(new)-1]))
+ rd.delim = "p";
+ else
+ rd.delim = "";
+ }
return do_foreach_partmaps(old, rename_partmap, &rd);
}
char * dm_mapname(int major, int minor);
int dm_remove_partmaps (const char * mapname, int need_sync,
int deferred_remove);
-int dm_get_uuid(char *name, char *uuid);
-int dm_get_info (char * mapname, struct dm_info ** dmi);
+int dm_get_uuid(const char *name, char *uuid);
+int dm_get_info (const char * mapname, struct dm_info ** dmi);
int dm_rename (const char * old, char * new, char * delim, int skip_kpartx);
int dm_reassign(const char * mapname);
int dm_reassign_table(const char *name, char *old, char *new);
int dm_setgeometry(struct multipath *mpp);
+struct multipath *dm_get_multipath(const char *name);
#define VERSION_GE(v, minv) ( \
(v[0] > minv[0]) || \
#include "blacklist.h"
#include "defaults.h"
#include "prio.h"
+#include "util.h"
#include <errno.h>
#include <inttypes.h>
#include "mpath_cmd.h"
declare_def_handler(wwids_file, set_str)
declare_def_snprint(wwids_file, print_str)
+declare_def_handler(prkeys_file, set_str)
+declare_def_snprint(prkeys_file, print_str)
+
declare_def_handler(retain_hwhandler, set_yes_no_undef)
declare_def_snprint_defint(retain_hwhandler, print_yes_no_undef, YNU_NO)
declare_ovr_handler(retain_hwhandler, set_yes_no_undef)
}
static int
-set_reservation_key(vector strvec, void *ptr)
+set_reservation_key(vector strvec, struct be64 *be64_ptr, int *source_ptr)
{
- unsigned char **uchar_ptr = (unsigned char **)ptr;
char *buff;
- char *tbuff;
- int j, k;
- int len;
uint64_t prkey;
buff = set_value(strvec);
if (!buff)
return 1;
- tbuff = buff;
-
- if (!memcmp("0x",buff, 2))
- buff = buff + 2;
-
- len = strlen(buff);
-
- k = strspn(buff, "0123456789aAbBcCdDeEfF");
-
- if (len != k) {
- FREE(tbuff);
- return 1;
+ if (strcmp(buff, "file") == 0) {
+ *source_ptr = PRKEY_SOURCE_FILE;
+ put_be64(*be64_ptr, 0);
+ FREE(buff);
+ return 0;
}
- if (1 != sscanf (buff, "%" SCNx64 "", &prkey))
- {
- FREE(tbuff);
+ if (parse_prkey(buff, &prkey) != 0) {
+ FREE(buff);
return 1;
}
-
- if (!*uchar_ptr)
- *uchar_ptr = (unsigned char *) malloc(8);
-
- memset(*uchar_ptr, 0, 8);
-
- for (j = 7; j >= 0; --j) {
- (*uchar_ptr)[j] = (prkey & 0xff);
- prkey >>= 8;
- }
-
- FREE(tbuff);
+ *source_ptr = PRKEY_SOURCE_CONF;
+ put_be64(*be64_ptr, prkey);
+ FREE(buff);
return 0;
}
int
-print_reservation_key(char * buff, int len, void * ptr)
+print_reservation_key(char * buff, int len, struct be64 key, int source)
{
- unsigned char **uchar_ptr = (unsigned char **)ptr;
- int i;
- unsigned char *keyp;
- uint64_t prkey = 0;
-
- if (!*uchar_ptr)
+ if (source == PRKEY_SOURCE_NONE)
return 0;
- keyp = (unsigned char *)(*uchar_ptr);
- for (i = 0; i < 8; i++) {
- if (i > 0)
- prkey <<= 8;
- prkey |= *keyp;
- keyp++;
- }
- return snprintf(buff, len, "0x%" PRIx64, prkey);
+ if (source == PRKEY_SOURCE_FILE)
+ return snprintf(buff, len, "file");
+ return snprintf(buff, len, "0x%" PRIx64, get_be64(key));
+}
+
+static int
+def_reservation_key_handler(struct config *conf, vector strvec)
+{
+ return set_reservation_key(strvec, &conf->reservation_key,
+ &conf->prkey_source);
+}
+
+static int
+snprint_def_reservation_key (struct config *conf, char * buff, int len,
+ void * data)
+{
+ return print_reservation_key(buff, len, conf->reservation_key,
+ conf->prkey_source);
+}
+
+static int
+mp_reservation_key_handler(struct config *conf, vector strvec)
+{
+ struct mpentry * mpe = VECTOR_LAST_SLOT(conf->mptable);
+ if (!mpe)
+ return 1;
+ return set_reservation_key(strvec, &mpe->reservation_key,
+ &mpe->prkey_source);
}
-declare_def_handler(reservation_key, set_reservation_key)
-declare_def_snprint(reservation_key, print_reservation_key)
-declare_mp_handler(reservation_key, set_reservation_key)
-declare_mp_snprint(reservation_key, print_reservation_key)
+static int
+snprint_mp_reservation_key (struct config *conf, char * buff, int len,
+ void * data)
+{
+ struct mpentry * mpe = (struct mpentry *)data;
+ return print_reservation_key(buff, len, mpe->reservation_key,
+ mpe->prkey_source);
+}
static int
set_off_int_undef(vector strvec, void *ptr)
install_keyword("dev_loss_tmo", &def_dev_loss_handler, &snprint_def_dev_loss);
install_keyword("bindings_file", &def_bindings_file_handler, &snprint_def_bindings_file);
install_keyword("wwids_file", &def_wwids_file_handler, &snprint_def_wwids_file);
+ install_keyword("prkeys_file", &def_prkeys_file_handler, &snprint_def_prkeys_file);
install_keyword("log_checker_err", &def_log_checker_err_handler, &snprint_def_log_checker_err);
install_keyword("reservation_key", &def_reservation_key_handler, &snprint_def_reservation_key);
install_keyword("retain_attached_hw_handler", &def_retain_hwhandler_handler, &snprint_def_retain_hwhandler);
#include "vector.h"
#endif
+#include "byteorder.h"
+
void init_keywords(vector keywords);
int get_sys_max_fds(int *);
int print_rr_weight (char * buff, int len, void *ptr);
int print_no_path_retry(char * buff, int len, void *ptr);
int print_fast_io_fail(char * buff, int len, void *ptr);
int print_dev_loss(char * buff, int len, void *ptr);
-int print_reservation_key(char * buff, int len, void * ptr);
+int print_reservation_key(char * buff, int len, struct be64 key, int source);
int print_off_int_undef(char * buff, int len, void *ptr);
#endif /* _DICT_H */
static int
nvme_sysfs_pathinfo (struct path * pp, vector hwtable)
{
- struct udev_device *parent, *nvme = NULL;
+ struct udev_device *parent;
+ const char *attr_path = NULL;
- parent = pp->udev;
- while (parent) {
- const char *subsys = udev_device_get_subsystem(parent);
+ attr_path = udev_device_get_sysname(pp->udev);
+ if (!attr_path)
+ return 1;
- if (subsys && !strncmp(subsys, "nvme", 4)) {
- nvme = parent;
- break;
- }
- parent = udev_device_get_parent(parent);
- }
- if (!nvme)
+ if (sscanf(attr_path, "nvme%dn%d",
+ &pp->sg_id.host_no,
+ &pp->sg_id.scsi_id) != 2)
+ return 1;
+ pp->sg_id.channel = 0;
+ pp->sg_id.lun = 0;
+
+ parent = udev_device_get_parent(pp->udev);
+ if (!parent)
return 1;
snprintf(pp->vendor_id, SCSI_VENDOR_SIZE, "NVME");
- snprintf(pp->product_id, SCSI_PRODUCT_SIZE, "%s", udev_device_get_sysattr_value(nvme, "model"));
- snprintf(pp->serial, SERIAL_SIZE, "%s", udev_device_get_sysattr_value(nvme, "serial"));
- snprintf(pp->rev, SCSI_REV_SIZE, "%s", udev_device_get_sysattr_value(nvme, "firmware_rev"));
+ snprintf(pp->product_id, SCSI_PRODUCT_SIZE, "%s",
+ udev_device_get_sysattr_value(parent, "model"));
+ snprintf(pp->serial, SERIAL_SIZE, "%s",
+ udev_device_get_sysattr_value(parent, "serial"));
+ snprintf(pp->rev, SCSI_REV_SIZE, "%s",
+ udev_device_get_sysattr_value(parent, "firmware_rev"));
+
+ condlog(3, "%s: vendor = %s", pp->dev, pp->vendor_id);
+ condlog(3, "%s: product = %s", pp->dev, pp->product_id);
+ condlog(3, "%s: serial = %s", pp->dev, pp->serial);
+ condlog(3, "%s: rev = %s", pp->dev, pp->rev);
- condlog(3, "%s: vendor:%s product:%s serial:%s rev:%s", pp->dev,
- pp->vendor_id, pp->product_id, pp->serial, pp->rev);
pp->hwe = find_hwe(hwtable, pp->vendor_id, pp->product_id, NULL);
return 0;
}
pp->offline = 0;
if (!strncmp(buff, "new", 3) ||
- !strncmp(buff, "reconnecting", 12) ||
- !strncmp(buff, "resetting", 9))
+ !strncmp(buff, "deleting", 8))
return PATH_PENDING;
else if (!strncmp(buff, "live", 4))
return PATH_UP;
}
int
-get_state (struct path * pp, struct config *conf, int daemon)
+get_state (struct path * pp, struct config *conf, int daemon, int oldstate)
{
struct checker * c = &pp->checker;
int state;
if (!conf->checker_timeout &&
sysfs_get_timeout(pp, &(c->timeout)) <= 0)
c->timeout = DEF_TIMEOUT;
- state = checker_check(c);
- condlog(3, "%s: state = %s", pp->dev, checker_state_name(state));
+ state = checker_check(c, oldstate);
+ condlog(3, "%s: %s state = %s", pp->dev,
+ checker_name(c), checker_state_name(state));
if (state != PATH_UP && state != PATH_GHOST &&
strlen(checker_message(c)))
condlog(3, "%s: checker msg is \"%s\"",
path_state = path_offline(pp);
if (path_state == PATH_REMOVED)
goto blank;
+ else if (mask & DI_NOIO) {
+ /*
+ * Avoid any IO on the device itself.
+ * Behave like DI_CHECKER in the "path unavailable" case.
+ */
+ pp->chkrstate = pp->state = path_state;
+ return PATHINFO_OK;
+ }
/*
* fetch info not available through sysfs
if (mask & DI_CHECKER) {
if (path_state == PATH_UP) {
- pp->chkrstate = pp->state = get_state(pp, conf, 0);
+ pp->chkrstate = pp->state = get_state(pp, conf, 0,
+ path_state);
if (pp->state == PATH_UNCHECKED ||
pp->state == PATH_WILD)
goto blank;
int do_tur (char *);
int path_offline (struct path *);
-int get_state (struct path * pp, struct config * conf, int daemon);
+int get_state (struct path * pp, struct config * conf, int daemon, int state);
int get_vpd_sgio (int fd, int pg, char * str, int maxlen);
int pathinfo (struct path * pp, struct config * conf, int mask);
int alloc_path_with_pathinfo (struct config *conf, struct udev_device *udevice,
__DI_PRIO,
__DI_WWID,
__DI_BLACKLIST,
+ __DI_NOIO,
};
#define DI_SYSFS (1 << __DI_SYSFS)
#define DI_PRIO (1 << __DI_PRIO)
#define DI_WWID (1 << __DI_WWID)
#define DI_BLACKLIST (1 << __DI_BLACKLIST)
+#define DI_NOIO (1 << __DI_NOIO) /* Avoid IO on the device */
#define DI_ALL (DI_SYSFS | DI_SERIAL | DI_CHECKER | DI_PRIO | \
DI_WWID)
.no_path_retry = 30,
},
{
- /* 3510 / 6020 and 6120 */
+ /* (Dot Hill) 3310, 3320, 3510 and 3511 */
.vendor = "SUN",
- .product = "(StorEdge 3510|T4)",
+ .product = "StorEdge 3",
.pgpolicy = MULTIBUS,
},
{
},
/*
* Infinidat
+ *
+ * Maintainer: Arnon Yaari
+ * Mail: arnony@infinidat.com
*/
{
.vendor = "NFINIDAT",
.product = "InfiniBox",
.pgpolicy = GROUP_BY_PRIO,
- .pgfailback = -FAILBACK_IMMEDIATE,
+ .pgfailback = 30,
.prio_name = PRIO_ALUA,
+ .selector = "round-robin 0",
+ .rr_weight = RR_WEIGHT_PRIO,
+ .no_path_retry = NO_PATH_RETRY_FAIL,
+ .minio = 1,
+ .minio_rq = 1,
+ .flush_on_last_del = FLUSH_ENABLED,
+ .fast_io_fail = 15,
+ .dev_loss = 15,
},
/*
* Nimble Storage
.pgpolicy = MULTIBUS,
.no_path_retry = 12,
},
+ {
+ /* iglu blaze family */
+ .vendor = "(XIOTECH|XIOtech)",
+ .product = "IGLU DISK",
+ .pgpolicy = MULTIBUS,
+ .no_path_retry = 30,
+ },
/*
* Violin Memory
*/
.vendor = "NVME",
.product = ".*",
.uid_attribute = "ID_WWN",
- .checker_name = DIRECTIO,
+ .checker_name = NONE,
.retain_hwhandler = RETAIN_HWHANDLER_OFF,
},
+ /*
+ * Dot Hill Systems - Seagate Technology
+ */
+ {
+ /* SANnet family */
+ .vendor = "DotHill",
+ .product = "SANnet",
+ .pgpolicy = MULTIBUS,
+ .no_path_retry = 30,
+ },
+ {
+ /* R/Evolution family */
+ .vendor = "DotHill",
+ .product = "R/Evo",
+ .pgpolicy = GROUP_BY_PRIO,
+ .pgfailback = -FAILBACK_IMMEDIATE,
+ .prio_name = PRIO_ALUA,
+ .no_path_retry = 30,
+ },
+ {
+ /* AssuredSAN family */
+ .vendor = "DotHill",
+ .product = "^DH",
+ .pgpolicy = GROUP_BY_PRIO,
+ .pgfailback = -FAILBACK_IMMEDIATE,
+ .prio_name = PRIO_ALUA,
+ .no_path_retry = 30,
+ },
/*
* EOL
*/
int monitored_count = 0;
vector_foreach_slot(vecs->pathvec, pp, i)
- if (pp->fd != -1)
+ if (pp->fd >= 0)
monitored_count++;
fwd += snprintf(buff + fwd, len - fwd, "\npaths: %d\nbusy: %s\n",
monitored_count, is_uevent_busy()? "True" : "False");
--- /dev/null
+#include "structs.h"
+#include "file.h"
+#include "debug.h"
+#include "config.h"
+#include "util.h"
+#include "propsel.h"
+#include "prkey.h"
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#include <errno.h>
+
+#define PRKEY_READ 0
+#define PRKEY_WRITE 1
+
+static int do_prkey(int fd, char *wwid, char *keystr, int cmd)
+{
+ char buf[4097];
+ char *ptr;
+ off_t start = 0;
+ int bytes;
+
+ while (1) {
+ if (lseek(fd, start, SEEK_SET) < 0) {
+ condlog(0, "prkey file read lseek failed : %s",
+ strerror(errno));
+ return 1;
+ }
+ bytes = read(fd, buf, 4096);
+ if (bytes < 0) {
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ condlog(0, "failed to read from prkey file : %s",
+ strerror(errno));
+ return 1;
+ }
+ if (!bytes) {
+ ptr = NULL;
+ break;
+ }
+ buf[bytes] = '\0';
+ ptr = strstr(buf, wwid);
+ while (ptr) {
+ if (ptr == buf || *(ptr - 1) != ' ' ||
+ *(ptr + strlen(wwid)) != '\n')
+ ptr = strstr(ptr + strlen(wwid), wwid);
+ else
+ break;
+ }
+ if (ptr) {
+ condlog(3, "found prkey for '%s'", wwid);
+ ptr[strlen(wwid)] = '\0';
+ if (ptr - PRKEY_SIZE < buf ||
+ (ptr - PRKEY_SIZE != buf &&
+ *(ptr - PRKEY_SIZE - 1) != '\n')) {
+ condlog(0, "malformed prkey file line for wwid: '%s'", ptr);
+ return 1;
+ }
+ ptr = ptr - PRKEY_SIZE;
+ break;
+ }
+ ptr = strrchr(buf, '\n');
+ if (ptr == NULL) {
+ condlog(4, "couldn't file newline, assuming end of file");
+ break;
+ }
+ start = start + (ptr - buf) + 1;
+ }
+ if (cmd == PRKEY_READ) {
+ if (!ptr || *ptr == '#')
+ return 1;
+ memcpy(keystr, ptr, PRKEY_SIZE - 1);
+ keystr[PRKEY_SIZE - 1] = '\0';
+ return 0;
+ }
+ if (!ptr && !keystr)
+ return 0;
+ if (ptr) {
+ if (lseek(fd, start + (ptr - buf), SEEK_SET) < 0) {
+ condlog(0, "prkey write lseek failed : %s",
+ strerror(errno));
+ return 1;
+ }
+ }
+ if (!keystr) {
+ if (safe_write(fd, "#", 1) < 0) {
+ condlog(0, "failed to write to prkey file : %s",
+ strerror(errno));
+ return 1;
+ }
+ return 0;
+ }
+ if (!ptr) {
+ if (lseek(fd, 0, SEEK_END) < 0) {
+ condlog(0, "prkey write lseek failed : %s",
+ strerror(errno));
+ return 1;
+ }
+ }
+ bytes = sprintf(buf, "%s %s\n", keystr, wwid);
+ if (safe_write(fd, buf, bytes) < 0) {
+ condlog(0, "failed to write to prkey file: %s",
+ strerror(errno));
+ return 1;
+ }
+ return 0;
+}
+
+int get_prkey(struct config *conf, struct multipath *mpp, uint64_t *prkey)
+{
+ int fd;
+ int unused;
+ int ret = 1;
+ char keystr[PRKEY_SIZE];
+
+ if (!strlen(mpp->wwid))
+ goto out;
+
+ fd = open_file(conf->prkeys_file, &unused, PRKEYS_FILE_HEADER);
+ if (fd < 0)
+ goto out;
+ ret = do_prkey(fd, mpp->wwid, keystr, PRKEY_READ);
+ if (ret)
+ goto out_file;
+ ret = !!parse_prkey(keystr, prkey);
+out_file:
+ close(fd);
+out:
+ return ret;
+}
+
+int set_prkey(struct config *conf, struct multipath *mpp, uint64_t prkey)
+{
+ int fd;
+ int can_write = 1;
+ int ret = 1;
+ char keystr[PRKEY_SIZE];
+
+ if (!strlen(mpp->wwid))
+ goto out;
+
+ fd = open_file(conf->prkeys_file, &can_write, PRKEYS_FILE_HEADER);
+ if (fd < 0)
+ goto out;
+ if (!can_write) {
+ condlog(0, "cannot set prkey, prkeys file is read-only");
+ goto out_file;
+ }
+ if (prkey) {
+ snprintf(keystr, PRKEY_SIZE, "0x%016" PRIx64, prkey);
+ keystr[PRKEY_SIZE - 1] = '\0';
+ ret = do_prkey(fd, mpp->wwid, keystr, PRKEY_WRITE);
+ }
+ else
+ ret = do_prkey(fd, mpp->wwid, NULL, PRKEY_WRITE);
+ if (ret == 0)
+ select_reservation_key(conf, mpp);
+ if (get_be64(mpp->reservation_key) != prkey)
+ ret = 1;
+out_file:
+ close(fd);
+out:
+ return ret;
+}
--- /dev/null
+#ifndef _PRKEY_H
+#define _PRKEY_H
+
+#include "structs.h"
+#include <inttypes.h>
+
+#define PRKEYS_FILE_HEADER \
+"# Multipath persistent reservation keys, Version : 1.0\n" \
+"# NOTE: this file is automatically maintained by the multipathd program.\n" \
+"# You should not need to edit this file in normal circumstances.\n" \
+"#\n" \
+"# Format:\n" \
+"# prkey wwid\n" \
+"#\n"
+
+int set_prkey(struct config *conf, struct multipath *mpp, uint64_t prkey);
+int get_prkey(struct config *conf, struct multipath *mpp, uint64_t *prkey);
+
+#endif /* _PRKEY_H */
#include "dict.h"
#include "util.h"
#include "prioritizers/alua_rtpg.h"
+#include "prkey.h"
#include <inttypes.h>
pgpolicyfn *pgpolicies[] = {
#define mp_set_mpe(var) \
do_set(var, mp->mpe, mp->var, "(setting: multipath.conf multipaths section)")
#define mp_set_hwe(var) \
-do_set(var, mp->hwe, mp->var, "(setting: array configuration)")
+do_set(var, mp->hwe, mp->var, "(setting: storage device configuration)")
#define mp_set_ovr(var) \
do_set(var, conf->overrides, mp->var, "(setting: multipath.conf overrides section)")
#define mp_set_conf(var) \
#define pp_set_mpe(var) \
do_set(var, mpe, pp->var, "(setting: multipath.conf multipaths section)")
#define pp_set_hwe(var) \
-do_set(var, pp->hwe, pp->var, "(setting: array configuration)")
+do_set(var, pp->hwe, pp->var, "(setting: storage device configuration)")
#define pp_set_conf(var) \
do_set(var, conf, pp->var, "(setting: multipath.conf defaults/devices section)")
#define pp_set_ovr(var) \
#define set_attr_conf(var, shift) \
do_attr_set(var, conf, shift, "(setting: multipath.conf defaults/devices section)")
+#define do_prkey_set(src, msg) \
+do { \
+ if (src && src->prkey_source != PRKEY_SOURCE_NONE) { \
+ mp->prkey_source = src->prkey_source; \
+ mp->reservation_key = src->reservation_key; \
+ origin = msg; \
+ goto out; \
+ } \
+} while (0)
+
int select_mode(struct config *conf, struct multipath *mp)
{
char *origin;
do_set(user_friendly_names, conf->overrides, user_friendly_names,
"(setting: multipath.conf overrides section)");
do_set(user_friendly_names, mp->hwe, user_friendly_names,
- "(setting: array configuration)");
+ "(setting: storage device configuration)");
do_set(user_friendly_names, conf, user_friendly_names,
"(setting: multipath.conf defaults/devices section)");
do_default(user_friendly_names, DEFAULT_USER_FRIENDLY_NAMES);
if (pp->detect_checker == DETECT_CHECKER_ON && pp->tpgs > 0) {
checker_name = TUR;
- origin = "(setting: array autodetected)";
+ origin = "(setting: storage device autodetected)";
goto out;
}
do_set(checker_name, conf->overrides, checker_name, "(setting: multipath.conf overrides section)");
- do_set(checker_name, pp->hwe, checker_name, "(setting: array configuration)");
+ do_set(checker_name, pp->hwe, checker_name, "(setting: storage device configuration)");
do_set(checker_name, conf, checker_name, "(setting: multipath.conf defaults/devices section)");
do_default(checker_name, DEFAULT_CHECKER);
out:
if (pp->detect_prio == DETECT_PRIO_ON) {
detect_prio(conf, pp);
if (prio_selected(p)) {
- origin = "(setting: array autodetected)";
+ origin = "(setting: storage device autodetected)";
goto out;
}
}
mpe = find_mpe(conf->mptable, pp->wwid);
set_prio(conf->multipath_dir, mpe, "(setting: multipath.conf multipaths section)");
set_prio(conf->multipath_dir, conf->overrides, "(setting: multipath.conf overrides section)");
- set_prio(conf->multipath_dir, pp->hwe, "(setting: array configuration)");
+ set_prio(conf->multipath_dir, pp->hwe, "(setting: storage device configuration)");
set_prio(conf->multipath_dir, conf, "(setting: multipath.conf defaults/devices section)");
prio_get(conf->multipath_dir, p, DEFAULT_PRIO, DEFAULT_PRIO_ARGS);
origin = "(setting: multipath internal)";
do_set(minio_rq, mp->mpe, mp->minio, "(setting: multipath.conf multipaths section)");
do_set(minio_rq, conf->overrides, mp->minio, "(setting: multipath.conf overrides section)");
- do_set(minio_rq, mp->hwe, mp->minio, "(setting: array configuration)");
+ do_set(minio_rq, mp->hwe, mp->minio, "(setting: storage device configuration)");
do_set(minio_rq, conf, mp->minio, "(setting: multipath.conf defaults/devices section)");
do_default(mp->minio, DEFAULT_MINIO_RQ);
out:
int select_reservation_key(struct config *conf, struct multipath *mp)
{
- char *origin, buff[12];
+ char *origin, buff[PRKEY_SIZE];
+ char *from_file = "";
+ uint64_t prkey = 0;
- mp_set_mpe(reservation_key);
- mp_set_conf(reservation_key);
- mp->reservation_key = NULL;
+ do_prkey_set(mp->mpe, "(setting: multipath.conf multipaths section)");
+ do_prkey_set(conf, "(setting: multipath.conf defaults/devices section)");
+ put_be64(mp->reservation_key, 0);
+ mp->prkey_source = PRKEY_SOURCE_NONE;
return 0;
out:
- print_reservation_key(buff, 12, &mp->reservation_key);
- condlog(3, "%s: reservation_key = %s %s", mp->alias, buff, origin);
+ if (mp->prkey_source == PRKEY_SOURCE_FILE) {
+ from_file = " (from prkeys file)";
+ if (get_prkey(conf, mp, &prkey) != 0)
+ put_be64(mp->reservation_key, 0);
+ else
+ put_be64(mp->reservation_key, prkey);
+ }
+ print_reservation_key(buff, PRKEY_SIZE, mp->reservation_key,
+ mp->prkey_source);
+ condlog(3, "%s: reservation_key = %s %s%s", mp->alias, buff, origin,
+ from_file);
return 0;
}
int add_feature(char **f, const char *n)
{
- int c = 0, d, l = 0;
- char *e, *p, *t;
- const char *q;
+ int c = 0, d, l;
+ char *e, *t;
if (!f)
return 1;
if (!n || *n == '0')
return 0;
+ if (strchr(n, ' ') != NULL) {
+ condlog(0, "internal error: feature \"%s\" contains spaces", n);
+ return 1;
+ }
+
/* default feature is null */
if(!*f)
{
}
/* Check if feature is already present */
- if (*f) {
- if (strstr(*f, n))
- return 0;
-
- /* Get feature count */
- c = strtoul(*f, &e, 10);
- if (*f == e)
- /* parse error */
- return 1;
+ if (strstr(*f, n))
+ return 0;
- /* Check if we need to increase feature count space */
- l = strlen(*f) + strlen(n) + 1;
+ /* Get feature count */
+ c = strtoul(*f, &e, 10);
+ if (*f == e || (*e != ' ' && *e != '\0')) {
+ condlog(0, "parse error in feature string \"%s\"", *f);
+ return 1;
}
- /* Count new features */
- if ((c % 10) == 9)
- l++;
+
+ /* Add 1 digit and 1 space */
+ l = strlen(e) + strlen(n) + 2;
+
c++;
- q = n;
- while (*q != '\0') {
- if (*q == ' ' && q[1] != '\0' && q[1] != ' ') {
- if ((c % 10) == 9)
- l++;
- c++;
- }
- q++;
- }
+ /* Check if we need more digits for feature count */
+ for (d = c; d >= 10; d /= 10)
+ l++;
t = MALLOC(l + 1);
if (!t)
return 1;
- memset(t, 0, l + 1);
-
- /* Update feature count */
- d = c;
- l = 1;
- while (d > 9) {
- d /= 10;
- l++;
- }
- p = t;
- snprintf(p, l + 2, "%0d ", c);
-
- /* Copy the feature string */
- p = NULL;
- if (*f)
- p = strchr(*f, ' ');
+ /* e: old feature string with leading space, or "" */
+ if (*e == ' ')
+ while (*(e + 1) == ' ')
+ e++;
- if (p) {
- while (*p == ' ')
- p++;
- strcat(t, p);
- strcat(t, " ");
- } else {
- p = t + strlen(t);
- }
- strcat(t, n);
+ snprintf(t, l + 1, "%0d%s %s", c, e, n);
FREE(*f);
*f = t;
#define _STRUCTS_H
#include <sys/types.h>
+#include <inttypes.h>
#include "prio.h"
+#include "byteorder.h"
#define WWID_SIZE 128
#define SERIAL_SIZE 65
#define NAME_SIZE 512
#define HOST_NAME_LEN 16
#define SLOT_NAME_SIZE 40
+#define PRKEY_SIZE 19
#define SCSI_VENDOR_SIZE 9
#define SCSI_PRODUCT_SIZE 17
INIT_OK,
};
+enum prkey_sources {
+ PRKEY_SOURCE_NONE,
+ PRKEY_SOURCE_CONF,
+ PRKEY_SOURCE_FILE,
+};
+
struct sg_id {
int host_no;
int channel;
void * mpcontext;
/* persistent management data*/
- unsigned char * reservation_key;
+ int prkey_source;
+ struct be64 reservation_key;
unsigned char prflag;
};
udev_unref(udev);
}
+static void monitor_cleanup(void *arg)
+{
+ struct udev_monitor *monitor = arg;
+
+ condlog(3, "Releasing uevent_monitor() resources");
+ udev_monitor_unref(monitor);
+}
+
/*
* Service the uevent queue.
*/
condlog(2, "failed to create udev monitor");
goto out;
}
+ pthread_cleanup_push(monitor_cleanup, monitor);
#ifdef LIBUDEV_API_RECVBUF
if (udev_monitor_set_receive_buffer_size(monitor, 128 * 1024 * 1024))
condlog(2, "failed to increase buffer size");
need_failback = 0;
out:
if (monitor)
- udev_monitor_unref(monitor);
+ pthread_cleanup_pop(1);
if (need_failback)
err = failback_listen();
pthread_cleanup_pop(1);
int i, ro = -1;
for (i = 0; uev->envp[i] != NULL; i++) {
- if (!strncmp(uev->envp[i], "DISK_RO", 6) && strlen(uev->envp[i]) > 7) {
+ if (!strncmp(uev->envp[i], "DISK_RO", 7) && strlen(uev->envp[i]) > 8) {
p = uev->envp[i] + 8;
ro = strtoul(p, &q, 10);
if (p == q) {
int i;
for (i = 0; uev->envp[i] != NULL; i++) {
- if (!strncmp(uev->envp[i], "DM_NAME", 6) &&
- strlen(uev->envp[i]) > 7) {
+ if (!strncmp(uev->envp[i], "DM_NAME", 7) &&
+ strlen(uev->envp[i]) > 8) {
p = MALLOC(strlen(uev->envp[i] + 8) + 1);
strcpy(p, uev->envp[i] + 8);
break;
#include <unistd.h>
#include <errno.h>
+#include "util.h"
#include "debug.h"
#include "memory.h"
#include "checkers.h"
pthread_once(&_lvc_initialized, _set_linux_version_code);
return _linux_version_code;
}
+
+int parse_prkey(char *ptr, uint64_t *prkey)
+{
+ if (!ptr)
+ return 1;
+ if (*ptr == '0')
+ ptr++;
+ if (*ptr == 'x' || *ptr == 'X')
+ ptr++;
+ if (*ptr == '\0' || strlen(ptr) > 16)
+ return 1;
+ if (strlen(ptr) != strspn(ptr, "0123456789aAbBcCdDeEfF"))
+ return 1;
+ if (sscanf(ptr, "%" SCNx64 "", prkey) != 1)
+ return 1;
+ return 0;
+}
+
+int safe_write(int fd, const void *buf, size_t count)
+{
+ while (count > 0) {
+ ssize_t r = write(fd, buf, count);
+ if (r < 0) {
+ if (errno == EINTR)
+ continue;
+ return -errno;
+ }
+ count -= r;
+ buf = (char *)buf + r;
+ }
+ return 0;
+}
#define _UTIL_H
#include <sys/types.h>
+#include <inttypes.h>
size_t strchop(char *);
int basenamecpy (const char * src, char * dst, int);
void setup_thread_attr(pthread_attr_t *attr, size_t stacksize, int detached);
int systemd_service_enabled(const char *dev);
int get_linux_version_code(void);
+int parse_prkey(char *ptr, uint64_t *prkey);
+int safe_write(int fd, const void *buf, size_t count);
+
#define KERNEL_VERSION(maj, min, ptc) ((((maj) * 256) + (min)) * 256 + (ptc))
#define safe_sprintf(var, format, args...) \
#ifndef _VERSION_H
#define _VERSION_H
-#define VERSION_CODE 0x000702
-#define DATE_CODE 0x080511
+#define VERSION_CODE 0x000703
+#define DATE_CODE 0x090514
#define PROG "multipath-tools"
CFLAGS += $(BIN_CFLAGS) -I$(multipathdir) -I$(mpathpersistdir)
LDFLAGS += $(BIN_LDFLAGS)
-LIBDEPS += -lpthread -ldevmapper -L$(mpathpersistdir) -lmpathpersist \
- -L$(multipathdir) -L$(mpathcmddir) -lmpathcmd -lmultipath -ludev
+LIBDEPS += -L$(mpathpersistdir) -lmpathpersist -L$(multipathdir) -lmultipath \
+ -L$(mpathcmddir) -lmpathcmd -lpthread -ldevmapper -ludev
EXEC = mpathpersist
GOTO="mpath_action"
# If the last path has failed mark the device not ready
-ENV{DM_ACTION}=="PATH_FAILED", ENV{DM_NR_VALID_PATHS}=="0",\
- ENV{MPATH_DEVICE_READY}="0", GOTO="mpath_action"
+# Note that DM_NR_VALID_PATHS is only set for PATH_FAILED|PATH_REINSTATED
+# events.
+# This may not be reliable, as events aren't necessarily received in order.
+ENV{DM_NR_VALID_PATHS}=="0", ENV{MPATH_DEVICE_READY}="0", GOTO="mpath_action"
+
+ENV{MPATH_SBIN_PATH}="/sbin"
+TEST!="$env{MPATH_SBIN_PATH}/multipath", ENV{MPATH_SBIN_PATH}="/usr/sbin"
+
+# Check the map state directly with multipath -U.
+# This doesn't attempt I/O on the device.
+PROGRAM=="$env{MPATH_SBIN_PATH}/multipath -U %k", GOTO="paths_ok"
+ENV{MPATH_DEVICE_READY}="0", GOTO="mpath_action"
+LABEL="paths_ok"
# Don't mark a device ready on a PATH_FAILED event. even if
# DM_NR_VALID_PATHS is greater than 0. Just keep the existing
# This event is either a PATH_REINSTATED or a table reload where
# there are active paths. Mark the device ready
-ENV{MPATH_DEVICE_READY}=""
+ENV{MPATH_DEVICE_READY}="1"
LABEL="mpath_action"
# DM_SUBSYSTEM_UDEV_FLAG0 is the "RELOAD" flag for multipath subsystem.
# something that should be reacted upon since it would be useless extra work.
# It's exactly mpath's job to provide *seamless* device access to any of the
# paths that are available underneath.
-ENV{DM_SUBSYSTEM_UDEV_FLAG0}=="1", ENV{DM_ACTIVATION}="0"
+ENV{DM_SUBSYSTEM_UDEV_FLAG0}=="1", \
+ ENV{DM_ACTIVATION}="0", ENV{MPATH_UNCHANGED}="1"
+
+# For path failed or reinstated events, unset DM_ACTIVATION.
+# This is similar to the DM_SUBSYSTEM_UDEV_FLAG0 case above.
+ENV{DM_ACTION}=="PATH_FAILED|PATH_REINSTATED", \
+ ENV{DM_ACTIVATION}="0", ENV{MPATH_UNCHANGED}="1"
# Do not initiate scanning if no path is available,
# otherwise there would be a hang or IO error on access.
# Also skip all foreign rules if no path is available.
# Remember the original value of DM_DISABLE_OTHER_RULES_FLAG
# and restore it back once we have at least one path available.
-ENV{MPATH_DEVICE_READY}=="0", ENV{.MPATH_DEVICE_READY_OLD}!="0",\
+ENV{MPATH_DEVICE_READY}=="0", ENV{.MPATH_DEVICE_READY_OLD}=="1",\
ENV{DM_DISABLE_OTHER_RULES_FLAG_OLD}=="",\
- ENV{DM_DISABLE_OTHER_RULES_FLAG_OLD}="$env{DM_UDEV_DISABLE_OTHER_RULES_FLAG}",\
- ENV{DM_UDEV_DISABLE_OTHER_RULES_FLAG}="1"
+ ENV{DM_DISABLE_OTHER_RULES_FLAG_OLD}="$env{DM_UDEV_DISABLE_OTHER_RULES_FLAG}"
+ENV{MPATH_DEVICE_READY}=="0", ENV{DM_UDEV_DISABLE_OTHER_RULES_FLAG}="1"
ENV{MPATH_DEVICE_READY}!="0", ENV{.MPATH_DEVICE_READY_OLD}=="0",\
ENV{DM_UDEV_DISABLE_OTHER_RULES_FLAG}="$env{DM_DISABLE_OTHER_RULES_FLAG_OLD}",\
ENV{DM_DISABLE_OTHER_RULES_FLAG_OLD}="",\
ENV{DM_ACTIVATION}="1"
+# The code to check multipath state ends here. We need to set
+# properties and symlinks regardless whether the map is usable or
+# not. If symlinks get lost, systemd may auto-unmount file systems.
+
LABEL="scan_import"
-ENV{DM_NOSCAN}!="1", GOTO="mpath_end"
-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"
+ENV{DM_NOSCAN}!="1", GOTO="import_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_LABEL"
+IMPORT{db}="ID_FS_LABEL_ENC"
+IMPORT{db}="ID_FS_VERSION"
+
+LABEL="import_end"
+
+# Multipath maps should take precedence over their members.
+ENV{DM_UDEV_LOW_PRIORITY_FLAG}!="1", OPTIONS+="link_priority=50"
+
+# Set some additional symlinks that typically exist for mpath
+# path members, too, and should be overridden.
+
+# kpartx_id is very robust, it works for suspended maps and maps
+# with 0 dependencies. It sets DM_TYPE, DM_PART, DM_WWN
+TEST=="/usr/lib/udev/kpartx_id", \
+ IMPORT{program}=="kpartx_id %M %m $env{DM_UUID}"
+
+ENV{DM_TYPE}=="?*", SYMLINK+="disk/by-id/$env{DM_TYPE}-$env{DM_NAME}"
+ENV{DM_WWN}=="?*", SYMLINK+="disk/by-id/wwn-$env{DM_WWN}"
LABEL="mpath_end"
CFLAGS += $(BIN_CFLAGS) -I$(multipathdir) -I$(mpathcmddir)
LDFLAGS += $(BIN_LDFLAGS)
-LIBDEPS += -lpthread -ldevmapper -ldl -L$(multipathdir) -lmultipath -ludev \
- -L$(mpathcmddir) -lmpathcmd
+LIBDEPS += -L$(multipathdir) -lmultipath -L$(mpathcmddir) -lmpathcmd \
+ -lpthread -ldevmapper -ldl -ludev
EXEC = multipath
" -F flush all multipath device maps\n"
" -a add a device wwid to the wwids file\n"
" -c check if a device should be a path in a multipath device\n"
+ " -C check if a multipath device has usable paths\n"
" -q allow queue_if_no_path when multipathd is not running\n"
" -d dry run, do not create or update devmaps\n"
- " -t dump internal hardware table\n"
+ " -t display the currently used multipathd configuration\n"
" -r force devmap reload\n"
" -i ignore wwids file\n"
" -B treat the bindings file as read only\n"
return 0;
}
+static int check_usable_paths(struct config *conf,
+ const char *devpath, enum devtypes dev_type)
+{
+ struct udev_device *ud = NULL;
+ struct multipath *mpp = NULL;
+ struct pathgroup *pg;
+ struct path *pp;
+ char *mapname;
+ vector pathvec = NULL;
+ char params[PARAMS_SIZE], status[PARAMS_SIZE];
+ dev_t devt;
+ int r = 1, i, j;
+
+ ud = get_udev_device(devpath, dev_type);
+ if (ud == NULL)
+ return r;
+
+ devt = udev_device_get_devnum(ud);
+ if (!dm_is_dm_major(major(devt))) {
+ condlog(1, "%s is not a dm device", devpath);
+ goto out;
+ }
+
+ mapname = dm_mapname(major(devt), minor(devt));
+ if (mapname == NULL) {
+ condlog(1, "dm device not found: %s", devpath);
+ goto out;
+ }
+
+ if (!dm_is_mpath(mapname)) {
+ condlog(1, "%s is not a multipath map", devpath);
+ goto free;
+ }
+
+ /* pathvec is needed for disassemble_map */
+ pathvec = vector_alloc();
+ if (pathvec == NULL)
+ goto free;
+
+ mpp = dm_get_multipath(mapname);
+ if (mpp == NULL)
+ goto free;
+
+ dm_get_map(mpp->alias, &mpp->size, params);
+ dm_get_status(mpp->alias, status);
+ disassemble_map(pathvec, params, mpp, 0);
+ disassemble_status(status, mpp);
+
+ vector_foreach_slot (mpp->pg, pg, i) {
+ vector_foreach_slot (pg->paths, pp, j) {
+ pp->udev = get_udev_device(pp->dev_t, DEV_DEVT);
+ if (pp->udev == NULL)
+ continue;
+ if (pathinfo(pp, conf, DI_SYSFS|DI_NOIO) != PATHINFO_OK)
+ continue;
+
+ if (pp->state == PATH_UP &&
+ pp->dmstate == PSTATE_ACTIVE) {
+ condlog(3, "%s: path %s is usable",
+ devpath, pp->dev);
+ r = 0;
+ goto found;
+ }
+ }
+ }
+found:
+ condlog(2, "%s:%s usable paths found", devpath, r == 0 ? "" : " no");
+free:
+ FREE(mapname);
+ free_multipath(mpp, FREE_PATHS);
+ vector_free(pathvec);
+out:
+ udev_device_unref(ud);
+ return r;
+}
/*
* Return value:
exit(1);
multipath_conf = conf;
conf->retrigger_tries = 0;
- while ((arg = getopt(argc, argv, ":adchl::FfM:v:p:b:BrR:itquwW")) != EOF ) {
+ while ((arg = getopt(argc, argv, ":adcChl::FfM:v:p:b:BrR:itquUwW")) != EOF ) {
switch(arg) {
case 1: printf("optarg : %s\n",optarg);
break;
case 'c':
cmd = CMD_VALID_PATH;
break;
+ case 'C':
+ cmd = CMD_USABLE_PATHS;
+ break;
case 'd':
if (cmd == CMD_CREATE)
cmd = CMD_DRY_RUN;
cmd = CMD_VALID_PATH;
dev_type = DEV_UEVENT;
break;
+ case 'U':
+ cmd = CMD_USABLE_PATHS;
+ dev_type = DEV_UEVENT;
+ break;
case 'w':
cmd = CMD_REMOVE_WWID;
break;
condlog(0, "failed to initialize prioritizers");
goto out;
}
-
+ if (cmd == CMD_USABLE_PATHS) {
+ r = check_usable_paths(conf, dev, dev_type);
+ goto out;
+ }
if (cmd == CMD_VALID_PATH &&
(!dev || dev_type == DEV_DEVMAP)) {
condlog(0, "the -c option requires a path to check");
.RB [\| \-b\ \c
.IR bindings_file \|]
.RB [\| \-d \|]
-.RB [\| \-h | \-l | \-ll | \-f | \-t | \-F | \-B | \-c | \-q | \|-r | \|-i | \-a | \|-u | \-w | \-W \|]
+.RB [\| \-h | \-l | \-ll | \-f | \-t | \-F | \-B | \-c | \-C | \-q | \-r | \-i | \-a | \-u | \-U | \-w | \-W \|]
.RB [\| \-p\ \c
.IR failover | multibus | group_by_serial | group_by_prio | group_by_node_name \|]
.RB [\| \-R\ \c
.
.TP
.B \-t
-Print internal hardware table to stdout.
+Display the currently used multipathd configuration.
.
.TP
.B \-r
Check if a block device should be a path in a multipath device.
.
.TP
+.B \-C
+Check if a multipath device has usable paths. This can be used to
+test whether or not I/O on this device is likely to succeed. The command
+itself doesn't attempt to do I/O on the device.
+.
+.TP
.B \-q
Allow device tables with \fIqueue_if_no_path\fR when multipathd is not running.
.
a path in a multipath device.
.
.TP
+.B \-U
+Check if the device specified in the program environment is a multipath device
+with usable paths. See \fB-C\fB.
+.
+.TP
.B \-w
Remove the WWID for the specified device from the WWIDs file.
.
.\" Update the date below if you make any significant change.
.\" Make sure there are no errors with:
.\" groff -z -wall -b -e -t multipath/multipath.conf.5
+.\" man --warnings -E UTF-8 -l -Tutf8 -Z multipath/multipath.conf.5 >/dev/null
.\"
.\" TODO: Look for XXX and ???
.\"
.\" ----------------------------------------------------------------------------
.
-.TH MULTIPATH.CONF 5 2017-05-11 "Linux"
+.TH MULTIPATH.CONF 5 2017-08-18 Linux
.
.
.\" ----------------------------------------------------------------------------
overwrite the built-in configuration table of \fBmultipathd\fP.
Any line whose first non-white-space character is a '#' is considered
a comment line. Empty lines are ignored.
+.PP
+Currently used multipathd configuration can be displayed with the \fBmultipath -t\fR
+or \fBmultipathd show config\fR command.
.
.
.\" ----------------------------------------------------------------------------
according to the identifier to promote effiecncy in processing uevents.
It has no default value, if you want to identify path by udev attribute
and to activate merging uevents for SCSI and DASD devices, you can set
-its value as:
+its value as: \fIuid_attrs "sd:ID_SERIAL dasd:ID_UID"\fR.
.RS
.TP
-\fBuid_attrs "sd:ID_SERIAL dasd:ID_UID"\fR
-.TP
The default is: \fB<unset>\fR
.RE
.
.B getuid_callout
(Superseded by \fIuid_attribute\fR) The default program and args to callout
to obtain a unique path identifier. Should be specified with an absolute path.
+.RS
.TP
+The default is: \fB<unset>\fR
+.RE
.
.
+.TP
.B prio
The name of the path priority routine. The specified routine
should return a numeric value specifying the relative priority
.TP
.I datacore
(Hardware-dependent)
-Generate the path priority for some Datacore storage arrays. Requires prio_args
+Generate the path priority for some DataCore storage arrays. Requires prio_args
keyword.
.TP
.I iet
.RS
.TP 8
.I hbtl
-regex can be of SCSI H:B:T:L format. For example: 1:0:.:. , *:0:0:.
+Regex can be of SCSI H:B:T:L format. For example: 1:0:.:. , *:0:0:.
.TP
.I devname
-regex can be of device name format. For example: sda , sd.e
+Regex can be of device name format. For example: sda , sd.e
.TP
.I serial
-regex can be of serial number format. For example: .*J1FR.*324 . The serial can
+Regex can be of serial number format. For example: .*J1FR.*324 . The serial can
be looked up through sysfs or by running multipathd show paths format "%z". For
example: 0395J1FR904324
.TP
.I wwn
-regex can be of the form \fI"host_wwnn:host_wwpn:target_wwnn:target_wwpn"\fR
+Regex can be of the form \fI"host_wwnn:host_wwpn:target_wwnn:target_wwpn"\fR
these values can be looked up through sysfs or by running \fImultipathd show paths format
"%N:%R:%n:%r"\fR. For example: 0x200100e08ba0aea0:0x210100e08ba0aea0:.*:.* , .*:.*:iqn.2009-10.com.redhat.msp.lab.ask-06:.*
.RE
.TP 12
.I path_latency
-Needs a value of the form
-\fI"<io_num>|<base_num>"\fR
+Needs a value of the form \fI"<io_num>|<base_num>"\fR
.RS
.TP 8
.I io_num
set will always be in their own path group.
.TP
.I datacore
-\fIpreferredsds\fR (required) denotes the preferred "SDS name" for datacore
-arrays. \fItimeout\fR (optional) is the timeout for the INQUIRY, in ms.
+.RS
+.TP 8
+.I preferredsds
+(Mandatory) The preferred "SDS name".
.TP
+.I timeout
+(Optional) The timeout for the INQUIRY, in ms.
+.RE
+.TP 12
.I iet
-\fIpreferredip=...\fR (required) denotes the preferred IP address (in dotted decimal
-notation) for iSCSI targets.
+.RS
+.TP 8
+.I preferredip=...
+(Mandatory) Th preferred IP address, in dotted decimal notation, for iSCSI targets.
+.RE
.TP
The default is: \fB<unset>\fR
.RE
Possible values for the feature list are:
.RS
.TP 12
-.\" XXX
.I queue_if_no_path
(Deprecated, superseded by \fIno_path_retry\fR) Queue I/O if no path is active.
Identical to the \fIno_path_retry\fR with \fIqueue\fR value. If both this
feature and \fIno_path_retry\fR are set, the latter value takes
precedence. See KNOWN ISSUES.
.TP
-.\" XXX
.I pg_init_retries <times>
(Since kernel 2.6.24) Number of times to retry pg_init, it must be between 1 and 50.
.TP
-.\" XXX
.I pg_init_delay_msecs <msecs>
(Since kernel 2.6.38) Number of msecs before pg_init retry, it must be between 0 and 60000.
.TP
-.\" XXX
.I queue_mode <mode>
(Since kernel 4.8) Select the the queueing mode per multipath device.
<mode> can be \fIbio\fR, \fIrq\fR or \fImq\fR, which corresponds to
bio-based, request-based, and block-multiqueue (blk-mq) request-based,
respectively.
-
The default depends on the kernel parameter \fBdm_mod.use_blk_mq\fR. It is
\fImq\fR if the latter is set, and \fIrq\fR otherwise.
+.TP
+The default is: \fB<unset>\fR
.RE
.
.
(Hardware-dependent)
Check the path state for HP/COMPAQ Smart Array(CCISS) controllers.
.TP
+.I none
+Do not check the device, fallback to use the values retrieved from sysfs
+.TP
.I rbd
Check if the path is in the Ceph blacklist and remap the path if it is.
.TP
.RS
.TP 12
.I value > 0
-number of retries until disable I/O queueing.
+Number of retries until disable I/O queueing.
.TP
.I fail
-for immediate failure (no I/O queueing).
+For immediate failure (no I/O queueing).
.TP
.I queue
-for never stop I/O queueing. Similar to \fIqueue_if_no_path\fR.
-.TP
-See KNOWN ISSUES.
+For never stop I/O queueing, similar to \fIqueue_if_no_path\fR. See KNOWN ISSUES.
.TP
The default is: \fBfail\fR
.RE
multipath will continue to use its configured hardware handler.
.RS
.PP
-The default is: \fByes\fR. Linux kernel 4.3 or newer always behaves as if
+The default is: \fByes\fR
+.PP
+\fBImportant Note:\fR Linux kernel 4.3 or newer always behaves as if
\fB"retain_attached_hw_handler yes"\fR was set.
.RE
.
ENV{DM_MULTIPATH_DEVICE_PATH}!="1", \
PROGRAM=="$env{MPATH_SBIN_PATH}/multipath -u %k", \
- ENV{DM_MULTIPATH_DEVICE_PATH}="1", ENV{ID_FS_TYPE}="none", \
+ ENV{DM_MULTIPATH_DEVICE_PATH}="1", ENV{ID_FS_TYPE}="mpath_member", \
ENV{SYSTEMD_READY}="0"
LABEL="end_mpath"
CFLAGS += $(BIN_CFLAGS) -I$(multipathdir) -I$(mpathpersistdir) \
-I$(mpathcmddir) -I$(thirdpartydir)
LDFLAGS += $(BIN_LDFLAGS)
-LIBDEPS += -ludev -ldl -L$(multipathdir) -lmultipath -L$(mpathpersistdir) \
- -lmpathpersist -L$(mpathcmddir) -lmpathcmd -lurcu -lpthread \
+LIBDEPS += -L$(multipathdir) -lmultipath -L$(mpathpersistdir) -lmpathpersist \
+ -L$(mpathcmddir) -lmpathcmd -ludev -ldl -lurcu -lpthread \
-ldevmapper -lreadline
ifdef SYSTEMD
r += add_key(keys, "unsetprstatus", UNSETPRSTATUS, 0);
r += add_key(keys, "format", FMT, 1);
r += add_key(keys, "json", JSON, 0);
+ r += add_key(keys, "getprkey", GETPRKEY, 0);
+ r += add_key(keys, "setprkey", SETPRKEY, 0);
+ r += add_key(keys, "unsetprkey", UNSETPRKEY, 0);
+ r += add_key(keys, "key", KEY, 1);
+
if (r) {
free_keys(keys);
add_handler(GETPRSTATUS+MAP, NULL);
add_handler(SETPRSTATUS+MAP, NULL);
add_handler(UNSETPRSTATUS+MAP, NULL);
+ add_handler(GETPRKEY+MAP, NULL);
+ add_handler(SETPRKEY+MAP+KEY, NULL);
+ add_handler(UNSETPRKEY+MAP, NULL);
add_handler(FORCEQ+DAEMON, NULL);
add_handler(RESTOREQ+DAEMON, NULL);
__UNSETPRSTATUS,
__FMT,
__JSON,
+ __GETPRKEY,
+ __SETPRKEY,
+ __UNSETPRKEY,
+ __KEY,
};
#define LIST (1 << __LIST)
#define UNSETPRSTATUS (1ULL << __UNSETPRSTATUS)
#define FMT (1ULL << __FMT)
#define JSON (1ULL << __JSON)
+#define GETPRKEY (1ULL << __GETPRKEY)
+#define SETPRKEY (1ULL << __SETPRKEY)
+#define UNSETPRKEY (1ULL << __UNSETPRKEY)
+#define KEY (1ULL << __KEY)
#define INITIAL_REPLY_LEN 1200
#include <errno.h>
#include <libudev.h>
#include "util.h"
+#include "prkey.h"
#include "main.h"
#include "cli.h"
vecs->pathvec, &refwwid);
if (refwwid) {
if (coalesce_paths(vecs, NULL, refwwid,
- FORCE_RELOAD_NONE, 1))
+ FORCE_RELOAD_NONE, CMD_NONE))
condlog(2, "%s: coalesce_paths failed",
param);
dm_lib_release();
return 0;
}
+
+int
+cli_getprkey(void * v, char ** reply, int * len, void * data)
+{
+ struct multipath * mpp;
+ struct vectors * vecs = (struct vectors *)data;
+ char *mapname = get_keyparam(v, MAP);
+
+ mapname = convert_dev(mapname, 0);
+ condlog(3, "%s: get persistent reservation key (operator)", mapname);
+ mpp = find_mp_by_str(vecs->mpvec, mapname);
+
+ if (!mpp)
+ return 1;
+
+ *reply = malloc(20);
+
+ if (!get_be64(mpp->reservation_key)) {
+ sprintf(*reply, "none\n");
+ *len = strlen(*reply) + 1;
+ return 0;
+ }
+ snprintf(*reply, 20, "0x%" PRIx64 "\n",
+ get_be64(mpp->reservation_key));
+ (*reply)[19] = '\0';
+ *len = strlen(*reply) + 1;
+ return 0;
+}
+
+int
+cli_unsetprkey(void * v, char ** reply, int * len, void * data)
+{
+ struct multipath * mpp;
+ struct vectors * vecs = (struct vectors *)data;
+ char *mapname = get_keyparam(v, MAP);
+ int ret;
+ struct config *conf;
+
+ mapname = convert_dev(mapname, 0);
+ condlog(3, "%s: unset persistent reservation key (operator)", mapname);
+ mpp = find_mp_by_str(vecs->mpvec, mapname);
+
+ if (!mpp)
+ return 1;
+
+ conf = get_multipath_config();
+ ret = set_prkey(conf, mpp, 0);
+ put_multipath_config(conf);
+
+ return ret;
+}
+
+int
+cli_setprkey(void * v, char ** reply, int * len, void * data)
+{
+ struct multipath * mpp;
+ struct vectors * vecs = (struct vectors *)data;
+ char *mapname = get_keyparam(v, MAP);
+ char *keyparam = get_keyparam(v, KEY);
+ uint64_t prkey;
+ int ret;
+ struct config *conf;
+
+ mapname = convert_dev(mapname, 0);
+ condlog(3, "%s: set persistent reservation key (operator)", mapname);
+ mpp = find_mp_by_str(vecs->mpvec, mapname);
+
+ if (!mpp)
+ return 1;
+
+ if (parse_prkey(keyparam, &prkey) != 0) {
+ condlog(0, "%s: invalid prkey : '%s'", mapname, keyparam);
+ return 1;
+ }
+
+ conf = get_multipath_config();
+ ret = set_prkey(conf, mpp, prkey);
+ put_multipath_config(conf);
+
+ return ret;
+}
int cli_getprstatus(void * v, char ** reply, int * len, void * data);
int cli_setprstatus(void * v, char ** reply, int * len, void * data);
int cli_unsetprstatus(void * v, char ** reply, int * len, void * data);
+int cli_getprkey(void * v, char ** reply, int * len, void * data);
+int cli_setprkey(void * v, char ** reply, int * len, void * data);
+int cli_unsetprkey(void * v, char ** reply, int * len, void * data);
case DAEMON_CONFIGURE:
return "STATUS=configure";
case DAEMON_IDLE:
- return "STATUS=idle";
case DAEMON_RUNNING:
- return "STATUS=running";
+ return "STATUS=up";
case DAEMON_SHUTDOWN:
return "STATUS=shutdown";
}
return NULL;
}
+static void do_sd_notify(enum daemon_status old_state)
+{
+ /*
+ * Checkerloop switches back and forth between idle and running state.
+ * No need to tell systemd each time.
+ * These notifications cause a lot of overhead on dbus.
+ */
+ if ((running_state == DAEMON_IDLE || running_state == DAEMON_RUNNING) &&
+ (old_state == DAEMON_IDLE || old_state == DAEMON_RUNNING))
+ return;
+ sd_notify(0, sd_notify_status());
+}
+
static void config_cleanup(void *arg)
{
pthread_mutex_unlock(&config_lock);
{
pthread_mutex_lock(&config_lock);
if (state != running_state) {
+ enum daemon_status old_state = running_state;
+
running_state = state;
pthread_cond_broadcast(&config_cond);
#ifdef USE_SYSTEMD
- sd_notify(0, sd_notify_status());
+ do_sd_notify(old_state);
#endif
}
pthread_mutex_unlock(&config_lock);
pthread_cleanup_push(config_cleanup, NULL);
pthread_mutex_lock(&config_lock);
if (running_state != state) {
+ enum daemon_status old_state = running_state;
+
if (running_state != DAEMON_IDLE) {
struct timespec ts;
running_state = state;
pthread_cond_broadcast(&config_cond);
#ifdef USE_SYSTEMD
- sd_notify(0, sd_notify_status());
+ do_sd_notify(old_state);
#endif
}
}
set_handler_callback(UNSETPRSTATUS+MAP, cli_unsetprstatus);
set_handler_callback(FORCEQ+DAEMON, cli_force_no_daemon_q);
set_handler_callback(RESTOREQ+DAEMON, cli_restore_no_daemon_q);
+ set_handler_callback(GETPRKEY+MAP, cli_getprkey);
+ set_handler_callback(SETPRKEY+MAP+KEY, cli_setprkey);
+ set_handler_callback(UNSETPRKEY+MAP, cli_unsetprkey);
umask(077);
uxsock_listen(&uxsock_trigger, ap);
if (newstate == PATH_UP) {
conf = get_multipath_config();
- newstate = get_state(pp, conf, 1);
+ newstate = get_state(pp, conf, 1, newstate);
put_multipath_config(conf);
} else
checker_clear_message(&pp->checker);
void * mpath_pr_event_handler_fn (void * pathp )
{
struct multipath * mpp;
- int i,j, ret, isFound;
+ int i, ret, isFound;
struct path * pp = (struct path *)pathp;
- unsigned char *keyp;
- uint64_t prkey;
struct prout_param_descriptor *param;
struct prin_resp *resp;
ret = MPATH_PR_SUCCESS;
goto out;
}
- prkey = 0;
- keyp = (unsigned char *)mpp->reservation_key;
- for (j = 0; j < 8; ++j) {
- if (j > 0)
- prkey <<= 8;
- prkey |= *keyp;
- ++keyp;
- }
- condlog(2, "Multipath reservation_key: 0x%" PRIx64 " ", prkey);
+ condlog(2, "Multipath reservation_key: 0x%" PRIx64 " ",
+ get_be64(mpp->reservation_key));
isFound =0;
for (i = 0; i < resp->prin_descriptor.prin_readkeys.additional_length/8; i++ )
{
condlog(2, "PR IN READKEYS[%d] reservation key:",i);
dumpHex((char *)&resp->prin_descriptor.prin_readkeys.key_list[i*8], 8 , -1);
- if (!memcmp(mpp->reservation_key, &resp->prin_descriptor.prin_readkeys.key_list[i*8], 8))
+ if (!memcmp(&mpp->reservation_key, &resp->prin_descriptor.prin_readkeys.key_list[i*8], 8))
{
condlog(2, "%s: pr key found in prin readkeys response", mpp->alias);
isFound =1;
param= malloc(sizeof(struct prout_param_descriptor));
memset(param, 0 , sizeof(struct prout_param_descriptor));
-
- for (j = 7; j >= 0; --j) {
- param->sa_key[j] = (prkey & 0xff);
- prkey >>= 8;
- }
+ memcpy(param->sa_key, &mpp->reservation_key, 8);
param->num_transportid = 0;
condlog(3, "device %s:%s", pp->dev, pp->mpp->wwid);
mpp = pp->mpp;
- if (!mpp->reservation_key)
+ if (get_be64(mpp->reservation_key))
return -1;
pthread_attr_init(&attr);