[multipathd] signal deadlock
authorBenjamin Marzinski <bmarzins@redhat.com>
Thu, 2 Apr 2009 20:36:41 +0000 (22:36 +0200)
committerChristophe Varoqui <christophe.varoqui@free.fr>
Thu, 2 Apr 2009 20:36:41 +0000 (22:36 +0200)
If multipathd is run with -v3, both the SIGHUP, and the SIGUSR1 signal handlers
will log a message.  If a multipathd thread receives one of these signals while
it has a log lock held, it deadlocks itself. Also, the SIGHUP handler will grab
the vecs lock, so if any thread receives a SIGHUP while holding the vecs lock,
it deadlocks itself.  This commit blocks the appropriate signals to guard
against this.

libmultipath/lock.c
libmultipath/lock.h
libmultipath/log_pthread.c
libmultipath/waiter.c
multipathd/main.c

index 54e2988..4439a51 100644 (file)
@@ -1,6 +1,16 @@
 #include <pthread.h>
+#include <signal.h>
 #include "lock.h"
 #include <stdio.h>
+
+void block_signal (int signum, sigset_t *old)
+{
+       sigset_t set;
+       sigemptyset(&set);
+       sigaddset(&set, signum);
+       pthread_sigmask(SIG_BLOCK, &set, old);
+}
+
 void cleanup_lock (void * data)
 {
        unlock ((*(struct mutex_lock *)data));
index 1f9a0f3..6897a74 100644 (file)
@@ -1,6 +1,8 @@
 #ifndef _LOCK_H
 #define _LOCK_H
 
+#include <signal.h>
+
 /*
  * Wrapper for the mutex. Includes a ref-count to keep
  * track of how many there are out-standing threads blocking
@@ -27,5 +29,6 @@ struct mutex_lock {
 #endif
 
 void cleanup_lock (void * data);
+void block_signal(int signum, sigset_t *old);
 
 #endif /* _LOCK_H */
index a1d4a10..6997cff 100644 (file)
 
 #include "log_pthread.h"
 #include "log.h"
+#include "lock.h"
 
 void log_safe (int prio, const char * fmt, va_list ap)
 {
+       sigset_t old;
+
+       block_signal(SIGUSR1, &old);
+       block_signal(SIGHUP, NULL);
+
        pthread_mutex_lock(logq_lock);
        log_enqueue(prio, fmt, ap);
        pthread_mutex_unlock(logq_lock);
@@ -21,6 +27,8 @@ void log_safe (int prio, const char * fmt, va_list ap)
        pthread_mutex_lock(logev_lock);
        pthread_cond_signal(logev_cond);
        pthread_mutex_unlock(logev_lock);
+
+       pthread_sigmask(SIG_SETMASK, &old, NULL);
 }
 
 static void flush_logqueue (void)
index 28b810f..99bfdbf 100644 (file)
@@ -32,11 +32,13 @@ struct event_thread *alloc_waiter (void)
 
 void free_waiter (void *data)
 {
+       sigset_t old;
        struct event_thread *wp = (struct event_thread *)data;
 
        /*
         * indicate in mpp that the wp is already freed storage
         */
+       block_signal(SIGHUP, &old);
        lock(wp->vecs->lock);
 
        if (wp->mpp)
@@ -51,6 +53,7 @@ void free_waiter (void *data)
                condlog(3, "free_waiter, mpp freed before wp=%p (%s).", wp, wp->mapname);
 
        unlock(wp->vecs->lock);
+       pthread_sigmask(SIG_SETMASK, &old, NULL);
 
        if (wp->dmt)
                dm_task_destroy(wp->dmt);
@@ -185,6 +188,8 @@ void *waitevent (void *et)
        waiter = (struct event_thread *)et;
        pthread_cleanup_push(free_waiter, et);
 
+       block_signal(SIGUSR1, NULL);
+       block_signal(SIGHUP, NULL);
        while (1) {
                r = waiteventloop(waiter);
 
index 9957f1f..a4ee10c 100644 (file)
@@ -688,6 +688,9 @@ out:
 static void *
 ueventloop (void * ap)
 {
+       block_signal(SIGUSR1, NULL);
+       block_signal(SIGHUP, NULL);
+
        if (uevent_listen(&uev_trigger, ap))
                fprintf(stderr, "error starting uevent listener");
 
@@ -697,6 +700,9 @@ ueventloop (void * ap)
 static void *
 uxlsnrloop (void * ap)
 {
+       block_signal(SIGUSR1, NULL);
+       block_signal(SIGHUP, NULL);
+
        if (cli_init())
                return NULL;
 
@@ -1007,6 +1013,7 @@ checkerloop (void *ap)
        struct path *pp;
        int count = 0;
        unsigned int i;
+       sigset_t old;
 
        mlockall(MCL_CURRENT | MCL_FUTURE);
        vecs = (struct vectors *)ap;
@@ -1020,6 +1027,7 @@ checkerloop (void *ap)
        }
 
        while (1) {
+               block_signal(SIGHUP, &old);
                pthread_cleanup_push(cleanup_lock, &vecs->lock);
                lock(vecs->lock);
                condlog(4, "tick");
@@ -1042,6 +1050,7 @@ checkerloop (void *ap)
                }
 
                lock_cleanup_pop(vecs->lock);
+               pthread_sigmask(SIG_SETMASK, &old, NULL);
                sleep(1);
        }
        return NULL;
@@ -1358,6 +1367,7 @@ child (void * param)
        /*
         * exit path
         */
+       block_signal(SIGHUP, NULL);
        lock(vecs->lock);
        remove_maps_and_stop_waiters(vecs);
        free_pathvec(vecs->pathvec, FREE_PATHS);