All of lore.kernel.org
 help / color / mirror / Atom feed
From: Martin Wilck <mwilck@suse.com>
To: Christophe Varoqui <christophe.varoqui@opensvc.com>,
	Benjamin Marzinski <bmarzins@redhat.com>
Cc: dm-devel@redhat.com, Martin Wilck <mwilck@suse.com>
Subject: [PATCH v5 16/22] multipath -u : set FIND_MULTIPATHS_WAIT_UNTIL from /dev/shm
Date: Sat, 14 Apr 2018 00:00:09 +0200	[thread overview]
Message-ID: <20180413220015.7032-17-mwilck@suse.com> (raw)
In-Reply-To: <20180413220015.7032-1-mwilck@suse.com>

In "find_multipaths smart" mode, use time stamps under
/dev/shm/multipath/find_multipaths to track waiting for multipath
siblings. When a path is first encountered and is "maybe" multipath, create
a file under /dev/shm, set its modification time to the expiry time of the
timer, and set the FIND_MULTIPATHS_WAIT_UNTIL variable. On later calls,
also set FIND_MULTIPATHS_WAIT_UNTIL to the expiry time (but don't change
the time stamp) if it's not expired yet, or 0 if it is expired. Set
FIND_MULTIPATHS_WAIT_UNTIL even if enough evidence becomes available to
decide if the path needs to be multipathed - this enables the udev rules to
detect that this is a device a timer has been started for, and stop it. By
using /dev/shm, we share information about "smart" timers between initrd
and root file system, and thus only calculate the timeout once.

Because we use major:minor to identify the devices in /dev/shm, and because
a removed device might be replaced by a different one with the same
major/minor number, add a rule to multipath.rules to remove the
find_multipaths marker on remove uevents.

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 libmultipath/file.c       |   2 +-
 libmultipath/file.h       |   1 +
 multipath/main.c          | 132 ++++++++++++++++++++++++++++++++++++++++++++++
 multipath/multipath.rules |   4 +-
 4 files changed, 137 insertions(+), 2 deletions(-)

diff --git a/libmultipath/file.c b/libmultipath/file.c
index d5165ec6..8727f160 100644
--- a/libmultipath/file.c
+++ b/libmultipath/file.c
@@ -36,7 +36,7 @@
  * See the file COPYING included with this distribution for more details.
  */
 
-static int
+int
 ensure_directories_exist(const char *str, mode_t dir_mode)
 {
 	char *pathname;
diff --git a/libmultipath/file.h b/libmultipath/file.h
index 70bffa53..29520c74 100644
--- a/libmultipath/file.h
+++ b/libmultipath/file.h
@@ -6,6 +6,7 @@
 #define _FILE_H
 
 #define FILE_TIMEOUT 30
+int ensure_directories_exist(const char *str, mode_t dir_mode);
 int open_file(const char *file, int *can_write, const char *header);
 
 #endif /* _FILE_H */
diff --git a/multipath/main.c b/multipath/main.c
index f62e18a9..5d847f08 100644
--- a/multipath/main.c
+++ b/multipath/main.c
@@ -29,6 +29,7 @@
 #include <ctype.h>
 #include <libudev.h>
 #include <syslog.h>
+#include <fcntl.h>
 
 #include "checkers.h"
 #include "prio.h"
@@ -60,6 +61,9 @@
 #include "uxsock.h"
 #include "mpath_cmd.h"
 #include "foreign.h"
+#include "propsel.h"
+#include "time-util.h"
+#include "file.h"
 
 int logsink;
 struct udev *udev;
@@ -350,14 +354,142 @@ out:
 	return r;
 }
 
