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@xxxxxxxx> --- 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 -- dm-devel mailing list dm-devel@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/dm-devel