#
#
-# Allow to force some libraries to be used statically. (Uncomment one of the
-# following lines or define the values when calling make.)
-#
-# WITH_LOCAL_LIBDM = 1
-# WITH_LOCAL_LIBSYSFS = 1
-#
# Uncomment to disable libdmmp support
# ENABLE_LIBDMMP = 0
#
# Uncomment to disable dmevents polling support
# ENABLE_DMEVENTS_POLL = 0
+# List of scsi device handler modules to load on boot, e.g.
+# SCSI_DH_MODULES_PRELOAD := scsi_dh_alua scsi_dh_rdac
+SCSI_DH_MODULES_PRELOAD :=
+
+
PKGCONFIG ?= pkg-config
ifeq ($(TOPDIR),)
SYSTEMDPATH=usr/lib
endif
+ifndef DEVMAPPER_INCDIR
+ ifeq ($(shell $(PKGCONFIG) --modversion devmapper >/dev/null 2>&1 && echo 1), 1)
+ DEVMAPPER_INCDIR = $(shell $(PKGCONFIG) --variable=includedir devmapper)
+ else
+ DEVMAPPER_INCDIR = /usr/include
+ endif
+endif
+
+ifndef LIBUDEV_INCDIR
+ ifeq ($(shell $(PKGCONFIG) --modversion libudev >/dev/null 2>&1 && echo 1), 1)
+ LIBUDEV_INCDIR = $(shell $(PKGCONFIG) --variable=includedir libudev)
+ else
+ LIBUDEV_INCDIR = /usr/include
+ endif
+endif
+
+# Allow user to override default location.
+ifndef LINUX_HEADERS_INCDIR
+ LINUX_HEADERS_INCDIR = /usr/include
+endif
+
prefix =
exec_prefix = $(prefix)
usr_prefix = $(prefix)
bindir = $(exec_prefix)/sbin
libudevdir = $(prefix)/$(SYSTEMDPATH)/udev
udevrulesdir = $(libudevdir)/rules.d
+modulesloaddir = $(prefix)/$(SYSTEMDPATH)/modules-load.d
multipathdir = $(TOPDIR)/libmultipath
man8dir = $(prefix)/usr/share/man/man8
man5dir = $(prefix)/usr/share/man/man5
includedir = $(prefix)/usr/include
pkgconfdir = $(usrlibdir)/pkgconfig
-GZIP = gzip -9 -c
+GZIP_PROG = gzip -9 -c
RM = rm -f
LN = ln -sf
INSTALL_PROGRAM = install
echo "$$found" \
)
+# Check whether a file contains a variable with name $1 in header file $2
+check_var = $(shell \
+ if grep -Eq "(^|[[:blank:]])$1([[:blank:]]|=|$$)" "$2"; then \
+ found=1; \
+ status="yes"; \
+ else \
+ found=0; \
+ status="no"; \
+ fi; \
+ echo 1>&2 "Checking for .. $1 in $2 ... $$status"; \
+ echo "$$found" \
+ )
+
%.o: %.c
@echo building $@ because of $?
$(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $<
Github page: https://github.com/opensvc/multipath-tools
+Building multipath-tools
+========================
+
+Prerequisites: development packages of for `libdevmapper`, `libreadline`,
+`libaio`, `libudev`, `libjson-c`, `liburcu`, and `libsystemd`.
+
+To build multipath-tools, type:
+
+ make
+ make DESTDIR="/my/target/dir" install
+
+To uninstall, type:
+
+ make uninstall
+
+Customizing the build
+---------------------
+
+The following variables can be passed to the `make` command line:
+
+ * `ENABLE_LIBDMMP=0`: disable building libdmmp
+ * `ENABLE_DMEVENTS_POLL=0`: disable support for the device-mapper event
+ polling API. For use with pre-5.0 kernels that don't supprt dmevent polling
+ (but even if you don't use this option, multipath-tools will work with
+ these kernels).
+ * `SCSI_DH_MODULES_PRELOAD="(list)"`: specify a space-separated list of SCSI
+ device handler kernel modules to load early during boot. Some
+ multipath-tools functionality depends on these modules being loaded
+ early. This option causes a *modules-load.d(5)* configuration file to be
+ created, thus it depends on functionality provided by *systemd*.
+ This variable only matters for `make install`.
+
+Note: The usefulness of the preload list depends on the kernel configuration.
+It's especially useful if `scsi_mod` is builtin but `scsi_dh_alua` and
+other device handler modules are built as modules. If `scsi_mod` itself is compiled
+as a module, it might make more sense to use a module softdep for the same
+purpose.
+
+See `Makefile.inc` for additional variables to customize paths and compiler
+flags.
+
+Special Makefile targets
+------------------------
+
+The following targets are intended for developers only.
+
+ * `make test` to build and run the unit tests
+ * `make valgrind-test` to run the unit tests under valgrind
+ * `make abi` to create an XML representation of the ABI of the libraries in
+ the `abi/` subdirectory
+ * `make abi-test` to compare the ABI of a different multipath-tools version,
+ which must be stored in the `reference-abi/` subdirectory. If this test
+ fails, the ABI has changed wrt the reference.
+ * `make compile-commands.json` to create input for [clangd](https://clangd.llvm.org/).
+
+
Add storage devices
===================
LIBDEPS += -ldevmapper
-ifneq ($(call check_func,dm_task_set_cookie,/usr/include/libdevmapper.h),0)
+ifneq ($(call check_func,dm_task_set_cookie,$(DEVMAPPER_INCDIR)/libdevmapper.h),0)
CFLAGS += -DLIBDM_API_COOKIE
endif
$(EXEC): $(OBJS)
$(CC) $(CFLAGS) $(OBJS) -o $(EXEC) $(LDFLAGS) $(LIBDEPS)
- $(GZIP) $(EXEC).8 > $(EXEC).8.gz
install: $(EXEC) $(EXEC).8
$(INSTALL_PROGRAM) -d $(DESTDIR)$(bindir)
$(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)
+ $(INSTALL_PROGRAM) -m 644 $(EXEC).8 $(DESTDIR)$(man8dir)
uninstall:
$(RM) $(DESTDIR)$(bindir)/$(EXEC)
- $(RM) $(DESTDIR)$(man8dir)/$(EXEC).8.gz
+ $(RM) $(DESTDIR)$(man8dir)/$(EXEC).8
$(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/68-del-part-nodes.rules
clean: dep_clean
- $(RM) core *.o $(EXEC) *.gz
+ $(RM) core *.o $(EXEC)
include $(wildcard $(OBJS:.o=.d))
* but again the multiple of the polynomial to subtract depends only on
* the high bits, the high 8 bits in this case.
*
- * The multile we need in that case is the low 32 bits of a 40-bit
+ * The multiple we need in that case is the low 32 bits of a 40-bit
* value whose high 8 bits are given, and which is a multiple of the
* generator polynomial. This is simply the CRC-32 of the given
* one-byte message.
#define MAX_PREFIX_LEN (_UUID_PREFIX_LEN + 4)
#define PARAMS_SIZE 1024
+#ifdef LIBDM_API_COOKIE
+# define __DM_API_COOKIE_UNUSED__ /* empty */
+#else
+# define __DM_API_COOKIE_UNUSED__ __attribute__((unused))
+#endif
+
int dm_prereq(char * str, uint32_t x, uint32_t y, uint32_t z)
{
int r = 1;
return r;
}
-int dm_simplecmd(int task, const char *name, int no_flush, uint16_t udev_flags)
+int dm_simplecmd(int task, const char *name, int no_flush, __DM_API_COOKIE_UNUSED__ uint16_t udev_flags)
{
int r = 0;
+#ifdef LIBDM_API_COOKIE
int udev_wait_flag = (task == DM_DEVICE_RESUME ||
task == DM_DEVICE_REMOVE);
-#ifdef LIBDM_API_COOKIE
uint32_t cookie = 0;
#endif
struct dm_task *dmt;
*
* Description: Returns 1 if PMBR is valid, 0 otherwise.
* Validity depends on two things:
- * 1) MSDOS signature is in the last two bytes of the MBR
+ * 1) MS-DOS signature is in the last two bytes of the MBR
* 2) One partition of type 0xEE is found
*/
static int
# Create dm tables for partitions on multipath devices.
ENV{DM_UUID}!="mpath-?*", GOTO="mpath_kpartx_end"
+# Ignore RAID members
+ENV{ID_FS_TYPE}=="linux_raid_member|isw_raid_member|ddf_raid_member", GOTO="mpath_kpartx_end"
+
# DM_SUBSYSTEM_UDEV_FLAG1 is the "skip_kpartx" flag.
# For events not generated by libdevmapper, we need to fetch it from db:
# - "change" events with DM_ACTIVATION!="1" (e.g. partition table changes)
LIBDEPS += $(shell $(PKGCONFIG) --libs json-c) -L$(mpathcmddir) -lmpathcmd -lpthread
all: $(LIBS) doc
-.PHONY: doc doc.gz clean install uninstall check speed_test dep_clean
+.PHONY: doc clean install uninstall check speed_test dep_clean
$(LIBS): $(OBJS)
$(CC) $(LDFLAGS) $(SHARED_FLAGS) -Wl,-soname=$@ -o $@ $(OBJS) $(LIBDEPS)
abi: $(DEVLIB:%.so=%.abi)
-install: doc.gz
+install:
mkdir -p $(DESTDIR)$(usrlibdir)
$(INSTALL_PROGRAM) -m 755 $(LIBS) $(DESTDIR)$(usrlibdir)/$(LIBS)
$(INSTALL_PROGRAM) -m 644 -D \
$(DESTDIR)$(pkgconfdir)/$(PKGFILE)
perl -i -pe 's|__INCLUDEDIR__|$(includedir)|g' \
$(DESTDIR)$(pkgconfdir)/$(PKGFILE)
- $(INSTALL_PROGRAM) -m 644 -t $(DESTDIR)$(man3dir) docs/man/*.3.gz
+ $(INSTALL_PROGRAM) -m 644 -t $(DESTDIR)$(man3dir) docs/man/*.3
uninstall:
$(RM) $(DESTDIR)$(usrlibdir)/$(LIBS)
$(RM) $(DESTDIR)$(pkgconfdir)/$(PKGFILE)
clean: dep_clean
- $(RM) core *.a *.o *.gz *.so *.so.* *.abi $(NV_VERSION_SCRIPT)
- $(RM) docs/man/*.gz
+ $(RM) core *.a *.o *.so *.so.* *.abi $(NV_VERSION_SCRIPT)
$(MAKE) -C test clean
include $(wildcard $(OBJS:.o=.d))
speed_test: all
$(MAKE) -C test speed_test
-doc.gz: doc $(patsubst %,%.gz,$(wildcard docs/man/*.3))
-
doc: docs/man/dmmp_strerror.3
-docs/man/%.3.gz: docs/man/%.3
- gzip -c $< >$@
-
docs/man/dmmp_strerror.3: $(HEADERS)
TEMPFILE=$(shell mktemp); \
cat $^ | perl docs/doc-preclean.pl >$$TEMPFILE; \
#include <libudev.h>
#include <errno.h>
#include <libdevmapper.h>
-#include <stdbool.h>
#include <unistd.h>
#include <assert.h>
#include <json.h>
j_token = json_tokener_new();
if (j_token == NULL) {
rc = DMMP_ERR_BUG;
- _error(ctx, "BUG: json_tokener_new() retuned NULL");
+ _error(ctx, "BUG: json_tokener_new() returned NULL");
goto out;
}
j_obj = json_tokener_parse_ex(j_token, j_str, strlen(j_str) + 1);
$(RM) $(DESTDIR)$(includedir)/mpath_cmd.h
clean: dep_clean
- $(RM) core *.a *.o *.so *.so.* *.gz *.abi $(NV_VERSION_SCRIPT)
+ $(RM) core *.a *.o *.so *.so.* *.abi $(NV_VERSION_SCRIPT)
include $(wildcard $(OBJS:.o=.d))
* mpath_recv_reply()
*
* RETURNS:
- * 0 on successs, and reply will either be NULL (if there was no
+ * 0 on success, and reply will either be NULL (if there was no
* reply data), or point to the reply string, which must be freed by
* the caller. -1 on failure (with errno set).
*/
LIBDEPS += -lmultipath -lmpathcmd -ldevmapper -lpthread -ldl
-OBJS = mpath_persist.o mpath_updatepr.o mpath_pr_ioctl.o
+OBJS = mpath_persist.o mpath_updatepr.o mpath_pr_ioctl.o mpath_persist_int.o
-all: $(DEVLIB) man
+all: $(DEVLIB)
$(LIBS): $(OBJS) $(VERSION_SCRIPT)
$(CC) $(LDFLAGS) $(SHARED_FLAGS) -Wl,-soname=$@ \
$(DEVLIB): $(LIBS)
$(LN) $(LIBS) $@
-man:
- $(GZIP) mpath_persistent_reserve_in.3 > mpath_persistent_reserve_in.3.gz
- $(GZIP) mpath_persistent_reserve_out.3 > mpath_persistent_reserve_out.3.gz
-
install: all
$(INSTALL_PROGRAM) -d $(DESTDIR)$(syslibdir)
$(INSTALL_PROGRAM) -m 755 $(LIBS) $(DESTDIR)$(syslibdir)/$(LIBS)
$(INSTALL_PROGRAM) -m 755 -d $(DESTDIR)$(man3dir)
$(INSTALL_PROGRAM) -m 755 -d $(DESTDIR)$(includedir)
$(LN) $(LIBS) $(DESTDIR)$(syslibdir)/$(DEVLIB)
- $(INSTALL_PROGRAM) -m 644 mpath_persistent_reserve_in.3.gz $(DESTDIR)$(man3dir)
- $(INSTALL_PROGRAM) -m 644 mpath_persistent_reserve_out.3.gz $(DESTDIR)$(man3dir)
+ $(INSTALL_PROGRAM) -m 644 mpath_persistent_reserve_in.3 $(DESTDIR)$(man3dir)
+ $(INSTALL_PROGRAM) -m 644 mpath_persistent_reserve_out.3 $(DESTDIR)$(man3dir)
$(INSTALL_PROGRAM) -m 644 mpath_persist.h $(DESTDIR)$(includedir)
uninstall:
$(RM) $(DESTDIR)$(syslibdir)/$(LIBS)
- $(RM) $(DESTDIR)$(man3dir)/mpath_persistent_reserve_in.3.gz
- $(RM) $(DESTDIR)$(man3dir)/mpath_persistent_reserve_out.3.gz
+ $(RM) $(DESTDIR)$(man3dir)/mpath_persistent_reserve_in.3
+ $(RM) $(DESTDIR)$(man3dir)/mpath_persistent_reserve_out.3
$(RM) $(DESTDIR)$(includedir)/mpath_persist.h
$(RM) $(DESTDIR)$(syslibdir)/$(DEVLIB)
clean: dep_clean
- $(RM) core *.a *.o *.so *.so.* *.gz *.abi $(NV_VERSION_SCRIPT)
+ $(RM) core *.a *.o *.so *.so.* *.abi $(NV_VERSION_SCRIPT)
include $(wildcard $(OBJS:.o=.d))
*
* See libmultipath.version for general policy about version numbers.
*/
-LIBMPATHPERSIST_2.0.0 {
+LIBMPATHPERSIST_2.1.0 {
global:
-
- __mpath_persistent_reserve_in;
- __mpath_persistent_reserve_out;
- dumpHex;
- mpath_alloc_prin_response;
+ /* public API as defined in mpath_persist.h */
+ libmpathpersist_exit;
+ libmpathpersist_init;
mpath_lib_exit;
mpath_lib_init;
mpath_mx_alloc_len;
+ mpath_persistent_reserve_free_vecs;
+ __mpath_persistent_reserve_in;
mpath_persistent_reserve_in;
mpath_persistent_reserve_init_vecs;
+ __mpath_persistent_reserve_out;
mpath_persistent_reserve_out;
- mpath_persistent_reserve_free_vecs;
+local: *;
+};
+
+__LIBMPATHPERSIST_INT_1.0.0 {
+ /* Internal use by multipath-tools */
+ dumpHex;
+ mpath_alloc_prin_response;
prin_do_scsi_ioctl;
prout_do_scsi_ioctl;
update_map_pr;
-
- /* added in 1.1.0 */
- libmpathpersist_init;
- libmpathpersist_exit;
-
-local: *;
};
#include <libdevmapper.h>
-#include "defaults.h"
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <fcntl.h>
-#include "vector.h"
-#include "checkers.h"
-#include "structs.h"
-#include "structs_vec.h"
-#include <libudev.h>
-#include "prio.h"
-#include <unistd.h>
-#include "devmapper.h"
-#include "debug.h"
-#include "config.h"
-#include "switchgroup.h"
-#include "discovery.h"
-#include "configure.h"
-#include "dmparser.h"
-#include <ctype.h>
-#include "propsel.h"
#include "util.h"
-#include "unaligned.h"
+#include "vector.h"
+#include "config.h"
+#include "debug.h"
+#include "devmapper.h"
#include "mpath_persist.h"
-#include "mpathpr.h"
-#include "mpath_pr_ioctl.h"
-
-#include <pthread.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-
-#define __STDC_FORMAT_MACROS 1
+#include "mpath_persist_int.h"
extern struct udev *udev;
return 0;
}
-int
-mpath_prin_activepath (struct multipath *mpp, int rq_servact,
- struct prin_resp * resp, int noisy)
-{
- int i,j, ret = MPATH_PR_DMMP_ERROR;
- struct pathgroup *pgp = NULL;
- struct path *pp = NULL;
-
- vector_foreach_slot (mpp->pg, pgp, j){
- vector_foreach_slot (pgp->paths, pp, i){
- if (!((pp->state == PATH_UP) ||
- (pp->state == PATH_GHOST))){
- condlog(2, "%s: %s not available. Skip.",
- mpp->wwid, pp->dev);
- condlog(3, "%s: status = %d.",
- mpp->wwid, pp->state);
- continue;
- }
-
- condlog(3, "%s: sending pr in command to %s ",
- mpp->wwid, pp->dev);
- ret = mpath_send_prin_activepath(pp->dev, rq_servact,
- resp, noisy);
- switch(ret)
- {
- case MPATH_PR_SUCCESS:
- case MPATH_PR_SENSE_INVALID_OP:
- return ret;
- default:
- continue;
- }
- }
- }
- return ret;
-}
-
static vector curmp;
static vector pathvec;
return __mpath_persistent_reserve_init_vecs(&curmp, &pathvec, verbose);
}
-static int mpath_get_map(vector curmp, vector pathvec, int fd, char **palias,
- struct multipath **pmpp)
-{
- int ret = MPATH_PR_DMMP_ERROR;
- struct stat info;
- int major, minor;
- char *alias;
- struct multipath *mpp;
-
- if (fstat(fd, &info) != 0){
- condlog(0, "stat error fd=%d", fd);
- return MPATH_PR_FILE_ERROR;
- }
- if(!S_ISBLK(info.st_mode)){
- condlog(3, "Failed to get major:minor. fd=%d", fd);
- return MPATH_PR_FILE_ERROR;
- }
-
- major = major(info.st_rdev);
- minor = minor(info.st_rdev);
- condlog(4, "Device %d:%d", major, minor);
-
- /* get alias from major:minor*/
- alias = dm_mapname(major, minor);
- if (!alias){
- condlog(0, "%d:%d failed to get device alias.", major, minor);
- return MPATH_PR_DMMP_ERROR;
- }
-
- condlog(3, "alias = %s", alias);
-
- if (dm_map_present(alias) && dm_is_mpath(alias) != 1){
- condlog(3, "%s: not a multipath device.", alias);
- goto out;
- }
-
- /* get info of all paths from the dm device */
- if (get_mpvec(curmp, pathvec, alias)){
- condlog(0, "%s: failed to get device info.", alias);
- goto out;
- }
-
- mpp = find_mp_by_alias(curmp, alias);
-
- if (!mpp) {
- condlog(0, "%s: devmap not registered.", alias);
- goto out;
- }
-
- ret = MPATH_PR_SUCCESS;
- if (pmpp)
- *pmpp = mpp;
- if (palias) {
- *palias = alias;
- alias = NULL;
- }
-out:
- free(alias);
- return ret;
-}
-
-static int do_mpath_persistent_reserve_in (vector curmp, vector pathvec,
- int fd, int rq_servact, struct prin_resp *resp, int noisy)
-{
- struct multipath *mpp;
- int ret;
-
- ret = mpath_get_map(curmp, pathvec, fd, NULL, &mpp);
- if (ret != MPATH_PR_SUCCESS)
- return ret;
-
- ret = mpath_prin_activepath(mpp, rq_servact, resp, noisy);
-
- return ret;
-}
-
-
int __mpath_persistent_reserve_in (int fd, int rq_servact,
struct prin_resp *resp, int noisy)
{
resp, noisy);
}
-static int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd,
- int rq_servact, int rq_scope, unsigned int rq_type,
- struct prout_param_descriptor *paramp, int noisy)
-{
- struct multipath *mpp;
- char *alias;
- int ret;
- uint64_t prkey;
- struct config *conf;
-
- ret = mpath_get_map(curmp, pathvec, fd, &alias, &mpp);
- if (ret != MPATH_PR_SUCCESS)
- return ret;
-
- conf = get_multipath_config();
- select_reservation_key(conf, mpp);
- select_all_tg_pt(conf, mpp);
- put_multipath_config(conf);
-
- memcpy(&prkey, paramp->sa_key, 8);
- if (mpp->prkey_source == PRKEY_SOURCE_FILE && prkey &&
- (rq_servact == MPATH_PROUT_REG_IGN_SA ||
- (rq_servact == MPATH_PROUT_REG_SA &&
- (!get_be64(mpp->reservation_key) ||
- memcmp(paramp->key, &mpp->reservation_key, 8) == 0)))) {
- memcpy(&mpp->reservation_key, paramp->sa_key, 8);
- if (update_prkey_flags(alias, get_be64(mpp->reservation_key),
- paramp->sa_flags)) {
- 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) &&
- (prkey || rq_servact != MPATH_PROUT_REG_IGN_SA)) {
- 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:
- case MPATH_PROUT_REG_IGN_SA:
- ret= mpath_prout_reg(mpp, rq_servact, rq_scope, rq_type, paramp, noisy);
- break;
- case MPATH_PROUT_RES_SA :
- case MPATH_PROUT_PREE_SA :
- case MPATH_PROUT_PREE_AB_SA :
- case MPATH_PROUT_CLEAR_SA:
- ret = mpath_prout_common(mpp, rq_servact, rq_scope, rq_type, paramp, noisy);
- break;
- case MPATH_PROUT_REL_SA:
- ret = mpath_prout_rel(mpp, rq_servact, rq_scope, rq_type, paramp, noisy);
- break;
- default:
- ret = MPATH_PR_OTHER;
- goto out1;
- }
-
- if ((ret == MPATH_PR_SUCCESS) && ((rq_servact == MPATH_PROUT_REG_SA) ||
- (rq_servact == MPATH_PROUT_REG_IGN_SA)))
- {
- 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(alias);
- return ret;
-}
-
int __mpath_persistent_reserve_out ( int fd, int rq_servact, int rq_scope,
unsigned int rq_type, struct prout_param_descriptor *paramp, int noisy)
__mpath_persistent_reserve_free_vecs(curmp, pathvec);
return ret;
}
-
-int
-get_mpvec (vector curmp, vector pathvec, char * refwwid)
-{
- int i;
- struct multipath *mpp;
-
- vector_foreach_slot (curmp, mpp, i){
- /*
- * discard out of scope maps
- */
- if (!mpp->alias) {
- condlog(0, "%s: map with empty alias!", __func__);
- continue;
- }
-
- if (mpp->pg != NULL)
- /* Already seen this one */
- continue;
-
- if (refwwid && strncmp (mpp->alias, refwwid, WWID_SIZE - 1))
- continue;
-
- if (update_multipath_table(mpp, pathvec, DI_CHECKER) != DMP_OK ||
- update_mpp_paths(mpp, pathvec)) {
- condlog(1, "error parsing map %s", mpp->wwid);
- remove_map(mpp, pathvec, curmp);
- i--;
- } else
- extract_hwe_from_path(mpp);
- }
- return MPATH_PR_SUCCESS ;
-}
-
-int mpath_send_prin_activepath (char * dev, int rq_servact,
- struct prin_resp * resp, int noisy)
-{
-
- int rc;
-
- rc = prin_do_scsi_ioctl(dev, rq_servact, resp, noisy);
-
- return (rc);
-}
-
-int mpath_prout_reg(struct multipath *mpp,int rq_servact, int rq_scope,
- unsigned int rq_type, struct prout_param_descriptor * paramp, int noisy)
-{
-
- int i, j, k;
- struct pathgroup *pgp = NULL;
- struct path *pp = NULL;
- int rollback = 0;
- int active_pathcount=0;
- int rc;
- int count=0;
- int status = MPATH_PR_SUCCESS;
- int all_tg_pt;
- uint64_t sa_key = 0;
-
- if (!mpp)
- return MPATH_PR_DMMP_ERROR;
-
- all_tg_pt = (mpp->all_tg_pt == ALL_TG_PT_ON ||
- paramp->sa_flags & MPATH_F_ALL_TG_PT_MASK);
- active_pathcount = count_active_paths(mpp);
-
- if (active_pathcount == 0) {
- condlog (0, "%s: no path available", mpp->wwid);
- return MPATH_PR_DMMP_ERROR;
- }
-
- struct threadinfo thread[active_pathcount];
- int hosts[active_pathcount];
-
- memset(thread, 0, sizeof(thread));
-
- /* init thread parameter */
- for (i =0; i< active_pathcount; i++){
- hosts[i] = -1;
- thread[i].param.rq_servact = rq_servact;
- thread[i].param.rq_scope = rq_scope;
- thread[i].param.rq_type = rq_type;
- thread[i].param.paramp = paramp;
- thread[i].param.noisy = noisy;
- thread[i].param.status = MPATH_PR_SKIP;
-
- condlog (3, "THREAD ID [%d] INFO]", i);
- condlog (3, "rq_servact=%d ", thread[i].param.rq_servact);
- condlog (3, "rq_scope=%d ", thread[i].param.rq_scope);
- condlog (3, "rq_type=%d ", thread[i].param.rq_type);
- condlog (3, "rkey=");
- condlog (3, "paramp->sa_flags =%02x ",
- thread[i].param.paramp->sa_flags);
- condlog (3, "noisy=%d ", thread[i].param.noisy);
- condlog (3, "status=%d ", thread[i].param.status);
- }
-
- pthread_attr_t attr;
- pthread_attr_init(&attr);
- pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
-
- vector_foreach_slot (mpp->pg, pgp, j){
- vector_foreach_slot (pgp->paths, pp, i){
- if (!((pp->state == PATH_UP) || (pp->state == PATH_GHOST))){
- condlog (1, "%s: %s path not up. Skip.", mpp->wwid, pp->dev);
- continue;
- }
- if (all_tg_pt && pp->sg_id.host_no != -1) {
- for (k = 0; k < count; k++) {
- if (pp->sg_id.host_no == hosts[k]) {
- condlog(3, "%s: %s host %d matches skip.", pp->wwid, pp->dev, pp->sg_id.host_no);
- break;
- }
- }
- if (k < count)
- continue;
- }
- strlcpy(thread[count].param.dev, pp->dev,
- FILE_NAME_SIZE);
-
- if (count && (thread[count].param.paramp->sa_flags & MPATH_F_SPEC_I_PT_MASK)){
- /*
- * Clearing SPEC_I_PT as transportids are already registered by now.
- */
- thread[count].param.paramp->sa_flags &= (~MPATH_F_SPEC_I_PT_MASK);
- }
-
- condlog (3, "%s: sending pr out command to %s", mpp->wwid, pp->dev);
-
- rc = pthread_create(&thread[count].id, &attr, mpath_prout_pthread_fn, (void *)(&thread[count].param));
- if (rc){
- condlog (0, "%s: failed to create thread %d", mpp->wwid, rc);
- thread[count].param.status = MPATH_PR_THREAD_ERROR;
- }
- else
- hosts[count] = pp->sg_id.host_no;
- count = count + 1;
- }
- }
- for( i=0; i < count ; i++){
- if (thread[i].param.status != MPATH_PR_THREAD_ERROR) {
- rc = pthread_join(thread[i].id, NULL);
- if (rc){
- condlog (0, "%s: Thread[%d] failed to join thread %d", mpp->wwid, i, rc);
- }
- }
- if (!rollback && (thread[i].param.status == MPATH_PR_RESERV_CONFLICT)){
- rollback = 1;
- sa_key = get_unaligned_be64(¶mp->sa_key[0]);
- status = MPATH_PR_RESERV_CONFLICT ;
- }
- if (!rollback && (status == MPATH_PR_SUCCESS)){
- status = thread[i].param.status;
- }
- }
- if (rollback && ((rq_servact == MPATH_PROUT_REG_SA) && sa_key != 0 )){
- condlog (3, "%s: ERROR: initiating pr out rollback", mpp->wwid);
- memcpy(¶mp->key, ¶mp->sa_key, 8);
- memset(¶mp->sa_key, 0, 8);
- for( i=0 ; i < count ; i++){
- if(thread[i].param.status == MPATH_PR_SUCCESS) {
- rc = pthread_create(&thread[i].id, &attr, mpath_prout_pthread_fn,
- (void *)(&thread[i].param));
- if (rc){
- condlog (0, "%s: failed to create thread for rollback. %d", mpp->wwid, rc);
- thread[i].param.status = MPATH_PR_THREAD_ERROR;
- }
- } else
- thread[i].param.status = MPATH_PR_SKIP;
- }
- for(i=0; i < count ; i++){
- if (thread[i].param.status != MPATH_PR_SKIP &&
- thread[i].param.status != MPATH_PR_THREAD_ERROR) {
- rc = pthread_join(thread[i].id, NULL);
- if (rc){
- condlog (3, "%s: failed to join thread while rolling back %d",
- mpp->wwid, i);
- }
- }
- }
- }
-
- pthread_attr_destroy(&attr);
- return (status);
-}
-
-void * mpath_prout_pthread_fn(void *p)
-{
- int ret;
- struct prout_param * param = (struct prout_param *)p;
-
- ret = prout_do_scsi_ioctl( param->dev,param->rq_servact, param->rq_scope,
- param->rq_type, param->paramp, param->noisy);
- param->status = ret;
- pthread_exit(NULL);
-}
-
-int mpath_prout_common(struct multipath *mpp,int rq_servact, int rq_scope,
- unsigned int rq_type, struct prout_param_descriptor* paramp, int noisy)
-{
- int i,j, ret;
- struct pathgroup *pgp = NULL;
- struct path *pp = NULL;
-
- vector_foreach_slot (mpp->pg, pgp, j){
- vector_foreach_slot (pgp->paths, pp, i){
- if (!((pp->state == PATH_UP) || (pp->state == PATH_GHOST))){
- condlog (1, "%s: %s path not up. Skip",
- mpp->wwid, pp->dev);
- continue;
- }
-
- condlog (3, "%s: sending pr out command to %s", mpp->wwid, pp->dev);
- ret = send_prout_activepath(pp->dev, rq_servact,
- rq_scope, rq_type,
- paramp, noisy);
- return ret ;
- }
- }
- condlog (0, "%s: no path available", mpp->wwid);
- return MPATH_PR_DMMP_ERROR;
-}
-
-int send_prout_activepath(char * dev, int rq_servact, int rq_scope,
- unsigned int rq_type, struct prout_param_descriptor * paramp, int noisy)
-{
- struct prout_param param;
- param.rq_servact = rq_servact;
- param.rq_scope = rq_scope;
- param.rq_type = rq_type;
- param.paramp = paramp;
- param.noisy = noisy;
- param.status = -1;
-
- pthread_t thread;
- pthread_attr_t attr;
- int rc;
-
- memset(&thread, 0, sizeof(thread));
- strlcpy(param.dev, dev, FILE_NAME_SIZE);
- /* Initialize and set thread joinable attribute */
- pthread_attr_init(&attr);
- pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
-
- rc = pthread_create(&thread, &attr, mpath_prout_pthread_fn, (void *)(¶m));
- if (rc){
- condlog (3, "%s: failed to create thread %d", dev, rc);
- return MPATH_PR_THREAD_ERROR;
- }
- /* Free attribute and wait for the other threads */
- pthread_attr_destroy(&attr);
- rc = pthread_join(thread, NULL);
-
- return (param.status);
-}
-
-int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope,
- unsigned int rq_type, struct prout_param_descriptor * paramp, int noisy)
-{
- int i, j;
- int num = 0;
- struct pathgroup *pgp = NULL;
- struct path *pp = NULL;
- int active_pathcount = 0;
- pthread_attr_t attr;
- int rc, found = 0;
- int count = 0;
- int status = MPATH_PR_SUCCESS;
- struct prin_resp resp;
- struct prout_param_descriptor *pamp;
- struct prin_resp *pr_buff;
- int length;
- struct transportid *pptr;
-
- if (!mpp)
- return MPATH_PR_DMMP_ERROR;
-
- active_pathcount = count_active_paths(mpp);
-
- if (active_pathcount == 0) {
- condlog (0, "%s: no path available", mpp->wwid);
- return MPATH_PR_DMMP_ERROR;
- }
-
- struct threadinfo thread[active_pathcount];
- memset(thread, 0, sizeof(thread));
- for (i = 0; i < active_pathcount; i++){
- thread[i].param.rq_servact = rq_servact;
- thread[i].param.rq_scope = rq_scope;
- thread[i].param.rq_type = rq_type;
- thread[i].param.paramp = paramp;
- thread[i].param.noisy = noisy;
- thread[i].param.status = MPATH_PR_SKIP;
-
- condlog (3, " path count = %d", i);
- condlog (3, "rq_servact=%d ", thread[i].param.rq_servact);
- condlog (3, "rq_scope=%d ", thread[i].param.rq_scope);
- condlog (3, "rq_type=%d ", thread[i].param.rq_type);
- condlog (3, "noisy=%d ", thread[i].param.noisy);
- condlog (3, "status=%d ", thread[i].param.status);
- }
-
- pthread_attr_init (&attr);
- pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_JOINABLE);
-
- vector_foreach_slot (mpp->pg, pgp, j){
- vector_foreach_slot (pgp->paths, pp, i){
- if (!((pp->state == PATH_UP) || (pp->state == PATH_GHOST))){
- condlog (1, "%s: %s path not up.", mpp->wwid, pp->dev);
- continue;
- }
-
- strlcpy(thread[count].param.dev, pp->dev,
- FILE_NAME_SIZE);
- condlog (3, "%s: sending pr out command to %s", mpp->wwid, pp->dev);
- rc = pthread_create (&thread[count].id, &attr, mpath_prout_pthread_fn,
- (void *) (&thread[count].param));
- if (rc) {
- condlog (0, "%s: failed to create thread. %d", mpp->wwid, rc);
- thread[count].param.status = MPATH_PR_THREAD_ERROR;
- }
- count = count + 1;
- }
- }
- pthread_attr_destroy (&attr);
- for (i = 0; i < count; i++){
- if (thread[i].param.status != MPATH_PR_THREAD_ERROR) {
- rc = pthread_join (thread[i].id, NULL);
- if (rc){
- condlog (1, "%s: failed to join thread. %d", mpp->wwid, rc);
- }
- }
- }
-
- for (i = 0; i < count; i++){
- /* check thread status here and return the status */
-
- if (thread[i].param.status == MPATH_PR_RESERV_CONFLICT)
- status = MPATH_PR_RESERV_CONFLICT;
- else if (status == MPATH_PR_SUCCESS
- && thread[i].param.status != MPATH_PR_RESERV_CONFLICT)
- status = thread[i].param.status;
- }
-
- status = mpath_prin_activepath (mpp, MPATH_PRIN_RRES_SA, &resp, noisy);
- if (status != MPATH_PR_SUCCESS){
- condlog (0, "%s: pr in read reservation command failed.", mpp->wwid);
- return MPATH_PR_OTHER;
- }
-
- num = resp.prin_descriptor.prin_readresv.additional_length / 8;
- if (num == 0){
- condlog (2, "%s: Path holding reservation is released.", mpp->wwid);
- return MPATH_PR_SUCCESS;
- }
- condlog (2, "%s: Path holding reservation is not avialable.", mpp->wwid);
-
- pr_buff = mpath_alloc_prin_response(MPATH_PRIN_RFSTAT_SA);
- if (!pr_buff){
- condlog (0, "%s: failed to alloc pr in response buffer.", mpp->wwid);
- return MPATH_PR_OTHER;
- }
-
- status = mpath_prin_activepath (mpp, MPATH_PRIN_RFSTAT_SA, pr_buff, noisy);
-
- if (status != MPATH_PR_SUCCESS){
- condlog (0, "%s: pr in read full status command failed.", mpp->wwid);
- goto out;
- }
-
- num = pr_buff->prin_descriptor.prin_readfd.number_of_descriptor;
- if (0 == num){
- goto out;
- }
- length = sizeof (struct prout_param_descriptor) + (sizeof (struct transportid *));
-
- pamp = (struct prout_param_descriptor *)malloc (length);
- if (!pamp){
- condlog (0, "%s: failed to alloc pr out parameter.", mpp->wwid);
- goto out1;
- }
-
- memset(pamp, 0, length);
-
- pamp->trnptid_list[0] = (struct transportid *) malloc (sizeof (struct transportid));
- if (!pamp->trnptid_list[0]){
- condlog (0, "%s: failed to alloc pr out transportid.", mpp->wwid);
- goto out1;
- }
-
- if (get_be64(mpp->reservation_key)){
- memcpy (pamp->key, &mpp->reservation_key, 8);
- condlog (3, "%s: reservation key set.", mpp->wwid);
- }
-
- status = mpath_prout_common (mpp, MPATH_PROUT_CLEAR_SA,
- rq_scope, rq_type, pamp, noisy);
-
- if (status) {
- condlog(0, "%s: failed to send CLEAR_SA", mpp->wwid);
- goto out1;
- }
-
- pamp->num_transportid = 1;
- pptr=pamp->trnptid_list[0];
-
- for (i = 0; i < num; i++){
- if (get_be64(mpp->reservation_key) &&
- memcmp(pr_buff->prin_descriptor.prin_readfd.descriptors[i]->key,
- &mpp->reservation_key, 8)){
- /*register with tarnsport id*/
- memset(pamp, 0, length);
- pamp->trnptid_list[0] = pptr;
- memset (pamp->trnptid_list[0], 0, sizeof (struct transportid));
- memcpy (pamp->sa_key,
- pr_buff->prin_descriptor.prin_readfd.descriptors[i]->key, 8);
- pamp->sa_flags = MPATH_F_SPEC_I_PT_MASK;
- pamp->num_transportid = 1;
-
- memcpy (pamp->trnptid_list[0],
- &pr_buff->prin_descriptor.prin_readfd.descriptors[i]->trnptid,
- sizeof (struct transportid));
- status = mpath_prout_common (mpp, MPATH_PROUT_REG_SA, 0, rq_type,
- pamp, noisy);
-
- pamp->sa_flags = 0;
- memcpy (pamp->key, pr_buff->prin_descriptor.prin_readfd.descriptors[i]->key, 8);
- memset (pamp->sa_key, 0, 8);
- pamp->num_transportid = 0;
- status = mpath_prout_common (mpp, MPATH_PROUT_REG_SA, 0, rq_type,
- pamp, noisy);
- }
- else
- {
- if (get_be64(mpp->reservation_key))
- found = 1;
- }
-
-
- }
-
- if (found){
- memset (pamp, 0, length);
- memcpy (pamp->sa_key, &mpp->reservation_key, 8);
- memset (pamp->key, 0, 8);
- status = mpath_prout_reg(mpp, MPATH_PROUT_REG_SA, rq_scope, rq_type, pamp, noisy);
- }
-
-
- free(pptr);
-out1:
- free (pamp);
-out:
- free (pr_buff);
- return (status);
-}
-
-void * mpath_alloc_prin_response(int prin_sa)
-{
- void * ptr = NULL;
- int size=0;
- switch (prin_sa)
- {
- case MPATH_PRIN_RKEY_SA:
- size = sizeof(struct prin_readdescr);
- break;
- case MPATH_PRIN_RRES_SA:
- size = sizeof(struct prin_resvdescr);
- break;
- case MPATH_PRIN_RCAP_SA:
- size=sizeof(struct prin_capdescr);
- break;
- case MPATH_PRIN_RFSTAT_SA:
- size = sizeof(struct print_fulldescr_list) +
- sizeof(struct prin_fulldescr *)*MPATH_MX_TIDS;
- break;
- }
- if (size > 0)
- {
- ptr = calloc(size, 1);
- }
- return ptr;
-}
-
-int update_map_pr(struct multipath *mpp)
-{
- int noisy=0;
- struct prin_resp *resp;
- unsigned int i;
- int ret, isFound;
-
- if (!get_be64(mpp->reservation_key))
- {
- /* Nothing to do. Assuming pr mgmt feature is disabled*/
- condlog(4, "%s: reservation_key not set in multipath.conf",
- mpp->alias);
- return MPATH_PR_SUCCESS;
- }
-
- resp = mpath_alloc_prin_response(MPATH_PRIN_RKEY_SA);
- if (!resp)
- {
- condlog(0,"%s : failed to alloc resp in update_map_pr", mpp->alias);
- return MPATH_PR_OTHER;
- }
- ret = mpath_prin_activepath(mpp, MPATH_PRIN_RKEY_SA, resp, noisy);
-
- if (ret != MPATH_PR_SUCCESS )
- {
- condlog(0,"%s : pr in read keys service action failed Error=%d", mpp->alias, ret);
- free(resp);
- return ret;
- }
-
- if (resp->prin_descriptor.prin_readkeys.additional_length == 0 )
- {
- condlog(3,"%s: No key found. Device may not be registered. ", mpp->alias);
- free(resp);
- return MPATH_PR_SUCCESS;
- }
-
- 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))
- {
- condlog(2, "%s: reservation key found in pr in readkeys response", mpp->alias);
- isFound =1;
- }
- }
-
- if (isFound)
- {
- mpp->prflag = 1;
- condlog(2, "%s: prflag flag set.", mpp->alias );
- }
-
- free(resp);
- return MPATH_PR_SUCCESS;
-}
#define MPATH_PROTOCOL_ID_SAS 0x06
-/*Transport ID FORMATE CODE */
+/*Transport ID FORMAT CODE */
#define MPATH_WWUI_DEVICE_NAME 0x00 /* World wide unique initiator device name */
#define MPATH_WWUI_PORT_IDENTIFIER 0x40 /* World wide unique initiator port identifier */
--- /dev/null
+#include <libdevmapper.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <libudev.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+
+#include "vector.h"
+#include "defaults.h"
+#include "checkers.h"
+#include "structs.h"
+#include "structs_vec.h"
+#include "prio.h"
+#include "devmapper.h"
+#include "debug.h"
+#include "config.h"
+#include "switchgroup.h"
+#include "discovery.h"
+#include "configure.h"
+#include "dmparser.h"
+#include "propsel.h"
+#include "util.h"
+#include "unaligned.h"
+
+#include "mpath_persist.h"
+#include "mpath_persist_int.h"
+#include "mpathpr.h"
+#include "mpath_pr_ioctl.h"
+
+struct prout_param {
+ char dev[FILE_NAME_SIZE];
+ int rq_servact;
+ int rq_scope;
+ unsigned int rq_type;
+ struct prout_param_descriptor *paramp;
+ int noisy;
+ int status;
+};
+
+struct threadinfo {
+ int status;
+ pthread_t id;
+ struct prout_param param;
+};
+
+static int mpath_send_prin_activepath (char * dev, int rq_servact,
+ struct prin_resp * resp, int noisy)
+{
+
+ int rc;
+
+ rc = prin_do_scsi_ioctl(dev, rq_servact, resp, noisy);
+
+ return (rc);
+}
+
+static int mpath_prin_activepath (struct multipath *mpp, int rq_servact,
+ struct prin_resp * resp, int noisy)
+{
+ int i,j, ret = MPATH_PR_DMMP_ERROR;
+ struct pathgroup *pgp = NULL;
+ struct path *pp = NULL;
+
+ vector_foreach_slot (mpp->pg, pgp, j){
+ vector_foreach_slot (pgp->paths, pp, i){
+ if (!((pp->state == PATH_UP) ||
+ (pp->state == PATH_GHOST))){
+ condlog(2, "%s: %s not available. Skip.",
+ mpp->wwid, pp->dev);
+ condlog(3, "%s: status = %d.",
+ mpp->wwid, pp->state);
+ continue;
+ }
+
+ condlog(3, "%s: sending pr in command to %s ",
+ mpp->wwid, pp->dev);
+ ret = mpath_send_prin_activepath(pp->dev, rq_servact,
+ resp, noisy);
+ switch(ret)
+ {
+ case MPATH_PR_SUCCESS:
+ case MPATH_PR_SENSE_INVALID_OP:
+ return ret;
+ default:
+ continue;
+ }
+ }
+ }
+ return ret;
+}
+
+void *mpath_alloc_prin_response(int prin_sa)
+{
+ void * ptr = NULL;
+ int size=0;
+ switch (prin_sa)
+ {
+ case MPATH_PRIN_RKEY_SA:
+ size = sizeof(struct prin_readdescr);
+ break;
+ case MPATH_PRIN_RRES_SA:
+ size = sizeof(struct prin_resvdescr);
+ break;
+ case MPATH_PRIN_RCAP_SA:
+ size=sizeof(struct prin_capdescr);
+ break;
+ case MPATH_PRIN_RFSTAT_SA:
+ size = sizeof(struct print_fulldescr_list) +
+ sizeof(struct prin_fulldescr *)*MPATH_MX_TIDS;
+ break;
+ }
+ if (size > 0)
+ {
+ ptr = calloc(size, 1);
+ }
+ return ptr;
+}
+
+static int get_mpvec(vector curmp, vector pathvec, char *refwwid)
+{
+ int i;
+ struct multipath *mpp;
+
+ vector_foreach_slot (curmp, mpp, i){
+ /*
+ * discard out of scope maps
+ */
+ if (!mpp->alias) {
+ condlog(0, "%s: map with empty alias!", __func__);
+ continue;
+ }
+
+ if (mpp->pg != NULL)
+ /* Already seen this one */
+ continue;
+
+ if (refwwid && strncmp (mpp->alias, refwwid, WWID_SIZE - 1))
+ continue;
+
+ if (update_multipath_table(mpp, pathvec, DI_CHECKER) != DMP_OK ||
+ update_mpp_paths(mpp, pathvec)) {
+ condlog(1, "error parsing map %s", mpp->wwid);
+ remove_map(mpp, pathvec, curmp);
+ i--;
+ } else
+ extract_hwe_from_path(mpp);
+ }
+ return MPATH_PR_SUCCESS ;
+}
+
+static int mpath_get_map(vector curmp, vector pathvec, int fd, char **palias,
+ struct multipath **pmpp)
+{
+ int ret = MPATH_PR_DMMP_ERROR;
+ struct stat info;
+ int major, minor;
+ char *alias;
+ struct multipath *mpp;
+
+ if (fstat(fd, &info) != 0){
+ condlog(0, "stat error fd=%d", fd);
+ return MPATH_PR_FILE_ERROR;
+ }
+ if(!S_ISBLK(info.st_mode)){
+ condlog(3, "Failed to get major:minor. fd=%d", fd);
+ return MPATH_PR_FILE_ERROR;
+ }
+
+ major = major(info.st_rdev);
+ minor = minor(info.st_rdev);
+ condlog(4, "Device %d:%d", major, minor);
+
+ /* get alias from major:minor*/
+ alias = dm_mapname(major, minor);
+ if (!alias){
+ condlog(0, "%d:%d failed to get device alias.", major, minor);
+ return MPATH_PR_DMMP_ERROR;
+ }
+
+ condlog(3, "alias = %s", alias);
+
+ if (dm_map_present(alias) && dm_is_mpath(alias) != 1){
+ condlog(3, "%s: not a multipath device.", alias);
+ goto out;
+ }
+
+ /* get info of all paths from the dm device */
+ if (get_mpvec(curmp, pathvec, alias)){
+ condlog(0, "%s: failed to get device info.", alias);
+ goto out;
+ }
+
+ mpp = find_mp_by_alias(curmp, alias);
+
+ if (!mpp) {
+ condlog(0, "%s: devmap not registered.", alias);
+ goto out;
+ }
+
+ ret = MPATH_PR_SUCCESS;
+ if (pmpp)
+ *pmpp = mpp;
+ if (palias) {
+ *palias = alias;
+ alias = NULL;
+ }
+out:
+ free(alias);
+ return ret;
+}
+
+int do_mpath_persistent_reserve_in(vector curmp, vector pathvec,
+ int fd, int rq_servact,
+ struct prin_resp *resp, int noisy)
+{
+ struct multipath *mpp;
+ int ret;
+
+ ret = mpath_get_map(curmp, pathvec, fd, NULL, &mpp);
+ if (ret != MPATH_PR_SUCCESS)
+ return ret;
+
+ ret = mpath_prin_activepath(mpp, rq_servact, resp, noisy);
+
+ return ret;
+}
+
+static void *mpath_prout_pthread_fn(void *p)
+{
+ int ret;
+ struct prout_param * param = (struct prout_param *)p;
+
+ ret = prout_do_scsi_ioctl( param->dev,param->rq_servact, param->rq_scope,
+ param->rq_type, param->paramp, param->noisy);
+ param->status = ret;
+ pthread_exit(NULL);
+}
+
+static int mpath_prout_reg(struct multipath *mpp,int rq_servact, int rq_scope,
+ unsigned int rq_type,
+ struct prout_param_descriptor * paramp, int noisy)
+{
+
+ int i, j, k;
+ struct pathgroup *pgp = NULL;
+ struct path *pp = NULL;
+ int rollback = 0;
+ int active_pathcount=0;
+ int rc;
+ int count=0;
+ int status = MPATH_PR_SUCCESS;
+ int all_tg_pt;
+ uint64_t sa_key = 0;
+
+ if (!mpp)
+ return MPATH_PR_DMMP_ERROR;
+
+ all_tg_pt = (mpp->all_tg_pt == ALL_TG_PT_ON ||
+ paramp->sa_flags & MPATH_F_ALL_TG_PT_MASK);
+ active_pathcount = count_active_paths(mpp);
+
+ if (active_pathcount == 0) {
+ condlog (0, "%s: no path available", mpp->wwid);
+ return MPATH_PR_DMMP_ERROR;
+ }
+
+ struct threadinfo thread[active_pathcount];
+ int hosts[active_pathcount];
+
+ memset(thread, 0, sizeof(thread));
+
+ /* init thread parameter */
+ for (i =0; i< active_pathcount; i++){
+ hosts[i] = -1;
+ thread[i].param.rq_servact = rq_servact;
+ thread[i].param.rq_scope = rq_scope;
+ thread[i].param.rq_type = rq_type;
+ thread[i].param.paramp = paramp;
+ thread[i].param.noisy = noisy;
+ thread[i].param.status = MPATH_PR_SKIP;
+
+ condlog (3, "THREAD ID [%d] INFO]", i);
+ condlog (3, "rq_servact=%d ", thread[i].param.rq_servact);
+ condlog (3, "rq_scope=%d ", thread[i].param.rq_scope);
+ condlog (3, "rq_type=%d ", thread[i].param.rq_type);
+ condlog (3, "rkey=");
+ condlog (3, "paramp->sa_flags =%02x ",
+ thread[i].param.paramp->sa_flags);
+ condlog (3, "noisy=%d ", thread[i].param.noisy);
+ condlog (3, "status=%d ", thread[i].param.status);
+ }
+
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
+
+ vector_foreach_slot (mpp->pg, pgp, j){
+ vector_foreach_slot (pgp->paths, pp, i){
+ if (!((pp->state == PATH_UP) || (pp->state == PATH_GHOST))){
+ condlog (1, "%s: %s path not up. Skip.", mpp->wwid, pp->dev);
+ continue;
+ }
+ if (all_tg_pt && pp->sg_id.host_no != -1) {
+ for (k = 0; k < count; k++) {
+ if (pp->sg_id.host_no == hosts[k]) {
+ condlog(3, "%s: %s host %d matches skip.", pp->wwid, pp->dev, pp->sg_id.host_no);
+ break;
+ }
+ }
+ if (k < count)
+ continue;
+ }
+ strlcpy(thread[count].param.dev, pp->dev,
+ FILE_NAME_SIZE);
+
+ if (count && (thread[count].param.paramp->sa_flags & MPATH_F_SPEC_I_PT_MASK)){
+ /*
+ * Clearing SPEC_I_PT as transportids are already registered by now.
+ */
+ thread[count].param.paramp->sa_flags &= (~MPATH_F_SPEC_I_PT_MASK);
+ }
+
+ condlog (3, "%s: sending pr out command to %s", mpp->wwid, pp->dev);
+
+ rc = pthread_create(&thread[count].id, &attr, mpath_prout_pthread_fn, (void *)(&thread[count].param));
+ if (rc){
+ condlog (0, "%s: failed to create thread %d", mpp->wwid, rc);
+ thread[count].param.status = MPATH_PR_THREAD_ERROR;
+ }
+ else
+ hosts[count] = pp->sg_id.host_no;
+ count = count + 1;
+ }
+ }
+ for( i=0; i < count ; i++){
+ if (thread[i].param.status != MPATH_PR_THREAD_ERROR) {
+ rc = pthread_join(thread[i].id, NULL);
+ if (rc){
+ condlog (0, "%s: Thread[%d] failed to join thread %d", mpp->wwid, i, rc);
+ }
+ }
+ if (!rollback && (thread[i].param.status == MPATH_PR_RESERV_CONFLICT)){
+ rollback = 1;
+ sa_key = get_unaligned_be64(¶mp->sa_key[0]);
+ status = MPATH_PR_RESERV_CONFLICT ;
+ }
+ if (!rollback && (status == MPATH_PR_SUCCESS)){
+ status = thread[i].param.status;
+ }
+ }
+ if (rollback && ((rq_servact == MPATH_PROUT_REG_SA) && sa_key != 0 )){
+ condlog (3, "%s: ERROR: initiating pr out rollback", mpp->wwid);
+ memcpy(¶mp->key, ¶mp->sa_key, 8);
+ memset(¶mp->sa_key, 0, 8);
+ for( i=0 ; i < count ; i++){
+ if(thread[i].param.status == MPATH_PR_SUCCESS) {
+ rc = pthread_create(&thread[i].id, &attr, mpath_prout_pthread_fn,
+ (void *)(&thread[i].param));
+ if (rc){
+ condlog (0, "%s: failed to create thread for rollback. %d", mpp->wwid, rc);
+ thread[i].param.status = MPATH_PR_THREAD_ERROR;
+ }
+ } else
+ thread[i].param.status = MPATH_PR_SKIP;
+ }
+ for(i=0; i < count ; i++){
+ if (thread[i].param.status != MPATH_PR_SKIP &&
+ thread[i].param.status != MPATH_PR_THREAD_ERROR) {
+ rc = pthread_join(thread[i].id, NULL);
+ if (rc){
+ condlog (3, "%s: failed to join thread while rolling back %d",
+ mpp->wwid, i);
+ }
+ }
+ }
+ }
+
+ pthread_attr_destroy(&attr);
+ return (status);
+}
+
+static int send_prout_activepath(char *dev, int rq_servact, int rq_scope,
+ unsigned int rq_type,
+ struct prout_param_descriptor * paramp, int noisy)
+{
+ struct prout_param param;
+ param.rq_servact = rq_servact;
+ param.rq_scope = rq_scope;
+ param.rq_type = rq_type;
+ param.paramp = paramp;
+ param.noisy = noisy;
+ param.status = -1;
+
+ pthread_t thread;
+ pthread_attr_t attr;
+ int rc;
+
+ memset(&thread, 0, sizeof(thread));
+ strlcpy(param.dev, dev, FILE_NAME_SIZE);
+ /* Initialize and set thread joinable attribute */
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
+
+ rc = pthread_create(&thread, &attr, mpath_prout_pthread_fn, (void *)(¶m));
+ if (rc){
+ condlog (3, "%s: failed to create thread %d", dev, rc);
+ return MPATH_PR_THREAD_ERROR;
+ }
+ /* Free attribute and wait for the other threads */
+ pthread_attr_destroy(&attr);
+ rc = pthread_join(thread, NULL);
+
+ return (param.status);
+}
+
+static int mpath_prout_common(struct multipath *mpp,int rq_servact, int rq_scope,
+ unsigned int rq_type,
+ struct prout_param_descriptor* paramp, int noisy)
+{
+ int i,j, ret;
+ struct pathgroup *pgp = NULL;
+ struct path *pp = NULL;
+
+ vector_foreach_slot (mpp->pg, pgp, j){
+ vector_foreach_slot (pgp->paths, pp, i){
+ if (!((pp->state == PATH_UP) || (pp->state == PATH_GHOST))){
+ condlog (1, "%s: %s path not up. Skip",
+ mpp->wwid, pp->dev);
+ continue;
+ }
+
+ condlog (3, "%s: sending pr out command to %s", mpp->wwid, pp->dev);
+ ret = send_prout_activepath(pp->dev, rq_servact,
+ rq_scope, rq_type,
+ paramp, noisy);
+ return ret ;
+ }
+ }
+ condlog (0, "%s: no path available", mpp->wwid);
+ return MPATH_PR_DMMP_ERROR;
+}
+
+static int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope,
+ unsigned int rq_type,
+ struct prout_param_descriptor * paramp, int noisy)
+{
+ int i, j;
+ int num = 0;
+ struct pathgroup *pgp = NULL;
+ struct path *pp = NULL;
+ int active_pathcount = 0;
+ pthread_attr_t attr;
+ int rc, found = 0;
+ int count = 0;
+ int status = MPATH_PR_SUCCESS;
+ struct prin_resp resp;
+ struct prout_param_descriptor *pamp;
+ struct prin_resp *pr_buff;
+ int length;
+ struct transportid *pptr;
+
+ if (!mpp)
+ return MPATH_PR_DMMP_ERROR;
+
+ active_pathcount = count_active_paths(mpp);
+
+ if (active_pathcount == 0) {
+ condlog (0, "%s: no path available", mpp->wwid);
+ return MPATH_PR_DMMP_ERROR;
+ }
+
+ struct threadinfo thread[active_pathcount];
+ memset(thread, 0, sizeof(thread));
+ for (i = 0; i < active_pathcount; i++){
+ thread[i].param.rq_servact = rq_servact;
+ thread[i].param.rq_scope = rq_scope;
+ thread[i].param.rq_type = rq_type;
+ thread[i].param.paramp = paramp;
+ thread[i].param.noisy = noisy;
+ thread[i].param.status = MPATH_PR_SKIP;
+
+ condlog (3, " path count = %d", i);
+ condlog (3, "rq_servact=%d ", thread[i].param.rq_servact);
+ condlog (3, "rq_scope=%d ", thread[i].param.rq_scope);
+ condlog (3, "rq_type=%d ", thread[i].param.rq_type);
+ condlog (3, "noisy=%d ", thread[i].param.noisy);
+ condlog (3, "status=%d ", thread[i].param.status);
+ }
+
+ pthread_attr_init (&attr);
+ pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_JOINABLE);
+
+ vector_foreach_slot (mpp->pg, pgp, j){
+ vector_foreach_slot (pgp->paths, pp, i){
+ if (!((pp->state == PATH_UP) || (pp->state == PATH_GHOST))){
+ condlog (1, "%s: %s path not up.", mpp->wwid, pp->dev);
+ continue;
+ }
+
+ strlcpy(thread[count].param.dev, pp->dev,
+ FILE_NAME_SIZE);
+ condlog (3, "%s: sending pr out command to %s", mpp->wwid, pp->dev);
+ rc = pthread_create (&thread[count].id, &attr, mpath_prout_pthread_fn,
+ (void *) (&thread[count].param));
+ if (rc) {
+ condlog (0, "%s: failed to create thread. %d", mpp->wwid, rc);
+ thread[count].param.status = MPATH_PR_THREAD_ERROR;
+ }
+ count = count + 1;
+ }
+ }
+ pthread_attr_destroy (&attr);
+ for (i = 0; i < count; i++){
+ if (thread[i].param.status != MPATH_PR_THREAD_ERROR) {
+ rc = pthread_join (thread[i].id, NULL);
+ if (rc){
+ condlog (1, "%s: failed to join thread. %d", mpp->wwid, rc);
+ }
+ }
+ }
+
+ for (i = 0; i < count; i++){
+ /* check thread status here and return the status */
+
+ if (thread[i].param.status == MPATH_PR_RESERV_CONFLICT)
+ status = MPATH_PR_RESERV_CONFLICT;
+ else if (status == MPATH_PR_SUCCESS
+ && thread[i].param.status != MPATH_PR_RESERV_CONFLICT)
+ status = thread[i].param.status;
+ }
+
+ status = mpath_prin_activepath (mpp, MPATH_PRIN_RRES_SA, &resp, noisy);
+ if (status != MPATH_PR_SUCCESS){
+ condlog (0, "%s: pr in read reservation command failed.", mpp->wwid);
+ return MPATH_PR_OTHER;
+ }
+
+ num = resp.prin_descriptor.prin_readresv.additional_length / 8;
+ if (num == 0){
+ condlog (2, "%s: Path holding reservation is released.", mpp->wwid);
+ return MPATH_PR_SUCCESS;
+ }
+ condlog (2, "%s: Path holding reservation is not available.", mpp->wwid);
+
+ pr_buff = mpath_alloc_prin_response(MPATH_PRIN_RFSTAT_SA);
+ if (!pr_buff){
+ condlog (0, "%s: failed to alloc pr in response buffer.", mpp->wwid);
+ return MPATH_PR_OTHER;
+ }
+
+ status = mpath_prin_activepath (mpp, MPATH_PRIN_RFSTAT_SA, pr_buff, noisy);
+
+ if (status != MPATH_PR_SUCCESS){
+ condlog (0, "%s: pr in read full status command failed.", mpp->wwid);
+ goto out;
+ }
+
+ num = pr_buff->prin_descriptor.prin_readfd.number_of_descriptor;
+ if (0 == num){
+ goto out;
+ }
+ length = sizeof (struct prout_param_descriptor) + (sizeof (struct transportid *));
+
+ pamp = (struct prout_param_descriptor *)malloc (length);
+ if (!pamp){
+ condlog (0, "%s: failed to alloc pr out parameter.", mpp->wwid);
+ goto out1;
+ }
+
+ memset(pamp, 0, length);
+
+ pamp->trnptid_list[0] = (struct transportid *) malloc (sizeof (struct transportid));
+ if (!pamp->trnptid_list[0]){
+ condlog (0, "%s: failed to alloc pr out transportid.", mpp->wwid);
+ goto out1;
+ }
+
+ if (get_be64(mpp->reservation_key)){
+ memcpy (pamp->key, &mpp->reservation_key, 8);
+ condlog (3, "%s: reservation key set.", mpp->wwid);
+ }
+
+ status = mpath_prout_common (mpp, MPATH_PROUT_CLEAR_SA,
+ rq_scope, rq_type, pamp, noisy);
+
+ if (status) {
+ condlog(0, "%s: failed to send CLEAR_SA", mpp->wwid);
+ goto out1;
+ }
+
+ pamp->num_transportid = 1;
+ pptr=pamp->trnptid_list[0];
+
+ for (i = 0; i < num; i++){
+ if (get_be64(mpp->reservation_key) &&
+ memcmp(pr_buff->prin_descriptor.prin_readfd.descriptors[i]->key,
+ &mpp->reservation_key, 8)){
+ /*register with tarnsport id*/
+ memset(pamp, 0, length);
+ pamp->trnptid_list[0] = pptr;
+ memset (pamp->trnptid_list[0], 0, sizeof (struct transportid));
+ memcpy (pamp->sa_key,
+ pr_buff->prin_descriptor.prin_readfd.descriptors[i]->key, 8);
+ pamp->sa_flags = MPATH_F_SPEC_I_PT_MASK;
+ pamp->num_transportid = 1;
+
+ memcpy (pamp->trnptid_list[0],
+ &pr_buff->prin_descriptor.prin_readfd.descriptors[i]->trnptid,
+ sizeof (struct transportid));
+ status = mpath_prout_common (mpp, MPATH_PROUT_REG_SA, 0, rq_type,
+ pamp, noisy);
+
+ pamp->sa_flags = 0;
+ memcpy (pamp->key, pr_buff->prin_descriptor.prin_readfd.descriptors[i]->key, 8);
+ memset (pamp->sa_key, 0, 8);
+ pamp->num_transportid = 0;
+ status = mpath_prout_common (mpp, MPATH_PROUT_REG_SA, 0, rq_type,
+ pamp, noisy);
+ }
+ else
+ {
+ if (get_be64(mpp->reservation_key))
+ found = 1;
+ }
+
+
+ }
+
+ if (found){
+ memset (pamp, 0, length);
+ memcpy (pamp->sa_key, &mpp->reservation_key, 8);
+ memset (pamp->key, 0, 8);
+ status = mpath_prout_reg(mpp, MPATH_PROUT_REG_SA, rq_scope, rq_type, pamp, noisy);
+ }
+
+
+ free(pptr);
+out1:
+ free (pamp);
+out:
+ free (pr_buff);
+ return (status);
+}
+
+int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd,
+ int rq_servact, int rq_scope, unsigned int rq_type,
+ struct prout_param_descriptor *paramp, int noisy)
+{
+ struct multipath *mpp;
+ char *alias;
+ int ret;
+ uint64_t prkey;
+ struct config *conf;
+
+ ret = mpath_get_map(curmp, pathvec, fd, &alias, &mpp);
+ if (ret != MPATH_PR_SUCCESS)
+ return ret;
+
+ conf = get_multipath_config();
+ select_reservation_key(conf, mpp);
+ select_all_tg_pt(conf, mpp);
+ put_multipath_config(conf);
+
+ memcpy(&prkey, paramp->sa_key, 8);
+ if (mpp->prkey_source == PRKEY_SOURCE_FILE && prkey &&
+ (rq_servact == MPATH_PROUT_REG_IGN_SA ||
+ (rq_servact == MPATH_PROUT_REG_SA &&
+ (!get_be64(mpp->reservation_key) ||
+ memcmp(paramp->key, &mpp->reservation_key, 8) == 0)))) {
+ memcpy(&mpp->reservation_key, paramp->sa_key, 8);
+ if (update_prkey_flags(alias, get_be64(mpp->reservation_key),
+ paramp->sa_flags)) {
+ 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) &&
+ (prkey || rq_servact != MPATH_PROUT_REG_IGN_SA)) {
+ 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:
+ case MPATH_PROUT_REG_IGN_SA:
+ ret= mpath_prout_reg(mpp, rq_servact, rq_scope, rq_type, paramp, noisy);
+ break;
+ case MPATH_PROUT_RES_SA :
+ case MPATH_PROUT_PREE_SA :
+ case MPATH_PROUT_PREE_AB_SA :
+ case MPATH_PROUT_CLEAR_SA:
+ ret = mpath_prout_common(mpp, rq_servact, rq_scope, rq_type, paramp, noisy);
+ break;
+ case MPATH_PROUT_REL_SA:
+ ret = mpath_prout_rel(mpp, rq_servact, rq_scope, rq_type, paramp, noisy);
+ break;
+ default:
+ ret = MPATH_PR_OTHER;
+ goto out1;
+ }
+
+ if ((ret == MPATH_PR_SUCCESS) && ((rq_servact == MPATH_PROUT_REG_SA) ||
+ (rq_servact == MPATH_PROUT_REG_IGN_SA)))
+ {
+ 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(alias);
+ return ret;
+}
+
+int update_map_pr(struct multipath *mpp)
+{
+ int noisy=0;
+ struct prin_resp *resp;
+ unsigned int i;
+ int ret, isFound;
+
+ if (!get_be64(mpp->reservation_key))
+ {
+ /* Nothing to do. Assuming pr mgmt feature is disabled*/
+ condlog(4, "%s: reservation_key not set in multipath.conf",
+ mpp->alias);
+ return MPATH_PR_SUCCESS;
+ }
+
+ resp = mpath_alloc_prin_response(MPATH_PRIN_RKEY_SA);
+ if (!resp)
+ {
+ condlog(0,"%s : failed to alloc resp in update_map_pr", mpp->alias);
+ return MPATH_PR_OTHER;
+ }
+ ret = mpath_prin_activepath(mpp, MPATH_PRIN_RKEY_SA, resp, noisy);
+
+ if (ret != MPATH_PR_SUCCESS )
+ {
+ condlog(0,"%s : pr in read keys service action failed Error=%d", mpp->alias, ret);
+ free(resp);
+ return ret;
+ }
+
+ if (resp->prin_descriptor.prin_readkeys.additional_length == 0 )
+ {
+ condlog(3,"%s: No key found. Device may not be registered. ", mpp->alias);
+ free(resp);
+ return MPATH_PR_SUCCESS;
+ }
+
+ 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))
+ {
+ condlog(2, "%s: reservation key found in pr in readkeys response", mpp->alias);
+ isFound =1;
+ }
+ }
+
+ if (isFound)
+ {
+ mpp->prflag = 1;
+ condlog(2, "%s: prflag flag set.", mpp->alias );
+ }
+
+ free(resp);
+ return MPATH_PR_SUCCESS;
+}
--- /dev/null
+#ifndef _MPATH_PERSIST_INT_H
+#define _MPATH_PERSIST_INT_H
+
+/*
+ * This header file contains symbols that are used by multipath-tools
+ * but aren't part of the public libmpathpersist API.
+ */
+
+void * mpath_alloc_prin_response(int prin_sa);
+int do_mpath_persistent_reserve_in(vector curmp, vector pathvec,
+ int fd, int rq_servact,
+ struct prin_resp *resp, int noisy);
+void *mpath_alloc_prin_response(int prin_sa);
+int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd,
+ int rq_servact, int rq_scope,
+ unsigned int rq_type,
+ struct prout_param_descriptor *paramp,
+ int noisy);
+int prin_do_scsi_ioctl(char * dev, int rq_servact, struct prin_resp * resp, int noisy);
+int prout_do_scsi_ioctl( char * dev, int rq_servact, int rq_scope,
+ unsigned int rq_type, struct prout_param_descriptor *paramp, int noisy);
+void dumpHex(const char* , int len, int no_ascii);
+int update_map_pr(struct multipath *mpp);
+
+#endif /* _MPATH_PERSIST_INT_H */
#include <unistd.h>
#include <string.h>
#include <sys/ioctl.h>
-#include <unistd.h>
#include <libudev.h>
#include "mpath_pr_ioctl.h"
#include "mpath_persist.h"
#include "unaligned.h"
#include "debug.h"
-
-#define FILE_NAME_SIZE 256
+#include "structs.h" /* FILE_NAME_SIZE */
#define TIMEOUT 2000
#define MAXRETRY 5
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
+#include <string.h>
#include <stdarg.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#ifndef MPATHPR_H
#define MPATHPR_H
-#include "structs.h" /* FILE_NAME_SIZE */
-
-struct prin_param {
- char dev[FILE_NAME_SIZE];
- int rq_servact;
- struct prin_resp *resp;
- int noisy;
- int status;
-};
-
-struct prout_param {
- char dev[FILE_NAME_SIZE];
- int rq_servact;
- int rq_scope;
- unsigned int rq_type;
- struct prout_param_descriptor *paramp;
- int noisy;
- int status;
-};
-
-struct threadinfo {
- int status;
- pthread_t id;
- struct prout_param param;
-};
-
-int prin_do_scsi_ioctl(char * dev, int rq_servact, struct prin_resp * resp, int noisy);
-int prout_do_scsi_ioctl( char * dev, int rq_servact, int rq_scope,
- unsigned int rq_type, struct prout_param_descriptor *paramp, int noisy);
-void * _mpath_pr_update (void *arg);
-int mpath_send_prin_activepath (char * dev, int rq_servact, struct prin_resp * resp, int noisy);
-int get_mpvec (vector curmp, vector pathvec, char * refwwid);
-void * mpath_prout_pthread_fn(void *p);
-void dumpHex(const char* , int len, int no_ascii);
-
-int mpath_prout_reg(struct multipath *mpp,int rq_servact, int rq_scope,
- unsigned int rq_type, struct prout_param_descriptor * paramp, int noisy);
-int mpath_prout_common(struct multipath *mpp,int rq_servact, int rq_scope,
- unsigned int rq_type, struct prout_param_descriptor * paramp, int noisy);
-int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope,
- unsigned int rq_type, struct prout_param_descriptor * paramp, int noisy);
-int send_prout_activepath(char * dev, int rq_servact, int rq_scope,
- unsigned int rq_type, struct prout_param_descriptor * paramp, int noisy);
+/*
+ * This header file contains symbols that are only used by
+ * libmpathpersist internally.
+ */
int update_prflag(char *mapname, int set);
int update_prkey_flags(char *mapname, uint64_t prkey, uint8_t sa_flags);
#define update_prkey(mapname, prkey) update_prkey_flags(mapname, prkey, 0)
-void * mpath_alloc_prin_response(int prin_sa);
-int update_map_pr(struct multipath *mpp);
#endif
$(RM) $(DESTDIR)$(includedir)/mpath_valid.h
clean: dep_clean
- $(RM) core *.a *.o *.so *.so.* *.gz *.abi $(NV_VERSION_SCRIPT)
+ $(RM) core *.a *.o *.so *.so.* *.abi $(NV_VERSION_SCRIPT)
include $(wildcard $(OBJS:.o=.d))
/*
* DESCRIPTION:
- * Reread the multipath configuration files and reinitalize
+ * Reread the multipath configuration files and reinitialize
* the device mapper multipath configuration. This function can
* be called as many times as necessary.
*
/*
* DESCRIPTION:
* Release the device mapper multipath configuration. This
- * function must be called to cleanup resoures allocated by
- * mpathvalid_init(). After calling this function, no futher
+ * function must be called to cleanup resources allocated by
+ * mpathvalid_init(). After calling this function, no further
* libmpathvalid functions may be called.
*
* RETURNS: 0 = Success, -1 = Failure
endif
endif
-ifneq ($(call check_func,dm_task_no_flush,/usr/include/libdevmapper.h),0)
+ifneq ($(call check_func,dm_task_no_flush,$(DEVMAPPER_INCDIR)/libdevmapper.h),0)
CFLAGS += -DLIBDM_API_FLUSH -D_GNU_SOURCE
endif
-ifneq ($(call check_func,dm_task_get_errno,/usr/include/libdevmapper.h),0)
+ifneq ($(call check_func,dm_task_get_errno,$(DEVMAPPER_INCDIR)/libdevmapper.h),0)
CFLAGS += -DLIBDM_API_GET_ERRNO
endif
-ifneq ($(call check_func,dm_task_set_cookie,/usr/include/libdevmapper.h),0)
+ifneq ($(call check_func,dm_task_set_cookie,$(DEVMAPPER_INCDIR)/libdevmapper.h),0)
CFLAGS += -DLIBDM_API_COOKIE
endif
-ifneq ($(call check_func,udev_monitor_set_receive_buffer_size,/usr/include/libudev.h),0)
+ifneq ($(call check_func,udev_monitor_set_receive_buffer_size,$(LIBUDEV_INCDIR)/libudev.h),0)
CFLAGS += -DLIBUDEV_API_RECVBUF
endif
-ifneq ($(call check_func,dm_task_deferred_remove,/usr/include/libdevmapper.h),0)
+ifneq ($(call check_func,dm_task_deferred_remove,$(DEVMAPPER_INCDIR)/libdevmapper.h),0)
CFLAGS += -DLIBDM_API_DEFERRED
endif
-ifneq ($(call check_func,dm_hold_control_dev,/usr/include/libdevmapper.h),0)
+ifneq ($(call check_func,dm_hold_control_dev,$(DEVMAPPER_INCDIR)/libdevmapper.h),0)
CFLAGS += -DLIBDM_API_HOLD_CONTROL
endif
+ifneq ($(call check_var,ELS_DTAG_LNK_INTEGRITY,$(LINUX_HEADERS_INCDIR)/scsi/fc/fc_els.h),0)
+ CFLAGS += -DFPIN_EVENT_HANDLER
+endif
+
+
OBJS = parser.o vector.o devmapper.o callout.o \
hwtable.o blacklist.o util.o dmparser.o config.o \
structs.o discovery.o propsel.o dict.o \
$(CC) $(CFLAGS) -Wno-unused-function -c -o $@ $<
# there are lots of "unused parameters" in dict.c
-# because not all handler / snprint methods nees all parameters
+# because not all handler / snprint methods need all parameters
dict.o: dict.c
$(CC) $(CFLAGS) -Wno-unused-parameter -c -o $@ $<
$(RM) $(DESTDIR)$(syslibdir)/$(DEVLIB)
clean: dep_clean
- $(RM) core *.a *.o *.so *.so.* *.gz *.abi nvme-ioctl.c nvme-ioctl.h $(NV_VERSION_SCRIPT)
+ $(RM) core *.a *.o *.so *.so.* *.abi nvme-ioctl.c nvme-ioctl.h $(NV_VERSION_SCRIPT)
include $(wildcard $(OBJS:.o=.d))
*/
#include <stdlib.h>
#include <errno.h>
-#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <limits.h>
#include "checkers.h"
#include "structs.h"
#include "config.h"
-#include "util.h"
-#include "errno.h"
#include "devmapper.h"
#include "strbuf.h"
#include <sys/stat.h>
#include <urcu.h>
#include <urcu/uatomic.h>
+#include <assert.h>
#include "debug.h"
#include "checkers.h"
*
* Rationale:
* Path checkers that do I/O may hang forever. To avoid blocking, some
- * checkers therefore use asyncronous, detached threads for checking
+ * checkers therefore use asynchronous, detached threads for checking
* the paths. These threads may continue hanging if multipathd is stopped.
* In this case, we can't unload the checker DSO at exit. In order to
* avoid race conditions and crashes, the entry point of the thread
#include <sys/ioctl.h>
#include <linux/fs.h>
#include <errno.h>
-#include <unistd.h>
#include <libaio.h>
#include "checkers.h"
/* Note: This checker type relies on the fact that only one checker can be run
* at a time, since multiple checkers share the same aio_group, and must be
- * able to modify other checker's async_reqs. If multple checkers become able
+ * able to modify other checker's async_reqs. If multiple checkers become able
* to be run at the same time, this checker will need to add locking, and
* probably polling on event fds, to deal with that */
#include "../libmultipath/sg_include.h"
#include "../libmultipath/util.h"
#include "../libmultipath/time-util.h"
-#include "../libmultipath/util.h"
#define TUR_CMD_LEN 6
#define HEAVY_CHECK_COUNT 10
dev_t devnum;
struct udev_device *udd;
- if (!mpp || !mpp->dmi) {
+ if (!mpp || !has_dm_info(mpp)) {
condlog(1, "%s called with empty mpp", __func__);
return NULL;
}
- devnum = makedev(mpp->dmi->major, mpp->dmi->minor);
+ devnum = makedev(mpp->dmi.major, mpp->dmi.minor);
udd = udev_device_new_from_devnum(udev, 'b', devnum);
if (!udd) {
condlog(1, "failed to get udev device for %s", mpp->alias);
}
void
-trigger_paths_udev_change(struct multipath *mpp, bool is_mpath)
+trigger_path_udev_change(struct path *pp, bool is_mpath)
{
- struct pathgroup *pgp;
- struct path *pp;
- int i, j;
/*
* If a path changes from multipath to non-multipath, we must
* synthesize an artificial "add" event, otherwise the LVM2 rules
* irritate ourselves with an "add", so use "change".
*/
const char *action = is_mpath ? "change" : "add";
+ const char *env;
+
+ if (!pp->udev)
+ return;
+ /*
+ * Paths that are already classified as multipath
+ * members don't need another uevent.
+ */
+ env = udev_device_get_property_value(
+ pp->udev, "DM_MULTIPATH_DEVICE_PATH");
+
+ if (is_mpath && env != NULL && !strcmp(env, "1")) {
+ /*
+ * If FIND_MULTIPATHS_WAIT_UNTIL is not "0",
+ * path is in "maybe" state and timer is running
+ * Send uevent now (see multipath.rules).
+ */
+ env = udev_device_get_property_value(
+ pp->udev, "FIND_MULTIPATHS_WAIT_UNTIL");
+ if (env == NULL || !strcmp(env, "0"))
+ return;
+ } else if (!is_mpath &&
+ (env == NULL || !strcmp(env, "0")))
+ return;
+
+ condlog(3, "triggering %s uevent for %s (is %smultipath member)",
+ action, pp->dev, is_mpath ? "" : "no ");
+ sysfs_attr_set_value(pp->udev, "uevent",
+ action, strlen(action));
+ trigger_partitions_udev_change(pp->udev, action,
+ strlen(action));
+}
+
+void
+trigger_paths_udev_change(struct multipath *mpp, bool is_mpath)
+{
+ struct pathgroup *pgp;
+ struct path *pp;
+ int i, j;
if (!mpp || !mpp->pg)
return;
vector_foreach_slot (mpp->pg, pgp, i) {
if (!pgp->paths)
continue;
- vector_foreach_slot(pgp->paths, pp, j) {
- const char *env;
-
- if (!pp->udev)
- continue;
- /*
- * Paths that are already classified as multipath
- * members don't need another uevent.
- */
- env = udev_device_get_property_value(
- pp->udev, "DM_MULTIPATH_DEVICE_PATH");
-
- if (is_mpath && env != NULL && !strcmp(env, "1")) {
- /*
- * If FIND_MULTIPATHS_WAIT_UNTIL is not "0",
- * path is in "maybe" state and timer is running
- * Send uevent now (see multipath.rules).
- */
- env = udev_device_get_property_value(
- pp->udev, "FIND_MULTIPATHS_WAIT_UNTIL");
- if (env == NULL || !strcmp(env, "0"))
- continue;
- } else if (!is_mpath &&
- (env == NULL || !strcmp(env, "0")))
- continue;
-
- condlog(3, "triggering %s uevent for %s (is %smultipath member)",
- action, pp->dev, is_mpath ? "" : "no ");
- sysfs_attr_set_value(pp->udev, "uevent",
- action, strlen(action));
- trigger_partitions_udev_change(pp->udev, action,
- strlen(action));
- }
+ vector_foreach_slot(pgp->paths, pp, j)
+ trigger_path_udev_change(pp, is_mpath);
}
mpp->needs_paths_uevent = 0;
return 0;
max_sectors_kb = mpp->max_sectors_kb;
if (is_reload) {
- if (!mpp->dmi && dm_get_info(mpp->alias, &mpp->dmi) != 0) {
+ if (!has_dm_info(mpp) &&
+ dm_get_info(mpp->alias, &mpp->dmi) != 0) {
condlog(1, "failed to get dm info for %s", mpp->alias);
return 1;
}
int get_refwwid (enum mpath_cmds cmd, const char *dev, enum devtypes dev_type,
vector pathvec, char **wwid);
struct udev_device *get_udev_device(const char *dev, enum devtypes dev_type);
+void trigger_path_udev_change(struct path *pp, bool is_mpath);
void trigger_paths_udev_change(struct multipath *mpp, bool is_mpath);
void trigger_partitions_udev_change(struct udev_device *dev, const char *action,
int len);
dm_task_set_ro(dmt);
if (task == DM_DEVICE_CREATE) {
- prefixed_uuid = calloc(1, UUID_PREFIX_LEN +
- strlen(mpp->wwid) + 1);
- if (!prefixed_uuid) {
+ if (asprintf(&prefixed_uuid, UUID_PREFIX "%s", mpp->wwid) < 0) {
condlog(0, "cannot create prefixed uuid : %s",
strerror(errno));
goto addout;
}
- sprintf(prefixed_uuid, UUID_PREFIX "%s", mpp->wwid);
if (!dm_task_set_uuid(dmt, prefixed_uuid))
goto freeout;
dm_task_skip_lockfs(dmt);
return 0;
}
-static int
-do_get_info(const char *name, struct dm_info *info)
+bool
+has_dm_info(const struct multipath *mpp)
+{
+ return (mpp && mpp->dmi.exists != 0);
+}
+
+int
+dm_get_info(const char *name, struct dm_info *info)
{
int r = -1;
struct dm_task *dmt;
+ if (!name || !info)
+ return r;
+
if (!(dmt = libmp_dm_task_create(DM_DEVICE_INFO)))
return r;
{
struct dm_info info;
- return (do_get_info(str, &info) == 0);
+ return (dm_get_info(str, &info) == 0);
}
int dm_get_map(const char *name, unsigned long long *size, char **outparams)
{
struct dm_info info;
- if (do_get_info(mapname, &info) != 0)
+ if (dm_get_info(mapname, &info) != 0)
return 1;
if (snprintf(dev_t, len, "%i:%i", info.major, info.minor) > len)
{
struct dm_info info;
- if (do_get_info(name, &info) != 0)
+ if (dm_get_info(name, &info) != 0)
return -1;
*major = info.major;
{
struct dm_info info;
- if (do_get_info(name, &info) != 0)
+ if (dm_get_info(name, &info) != 0)
return -1;
return info.event_nr;
{
struct dm_info info;
- if (do_get_info(name, &info) != 0)
+ if (dm_get_info(name, &info) != 0)
return -1;
return info.suspended;
{
struct dm_info info;
- if (do_get_info(mapname, &info) != 0)
+ if (dm_get_info(mapname, &info) != 0)
return -1;
return info.deferred_remove;
#endif
-static struct dm_info *
-alloc_dminfo (void)
-{
- return calloc(1, sizeof(struct dm_info));
-}
-
-int
-dm_get_info (const char * mapname, struct dm_info ** dmi)
-{
- if (!mapname)
- return 1;
-
- if (!*dmi)
- *dmi = alloc_dminfo();
-
- if (!*dmi)
- return 1;
-
- if (do_get_info(mapname, *dmi) != 0) {
- free(*dmi);
- *dmi = NULL;
- return 1;
- }
- return 0;
-}
-
struct rename_data {
const char *old;
char *new;
int dm_remove_partmaps (const char * mapname, int need_sync,
int deferred_remove);
int dm_get_uuid(const char *name, char *uuid, int uuid_len);
-int dm_get_info (const char * mapname, struct dm_info ** dmi);
+bool has_dm_info(const struct multipath *mpp);
+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);
find_multipaths_optvals[conf->find_multipaths]);
}
+static const char * const marginal_pathgroups_optvals[] = {
+ [MARGINAL_PATHGROUP_OFF] = "off",
+ [MARGINAL_PATHGROUP_ON] = "on",
+#ifdef FPIN_EVENT_HANDLER
+ [MARGINAL_PATHGROUP_FPIN] = "fpin",
+#endif
+};
+
+static int
+def_marginal_pathgroups_handler(struct config *conf, vector strvec,
+ const char *file, int line_nr)
+{
+ char *buff;
+ unsigned int i;
+
+ buff = set_value(strvec);
+ if (!buff)
+ return 1;
+ for (i = MARGINAL_PATHGROUP_OFF;
+ i < ARRAY_SIZE(marginal_pathgroups_optvals); i++) {
+ if (marginal_pathgroups_optvals[i] != NULL &&
+ !strcmp(buff, marginal_pathgroups_optvals[i])) {
+ conf->marginal_pathgroups = i;
+ break;
+ }
+ }
+
+ if (i >= ARRAY_SIZE(marginal_pathgroups_optvals)) {
+ if (strcmp(buff, "no") == 0 || strcmp(buff, "0") == 0)
+ conf->marginal_pathgroups = MARGINAL_PATHGROUP_OFF;
+ else if (strcmp(buff, "yes") == 0 || strcmp(buff, "1") == 0)
+ conf->marginal_pathgroups = MARGINAL_PATHGROUP_ON;
+ /* This can only be true if FPIN_EVENT_HANDLER isn't defined,
+ * otherwise this check will have already happened above */
+ else if (strcmp(buff, "fpin") == 0)
+ condlog(1, "%s line %d, support for \"fpin\" is not compiled in for marginal_pathgroups", file, line_nr);
+ else
+ condlog(1, "%s line %d, invalid value for marginal_pathgroups: \"%s\"",
+ file, line_nr, buff);
+ }
+ free(buff);
+ return 0;
+}
+
+static int
+snprint_def_marginal_pathgroups(struct config *conf, struct strbuf *buff,
+ const void *data)
+{
+ return append_strbuf_quoted(buff,
+ marginal_pathgroups_optvals[conf->marginal_pathgroups]);
+}
+
+
declare_def_handler(selector, set_str)
declare_def_snprint_defstr(selector, print_str, DEFAULT_SELECTOR)
declare_hw_handler(selector, set_str)
declare_hw_handler(all_tg_pt, set_yes_no_undef)
declare_hw_snprint(all_tg_pt, print_yes_no_undef)
-declare_def_handler(marginal_pathgroups, set_yes_no)
-declare_def_snprint(marginal_pathgroups, print_yes_no)
-
declare_def_handler(recheck_wwid, set_yes_no_undef)
declare_def_snprint_defint(recheck_wwid, print_yes_no_undef, DEFAULT_RECHECK_WWID)
declare_ovr_handler(recheck_wwid, set_yes_no_undef)
return 0;
}
-int sysfs_get_host_adapter_name(const struct path *pp, char *adapter_name)
-{
- int proto_id;
-
- if (!pp || !adapter_name)
- return 1;
-
- proto_id = pp->sg_id.proto_id;
-
- if (proto_id != SCSI_PROTOCOL_FCP &&
- proto_id != SCSI_PROTOCOL_SAS &&
- proto_id != SCSI_PROTOCOL_ISCSI &&
- proto_id != SCSI_PROTOCOL_SRP) {
- return 1;
- }
- /* iscsi doesn't have adapter info in sysfs
- * get ip_address for grouping paths
- */
- if (pp->sg_id.proto_id == SCSI_PROTOCOL_ISCSI)
- return sysfs_get_iscsi_ip_address(pp, adapter_name);
-
- /* fetch adapter pci name for other protocols
- */
- return sysfs_get_host_pci_name(pp, adapter_name);
-}
-
-int sysfs_get_host_pci_name(const struct path *pp, char *pci_name)
+static int sysfs_get_host_bus_id(const struct path *pp, char *bus_id)
{
struct udev_device *hostdev, *parent;
char host_name[HOST_NAME_LEN];
- const char *driver_name, *value;
+ const char *driver_name, *subsystem_name, *value;
- if (!pp || !pci_name)
+ if (!pp || !bus_id)
return 1;
- sprintf(host_name, "host%d", pp->sg_id.host_no);
+ snprintf(host_name, sizeof(host_name), "host%d", pp->sg_id.host_no);
hostdev = udev_device_new_from_subsystem_sysname(udev,
"scsi_host", host_name);
if (!hostdev)
return 1;
- parent = udev_device_get_parent(hostdev);
- while (parent) {
+ for (parent = udev_device_get_parent(hostdev);
+ parent;
+ parent = udev_device_get_parent(parent)) {
driver_name = udev_device_get_driver(parent);
- if (!driver_name) {
- parent = udev_device_get_parent(parent);
- continue;
- }
- if (!strcmp(driver_name, "pcieport"))
+ subsystem_name = udev_device_get_subsystem(parent);
+ if (driver_name && !strcmp(driver_name, "pcieport"))
+ break;
+ if (subsystem_name && !strcmp(subsystem_name, "ccw"))
break;
- parent = udev_device_get_parent(parent);
}
if (parent) {
- /* pci_device found
+ /* pci_device or ccw fcp device found
*/
value = udev_device_get_sysname(parent);
return 1;
}
- strncpy(pci_name, value, SLOT_NAME_SIZE);
+ strlcpy(bus_id, value, SLOT_NAME_SIZE);
udev_device_unref(hostdev);
return 0;
}
return 1;
}
+int sysfs_get_host_adapter_name(const struct path *pp, char *adapter_name)
+{
+ int proto_id;
+
+ if (!pp || !adapter_name)
+ return 1;
+
+ proto_id = pp->sg_id.proto_id;
+
+ if (proto_id != SCSI_PROTOCOL_FCP &&
+ proto_id != SCSI_PROTOCOL_SAS &&
+ proto_id != SCSI_PROTOCOL_ISCSI &&
+ proto_id != SCSI_PROTOCOL_SRP) {
+ return 1;
+ }
+ /* iscsi doesn't have adapter info in sysfs
+ * get ip_address for grouping paths
+ */
+ if (pp->sg_id.proto_id == SCSI_PROTOCOL_ISCSI)
+ return sysfs_get_iscsi_ip_address(pp, adapter_name);
+
+ /* fetch adapter bus-ID for other protocols
+ */
+ return sysfs_get_host_bus_id(pp, adapter_name);
+}
+
int sysfs_get_iscsi_ip_address(const struct path *pp, char *ip_address)
{
struct udev_device *hostdev;
int i;
unsigned int dev_loss_tmo = mpp->dev_loss;
struct path *err_path = NULL;
+ STATIC_BITFIELD(bf, LAST_BUS_PROTOCOL_ID + 1);
if (mpp->no_path_retry > 0) {
uint64_t no_path_retry_tmo =
sysfs_set_eh_deadline(mpp, pp);
}
- if (err_path) {
+ if (err_path && !is_bit_set_in_bitfield(bus_protocol_id(pp), bf)) {
STRBUF_ON_STACK(proto_buf);
snprint_path_protocol(&proto_buf, err_path);
condlog(2, "%s: setting dev_loss_tmo is unsupported for protocol %s",
mpp->alias, get_strbuf_str(&proto_buf));
+ set_bit_in_bitfield(bus_protocol_id(pp), bf);
}
return 0;
}
* Avoid any IO on the device itself.
* simply use the path_offline() return as its state
*/
- pp->chkrstate = pp->state = path_state;
+ if (path_state != PATH_PENDING ||
+ pp->state == PATH_UNCHECKED ||
+ pp->state == PATH_WILD)
+ pp->chkrstate = pp->state = path_state;
return PATHINFO_OK;
}
struct path **pp_ptr);
int sysfs_set_scsi_tmo (struct multipath *mpp, unsigned int checkint);
int sysfs_get_timeout(const struct path *pp, unsigned int *timeout);
-int sysfs_get_host_pci_name(const struct path *pp, char *pci_name);
int sysfs_get_iscsi_ip_address(const struct path *pp, char *ip_address);
int sysfs_get_host_adapter_name(const struct path *pp,
char *adapter_name);
}
/*
- * Caution callers: If this function encounters yet unkown path devices, it
+ * Caution callers: If this function encounters yet unknown path devices, it
* adds them uninitialized to the mpp.
* Call update_pathvec_from_dm() after this function to make sure
* all data structures are in a sane state.
free(word);
/*
- * PG Status (discarded, would be '0' anyway)
+ * Path Selector Group Arguments
*/
- p += get_word(p, NULL);
+ p += get_word(p, &word);
+
+ if (!word)
+ return 1;
+
+ num_pg_args = atoi(word);
+ free(word);
+
+ /* Ignore ps group arguments */
+ for (j = 0; j < num_pg_args; j++)
+ p += get_word(p, NULL);
p += get_word(p, &word);
/**
* method: init(api, name)
* Initialize foreign library, and check API compatibility
- * return pointer to opaque internal data strucure if successful,
+ * return pointer to opaque internal data structure if successful,
* NULL otherwise.
*
* @param[in] api: API version
static int nvme_style(__attribute__((unused)) const struct gen_multipath* gm,
struct strbuf *buf, __attribute__((unused)) int verbosity)
{
- return append_strbuf_str(buf, "%%w [%%G]:%%d %%s");
+ return append_strbuf_str(buf, "%w [%G]:%d %s");
}
static const struct gen_multipath_ops nvme_map_ops = {
{
/* Linux-IO Target */
.vendor = "(LIO-ORG|SUSE)",
- .product = "RBD",
+ .product = ".",
.hwhandler = "1 alua",
.pgpolicy = GROUP_BY_PRIO,
.pgfailback = -FAILBACK_IMMEDIATE,
* The new version inherits the previous ones.
*/
-LIBMULTIPATH_13.0.0 {
+LIBMULTIPATH_14.0.0 {
global:
/* symbols referenced by multipath and multipathd */
add_foreign;
check_foreign;
cleanup_charp;
cleanup_lock;
+ cleanup_mutex;
cleanup_ucharp;
close_fd;
coalesce_paths;
get_used_hwes;
get_vpd_sgio;
group_by_prio;
+ has_dm_info;
init_checkers;
init_config;
init_foreign;
select_reservation_key;
send_packet;
set_max_fds;
- __set_no_path_retry;
+ set_no_path_retry;
set_path_removed;
set_prkey;
setup_map;
sysfs_is_multipathed;
timespeccmp;
timespecsub;
+ trigger_path_udev_change;
trigger_paths_udev_change;
truncate_strbuf;
udev;
#include <sys/stat.h>
#include <dirent.h>
#include <unistd.h>
-#include <string.h>
#include <errno.h>
#include <assert.h>
#include <libudev.h>
" \"host_wwpn\" : \"%R\",\n" \
" \"target_wwpn\" : \"%r\",\n" \
" \"host_adapter\" : \"%a\",\n" \
+ " \"lun_hex\" : \"%L\",\n" \
" \"marginal_st\" : \"%M\""
#define PROGRESS_LEN 10
static int
snprint_sysfs (struct strbuf *buff, const struct multipath * mpp)
{
- if (mpp->dmi)
- return print_strbuf(buff, "dm-%i", mpp->dmi->minor);
+ if (has_dm_info(mpp))
+ return print_strbuf(buff, "dm-%i", mpp->dmi.minor);
else
return append_strbuf_str(buff, "undef");
}
static int
snprint_ro (struct strbuf *buff, const struct multipath * mpp)
{
- if (!mpp->dmi)
+ if (!has_dm_info(mpp))
return append_strbuf_str(buff, "undef");
- if (mpp->dmi->read_only)
+ if (mpp->dmi.read_only)
return append_strbuf_str(buff, "ro");
else
return append_strbuf_str(buff, "rw");
static int
snprint_dm_map_state (struct strbuf *buff, const struct multipath * mpp)
{
- if (mpp->dmi && mpp->dmi->suspended)
+ if (!has_dm_info(mpp))
+ return append_strbuf_str(buff, "undef");
+ else if (mpp->dmi.suspended)
return append_strbuf_str(buff, "suspend");
else
return append_strbuf_str(buff, "active");
pp->sg_id.lun);
}
+
+static int
+snprint_path_lunhex (struct strbuf *buff, const struct path * pp)
+{
+ uint64_t lunhex = SCSI_INVALID_LUN, scsilun;
+
+ if (!pp || pp->sg_id.host_no < 0)
+ return print_strbuf(buff, "0x%016" PRIx64, lunhex);
+
+ scsilun = pp->sg_id.lun;
+ /* cf. Linux kernel function int_to_scsilun() */
+ lunhex = ((scsilun & 0x000000000000ffffULL) << 48) |
+ ((scsilun & 0x00000000ffff0000ULL) << 16) |
+ ((scsilun & 0x0000ffff00000000ULL) >> 16) |
+ ((scsilun & 0xffff000000000000ULL) >> 48);
+ return print_strbuf(buff, "0x%016" PRIx64, lunhex);
+}
+
static int
snprint_dev (struct strbuf *buff, const struct path * pp)
{
int
snprint_path_protocol(struct strbuf *buff, const struct path * pp)
{
- switch (pp->bus) {
- case SYSFS_BUS_SCSI:
- switch (pp->sg_id.proto_id) {
- case SCSI_PROTOCOL_FCP:
- return append_strbuf_str(buff, "scsi:fcp");
- case SCSI_PROTOCOL_SPI:
- return append_strbuf_str(buff, "scsi:spi");
- case SCSI_PROTOCOL_SSA:
- return append_strbuf_str(buff, "scsi:ssa");
- case SCSI_PROTOCOL_SBP:
- return append_strbuf_str(buff, "scsi:sbp");
- case SCSI_PROTOCOL_SRP:
- return append_strbuf_str(buff, "scsi:srp");
- case SCSI_PROTOCOL_ISCSI:
- return append_strbuf_str(buff, "scsi:iscsi");
- case SCSI_PROTOCOL_SAS:
- return append_strbuf_str(buff, "scsi:sas");
- case SCSI_PROTOCOL_ADT:
- return append_strbuf_str(buff, "scsi:adt");
- case SCSI_PROTOCOL_ATA:
- return append_strbuf_str(buff, "scsi:ata");
- case SCSI_PROTOCOL_USB:
- return append_strbuf_str(buff, "scsi:usb");
- case SCSI_PROTOCOL_UNSPEC:
- default:
- return append_strbuf_str(buff, "scsi:unspec");
- }
- case SYSFS_BUS_CCW:
- return append_strbuf_str(buff, "ccw");
- case SYSFS_BUS_CCISS:
- return append_strbuf_str(buff, "cciss");
- case SYSFS_BUS_NVME:
- return append_strbuf_str(buff, "nvme");
- case SYSFS_BUS_UNDEF:
- default:
- return append_strbuf_str(buff, "undef");
- }
+ static const char * const protocol_name[LAST_BUS_PROTOCOL_ID + 1] = {
+ [SYSFS_BUS_UNDEF] = "undef",
+ [SYSFS_BUS_CCW] = "ccw",
+ [SYSFS_BUS_CCISS] = "cciss",
+ [SYSFS_BUS_NVME] = "nvme",
+ [SYSFS_BUS_SCSI + SCSI_PROTOCOL_FCP] = "scsi:fcp",
+ [SYSFS_BUS_SCSI + SCSI_PROTOCOL_SPI] = "scsi:spi",
+ [SYSFS_BUS_SCSI + SCSI_PROTOCOL_SSA] = "scsi:ssa",
+ [SYSFS_BUS_SCSI + SCSI_PROTOCOL_SBP] = "scsi:sbp",
+ [SYSFS_BUS_SCSI + SCSI_PROTOCOL_SRP] = "scsi:srp",
+ [SYSFS_BUS_SCSI + SCSI_PROTOCOL_ISCSI] = "scsi:iscsi",
+ [SYSFS_BUS_SCSI + SCSI_PROTOCOL_SAS] = "scsi:sas",
+ [SYSFS_BUS_SCSI + SCSI_PROTOCOL_ADT] = "scsi:adt",
+ [SYSFS_BUS_SCSI + SCSI_PROTOCOL_ATA] = "scsi:ata",
+ [SYSFS_BUS_SCSI + SCSI_PROTOCOL_USB] = "scsi:usb",
+ [SYSFS_BUS_SCSI + SCSI_PROTOCOL_UNSPEC] = "scsi:unspec",
+ };
+ const char *pn = protocol_name[bus_protocol_id(pp)];
+
+ assert(pn != NULL);
+ return append_strbuf_str(buff, pn);
}
static int
{'0', "failures", snprint_path_failures},
{'P', "protocol", snprint_path_protocol},
{'I', "init_st", snprint_initialized},
+ {'L', "LUN hex", snprint_path_lunhex},
};
static const struct pathgroup_data pgd[] = {
libpriopath_latency.so \
libpriosysfs.so
-ifneq ($(call check_file,/usr/include/linux/nvme_ioctl.h),0)
+ifneq ($(call check_file,$(LINUX_HEADERS_INCDIR)/linux/nvme_ioctl.h),0)
LIBS += libprioana.so
CFLAGS += -I../nvme
endif
struct rtpg_tpg_dscr {
unsigned char b0; /* x....... = pref(ered) port */
/* .xxx.... = reserved */
- /* ....xxxx = asymetric access state */
+ /* ....xxxx = asymmetric access state*/
unsigned char b1; /* xxx..... = reserved */
/* ...x.... = LBA dependent support */
/* ....x... = unavailable support */
if (lg_avglatency > lg_maxavglatency) {
pp_pl_log(2,
- "%s: average latency (%lld us) is outside the thresold (%lld us)",
+ "%s: average latency (%lld us) is outside the threshold (%lld us)",
pp->dev, (long long)pow(base_num, lg_avglatency),
(long long)MAX_AVG_LATENCY);
return DEFAULT_PRIORITY;
"(setting: multipath command line [-p] flag)";
static const char autodetect_origin[] =
"(setting: storage device autodetected)";
+static const char fpin_marginal_path_origin[] =
+ "(setting: overridden by marginal_path_fpin)";
static const char marginal_path_origin[] =
"(setting: implied by marginal_path check)";
static const char delay_watch_origin[] =
const char *origin;
STRBUF_ON_STACK(buff);
- if (marginal_path_check_enabled(mp)) {
+ if (marginal_path_check_enabled(mp) || (conf->marginal_pathgroups == MARGINAL_PATHGROUP_FPIN)) {
mp->san_path_err_threshold = NU_NO;
- origin = marginal_path_origin;
+ if (conf->marginal_pathgroups == MARGINAL_PATHGROUP_FPIN)
+ origin = fpin_marginal_path_origin;
+ else
+ origin = marginal_path_origin;
goto out;
}
mp_set_mpe(san_path_err_threshold);
const char *origin;
STRBUF_ON_STACK(buff);
- if (marginal_path_check_enabled(mp)) {
+ if (marginal_path_check_enabled(mp) || (conf->marginal_pathgroups == MARGINAL_PATHGROUP_FPIN)) {
mp->san_path_err_forget_rate = NU_NO;
- origin = marginal_path_origin;
+ if (conf->marginal_pathgroups == MARGINAL_PATHGROUP_FPIN)
+ origin = fpin_marginal_path_origin;
+ else
+ origin = marginal_path_origin;
goto out;
}
mp_set_mpe(san_path_err_forget_rate);
const char *origin;
STRBUF_ON_STACK(buff);
- if (marginal_path_check_enabled(mp)) {
+ if (marginal_path_check_enabled(mp) || (conf->marginal_pathgroups == MARGINAL_PATHGROUP_FPIN)) {
mp->san_path_err_recovery_time = NU_NO;
- origin = marginal_path_origin;
+ if (conf->marginal_pathgroups == MARGINAL_PATHGROUP_FPIN)
+ origin = fpin_marginal_path_origin;
+ else
+ origin = marginal_path_origin;
goto out;
}
mp_set_mpe(san_path_err_recovery_time);
const char *origin;
STRBUF_ON_STACK(buff);
+ if (conf->marginal_pathgroups == MARGINAL_PATHGROUP_FPIN) {
+ mp->marginal_path_err_sample_time = NU_NO;
+ origin = fpin_marginal_path_origin;
+ goto out;
+ }
+
mp_set_mpe(marginal_path_err_sample_time);
mp_set_ovr(marginal_path_err_sample_time);
mp_set_hwe(marginal_path_err_sample_time);
const char *origin;
STRBUF_ON_STACK(buff);
+ if (conf->marginal_pathgroups == MARGINAL_PATHGROUP_FPIN) {
+ mp->marginal_path_err_rate_threshold = NU_NO;
+ origin = fpin_marginal_path_origin;
+ goto out;
+ }
+
mp_set_mpe(marginal_path_err_rate_threshold);
mp_set_ovr(marginal_path_err_rate_threshold);
mp_set_hwe(marginal_path_err_rate_threshold);
const char *origin;
STRBUF_ON_STACK(buff);
+ if (conf->marginal_pathgroups == MARGINAL_PATHGROUP_FPIN) {
+ mp->marginal_path_err_recheck_gap_time = NU_NO;
+ origin = fpin_marginal_path_origin;
+ goto out;
+ }
+
mp_set_mpe(marginal_path_err_recheck_gap_time);
mp_set_ovr(marginal_path_err_recheck_gap_time);
mp_set_hwe(marginal_path_err_recheck_gap_time);
const char *origin;
STRBUF_ON_STACK(buff);
+ if (conf->marginal_pathgroups == MARGINAL_PATHGROUP_FPIN) {
+ mp->marginal_path_double_failed_time = NU_NO;
+ origin = fpin_marginal_path_origin;
+ goto out;
+ }
+
mp_set_mpe(marginal_path_double_failed_time);
mp_set_ovr(marginal_path_double_failed_time);
mp_set_hwe(marginal_path_double_failed_time);
#include "prio.h"
#include "prioritizers/alua_spc3.h"
#include "dm-generic.h"
+#include "devmapper.h"
struct adapter_group *
alloc_adaptergroup(void)
mpp->alias = NULL;
}
- if (mpp->dmi) {
- free(mpp->dmi);
- mpp->dmi = NULL;
- }
-
if (!free_paths && mpp->pg) {
struct pathgroup *pgp;
struct path *pp;
return NULL;
vector_foreach_slot (mpvec, mpp, i) {
- if (!mpp->dmi)
+ if (!has_dm_info(mpp))
continue;
- if (mpp->dmi->minor == minor)
+ if (mpp->dmi.minor == minor)
return mpp;
}
return NULL;
return 0;
}
+
+unsigned int bus_protocol_id(const struct path *pp) {
+ if (!pp || pp->bus < 0 || pp->bus > SYSFS_BUS_SCSI)
+ return SYSFS_BUS_UNDEF;
+ if (pp->bus != SYSFS_BUS_SCSI)
+ return pp->bus;
+ if ((int)pp->sg_id.proto_id < 0 || pp->sg_id.proto_id > SCSI_PROTOCOL_UNSPEC)
+ return SYSFS_BUS_UNDEF;
+ return SYSFS_BUS_SCSI + pp->sg_id.proto_id;
+}
#include <sys/types.h>
#include <inttypes.h>
#include <stdbool.h>
+#include <libdevmapper.h>
#include "prio.h"
#include "byteorder.h"
FAILBACK_FOLLOWOVER
};
+/* SYSFS_BUS_SCSI should be last, see bus_protocol_id() */
enum sysfs_buses {
SYSFS_BUS_UNDEF,
- SYSFS_BUS_SCSI,
SYSFS_BUS_CCW,
SYSFS_BUS_CCISS,
SYSFS_BUS_NVME,
+ SYSFS_BUS_SCSI,
};
enum pathstates {
__FIND_MULTIPATHS_LAST,
};
+enum marginal_pathgroups_mode {
+ MARGINAL_PATHGROUP_OFF = YN_NO,
+ MARGINAL_PATHGROUP_ON = YN_YES,
+ MARGINAL_PATHGROUP_FPIN,
+};
+
enum flush_states {
FLUSH_UNDEF = YNU_UNDEF,
FLUSH_DISABLED = YNU_NO,
SCSI_PROTOCOL_ADT = 7, /* Media Changers */
SCSI_PROTOCOL_ATA = 8,
SCSI_PROTOCOL_USB = 9, /* USB Attached SCSI (UAS), and others */
- SCSI_PROTOCOL_UNSPEC = 0xf, /* No specific protocol */
+ SCSI_PROTOCOL_UNSPEC = 0xa, /* No specific protocol */
};
+/*
+ * Linear ordering of bus/protocol
+ * This assumes that SYSFS_BUS_SCSI is last in enum sysfs_buses
+ * SCSI is the only bus type for which we distinguish protocols.
+ */
+#define LAST_BUS_PROTOCOL_ID (SYSFS_BUS_SCSI + SCSI_PROTOCOL_UNSPEC)
+unsigned int bus_protocol_id(const struct path *pp);
+
#define SCSI_INVALID_LUN ~0ULL
enum no_undef_states {
unsigned long long size;
vector paths;
vector pg;
- struct dm_info * dmi;
+ struct dm_info dmi;
/* configlet pointers */
char * alias;
unsigned char prflag;
int all_tg_pt;
struct gen_multipath generic_mp;
+ bool fpin_must_reload;
};
static inline int marginal_path_check_enabled(const struct multipath *mpp)
}
}
-void __set_no_path_retry(struct multipath *mpp, bool check_features)
+void set_no_path_retry(struct multipath *mpp)
{
bool is_queueing = false; /* assign a value to make gcc happy */
- check_features = check_features && mpp->features != NULL;
- if (check_features)
+ if (mpp->features)
is_queueing = strstr(mpp->features, "queue_if_no_path");
switch (mpp->no_path_retry) {
case NO_PATH_RETRY_UNDEF:
break;
case NO_PATH_RETRY_FAIL:
- if (!check_features || is_queueing)
+ if (!mpp->features || is_queueing)
dm_queue_if_no_path(mpp->alias, 0);
break;
case NO_PATH_RETRY_QUEUE:
- if (!check_features || !is_queueing)
+ if (!mpp->features || !is_queueing)
dm_queue_if_no_path(mpp->alias, 1);
break;
default:
* If in_recovery is set, leave_recovery_mode() takes
* care of dm_queue_if_no_path. Otherwise, do it here.
*/
- if ((!check_features || !is_queueing) &&
+ if ((!mpp->features || !is_queueing) &&
!mpp->in_recovery)
dm_queue_if_no_path(mpp->alias, 1);
leave_recovery_mode(mpp);
- } else
+ } else if (pathcount(mpp, PATH_PENDING) == 0)
enter_recovery_mode(mpp);
break;
}
struct mutex_lock lock; /* defined in lock.h */
};
-void __set_no_path_retry(struct multipath *mpp, bool check_features);
-#define set_no_path_retry(mpp) __set_no_path_retry(mpp, true)
+void set_no_path_retry(struct multipath *mpp);
int adopt_paths (vector pathvec, struct multipath * mpp);
void orphan_path (struct path * pp, const char *reason);
#include <sys/mman.h>
#include <sys/time.h>
#include <libudev.h>
-#include <errno.h>
#include "debug.h"
#include "list.h"
uevent_can_merge(struct uevent *earlier, struct uevent *later)
{
/* merge paths uevents
- * whose wwids exsit and are same
+ * whose wwids exist and are same
* and actions are same,
* and actions are addition or deletion
*/
bitfield_t bits[];
};
+#define STATIC_BITFIELD(name, length) \
+ static struct { \
+ unsigned int len; \
+ bitfield_t bits[((length) - 1) / bits_per_slot + 1]; \
+ } __static__ ## name = { \
+ .len = (length), \
+ .bits = { 0, }, \
+ }; \
+ struct bitfield *name = (struct bitfield *)& __static__ ## name
+
struct bitfield *alloc_bitfield(unsigned int maxbit);
void _log_bitfield_overflow(const char *f, unsigned int bit, unsigned int len);
#ifndef _VERSION_H
#define _VERSION_H
-#define VERSION_CODE 0x000808
-#define DATE_CODE 0x030c15
+#define VERSION_CODE 0x000809
+#define DATE_CODE 0x100216
#define PROG "multipath-tools"
$(EXEC): $(OBJS)
$(CC) $(OBJS) -o $(EXEC) $(LDFLAGS) $(CFLAGS) $(LIBDEPS)
- $(GZIP) $(EXEC).8 > $(EXEC).8.gz
install:
$(INSTALL_PROGRAM) -d $(DESTDIR)$(bindir)
$(INSTALL_PROGRAM) -m 755 $(EXEC) $(DESTDIR)$(bindir)/
$(INSTALL_PROGRAM) -d $(DESTDIR)$(man8dir)
- $(INSTALL_PROGRAM) -m 644 $(EXEC).8.gz $(DESTDIR)$(man8dir)
+ $(INSTALL_PROGRAM) -m 644 $(EXEC).8 $(DESTDIR)$(man8dir)
clean: dep_clean
- $(RM) core *.o $(EXEC) *.gz
+ $(RM) core *.o $(EXEC)
include $(wildcard $(OBJS:.o=.d))
uninstall:
$(RM) $(DESTDIR)$(bindir)/$(EXEC)
- $(RM) $(DESTDIR)$(man8dir)/$(EXEC).8.gz
+ $(RM) $(DESTDIR)$(man8dir)/$(EXEC).8
dep_clean:
$(RM) $(OBJS:.o=.d)
#include <getopt.h>
#include <libudev.h>
#include "mpath_persist.h"
+#include "mpath_persist_int.h"
#include "main.h"
#include "debug.h"
#include <pthread.h>
$(EXEC): $(OBJS) $(multipathdir)/libmultipath.so $(mpathcmddir)/libmpathcmd.so
$(CC) $(CFLAGS) $(OBJS) -o $(EXEC) $(LDFLAGS) $(LIBDEPS)
- $(GZIP) $(EXEC).8 > $(EXEC).8.gz
- $(GZIP) $(EXEC).conf.5 > $(EXEC).conf.5.gz
install:
$(INSTALL_PROGRAM) -d $(DESTDIR)$(bindir)
$(INSTALL_PROGRAM) -m 755 $(EXEC) $(DESTDIR)$(bindir)/
$(INSTALL_PROGRAM) -d $(DESTDIR)$(udevrulesdir)
$(INSTALL_PROGRAM) -m 644 11-dm-mpath.rules $(DESTDIR)$(udevrulesdir)
- $(INSTALL_PROGRAM) -m 644 $(EXEC).rules $(DESTDIR)$(libudevdir)/rules.d/56-multipath.rules
+ $(INSTALL_PROGRAM) -m 644 $(EXEC).rules $(DESTDIR)$(udevrulesdir)/56-multipath.rules
+ $(INSTALL_PROGRAM) -d $(DESTDIR)$(modulesloaddir)
+ $(INSTALL_PROGRAM) -m 644 modules-load.conf $(DESTDIR)$(modulesloaddir)/multipath.conf
$(INSTALL_PROGRAM) -d $(DESTDIR)$(man8dir)
- $(INSTALL_PROGRAM) -m 644 $(EXEC).8.gz $(DESTDIR)$(man8dir)
+ $(INSTALL_PROGRAM) -m 644 $(EXEC).8 $(DESTDIR)$(man8dir)
$(INSTALL_PROGRAM) -d $(DESTDIR)$(man5dir)
- $(INSTALL_PROGRAM) -m 644 $(EXEC).conf.5.gz $(DESTDIR)$(man5dir)
+ $(INSTALL_PROGRAM) -m 644 $(EXEC).conf.5 $(DESTDIR)$(man5dir)
+ifneq ($(SCSI_DH_MODULES_PRELOAD),)
+ $(INSTALL_PROGRAM) -m 644 scsi_dh.conf $(DESTDIR)$(modulesloaddir)/scsi_dh.conf
+ for _x in $(SCSI_DH_MODULES_PRELOAD); do echo "$$_x"; done \
+ >>$(DESTDIR)$(modulesloaddir)/scsi_dh.conf
+endif
uninstall:
$(RM) $(DESTDIR)$(bindir)/$(EXEC)
$(RM) $(DESTDIR)$(udevrulesdir)/11-dm-mpath.rules
+ $(RM) $(DESTDIR)$(modulesloaddir)/multipath.conf
+ $(RM) $(DESTDIR)$(modulesloaddir)/scsi_dh.conf
$(RM) $(DESTDIR)$(libudevdir)/rules.d/56-multipath.rules
- $(RM) $(DESTDIR)$(man8dir)/$(EXEC).8.gz
- $(RM) $(DESTDIR)$(man5dir)/$(EXEC).conf.5.gz
+ $(RM) $(DESTDIR)$(man8dir)/$(EXEC).8
+ $(RM) $(DESTDIR)$(man5dir)/$(EXEC).conf.5
clean: dep_clean
- $(RM) core *.o $(EXEC) *.gz
+ $(RM) core *.o $(EXEC)
include $(wildcard $(OBJS:.o=.d))
#include "time-util.h"
#include "file.h"
#include "valid.h"
-#include "alias.h"
/*
* Return values of configure(), check_path_valid(), and main().
--- /dev/null
+# load dm-multipath early, both multipathd and multipath depend on it
+# (note that multipath may be called from udev rules!)
+dm-multipath
.B uid_attrs
.
Setting this option activates \fBmerging uevents\fR by WWID, which may improve
-uevent processing effiency. Moreover, it's an alternative method to configure
+uevent processing efficiency. Moreover, it's an alternative method to configure
the udev properties to use for determining unique path identifiers (WWIDs).
.RS
.PP
this to the system limit from \fI/proc/sys/fs/nr_open\fR. If this is not set, the
maximum number of open fds is taken from the calling process. It is usually
1024. To be safe, this should be set to the maximum number of paths plus 32,
-if that number is greated than 1024.
+if that number is greater than 1024.
.RS
.TP
The default is: \fBmax\fR
.I yes
and the SCSI layer has already attached a hardware_handler to the device,
multipath will not force the device to use the hardware_handler specified by
-mutipath.conf. If the SCSI layer has not attached a hardware handler,
+multipath.conf. If the SCSI layer has not attached a hardware handler,
multipath will continue to use its configured hardware handler.
.RS
.PP
.
.TP
.B marginal_pathgroups
-If set to \fIno\fR, the \fIdelay_*_checks\fR, \fImarginal_path_*\fR, and
+If set to \fIoff\fR, the \fIdelay_*_checks\fR, \fImarginal_path_*\fR, and
\fIsan_path_err_*\fR options will keep marginal, or \(dqshaky\(dq, paths from
being reinstated until they have been monitored for some time. This can cause
situations where all non-marginal paths are down, and no paths are usable
until multipathd detects this and reinstates a marginal path. If the multipath
device is not configured to queue IO in this case, it can cause IO errors to
occur, even though there are marginal paths available. However, if this
-option is set to \fIyes\fR, when one of the marginal path detecting methods
+option is set to \fIon\fR, when one of the marginal path detecting methods
determines that a path is marginal, it will be reinstated and placed in a
-seperate pathgroup that will only be used after all the non-marginal pathgroups
-have been tried first. This prevents the possibility of IO errors occuring
+separate pathgroup that will only be used after all the non-marginal pathgroups
+have been tried first. This prevents the possibility of IO errors occurring
while marginal paths are still usable. After the path has been monitored
for the configured time, and is declared healthy, it will be returned to its
-normal pathgroup. See "Shaky paths detection" below for more information.
+normal pathgroup.
+If this option is set to \fIfpin\fR, multipathd will receive fpin
+notifications, set path states to "marginal" accordingly, and regroup paths
+as described for \fIon\fR. This option can't be used in combination
+with other options for "Shaky path detection" (see below). \fBNote:\fR If this
+is set to \fIfpin\fR, the \fImarginal_path_*\fR and \fIsan_path_err_*\fR
+options are implicitly set to \fIno\fP. Also, this option cannot be switched
+either to or from \fIfpin\fR on a multipathd reconfigure. multipathd must be
+restarted for the change to take effect.
+See "Shaky paths detection" below for more information.
.RS
.TP
-The default is: \fBno\fR
+The default is: \fBoff\fR
.RE
.
.
.I smart
This differs from \fIfind_multipaths yes\fR only in
the way it treats new devices for which only one path has been
-detected yet. When such a device is first encounted in udev rules, it is
+detected yet. When such a device is first encountered in udev rules, it is
treated as a multipath device. multipathd waits whether additional paths with
the same WWID appears. If that happens, it sets up a multipath map. If it
doesn\'t happen until a
The \fImultipath\fR subsection recognizes the following attributes:
.TP 17
.B wwid
-(Mandatory) World Wide Identifier. Detected multipath maps are matched agains this attribute.
+(Mandatory) World Wide Identifier. Detected multipath maps are matched against this attribute.
Note that, unlike the \fIwwid\fR attribute in the \fIblacklist\fR section,
this is \fBnot\fR a regular expression or a substring; WWIDs must match
exactly inside the multipaths section.
The vendor specific vpd page information, using the vpd page abbreviation.
The vpd page abbreviation can be found by running \fIsg_vpd -e\fR. multipathd
will use this information to gather device specific information that can be
-displayed with the \fI%g\fR wilcard for the \fImultipathd show maps format\fR
+displayed with the \fI%g\fR wildcard for the \fImultipathd show maps format\fR
and \fImultipathd show paths format\fR commands. Currently only the
\fBhp3par\fR vpd page is supported.
.TP
.SH "Shaky paths detection"
.\" ----------------------------------------------------------------------------
.
-A common problem in SAN setups is the occurence of intermittent errors: a
+A common problem in SAN setups is the occurrence of intermittent errors: a
path is unreachable, then reachable again for a short time, disappears again,
and so forth. This happens typically on unstable interconnects. It is
undesirable to switch pathgroups unnecessarily on such frequent, unreliable
path checks by multipathd, which is variable and controlled by the
\fIpolling_interval\fR and \fImax_polling_interval\fR parameters.
.
+.TP
+.B \(dqFPIN \(dq failure tracking
+Fibre channel fabrics can notify hosts about fabric-level issues such
+as integrity failures or congestion with so-called Fabric Performance
+Impact Notifications (FPINs).On receiving the fpin notifications through ELS
+multipathd will move the affected path and port states to marginal.
+.
.RS 8
.LP
This method is \fBdeprecated\fR in favor of the \(dqmarginal_path\(dq failure
--- /dev/null
+# Load SCSI device handler modules for multipath early
+# This file may be empty
include ../Makefile.inc
-ifneq ($(call check_func,dm_task_get_errno,/usr/include/libdevmapper.h),0)
+ifneq ($(call check_func,dm_task_get_errno,$(DEVMAPPER_INCDIR)/libdevmapper.h),0)
CFLAGS += -DLIBDM_API_GET_ERRNO
endif
+ifneq ($(call check_var,ELS_DTAG_LNK_INTEGRITY,$(LINUX_HEADERS_INCDIR)/scsi/fc/fc_els.h),0)
+ CFLAGS += -DFPIN_EVENT_HANDLER
+ FPIN_SUPPORT = 1
+endif
#
# debugging stuff
#
OBJS = main.o pidfile.o uxlsnr.o uxclnt.o cli.o cli_handlers.o waiter.o \
dmevents.o init_unwinder.o
+ifeq ($(FPIN_SUPPORT),1)
+OBJS += fpin_handlers.o
+endif
+
+
+
EXEC = multipathd
all : $(EXEC)
$(EXEC): $(OBJS) $(multipathdir)/libmultipath.so $(mpathcmddir)/libmpathcmd.so
$(CC) $(CFLAGS) $(OBJS) $(LDFLAGS) -o $(EXEC) $(LIBDEPS)
- $(GZIP) $(EXEC).8 > $(EXEC).8.gz
cli_handlers.o: cli_handlers.c
$(CC) $(CFLAGS) -Wno-unused-parameter -c -o $@ $<
$(INSTALL_PROGRAM) -m 644 $(EXEC).socket $(DESTDIR)$(unitdir)
endif
$(INSTALL_PROGRAM) -d $(DESTDIR)$(man8dir)
- $(INSTALL_PROGRAM) -m 644 $(EXEC).8.gz $(DESTDIR)$(man8dir)
+ $(INSTALL_PROGRAM) -m 644 $(EXEC).8 $(DESTDIR)$(man8dir)
uninstall:
$(RM) $(DESTDIR)$(bindir)/$(EXEC)
- $(RM) $(DESTDIR)$(man8dir)/$(EXEC).8.gz
+ $(RM) $(DESTDIR)$(man8dir)/$(EXEC).8
$(RM) $(DESTDIR)$(unitdir)/$(EXEC).service
$(RM) $(DESTDIR)$(unitdir)/$(EXEC).socket
clean: dep_clean
- $(RM) core *.o $(EXEC) *.gz
+ $(RM) core *.o $(EXEC)
include $(wildcard $(OBJS:.o=.d))
if (!waiter) {
- condlog(0, "dmevents waiter not intialized");
+ condlog(0, "dmevents waiter not initialized");
return NULL;
}
--- /dev/null
+#ifndef __FPIN_H__
+#define __FPIN_H__
+
+#ifdef FPIN_EVENT_HANDLER
+void *fpin_fabric_notification_receiver(void *unused);
+void *fpin_els_li_consumer(void *data);
+void fpin_clean_marginal_dev_list(__attribute__((unused)) void *arg);
+#else
+static void *fpin_fabric_notification_receiver(__attribute__((unused))void *unused)
+{
+ return NULL;
+}
+static void *fpin_els_li_consumer(__attribute__((unused))void *data)
+{
+ return NULL;
+}
+/* fpin_clean_marginal_dev_list() is never called */
+#endif
+
+#endif
--- /dev/null
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <libudev.h>
+#include <scsi/scsi_netlink_fc.h>
+#include <scsi/fc/fc_els.h>
+
+#include "parser.h"
+#include "vector.h"
+#include "structs.h"
+#include "structs_vec.h"
+#include "main.h"
+#include "debug.h"
+#include "util.h"
+#include "sysfs.h"
+
+#include "fpin.h"
+#include "devmapper.h"
+
+static pthread_cond_t fpin_li_cond = PTHREAD_COND_INITIALIZER;
+static pthread_mutex_t fpin_li_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t fpin_li_marginal_dev_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+static LIST_HEAD(els_marginal_list_head);
+static LIST_HEAD(fpin_li_marginal_dev_list_head);
+
+
+#define DEF_RX_BUF_SIZE 4096
+#define DEV_NAME_LEN 128
+#define FCH_EVT_LINKUP 0x2
+#define FCH_EVT_LINK_FPIN 0x501
+#define FCH_EVT_RSCN 0x5
+
+#define list_first_entry(ptr, type, member) \
+ list_entry((ptr)->next, type, member)
+
+/* max ELS frame Size */
+#define FC_PAYLOAD_MAXLEN 2048
+
+struct els_marginal_list {
+ uint32_t event_code;
+ uint16_t host_num;
+ uint16_t length;
+ char payload[FC_PAYLOAD_MAXLEN];
+ struct list_head node;
+};
+/* Structure to store the marginal devices info */
+struct marginal_dev_list {
+ char dev_t[BLK_DEV_SIZE];
+ uint32_t host_num;
+ struct list_head node;
+};
+
+static void _udev_device_unref(void *p)
+{
+ udev_device_unref(p);
+}
+
+
+/*set/unset the path state to marginal*/
+static int fpin_set_pathstate(struct path *pp, bool set)
+{
+ const char *action = set ? "set" : "unset";
+
+ if (!pp || !pp->mpp || !pp->mpp->alias)
+ return -1;
+
+ condlog(3, "\n%s: %s marginal path %s (fpin)",
+ action, pp->mpp->alias, pp->dev_t);
+ pp->marginal = set;
+ pp->mpp->fpin_must_reload = true;
+ return 0;
+}
+
+/* This will unset marginal state of a device*/
+static void fpin_path_unsetmarginal(char *devname, struct vectors *vecs)
+{
+ struct path *pp;
+
+ pp = find_path_by_dev(vecs->pathvec, devname);
+ if (!pp)
+ pp = find_path_by_devt(vecs->pathvec, devname);
+
+ fpin_set_pathstate(pp, false);
+}
+
+/*This will set the marginal state of a device*/
+static int fpin_path_setmarginal(struct path *pp)
+{
+ return fpin_set_pathstate(pp, true);
+}
+
+/* Unsets all the devices in the list from marginal state */
+static void
+fpin_unset_marginal_dev(uint32_t host_num, struct vectors *vecs)
+{
+ struct marginal_dev_list *tmp_marg = NULL;
+ struct marginal_dev_list *marg = NULL;
+ struct multipath *mpp;
+ int ret = 0;
+ int i;
+
+ pthread_cleanup_push(cleanup_lock, &vecs->lock);
+ lock(&vecs->lock);
+ pthread_testcancel();
+
+ pthread_mutex_lock(&fpin_li_marginal_dev_mutex);
+ pthread_cleanup_push(cleanup_mutex, &fpin_li_marginal_dev_mutex);
+ pthread_testcancel();
+ if (list_empty(&fpin_li_marginal_dev_list_head)) {
+ condlog(4, "Marginal List is empty\n");
+ goto empty;
+ }
+ list_for_each_entry_safe(marg, tmp_marg, &fpin_li_marginal_dev_list_head, node) {
+ if (marg->host_num != host_num)
+ continue;
+ condlog(4, " unsetting marginal dev: is %s %d\n",
+ tmp_marg->dev_t, tmp_marg->host_num);
+ fpin_path_unsetmarginal(marg->dev_t, vecs);
+ list_del(&marg->node);
+ free(marg);
+ }
+empty:
+ pthread_cleanup_pop(1);
+ /* walk backwards because reload_and_sync_map() can remove mpp */
+ vector_foreach_slot_backwards(vecs->mpvec, mpp, i) {
+ if (mpp->fpin_must_reload) {
+ ret = reload_and_sync_map(mpp, vecs, 0);
+ if (ret == 2)
+ condlog(2, "map removed during reload");
+ else
+ mpp->fpin_must_reload = false;
+ }
+ }
+ pthread_cleanup_pop(1);
+}
+
+/*
+ * On Receiving the frame from HBA driver, insert the frame into link
+ * integrity frame list which will be picked up later by consumer thread for
+ * processing.
+ */
+static int
+fpin_els_add_li_frame(struct fc_nl_event *fc_event)
+{
+ struct els_marginal_list *els_mrg = NULL;
+ int ret = 0;
+
+ if (fc_event->event_datalen > FC_PAYLOAD_MAXLEN)
+ return -EINVAL;
+
+ pthread_mutex_lock(&fpin_li_mutex);
+ pthread_cleanup_push(cleanup_mutex, &fpin_li_mutex);
+ pthread_testcancel();
+ els_mrg = calloc(1, sizeof(struct els_marginal_list));
+ if (els_mrg != NULL) {
+ els_mrg->host_num = fc_event->host_no;
+ els_mrg->event_code = fc_event->event_code;
+ els_mrg->length = fc_event->event_datalen;
+ memcpy(els_mrg->payload, &(fc_event->event_data), fc_event->event_datalen);
+ list_add_tail(&els_mrg->node, &els_marginal_list_head);
+ pthread_cond_signal(&fpin_li_cond);
+ } else
+ ret = -ENOMEM;
+ pthread_cleanup_pop(1);
+ return ret;
+
+}
+
+/*Sets the rport port_state to marginal*/
+static void fpin_set_rport_marginal(struct udev_device *rport_dev)
+{
+ sysfs_attr_set_value(rport_dev, "port_state",
+ "Marginal", strlen("Marginal"));
+}
+
+/*Add the marginal devices info into the list*/
+static void
+fpin_add_marginal_dev_info(uint32_t host_num, char *devname)
+{
+ struct marginal_dev_list *newdev = NULL;
+
+ newdev = calloc(1, sizeof(struct marginal_dev_list));
+ if (newdev != NULL) {
+ newdev->host_num = host_num;
+ strlcpy(newdev->dev_t, devname, BLK_DEV_SIZE);
+ condlog(4, "\n%s hostno %d devname %s\n", __func__,
+ host_num, newdev->dev_t);
+ pthread_mutex_lock(&fpin_li_marginal_dev_mutex);
+ list_add_tail(&(newdev->node),
+ &fpin_li_marginal_dev_list_head);
+ pthread_mutex_unlock(&fpin_li_marginal_dev_mutex);
+ }
+}
+
+/*
+ * This function goes through the vecs->pathvec, and for
+ * each path, check that the host number,
+ * the target WWPN associated with the path matches
+ * with the els wwpn and sets the path and port state to
+ * Marginal
+ */
+static int fpin_chk_wwn_setpath_marginal(uint16_t host_num, struct vectors *vecs,
+ uint64_t els_wwpn)
+{
+ struct path *pp;
+ struct multipath *mpp;
+ int i, k;
+ char rport_id[42];
+ const char *value = NULL;
+ struct udev_device *rport_dev = NULL;
+ uint64_t wwpn;
+ int ret = 0;
+
+ pthread_cleanup_push(cleanup_lock, &vecs->lock);
+ lock(&vecs->lock);
+ pthread_testcancel();
+
+ vector_foreach_slot(vecs->pathvec, pp, k) {
+ /* Checks the host number and also for the SCSI FCP */
+ if (pp->sg_id.proto_id != SCSI_PROTOCOL_FCP || host_num != pp->sg_id.host_no)
+ continue;
+ sprintf(rport_id, "rport-%d:%d-%d",
+ pp->sg_id.host_no, pp->sg_id.channel, pp->sg_id.transport_id);
+ rport_dev = udev_device_new_from_subsystem_sysname(udev,
+ "fc_remote_ports", rport_id);
+ if (!rport_dev) {
+ condlog(2, "%s: No fc_remote_port device for '%s'", pp->dev,
+ rport_id);
+ continue;
+ }
+ pthread_cleanup_push(_udev_device_unref, rport_dev);
+ value = udev_device_get_sysattr_value(rport_dev, "port_name");
+ if (!value)
+ goto unref;
+
+ if (value)
+ wwpn = strtol(value, NULL, 16);
+ /*
+ * If the port wwpn matches sets the path and port state
+ * to marginal
+ */
+ if (wwpn == els_wwpn) {
+ ret = fpin_path_setmarginal(pp);
+ if (ret < 0)
+ goto unref;
+ fpin_set_rport_marginal(rport_dev);
+ fpin_add_marginal_dev_info(host_num, pp->dev);
+ }
+unref:
+ pthread_cleanup_pop(1);
+ }
+ /* walk backwards because reload_and_sync_map() can remove mpp */
+ vector_foreach_slot_backwards(vecs->mpvec, mpp, i) {
+ if (mpp->fpin_must_reload) {
+ ret = reload_and_sync_map(mpp, vecs, 0);
+ if (ret == 2)
+ condlog(2, "map removed during reload");
+ else
+ mpp->fpin_must_reload = false;
+ }
+ }
+ pthread_cleanup_pop(1);
+ return ret;
+}
+
+/*
+ * This function loops around all the impacted wwns received as part of els
+ * frame and sets the associated path and port states to marginal.
+ */
+static int
+fpin_parse_li_els_setpath_marginal(uint16_t host_num, struct fc_tlv_desc *tlv,
+ struct vectors *vecs)
+{
+ uint32_t wwn_count = 0, iter = 0;
+ uint64_t wwpn;
+ struct fc_fn_li_desc *li_desc = (struct fc_fn_li_desc *)tlv;
+ int count = 0;
+ int ret = 0;
+
+ /* Update the wwn to list */
+ wwn_count = be32_to_cpu(li_desc->pname_count);
+ condlog(4, "Got wwn count as %d\n", wwn_count);
+
+ for (iter = 0; iter < wwn_count; iter++) {
+ wwpn = be64_to_cpu(li_desc->pname_list[iter]);
+ ret = fpin_chk_wwn_setpath_marginal(host_num, vecs, wwpn);
+ if (ret < 0)
+ condlog(2, "failed to set the path marginal associated with wwpn: 0x%" PRIx64 "\n", wwpn);
+
+ count++;
+ }
+ return count;
+}
+
+/*
+ * This function process the ELS frame received from HBA driver,
+ * and sets the path associated with the port wwn to marginal
+ * and also set the port state to marginal.
+ */
+static int
+fpin_process_els_frame(uint16_t host_num, char *fc_payload, struct vectors *vecs)
+{
+
+ int count = -1;
+ struct fc_els_fpin *fpin = (struct fc_els_fpin *)fc_payload;
+ struct fc_tlv_desc *tlv;
+
+ tlv = (struct fc_tlv_desc *)&fpin->fpin_desc[0];
+
+ /*
+ * Parse the els frame and set the affected paths and port
+ * state to marginal
+ */
+ count = fpin_parse_li_els_setpath_marginal(host_num, tlv, vecs);
+ if (count <= 0)
+ condlog(4, "Could not find any WWNs, ret = %d\n",
+ count);
+ return count;
+}
+
+/*
+ * This function process the FPIN ELS frame received from HBA driver,
+ * and push the frame to appropriate frame list. Currently we have only FPIN
+ * LI frame list.
+ */
+static int
+fpin_handle_els_frame(struct fc_nl_event *fc_event)
+{
+ int ret = -1;
+ uint32_t els_cmd;
+ struct fc_els_fpin *fpin = (struct fc_els_fpin *)&fc_event->event_data;
+ struct fc_tlv_desc *tlv;
+ uint32_t dtag;
+
+ els_cmd = (uint32_t)fc_event->event_data;
+ tlv = (struct fc_tlv_desc *)&fpin->fpin_desc[0];
+ dtag = be32_to_cpu(tlv->desc_tag);
+ condlog(4, "Got CMD in add as 0x%x fpin_cmd 0x%x dtag 0x%x\n",
+ els_cmd, fpin->fpin_cmd, dtag);
+
+ if ((fc_event->event_code == FCH_EVT_LINK_FPIN) ||
+ (fc_event->event_code == FCH_EVT_LINKUP) ||
+ (fc_event->event_code == FCH_EVT_RSCN)) {
+
+ if (els_cmd == ELS_FPIN) {
+ /*
+ * Check the type of fpin by checking the tag info
+ * At present we are supporting only LI events
+ */
+ if (dtag == ELS_DTAG_LNK_INTEGRITY) {
+ /*Push the Payload to FPIN frame queue. */
+ ret = fpin_els_add_li_frame(fc_event);
+ if (ret != 0)
+ condlog(0, "Failed to process LI frame with error %d\n",
+ ret);
+ } else {
+ condlog(4, "Unsupported FPIN received 0x%x\n", dtag);
+ return ret;
+ }
+ } else {
+ /*Push the Payload to FPIN frame queue. */
+ ret = fpin_els_add_li_frame(fc_event);
+ if (ret != 0)
+ condlog(0, "Failed to process Linkup/RSCN event with error %d evnt %d\n",
+ ret, fc_event->event_code);
+ }
+ } else
+ condlog(4, "Invalid command received: 0x%x\n", els_cmd);
+ return ret;
+}
+
+/*cleans the global marginal dev list*/
+void fpin_clean_marginal_dev_list(__attribute__((unused)) void *arg)
+{
+ struct marginal_dev_list *tmp_marg = NULL;
+
+ pthread_mutex_lock(&fpin_li_marginal_dev_mutex);
+ while (!list_empty(&fpin_li_marginal_dev_list_head)) {
+ tmp_marg = list_first_entry(&fpin_li_marginal_dev_list_head,
+ struct marginal_dev_list, node);
+ list_del(&tmp_marg->node);
+ free(tmp_marg);
+ }
+ pthread_mutex_unlock(&fpin_li_marginal_dev_mutex);
+}
+
+/* Cleans the global els marginal list */
+static void fpin_clean_els_marginal_list(void *arg)
+{
+ struct list_head *head = (struct list_head *)arg;
+ struct els_marginal_list *els_marg;
+
+ while (!list_empty(head)) {
+ els_marg = list_first_entry(head, struct els_marginal_list,
+ node);
+ list_del(&els_marg->node);
+ free(els_marg);
+ }
+}
+
+static void rcu_unregister(__attribute__((unused)) void *param)
+{
+ rcu_unregister_thread();
+}
+/*
+ * This is the FPIN ELS consumer thread. The thread sleeps on pthread cond
+ * variable unless notified by fpin_fabric_notification_receiver thread.
+ * This thread is only to process FPIN-LI ELS frames. A new thread and frame
+ * list will be added if any more ELS frames types are to be supported.
+ */
+void *fpin_els_li_consumer(void *data)
+{
+ struct list_head marginal_list_head;
+ int ret = 0;
+ uint16_t host_num;
+ struct els_marginal_list *els_marg;
+ uint32_t event_code;
+ struct vectors *vecs = (struct vectors *)data;
+
+ pthread_cleanup_push(rcu_unregister, NULL);
+ rcu_register_thread();
+ pthread_cleanup_push(fpin_clean_marginal_dev_list, NULL);
+ INIT_LIST_HEAD(&marginal_list_head);
+ pthread_cleanup_push(fpin_clean_els_marginal_list,
+ (void *)&marginal_list_head);
+ for ( ; ; ) {
+ pthread_mutex_lock(&fpin_li_mutex);
+ pthread_cleanup_push(cleanup_mutex, &fpin_li_mutex);
+ pthread_testcancel();
+ while (list_empty(&els_marginal_list_head))
+ pthread_cond_wait(&fpin_li_cond, &fpin_li_mutex);
+
+ if (!list_empty(&els_marginal_list_head)) {
+ condlog(4, "Invoke List splice tail\n");
+ list_splice_tail_init(&els_marginal_list_head, &marginal_list_head);
+ }
+ pthread_cleanup_pop(1);
+
+ while (!list_empty(&marginal_list_head)) {
+ els_marg = list_first_entry(&marginal_list_head,
+ struct els_marginal_list, node);
+ host_num = els_marg->host_num;
+ event_code = els_marg->event_code;
+ /* Now finally process FPIN LI ELS Frame */
+ condlog(4, "Got a new Payload buffer, processing it\n");
+ if ((event_code == FCH_EVT_LINKUP) || (event_code == FCH_EVT_RSCN))
+ fpin_unset_marginal_dev(host_num, vecs);
+ else {
+ ret = fpin_process_els_frame(host_num, els_marg->payload, vecs);
+ if (ret <= 0)
+ condlog(0, "ELS frame processing failed with ret %d\n", ret);
+ }
+ list_del(&els_marg->node);
+ free(els_marg);
+
+ }
+ }
+
+ pthread_cleanup_pop(1);
+ pthread_cleanup_pop(1);
+ pthread_cleanup_pop(1);
+ return NULL;
+}
+
+static void receiver_cleanup_list(__attribute__((unused)) void *arg)
+{
+ pthread_mutex_lock(&fpin_li_mutex);
+ fpin_clean_els_marginal_list(&els_marginal_list_head);
+ pthread_mutex_unlock(&fpin_li_mutex);
+}
+
+/*
+ * Listen for ELS frames from driver. on receiving the frame payload,
+ * push the payload to a list, and notify the fpin_els_li_consumer thread to
+ * process it. Once consumer thread is notified, return to listen for more ELS
+ * frames from driver.
+ */
+void *fpin_fabric_notification_receiver(__attribute__((unused))void *unused)
+{
+ int ret;
+ long fd;
+ uint32_t els_cmd;
+ struct fc_nl_event *fc_event = NULL;
+ struct sockaddr_nl fc_local;
+ unsigned char buf[DEF_RX_BUF_SIZE] __attribute__((aligned(sizeof(uint64_t))));
+ size_t plen = 0;
+
+ pthread_cleanup_push(rcu_unregister, NULL);
+ rcu_register_thread();
+
+ pthread_cleanup_push(receiver_cleanup_list, NULL);
+ fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_SCSITRANSPORT);
+ if (fd < 0) {
+ condlog(0, "fc socket error %ld", fd);
+ return NULL;
+ }
+
+ pthread_cleanup_push(close_fd, (void *)fd);
+ memset(&fc_local, 0, sizeof(fc_local));
+ fc_local.nl_family = AF_NETLINK;
+ fc_local.nl_groups = ~0;
+ fc_local.nl_pid = getpid();
+ ret = bind(fd, (struct sockaddr *)&fc_local, sizeof(fc_local));
+ if (ret == -1) {
+ condlog(0, "fc socket bind error %d\n", ret);
+ goto out;
+ }
+ for ( ; ; ) {
+ condlog(4, "Waiting for ELS...\n");
+ ret = read(fd, buf, DEF_RX_BUF_SIZE);
+ if (ret < 0) {
+ condlog(0, "failed to read the els frame (%d)", ret);
+ continue;
+ }
+ condlog(4, "Got a new request %d\n", ret);
+ if (!NLMSG_OK((struct nlmsghdr *)buf, (unsigned int)ret)) {
+ condlog(0, "bad els frame read (%d)", ret);
+ continue;
+ }
+ /* Push the frame to appropriate frame list */
+ plen = NLMSG_PAYLOAD((struct nlmsghdr *)buf, 0);
+ fc_event = (struct fc_nl_event *)NLMSG_DATA(buf);
+ if (plen < sizeof(*fc_event)) {
+ condlog(0, "too short (%d) to be an FC event", ret);
+ continue;
+ }
+ els_cmd = (uint32_t)fc_event->event_data;
+ condlog(4, "Got host no as %d, event 0x%x, len %d evntnum %d evntcode %d\n",
+ fc_event->host_no, els_cmd, fc_event->event_datalen,
+ fc_event->event_num, fc_event->event_code);
+ fpin_handle_els_frame(fc_event);
+ }
+out:
+ pthread_cleanup_pop(1);
+ pthread_cleanup_pop(1);
+ pthread_cleanup_pop(1);
+ return NULL;
+}
#include <linux/oom.h>
#include <libudev.h>
#include <urcu.h>
+#include "fpin.h"
#ifdef USE_SYSTEMD
#include <systemd/sd-daemon.h>
#endif
#include "prio.h"
#include "wwids.h"
#include "pgpolicies.h"
-#include "uevent.h"
#include "log.h"
#include "uxsock.h"
#include "alias.h"
#include "mpath_cmd.h"
#include "mpath_persist.h"
+#include "mpath_persist_int.h"
#include "prioritizers/alua_rtpg.h"
#include "waiter.h"
#include "dmevents.h"
#include "io_err_stat.h"
-#include "wwids.h"
#include "foreign.h"
#include "../third-party/valgrind/drd.h"
#include "init_unwinder.h"
-#define FILE_NAME_SIZE 256
#define CMDSIZE 160
#define MSG_SIZE 32
+int mpath_pr_event_handle(struct path *pp);
+void * mpath_pr_event_handler_fn (void * );
+
#define LOG_MSG(lvl, pp) \
do { \
if (pp->mpp && checker_selected(&pp->checker) && \
pid_t daemon_pid;
static pthread_mutex_t config_lock = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t config_cond;
-static pthread_t check_thr, uevent_thr, uxlsnr_thr, uevq_thr, dmevent_thr;
+static pthread_t check_thr, uevent_thr, uxlsnr_thr, uevq_thr, dmevent_thr,
+ fpin_thr, fpin_consumer_thr;
static bool check_thr_started, uevent_thr_started, uxlsnr_thr_started,
- uevq_thr_started, dmevent_thr_started;
+ uevq_thr_started, dmevent_thr_started, fpin_thr_started,
+ fpin_consumer_thr_started;
static int pid_fd = -1;
static inline enum daemon_status get_running_state(void)
conf = get_multipath_config();
reassign_maps = conf->reassign_maps;
put_multipath_config(conf);
+ dm_get_info(mpp->alias, &mpp->dmi);
if (mpp->wait_for_udev) {
mpp->wait_for_udev = 0;
if (get_delayed_reconfig() &&
}
if (strcmp(mpp->alias, alias)) {
condlog(2, "%s: minor number mismatch (map %d, event %d)",
- mpp->alias, mpp->dmi->minor, minor);
+ mpp->alias, mpp->dmi.minor, minor);
return 1;
}
return flush_map(mpp, vecs, 0);
free_path(pp);
return 1;
}
+ if (mpp)
+ trigger_path_udev_change(pp, true);
if (mpp && mpp->wait_for_udev &&
(pathcount(mpp, PATH_UP) > 0 ||
(pathcount(mpp, PATH_GHOST) > 0 &&
}
static int
+sysfs_get_ro (struct path *pp)
+{
+ int ro;
+ char buff[3]; /* Either "0\n\0" or "1\n\0" */
+
+ if (!pp->udev)
+ return -1;
+
+ if (sysfs_attr_get_value(pp->udev, "ro", buff, sizeof(buff)) <= 0) {
+ condlog(3, "%s: Cannot read ro attribute in sysfs", pp->dev);
+ return -1;
+ }
+
+ if (sscanf(buff, "%d\n", &ro) != 1 || ro < 0 || ro > 1) {
+ condlog(3, "%s: Cannot parse ro attribute", pp->dev);
+ return -1;
+ }
+
+ return ro;
+}
+
+static bool
+needs_ro_update(struct multipath *mpp, int ro)
+{
+ struct pathgroup * pgp;
+ struct path * pp;
+ unsigned int i, j;
+
+ if (!mpp || ro < 0)
+ return false;
+ if (!has_dm_info(mpp))
+ return true;
+ if (mpp->dmi.read_only == ro)
+ return false;
+ if (ro == 1)
+ return true;
+ vector_foreach_slot (mpp->pg, pgp, i) {
+ vector_foreach_slot (pgp->paths, pp, j) {
+ if (sysfs_get_ro(pp) == 1)
+ return false;
+ }
+ }
+ return true;
+}
+
+static int
uev_update_path (struct uevent *uev, struct vectors * vecs)
{
int ro, retval = 0, rc;
}
ro = uevent_get_disk_ro(uev);
- if (mpp && ro >= 0) {
+ if (needs_ro_update(mpp, ro)) {
condlog(2, "%s: update path write_protect to '%d' (uevent)", uev->kernel, ro);
if (mpp->wait_for_udev)
condlog(0, "%s: spurious uevent, path not found", uev->kernel);
}
- /* pp->initalized must not be INIT_PARTIAL if needs_reinit is set */
+ /* pp->initialized must not be INIT_PARTIAL if needs_reinit is set */
if (needs_reinit)
retval = uev_add_path(uev, vecs, 1);
return retval;
/*
* Wait for initial reconfiguration to finish, while
- * hadling signals
+ * handling signals
*/
while (wait_for_state_change_if(DAEMON_CONFIGURE, 50)
== DAEMON_CONFIGURE)
/*
* This function is only called when the path state changes
* from "bad" to "good". pp->state reflects the *previous* state.
- * If this was "bad", we know that a failure must have occured
+ * If this was "bad", we know that a failure must have occurred
* beforehand, and count that.
* Note that we count path state _changes_ this way. If a path
* remains in "bad" state, failure count is not increased.
/*
* provision a next check soonest,
- * in case we exit abnormaly from here
+ * in case we exit abnormally from here
*/
pp->tick = checkint;
reconfigure (struct vectors * vecs)
{
struct config * old, *conf;
+ int old_marginal_pathgroups;
conf = load_config(DEFAULT_CONFIGFILE);
if (!conf)
uxsock_timeout = conf->uxsock_timeout;
old = rcu_dereference(multipath_conf);
+ old_marginal_pathgroups = old->marginal_pathgroups;
+ if ((old_marginal_pathgroups == MARGINAL_PATHGROUP_FPIN) !=
+ (conf->marginal_pathgroups == MARGINAL_PATHGROUP_FPIN)) {
+ condlog(1, "multipathd must be restarted to turn %s fpin marginal paths",
+ (old_marginal_pathgroups == MARGINAL_PATHGROUP_FPIN)?
+ "off" : "on");
+ conf->marginal_pathgroups = old_marginal_pathgroups;
+ }
conf->sequence_nr = old->sequence_nr + 1;
rcu_assign_pointer(multipath_conf, conf);
call_rcu(&old->rcu, rcu_free_config);
-
+#ifdef FPIN_EVENT_HANDLER
+ fpin_clean_marginal_dev_list(NULL);
+#endif
configure(vecs);
pthread_cancel(uevq_thr);
if (dmevent_thr_started)
pthread_cancel(dmevent_thr);
+ if (fpin_thr_started)
+ pthread_cancel(fpin_thr);
+ if (fpin_consumer_thr_started)
+ pthread_cancel(fpin_consumer_thr);
+
if (check_thr_started)
pthread_join(check_thr, NULL);
pthread_join(uevq_thr, NULL);
if (dmevent_thr_started)
pthread_join(dmevent_thr, NULL);
+ if (fpin_thr_started)
+ pthread_join(fpin_thr, NULL);
+ if (fpin_consumer_thr_started)
+ pthread_join(fpin_consumer_thr, NULL);
+
/*
* As all threads are joined now, and we're in DAEMON_SHUTDOWN
char *envp;
enum daemon_status state;
int exit_code = 1;
+ int fpin_marginal_paths = 0;
init_unwinder();
mlockall(MCL_CURRENT | MCL_FUTURE);
setscheduler();
set_oom_adj();
-
+#ifdef FPIN_EVENT_HANDLER
+ if (conf->marginal_pathgroups == MARGINAL_PATHGROUP_FPIN)
+ fpin_marginal_paths = 1;
+#endif
/*
* Startup done, invalidate configuration
*/
goto failed;
} else
uevq_thr_started = true;
+
+ if (fpin_marginal_paths) {
+ if ((rc = pthread_create(&fpin_thr, &misc_attr,
+ fpin_fabric_notification_receiver, NULL))) {
+ condlog(0, "failed to create the fpin receiver thread: %d", rc);
+ goto failed;
+ } else
+ fpin_thr_started = true;
+
+ if ((rc = pthread_create(&fpin_consumer_thr,
+ &misc_attr, fpin_els_li_consumer, vecs))) {
+ condlog(0, "failed to create the fpin consumer thread thread: %d", rc);
+ goto failed;
+ } else
+ fpin_consumer_thr_started = true;
+ }
pthread_attr_destroy(&misc_attr);
while (1) {
REMOVE_PATH_FAILURE = 0x0, /* path could not be removed. It is still
* part of the kernel map, but its state
* is set to INIT_REMOVED, and it will be
- * removed at the next possible occassion */
+ * removed at the next possible occasion */
REMOVE_PATH_SUCCESS = 0x1, /* path was removed */
REMOVE_PATH_DELAY = 0x2, /* path is set to be removed later. it
* currently still exists and is part of the
* because the path was also removed */
};
-struct prout_param_descriptor;
-struct prin_resp;
-
extern pid_t daemon_pid;
extern int uxsock_timeout;
int ev_add_map (char *, const char *, struct vectors *);
int ev_remove_map (char *, char *, int, struct vectors *);
int flush_map(struct multipath *, struct vectors *, int);
-void * mpath_alloc_prin_response(int prin_sa);
-int prin_do_scsi_ioctl(char *, int rq_servact, struct prin_resp * resp,
- int noisy);
-void dumpHex(const char * , int len, int no_ascii);
-int prout_do_scsi_ioctl(char * , int rq_servact, int rq_scope,
- unsigned int rq_type,
- struct prout_param_descriptor *param, int noisy);
-int mpath_pr_event_handle(struct path *pp);
-void * mpath_pr_event_handler_fn (void * );
-int update_map_pr(struct multipath *mpp);
-void * mpath_pr_event_handler_fn (void * pathp );
+
void handle_signals(bool);
int __setup_multipath (struct vectors * vecs, struct multipath * mpp,
int reset);
.B \-w
Since kernel 4.14 a new device-mapper event polling interface is used for updating
multipath devices on dmevents. Use this flag to force it to use the old event
-waiting method, based on creating a seperate thread for each device.
+waiting method, based on creating a separate thread for each device.
.
.
.
[Unit]
Description=Device-Mapper Multipath Device Controller
-Before=iscsi.service iscsid.service lvm2-activation-early.service
+Before=lvm2-activation-early.service
Before=local-fs-pre.target blk-availability.service shutdown.target
Wants=systemd-udevd-kernel.socket
After=systemd-udevd-kernel.socket
After=multipathd.socket systemd-remount-fs.service
+Before=initrd-cleanup.service
DefaultDependencies=no
Conflicts=shutdown.target
+Conflicts=initrd-cleanup.service
ConditionKernelCommandLine=!nompath
ConditionKernelCommandLine=!multipath=off
ConditionVirtualization=!container
[Service]
Type=notify
NotifyAccess=main
-LimitCORE=infinity
-ExecStartPre=-/sbin/modprobe -a scsi_dh_alua scsi_dh_emc scsi_dh_rdac dm-multipath
ExecStart=/sbin/multipathd -d -s
ExecReload=/sbin/multipathd reconfigure
TasksMax=infinity
};
#define POLLFD_CHUNK (4096 / sizeof(struct pollfd))
-/* Minimum mumber of pollfds to reserve for clients */
+/* Minimum number of pollfds to reserve for clients */
#define MIN_POLLS (POLLFD_CHUNK - POLLFDS_BASE)
/*
* Max number of client connections allowed
loss. However, the user needs to specify a device to be used. Set the
environment variable `DIO_TEST_DEV` to the path of the device.
Alternatively, create a file `directio_test_dev` under
-the `tests` directory containting a single line that sets this environment
+the `tests` directory containing a single line that sets this environment
variable in Bourne Shell syntax, like this:
DIO_TEST_DEV=/dev/sdc3
static const struct key_value prd_baz = { _product, "baz" };
static const struct key_value wwid_test = { _wwid, default_wwid };
-/* Regular expresssions */
+/* Regular expressions */
static const struct key_value vnd__oo = { _vendor, ".oo" };
static const struct key_value vnd_t_oo = { _vendor, "^.oo" };
static const struct key_value prd_ba_ = { _product, "ba." };
}
/*
- * Device section with a simple entry qith double quotes ('foo:"bar"')
+ * Device section with a simple entry with double quotes ('foo:"bar"')
*/
static void test_quoted_hwe(const struct hwt_state *hwt)
{
}
/*
- * Two identical device entries kv1 and kv2, trival regex ("string").
+ * Two identical device entries kv1 and kv2, trivial regex ("string").
* Both are added to the main config file.
* These entries are NOT merged.
* This could happen in a large multipath.conf file.
}
/*
- * Two identical device entries kv1 and kv2, trival regex ("string").
+ * Two identical device entries kv1 and kv2, trivial regex ("string").
* Both are added to an extra config file.
* This could happen in a large multipath.conf file.
*
}
/*
- * Two identical device entries kv1 and kv2, trival regex ("string").
+ * Two identical device entries kv1 and kv2, trivial regex ("string").
* Both are added to an extra config file.
* An empty entry kv0 with the same string exists in the main config file.
*
}
/*
- * Two identical device entries kv1 and kv2, trival regex ("string").
+ * Two identical device entries kv1 and kv2, trivial regex ("string").
* kv1 is added to the main config file, kv2 to a config_dir file.
* These entries are merged.
* This case is more important as you may think, because it's equivalent
#include <stddef.h>
#include <setjmp.h>
#include <stdlib.h>
-#include <stdbool.h>
#include <cmocka.h>
#include <errno.h>
#include "strbuf.h"