+enum {
+	FIND_MULTIPATHS_WAIT_DONE = 0,
+	FIND_MULTIPATHS_WAITING = 1,
+	FIND_MULTIPATHS_ERROR = -1,
+	FIND_MULTIPATHS_NEVER = -2,
+};
+
+static const char shm_find_mp_dir[] = MULTIPATH_SHM_BASE "find_multipaths";
+static void close_fd(void *arg)
+{
+	close((long)arg);
+}
+
+/**
+ * find_multipaths_check_timeout(wwid, tmo)
+ * Helper for "find_multipaths smart"
+ *
+ * @param[in] pp: path to check / record
+ * @param[in] tmo: configured timeout for this WWID, or value <= 0 for checking
+ * @param[out] until: timestamp until we must wait, CLOCK_REALTIME, if return
+ *             value is FIND_MULTIPATHS_WAITING
+ * @returns: FIND_MULTIPATHS_WAIT_DONE, if waiting has finished
+ * @returns: FIND_MULTIPATHS_ERROR, if internal error occurred
+ * @returns: FIND_MULTIPATHS_NEVER, if tmo is 0 and we didn't wait for this
+ *           device
+ * @returns: FIND_MULTIPATHS_WAITING, if timeout hasn't expired
+ */
+static int find_multipaths_check_timeout(const struct path *pp, long tmo,
+					 struct timespec *until)
+{
+	char path[PATH_MAX];
+	struct timespec now, ftimes[2], tdiff;
+	struct stat st;
+	long fd;
+	int r, err, retries = 0;
+
+	clock_gettime(CLOCK_REALTIME, &now);
+
+	if (snprintf(path, sizeof(path), "%s/%s", shm_find_mp_dir, pp->dev_t)
+	    >= sizeof(path)) {
+		condlog(1, "%s: path name overflow", __func__);
+		return FIND_MULTIPATHS_ERROR;
+	}
+
+	if (ensure_directories_exist(path, 0700)) {
+		condlog(1, "%s: error creating directories", __func__);
+		return FIND_MULTIPATHS_ERROR;
+	}
+
+retry:
+	fd = open(path, O_RDONLY);
+	if (fd != -1) {
+		pthread_cleanup_push(close_fd, (void *)fd);
+		r = fstat(fd, &st);
+		if (r != 0)
+			err = errno;
+		pthread_cleanup_pop(1);
+
+	} else if (tmo > 0) {
+		if (errno == ENOENT)
+			fd = open(path, O_RDWR|O_EXCL|O_CREAT, 0644);
+		if (fd == -1) {
+			if (errno == EEXIST && !retries++)
+				/* We could have raced with another process */
+				goto retry;
+			condlog(1, "%s: error opening %s: %s",
+				__func__, path, strerror(errno));
+			return FIND_MULTIPATHS_ERROR;
+		};
+
+		pthread_cleanup_push(close_fd, (void *)fd);
+		/*
+		 * We just created the file. Set st_mtim to our desired
+		 * expiry time.
+		 */
+		ftimes[0].tv_sec = 0;
+		ftimes[0].tv_nsec = UTIME_OMIT;
+		ftimes[1].tv_sec = now.tv_sec + tmo;
+		ftimes[1].tv_nsec = now.tv_nsec;
+		if (futimens(fd, ftimes) != 0) {
+			condlog(1, "%s: error in futimens(%s): %s", __func__,
+				path, strerror(errno));
+		}
+		r = fstat(fd, &st);
+		if (r != 0)
+			err = errno;
+		pthread_cleanup_pop(1);
+	} else
+		return FIND_MULTIPATHS_NEVER;
+
+	if (r != 0) {
+		condlog(1, "%s: error in fstat for %s: %s", __func__,
+			path, strerror(err));
+		return FIND_MULTIPATHS_ERROR;
+	}
+
+	timespecsub(&st.st_mtim, &now, &tdiff);
+
+	if (tdiff.tv_sec <= 0)
+		return FIND_MULTIPATHS_WAIT_DONE;
+
+	*until = tdiff;
+	return FIND_MULTIPATHS_WAITING;
+}
+
 static int print_cmd_valid(int k, const vector pathvec,
 			   struct config *conf)
 {
 	static const int vals[] = { 1, 0, 2 };
+	int wait = FIND_MULTIPATHS_NEVER;
+	struct timespec until;
+	struct path *pp;
 
 	if (k < 0 || k >= sizeof(vals))
 		return 1;
 
+	if (k == 2) {
+		/*
+		 * Caller ensures that pathvec[0] is the path to
+		 * examine.
+		 */
+		pp = VECTOR_SLOT(pathvec, 0);
+		select_find_multipaths_timeout(conf, pp);
+		wait = find_multipaths_check_timeout(
+			pp, pp->find_multipaths_timeout, &until);
+		if (wait != FIND_MULTIPATHS_WAITING)
+			k = 1;
+	} else if (pathvec != NULL) {
+		pp = VECTOR_SLOT(pathvec, 0);
+		wait = find_multipaths_check_timeout(pp, 0, &until);
+	}
+	if (wait == FIND_MULTIPATHS_WAITING)
+		printf("FIND_MULTIPATHS_WAIT_UNTIL=\"%ld.%06ld\"\n",
+			       until.tv_sec, until.tv_nsec/1000);
+	else if (wait == FIND_MULTIPATHS_WAIT_DONE)
+		printf("FIND_MULTIPATHS_WAIT_UNTIL=\"0\"\n");
 	printf("DM_MULTIPATH_DEVICE_PATH=\"%d\"\n", vals[k]);
 	return k == 1;
 }
diff --git a/multipath/multipath.rules b/multipath/multipath.rules
index aab64dc7..37f4f6d8 100644
--- a/multipath/multipath.rules
+++ b/multipath/multipath.rules
@@ -1,7 +1,9 @@
 # Set DM_MULTIPATH_DEVICE_PATH if the device should be handled by multipath
 SUBSYSTEM!="block", GOTO="end_mpath"
-ACTION!="add|change", GOTO="end_mpath"
 KERNEL!="sd*|dasd*|nvme*", GOTO="end_mpath"
+ACTION=="remove", TEST=="/dev/shm/multipath/find_multipaths/$major:$minor", \
+	RUN+="/usr/bin/rm -f /dev/shm/multipath/find_multipaths/$major:$minor"
+ACTION!="add|change", GOTO="end_mpath"
 
 IMPORT{cmdline}="nompath"
 ENV{nompath}=="?*", GOTO="end_mpath"
