[dm-devel] [RFC PATCH] multipathd: strict_timing without signals

Martin Wilck mwilck at suse.com
Wed Mar 7 14:17:51 UTC 2018


The internal usage of SIGALRM by setitimer() function may cause subtle
conflicts with other uses of SIGALRM, either by multipath-tools code itself
(e.g. lock_file()) or libc (e.g. glob()).

This patch changes the checkerloop to use an interval timer and a pthread
condition variable. No signals are used except those used by libpthread
behind the scenes.

Signed-off-by: Martin Wilck <mwilck at suse.com>
---
 multipathd/Makefile |   2 +-
 multipathd/main.c   | 113 ++++++++++++++++++++++++++++++++++++++--------------
 2 files changed, 85 insertions(+), 30 deletions(-)

diff --git a/multipathd/Makefile b/multipathd/Makefile
index 4c9d29634160..c348f02ef7f8 100644
--- a/multipathd/Makefile
+++ b/multipathd/Makefile
@@ -10,7 +10,7 @@ CFLAGS += $(BIN_CFLAGS) -I$(multipathdir) -I$(mpathpersistdir) \
 	  -I$(mpathcmddir) -I$(thirdpartydir)
 LDFLAGS += $(BIN_LDFLAGS)
 LIBDEPS += -L$(multipathdir) -lmultipath -L$(mpathpersistdir) -lmpathpersist \
-	   -L$(mpathcmddir) -lmpathcmd -ludev -ldl -lurcu -lpthread \
+	   -L$(mpathcmddir) -lmpathcmd -ludev -ldl -lurcu -lpthread -lrt \
 	   -ldevmapper -lreadline
 
 ifdef SYSTEMD
diff --git a/multipathd/main.c b/multipathd/main.c
index 6e6c52a52783..c9c57c5baece 100644
--- a/multipathd/main.c
+++ b/multipathd/main.c
@@ -1848,6 +1848,68 @@ static void init_path_check_interval(struct vectors *vecs)
 	}
 }
 
+static pthread_mutex_t checker_lock = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t checker_cond = PTHREAD_COND_INITIALIZER;
+
+static void checker_notify(union sigval sv) {
+
+	pthread_mutex_lock(&checker_lock);
+	pthread_cond_broadcast(&checker_cond);
+	pthread_mutex_unlock(&checker_lock);
+}
+
+static void unlock_mutex(void *arg)
+{
+	pthread_mutex_unlock((pthread_mutex_t*)arg);
+}
+
+void cleanup_timer(void *arg)
+{
+	timer_t tmr = (timer_t)arg;
+
+	if (tmr != 0)
+		timer_delete(tmr);
+}
+
+static timer_t setup_check_timer(void)
+{
+	timer_t tmr = (timer_t)0;
+	struct sigevent se;
+
+	memset(&se, 0, sizeof(se));
+	se.sigev_notify = SIGEV_THREAD;
+	se.sigev_notify_function = checker_notify;
+
+	if (timer_create(CLOCK_MONOTONIC, &se, &tmr)) {
+		condlog(2, "%s: errno %d creating monotonic timer",
+			__func__, errno);
+		if (timer_create(CLOCK_REALTIME, &se, &tmr))
+			condlog(0, "%s: errno %d creating timer",
+				__func__, errno);
+	}
+	return tmr;
+}
+
+static int set_check_timer(timer_t tmr, bool arm)
+{
+	struct itimerspec is;
+
+	if (tmr == 0)
+		return -1;
+
+	if (arm) {
+		is.it_value.tv_sec = 1;
+		is.it_interval.tv_sec = 1;
+	} else {
+		is.it_value.tv_sec = 0;
+		is.it_interval.tv_sec = 0;
+	}
+	is.it_value.tv_nsec = 0;
+	is.it_interval.tv_nsec = 0;
+
+	return timer_settime(tmr, 0, &is, NULL);
+}
+
 static void *
 checkerloop (void *ap)
 {
@@ -1855,9 +1917,10 @@ checkerloop (void *ap)
 	struct path *pp;
 	int count = 0;
 	unsigned int i;
-	struct itimerval timer_tick_it;
 	struct timespec last_time;
 	struct config *conf;
+	timer_t check_timer;
+	int old_strict_timing = 0;
 
 	pthread_cleanup_push(rcu_unregister, NULL);
 	rcu_register_thread();
@@ -1865,6 +1928,9 @@ checkerloop (void *ap)
 	vecs = (struct vectors *)ap;
 	condlog(2, "path checkers start up");
 
+	check_timer = setup_check_timer();
+	pthread_cleanup_push(cleanup_timer, (void*)check_timer);
+
 	/* Tweak start time for initial path check */
 	if (clock_gettime(CLOCK_MONOTONIC, &last_time) != 0)
 		last_time.tv_sec = 0;
@@ -1873,8 +1939,7 @@ checkerloop (void *ap)
 
 	while (1) {
 		struct timespec diff_time, start_time, end_time;
-		int num_paths = 0, ticks = 0, signo, strict_timing, rc = 0;
-		sigset_t mask;
+		int num_paths = 0, ticks = 0, strict_timing, rc = 0;
 
 		if (clock_gettime(CLOCK_MONOTONIC, &start_time) != 0)
 			start_time.tv_sec = 0;
@@ -1956,40 +2021,30 @@ checkerloop (void *ap)
 		}
 		check_foreign();
 		post_config_state(DAEMON_IDLE);
+
 		conf = get_multipath_config();
 		strict_timing = conf->strict_timing;
 		put_multipath_config(conf);
-		if (!strict_timing)
+
+		if (check_timer && (old_strict_timing != strict_timing) &&
+		    set_check_timer(check_timer, !!strict_timing) != 0) {
+			condlog(1, "%s: errno %d %sarming interrupt timer",
+				__func__, errno, strict_timing ? "" : "dis");
+			strict_timing = 0;
+		}
+		old_strict_timing = strict_timing;
+
+		if (!strict_timing || !check_timer)
 			sleep(1);
 		else {
-			timer_tick_it.it_interval.tv_sec = 0;
-			timer_tick_it.it_interval.tv_usec = 0;
-			if (diff_time.tv_nsec) {
-				timer_tick_it.it_value.tv_sec = 0;
-				timer_tick_it.it_value.tv_usec =
-				     1000UL * 1000 * 1000 - diff_time.tv_nsec;
-			} else {
-				timer_tick_it.it_value.tv_sec = 1;
-				timer_tick_it.it_value.tv_usec = 0;
-			}
-			setitimer(ITIMER_REAL, &timer_tick_it, NULL);
-
-			sigemptyset(&mask);
-			sigaddset(&mask, SIGALRM);
-			condlog(3, "waiting for %lu.%06lu secs",
-				timer_tick_it.it_value.tv_sec,
-				timer_tick_it.it_value.tv_usec);
-			if (sigwait(&mask, &signo) != 0) {
-				condlog(3, "sigwait failed with error %d",
-					errno);
-				conf = get_multipath_config();
-				conf->strict_timing = 0;
-				put_multipath_config(conf);
-				break;
-			}
+			pthread_mutex_lock(&checker_lock);
+			pthread_cleanup_push(unlock_mutex, &checker_lock);
+			pthread_cond_wait(&checker_cond, &checker_lock);
+			pthread_cleanup_pop(1);
 		}
 	}
 	pthread_cleanup_pop(1);
+	pthread_cleanup_pop(1);
 	return NULL;
 }
 
-- 
2.16.1




More information about the dm-devel mailing list