From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Mon, 4 Jun 2018 22:04:44 -0500
Subject: [PATCH] mpathpersist: fix aptpl support

The "Active Persist Through Power Loss" flag must be set whenever a key
is registered. However, there is no way for multipathd to know if this
was set by mpathpersist. The result is that if a path goes down and
comes back up (or if it wasn't up when mpathpersist was first run)
multipathd will clear the aptpl flag when it reregisters the key on it.

To fix this, multipath.conf now accepts an optional ":aptpl" appended
on the reservation_key value. If this is added to the reservation_key
multipathd will set the aptpl flag when it reregisters the key. If
reservation_key is set to "file", this will automatically be tracked
in the /etc/multipath/prkeys file.

To track this flag in the prkeys file, without changing the format
I've made "0x<key>" stand for non-aptpl keys, and "0X<key>" stand
for aptpl keys. Since previously, all keys used a lower-case x, this
will default to the current behavior for existing keys. Obviously, the
next time mpathpersist is run, this will be changed if --param-aptpl
is used.  Since there are no more flags that are in sg_persist that
multipathd needs to care about in mpathpersist, there shouldn't need
to be any more flags added to the prkeys file.

Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
 libmpathpersist/mpath_persist.c  |  3 ++-
 libmpathpersist/mpath_updatepr.c | 11 +++++++----
 libmpathpersist/mpathpr.h        |  3 ++-
 libmultipath/Makefile            |  2 +-
 libmultipath/config.h            |  2 ++
 libmultipath/dict.c              | 23 +++++++++++++++++++----
 libmultipath/dict.h              |  3 ++-
 libmultipath/prkey.c             | 27 ++++++++++++++++++++++++---
 libmultipath/prkey.h             |  6 ++++--
 libmultipath/propsel.c           |  6 ++++--
 libmultipath/structs.h           |  1 +
 libmultipath/util.c              | 16 ++++++++++++++++
 libmultipath/util.h              |  1 +
 multipath/multipath.conf.5       |  7 +++++--
 multipathd/cli_handlers.c        | 15 ++++++++++-----
 multipathd/main.c                |  1 +
 16 files changed, 101 insertions(+), 26 deletions(-)

diff --git a/libmpathpersist/mpath_persist.c b/libmpathpersist/mpath_persist.c
index ca91c55..6e9e67f 100644
--- a/libmpathpersist/mpath_persist.c
+++ b/libmpathpersist/mpath_persist.c
@@ -344,7 +344,8 @@ int mpath_persistent_reserve_out ( int fd, int rq_servact, int rq_scope,
 	      rq_servact == MPATH_PROUT_REG_SA) ||
 	     rq_servact == MPATH_PROUT_REG_IGN_SA)) {
 		memcpy(&mpp->reservation_key, paramp->sa_key, 8);
-		if (update_prkey(alias, get_be64(mpp->reservation_key))) {
+		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;
diff --git a/libmpathpersist/mpath_updatepr.c b/libmpathpersist/mpath_updatepr.c
index 8063e90..0aca28e 100644
--- a/libmpathpersist/mpath_updatepr.c
+++ b/libmpathpersist/mpath_updatepr.c
@@ -1,7 +1,5 @@
 #include <stdio.h>
 #include <unistd.h>
-#include <errno.h>
-
 #include <stdlib.h>
 #include <stdarg.h>
 #include <fcntl.h>
@@ -11,6 +9,8 @@
 #include <sys/un.h>
 #include <poll.h>
 #include <errno.h>
+#include <libudev.h>
+#include <mpath_persist.h>
 #include "debug.h"
 #include "mpath_cmd.h"
 #include "uxsock.h"
@@ -59,11 +59,14 @@ int update_prflag(char *mapname, int set) {
 	return do_update_pr(mapname, (set)? "setprstatus" : "unsetprstatus");
 }
 
-int update_prkey(char *mapname, uint64_t prkey) {
+int update_prkey_flags(char *mapname, uint64_t prkey, uint8_t sa_flags) {
 	char str[256];
+	char *flagstr = "";
 
+	if (sa_flags & MPATH_F_APTPL_MASK)
+		flagstr = ":aptpl";
 	if (prkey)
-		sprintf(str, "setprkey key %" PRIx64, prkey);
+		sprintf(str, "setprkey key %" PRIx64 "%s", prkey, flagstr);
 	else
 		sprintf(str, "unsetprkey");
 	return do_update_pr(mapname, str);
diff --git a/libmpathpersist/mpathpr.h b/libmpathpersist/mpathpr.h
index 72feb60..5ea8cd6 100644
--- a/libmpathpersist/mpathpr.h
+++ b/libmpathpersist/mpathpr.h
@@ -46,7 +46,8 @@ int send_prout_activepath(char * dev, int rq_servact, int rq_scope,
 	unsigned int rq_type,   struct prout_param_descriptor * paramp, int noisy);
 
 int update_prflag(char *mapname, int set);
-int update_prkey(char *mapname, uint64_t prkey);
+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);
 
diff --git a/libmultipath/Makefile b/libmultipath/Makefile
index f51786d..33f5269 100644
--- a/libmultipath/Makefile
+++ b/libmultipath/Makefile
@@ -7,7 +7,7 @@ SONAME = 0
 DEVLIB = libmultipath.so
 LIBS = $(DEVLIB).$(SONAME)
 
-CFLAGS += $(LIB_CFLAGS) -I$(mpathcmddir)
+CFLAGS += $(LIB_CFLAGS) -I$(mpathcmddir) -I$(mpathpersistdir)
 
 LIBDEPS += -lpthread -ldl -ldevmapper -ludev -L$(mpathcmddir) -lmpathcmd -lurcu -laio
 
diff --git a/libmultipath/config.h b/libmultipath/config.h
index 1bf708a..fcbe3fc 100644
--- a/libmultipath/config.h
+++ b/libmultipath/config.h
@@ -98,6 +98,7 @@ struct mpentry {
 	char * prio_args;
 	int prkey_source;
 	struct be64 reservation_key;
+	uint8_t sa_flags;
 	int pgpolicy;
 	int pgfailback;
 	int rr_weight;
@@ -197,6 +198,7 @@ struct config {
 	int prkey_source;
 	int all_tg_pt;
 	struct be64 reservation_key;
+	uint8_t sa_flags;
 
 	vector keywords;
 	vector mptable;
diff --git a/libmultipath/dict.c b/libmultipath/dict.c
index 2557b8a..7ad0f5a 100644
--- a/libmultipath/dict.c
+++ b/libmultipath/dict.c
@@ -22,6 +22,8 @@
 #include "util.h"
 #include <errno.h>
 #include <inttypes.h>
+#include <libudev.h>
+#include <mpath_persist.h>
 #include "mpath_cmd.h"
 #include "dict.h"
 
@@ -1012,10 +1014,12 @@ snprint_def_log_checker_err (struct config *conf, char * buff, int len,
 }
 
 static int
-set_reservation_key(vector strvec, struct be64 *be64_ptr, int *source_ptr)
+set_reservation_key(vector strvec, struct be64 *be64_ptr, uint8_t *flags_ptr,
+		    int *source_ptr)
 {
 	char *buff;
 	uint64_t prkey;
+	uint8_t sa_flags;
 
 	buff = set_value(strvec);
 	if (!buff)
@@ -1023,35 +1027,43 @@ set_reservation_key(vector strvec, struct be64 *be64_ptr, int *source_ptr)
 
 	if (strcmp(buff, "file") == 0) {
 		*source_ptr = PRKEY_SOURCE_FILE;
+		*flags_ptr = 0;
 		put_be64(*be64_ptr, 0);
 		FREE(buff);
 		return 0;
 	}
 
-	if (parse_prkey(buff, &prkey) != 0) {
+	if (parse_prkey_flags(buff, &prkey, &sa_flags) != 0) {
 		FREE(buff);
 		return 1;
 	}
 	*source_ptr = PRKEY_SOURCE_CONF;
+	*flags_ptr = sa_flags;
 	put_be64(*be64_ptr, prkey);
 	FREE(buff);
 	return 0;
 }
 
 int
-print_reservation_key(char * buff, int len, struct be64 key, int source)
+print_reservation_key(char * buff, int len, struct be64 key, uint8_t flags,
+		      int source)
 {
+	char *flagstr = "";
 	if (source == PRKEY_SOURCE_NONE)
 		return 0;
 	if (source == PRKEY_SOURCE_FILE)
 		return snprintf(buff, len, "file");
-	return snprintf(buff, len, "0x%" PRIx64, get_be64(key));
+	if (flags & MPATH_F_APTPL_MASK)
+		flagstr = ":aptpl";
+	return snprintf(buff, len, "0x%" PRIx64 "%s", get_be64(key),
+			flagstr);
 }
 
 static int
 def_reservation_key_handler(struct config *conf, vector strvec)
 {
 	return set_reservation_key(strvec, &conf->reservation_key,
+				   &conf->sa_flags,
 				   &conf->prkey_source);
 }
 
@@ -1060,6 +1072,7 @@ snprint_def_reservation_key (struct config *conf, char * buff, int len,
 			     const void * data)
 {
 	return print_reservation_key(buff, len, conf->reservation_key,
+				     conf->sa_flags,
 				     conf->prkey_source);
 }
 
@@ -1070,6 +1083,7 @@ mp_reservation_key_handler(struct config *conf, vector strvec)
 	if (!mpe)
 		return 1;
 	return set_reservation_key(strvec, &mpe->reservation_key,
+				   &mpe->sa_flags,
 				   &mpe->prkey_source);
 }
 
@@ -1079,6 +1093,7 @@ snprint_mp_reservation_key (struct config *conf, char * buff, int len,
 {
 	const struct mpentry * mpe = (const struct mpentry *)data;
 	return print_reservation_key(buff, len, mpe->reservation_key,
+				     mpe->sa_flags,
 				     mpe->prkey_source);
 }
 
diff --git a/libmultipath/dict.h b/libmultipath/dict.h
index 7564892..a40ac66 100644
--- a/libmultipath/dict.h
+++ b/libmultipath/dict.h
@@ -15,6 +15,7 @@ int print_pgpolicy(char *buff, int len, long v);
 int print_no_path_retry(char *buff, int len, long v);
 int print_fast_io_fail(char *buff, int len, long v);
 int print_dev_loss(char *buff, int len, unsigned long v);
-int print_reservation_key(char * buff, int len, struct be64 key, int source);
+int print_reservation_key(char * buff, int len, struct be64 key, uint8_t
+			  flags, int source);
 int print_off_int_undef(char *buff, int len, long v);
 #endif /* _DICT_H */
diff --git a/libmultipath/prkey.c b/libmultipath/prkey.c
index 89b90ed..d645f81 100644
--- a/libmultipath/prkey.c
+++ b/libmultipath/prkey.c
@@ -11,6 +11,8 @@
 #include <string.h>
 #include <inttypes.h>
 #include <errno.h>
+#include <libudev.h>
+#include <mpath_persist.h>
 
 #define PRKEY_READ 0
 #define PRKEY_WRITE 1
@@ -108,7 +110,8 @@ static int do_prkey(int fd, char *wwid, char *keystr, int cmd)
 	return 0;
 }
 
-int get_prkey(struct config *conf, struct multipath *mpp, uint64_t *prkey)
+int get_prkey(struct config *conf, struct multipath *mpp, uint64_t *prkey,
+	      uint8_t *sa_flags)
 {
 	int fd;
 	int unused;
@@ -124,6 +127,9 @@ int get_prkey(struct config *conf, struct multipath *mpp, uint64_t *prkey)
 	ret = do_prkey(fd, mpp->wwid, keystr, PRKEY_READ);
 	if (ret)
 		goto out_file;
+	*sa_flags = 0;
+	if (strchr(keystr, 'X'))
+		*sa_flags = MPATH_F_APTPL_MASK;
 	ret = !!parse_prkey(keystr, prkey);
 out_file:
 	close(fd);
@@ -131,7 +137,8 @@ out:
 	return ret;
 }
 
-int set_prkey(struct config *conf, struct multipath *mpp, uint64_t prkey)
+int set_prkey(struct config *conf, struct multipath *mpp, uint64_t prkey,
+	      uint8_t sa_flags)
 {
 	int fd;
 	int can_write = 1;
@@ -141,6 +148,12 @@ int set_prkey(struct config *conf, struct multipath *mpp, uint64_t prkey)
 	if (!strlen(mpp->wwid))
 		goto out;
 
+	if (sa_flags & ~MPATH_F_APTPL_MASK) {
+		condlog(0, "unsupported pr flags, 0x%x",
+			sa_flags & ~MPATH_F_APTPL_MASK);
+		sa_flags &= MPATH_F_APTPL_MASK;
+	}
+
 	fd = open_file(conf->prkeys_file, &can_write, PRKEYS_FILE_HEADER);
 	if (fd < 0)
 		goto out;
@@ -149,7 +162,15 @@ int set_prkey(struct config *conf, struct multipath *mpp, uint64_t prkey)
 		goto out_file;
 	}
 	if (prkey) {
-		snprintf(keystr, PRKEY_SIZE, "0x%016" PRIx64, prkey);
+		/* using the capitalization of the 'x' is a hack, but
+		 * it's unlikely that mpath_persist will support more options
+		 * since sg_persist doesn't, and this lets us keep the
+		 * same file format as before instead of needing to change
+		 * the format of the prkeys file */
+		if (sa_flags)
+			snprintf(keystr, PRKEY_SIZE, "0X%016" PRIx64, prkey);
+		else
+			snprintf(keystr, PRKEY_SIZE, "0x%016" PRIx64, prkey);
 		keystr[PRKEY_SIZE - 1] = '\0';
 		ret = do_prkey(fd, mpp->wwid, keystr, PRKEY_WRITE);
 	}
diff --git a/libmultipath/prkey.h b/libmultipath/prkey.h
index 4028e70..6739191 100644
--- a/libmultipath/prkey.h
+++ b/libmultipath/prkey.h
@@ -13,7 +13,9 @@
 "# prkey wwid\n" \
 "#\n"
 
-int set_prkey(struct config *conf, struct multipath *mpp, uint64_t prkey);
-int get_prkey(struct config *conf, struct multipath *mpp, uint64_t *prkey);
+int set_prkey(struct config *conf, struct multipath *mpp, uint64_t prkey,
+	      uint8_t sa_flags);
+int get_prkey(struct config *conf, struct multipath *mpp, uint64_t *prkey,
+	      uint8_t *sa_flags);
 
 #endif /* _PRKEY_H */
diff --git a/libmultipath/propsel.c b/libmultipath/propsel.c
index 9ca1355..62a6893 100644
--- a/libmultipath/propsel.c
+++ b/libmultipath/propsel.c
@@ -106,6 +106,7 @@ do {									\
 	if (src && src->prkey_source != PRKEY_SOURCE_NONE) {		\
 		mp->prkey_source = src->prkey_source;			\
 		mp->reservation_key = src->reservation_key;		\
+		mp->sa_flags = src->sa_flags;				\
 		origin = msg;						\
 		goto out;						\
 	}								\
@@ -703,18 +704,19 @@ int select_reservation_key(struct config *conf, struct multipath *mp)
 	do_prkey_set(mp->mpe, multipaths_origin);
 	do_prkey_set(conf, conf_origin);
 	put_be64(mp->reservation_key, 0);
+	mp->sa_flags = 0;
 	mp->prkey_source = PRKEY_SOURCE_NONE;
 	return 0;
 out:
 	if (mp->prkey_source == PRKEY_SOURCE_FILE) {
 		from_file = " (from prkeys file)";
-		if (get_prkey(conf, mp, &prkey) != 0)
+		if (get_prkey(conf, mp, &prkey, &mp->sa_flags) != 0)
 			put_be64(mp->reservation_key, 0);
 		else
 			put_be64(mp->reservation_key, prkey);
 	}
 	print_reservation_key(buff, PRKEY_SIZE, mp->reservation_key,
-			      mp->prkey_source);
+			      mp->sa_flags, mp->prkey_source);
 	condlog(3, "%s: reservation_key = %s %s%s", mp->alias, buff, origin,
 		from_file);
 	return 0;
diff --git a/libmultipath/structs.h b/libmultipath/structs.h
index 0194b1e..987479f 100644
--- a/libmultipath/structs.h
+++ b/libmultipath/structs.h
@@ -367,6 +367,7 @@ struct multipath {
 	/* persistent management data*/
 	int prkey_source;
 	struct be64 reservation_key;
+	uint8_t sa_flags;
 	unsigned char prflag;
 	int all_tg_pt;
 	struct gen_multipath generic_mp;
diff --git a/libmultipath/util.c b/libmultipath/util.c
index 7251ad0..8d8fcc8 100644
--- a/libmultipath/util.c
+++ b/libmultipath/util.c
@@ -10,6 +10,8 @@
 #include <dirent.h>
 #include <unistd.h>
 #include <errno.h>
+#include <libudev.h>
+#include <mpath_persist.h>
 
 #include "util.h"
 #include "debug.h"
@@ -435,6 +437,20 @@ int parse_prkey(char *ptr, uint64_t *prkey)
 	return 0;
 }
 
+int parse_prkey_flags(char *ptr, uint64_t *prkey, uint8_t *flags)
+{
+	char *flagstr;
+
+	flagstr = strchr(ptr, ':');
+	*flags = 0;
+	if (flagstr) {
+		*flagstr++ = '\0';
+		if (strlen(flagstr) == 5 && strcmp(flagstr, "aptpl") == 0)
+			*flags = MPATH_F_APTPL_MASK;
+	}
+	return parse_prkey(ptr, prkey);
+}
+
 int safe_write(int fd, const void *buf, size_t count)
 {
 	while (count > 0) {
diff --git a/libmultipath/util.h b/libmultipath/util.h
index a3ab894..56cec76 100644
--- a/libmultipath/util.h
+++ b/libmultipath/util.h
@@ -19,6 +19,7 @@ void setup_thread_attr(pthread_attr_t *attr, size_t stacksize, int detached);
 int systemd_service_enabled(const char *dev);
 int get_linux_version_code(void);
 int parse_prkey(char *ptr, uint64_t *prkey);
+int parse_prkey_flags(char *ptr, uint64_t *prkey, uint8_t *flags);
 int safe_write(int fd, const void *buf, size_t count);
 
 #define KERNEL_VERSION(maj, min, ptc) ((((maj) * 256) + (min)) * 256 + (ptc))
diff --git a/multipath/multipath.conf.5 b/multipath/multipath.conf.5
index 31f4585..30d8598 100644
--- a/multipath/multipath.conf.5
+++ b/multipath/multipath.conf.5
@@ -726,14 +726,17 @@ This is the service action reservation key used by mpathpersist. It must be
 set for all multipath devices using persistent reservations, and it must be
 the same as the RESERVATION KEY field of the PERSISTENT RESERVE OUT parameter
 list which contains an 8-byte value provided by the application client to the
-device server to identify the I_T nexus.
+device server to identify the I_T nexus. If the \fI--param-aptpl\fR option is
+used when registering the key with mpathpersist, \fB:aptpl\fR must be appended
+to the end of the reservation key.
 .RS
 .PP
 Alternatively, this can be set to \fBfile\fR, which will store the RESERVATION
 KEY registered by mpathpersist in the \fIprkeys_file\fR. multipathd will then
 use this key to register additional paths as they appear.  When the
 registration is removed, the RESERVATION KEY is removed from the
-\fIprkeys_file\fR.
+\fIprkeys_file\fR. The prkeys file will automatically keep track of whether
+the key was registered with \fI--param-aptpl\fR.
 .TP
 The default is: \fB<unset>\fR
 .RE
diff --git a/multipathd/cli_handlers.c b/multipathd/cli_handlers.c
index ba50fb8..6452796 100644
--- a/multipathd/cli_handlers.c
+++ b/multipathd/cli_handlers.c
@@ -21,6 +21,7 @@
 #include "sysfs.h"
 #include <errno.h>
 #include <libudev.h>
+#include <mpath_persist.h>
 #include "util.h"
 #include "prkey.h"
 #include "propsel.h"
@@ -1463,6 +1464,7 @@ cli_getprkey(void * v, char ** reply, int * len, void * data)
 	struct multipath * mpp;
 	struct vectors * vecs = (struct vectors *)data;
 	char *mapname = get_keyparam(v, MAP);
+	char *flagstr = "";
 
 	mapname = convert_dev(mapname, 0);
 	condlog(3, "%s: get persistent reservation key (operator)", mapname);
@@ -1478,8 +1480,10 @@ cli_getprkey(void * v, char ** reply, int * len, void * data)
 		*len = strlen(*reply) + 1;
 		return 0;
 	}
-	snprintf(*reply, 20, "0x%" PRIx64 "\n",
-		 get_be64(mpp->reservation_key));
+	if (mpp->sa_flags & MPATH_F_APTPL_MASK)
+		flagstr = ":aptpl";
+	snprintf(*reply, 20, "0x%" PRIx64 "%s\n",
+		 get_be64(mpp->reservation_key), flagstr);
 	(*reply)[19] = '\0';
 	*len = strlen(*reply) + 1;
 	return 0;
@@ -1503,7 +1507,7 @@ cli_unsetprkey(void * v, char ** reply, int * len, void * data)
 
 	conf = get_multipath_config();
 	pthread_cleanup_push(put_multipath_config, conf);
-	ret = set_prkey(conf, mpp, 0);
+	ret = set_prkey(conf, mpp, 0, 0);
 	pthread_cleanup_pop(1);
 
 	return ret;
@@ -1517,6 +1521,7 @@ cli_setprkey(void * v, char ** reply, int * len, void * data)
 	char *mapname = get_keyparam(v, MAP);
 	char *keyparam = get_keyparam(v, KEY);
 	uint64_t prkey;
+	uint8_t flags;
 	int ret;
 	struct config *conf;
 
@@ -1527,14 +1532,14 @@ cli_setprkey(void * v, char ** reply, int * len, void * data)
 	if (!mpp)
 		return 1;
 
-	if (parse_prkey(keyparam, &prkey) != 0) {
+	if (parse_prkey_flags(keyparam, &prkey, &flags) != 0) {
 		condlog(0, "%s: invalid prkey : '%s'", mapname, keyparam);
 		return 1;
 	}
 
 	conf = get_multipath_config();
 	pthread_cleanup_push(put_multipath_config, conf);
-	ret = set_prkey(conf, mpp, prkey);
+	ret = set_prkey(conf, mpp, prkey, flags);
 	pthread_cleanup_pop(1);
 
 	return ret;
diff --git a/multipathd/main.c b/multipathd/main.c
index d40c416..6b1e782 100644
--- a/multipathd/main.c
+++ b/multipathd/main.c
@@ -3089,6 +3089,7 @@ void *  mpath_pr_event_handler_fn (void * pathp )
 
 	param= malloc(sizeof(struct prout_param_descriptor));
 	memset(param, 0 , sizeof(struct prout_param_descriptor));
+	param->sa_flags = mpp->sa_flags;
 	memcpy(param->sa_key, &mpp->reservation_key, 8);
 	param->num_transportid = 0;
 
-- 
2.7.4