-- 
2.16.1

  parent reply	other threads:[~2018-04-13 22:00 UTC|newest]

Thread overview: 50+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-04-13 21:59 [PATCH v5 00/22] multipath path classification Martin Wilck
2018-04-13 21:59 ` [PATCH v5 01/22] Revert "multipath: ignore -i if find_multipaths is set" Martin Wilck
2018-04-16  6:07   ` Hannes Reinecke
2018-04-13 21:59 ` [PATCH v5 02/22] Revert "multipathd: imply -n " Martin Wilck
2018-04-16  6:07   ` Hannes Reinecke
2018-04-13 21:59 ` [PATCH v5 03/22] libmultipath: should_multipath: keep existing maps Martin Wilck
2018-04-16  6:08   ` Hannes Reinecke
2018-04-13 21:59 ` [PATCH v5 04/22] multipath -u -i: respect entries in WWIDs file Martin Wilck
2018-04-16  6:09   ` Hannes Reinecke
2018-04-13 21:59 ` [PATCH v5 05/22] libmultipath: trigger change uevent on new device creation Martin Wilck
2018-04-16  6:10   ` Hannes Reinecke
2018-04-13 21:59 ` [PATCH v5 06/22] libmultipath: trigger path uevent only when necessary Martin Wilck
2018-04-16  6:10   ` Hannes Reinecke
2018-04-13 22:00 ` [PATCH v5 07/22] libmultipath: change find_multipaths option to multi-value Martin Wilck
2018-04-16  6:11   ` Hannes Reinecke
2018-04-13 22:00 ` [PATCH v5 08/22] libmultipath: use const char* in open_file() Martin Wilck
2018-04-16  6:11   ` Hannes Reinecke
2018-04-13 22:00 ` [PATCH v5 09/22] libmultipath: functions to indicate mapping failure in /dev/shm Martin Wilck
2018-04-16  6:12   ` Hannes Reinecke
2018-04-13 22:00 ` [PATCH v5 10/22] libmultipath: indicate wwid failure in dm_addmap_create() Martin Wilck
2018-04-16  6:13   ` Hannes Reinecke
2018-04-13 22:00 ` [PATCH v5 11/22] multipath -u: common code path for result message Martin Wilck
2018-04-16  6:14   ` Hannes Reinecke
2018-04-13 22:00 ` [PATCH v5 12/22] multipath -u: change output to environment/key format Martin Wilck
2018-04-16  6:14   ` Hannes Reinecke
2018-04-13 22:00 ` [PATCH v5 13/22] multipath -u: treat failed wwids as invalid Martin Wilck
2018-04-16  6:21   ` Hannes Reinecke
2018-04-13 22:00 ` [PATCH v5 14/22] multipath -u: add DM_MULTIPATH_DEVICE_PATH=2 for "maybe" Martin Wilck
2018-04-16  6:23   ` Hannes Reinecke
2018-04-13 22:00 ` [PATCH v5 15/22] libmultipath: implement find_multipaths_timeout Martin Wilck
2018-04-16  6:23   ` Hannes Reinecke
2018-04-13 22:00 ` Martin Wilck [this message]
2018-04-16  6:25   ` [PATCH v5 16/22] multipath -u : set FIND_MULTIPATHS_WAIT_UNTIL from /dev/shm Hannes Reinecke
2018-04-16 19:40   ` Benjamin Marzinski
2018-04-13 22:00 ` [PATCH v5 17/22] multipath -u: cleanup logic Martin Wilck
2018-04-16  6:26   ` Hannes Reinecke
2018-04-16 20:52   ` Benjamin Marzinski
2018-04-13 22:00 ` [PATCH v5 18/22] multipath -u: quick check if path is multipathed Martin Wilck
2018-04-16  6:29   ` Hannes Reinecke
2018-04-13 22:00 ` [PATCH v5 19/22] multipath -u: don't grab devices already passed to system Martin Wilck
2018-04-16  6:30   ` Hannes Reinecke
2018-04-16 21:29   ` Benjamin Marzinski
2018-04-13 22:00 ` [PATCH v5 20/22] multipath -u: test if path is busy Martin Wilck
2018-04-16  6:32   ` Hannes Reinecke
2018-04-16  8:00     ` Martin Wilck
2018-04-16 23:15   ` Benjamin Marzinski
2018-04-13 22:00 ` [PATCH v5 21/22] libmultipath: enable find_multipaths "smart" Martin Wilck
2018-04-16  6:40   ` Hannes Reinecke
2018-04-13 22:00 ` [PATCH v5 22/22] multipath.rules: find_multipaths "smart" logic Martin Wilck
2018-04-16  6:40   ` Hannes Reinecke

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20180413220015.7032-17-mwilck@suse.com \
    --to=mwilck@suse.com \
    --cc=bmarzins@redhat.com \
    --cc=christophe.varoqui@opensvc.com \
    --cc=dm-devel@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.