[dm-devel] [RFC PATCH 2/6] multipathd: protect all access to running_state
Martin Wilck
mwilck at suse.com
Fri Jan 4 17:59:10 UTC 2019
Chonyun Wu's latest patch has shown that the handling of the daemon
state variable running_state is racy and difficult to get right. It's
not a good candidate for a "benign race" annotation. So, as a first
step to sanitizing it, make sure all accesses to the state variable
are protected by config_lock.
The patch also replaces "if" with "while" in several places where the
code was supposed to wait until a certain state was reached. It's
important that DAEMON_SHUTDOWN terminates all loops of this kind.
Signed-off-by: Martin Wilck <mwilck at suse.com>
---
multipathd/main.c | 79 ++++++++++++++++++++++++++++-------------------
1 file changed, 48 insertions(+), 31 deletions(-)
diff --git a/multipathd/main.c b/multipathd/main.c
index 6a5d105a..6fc6a3ac 100644
--- a/multipathd/main.c
+++ b/multipathd/main.c
@@ -126,11 +126,21 @@ int poll_dmevents = 0;
#else
int poll_dmevents = 1;
#endif
-enum daemon_status running_state = DAEMON_INIT;
+enum daemon_status _running_state = DAEMON_INIT;
pid_t daemon_pid;
pthread_mutex_t config_lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t config_cond;
+static inline enum daemon_status get_running_state(void)
+{
+ enum daemon_status st;
+
+ pthread_mutex_lock(&config_lock);
+ st = _running_state;
+ pthread_mutex_unlock(&config_lock);
+ return st;
+}
+
/*
* global copy of vecs for use in sig handlers
*/
@@ -148,7 +158,7 @@ static volatile sig_atomic_t log_reset_sig;
const char *
daemon_status(void)
{
- switch (running_state) {
+ switch (get_running_state()) {
case DAEMON_INIT:
return "init";
case DAEMON_START:
@@ -168,10 +178,10 @@ daemon_status(void)
/*
* I love you too, systemd ...
*/
-const char *
-sd_notify_status(void)
+static const char *
+sd_notify_status(enum daemon_status state)
{
- switch (running_state) {
+ switch (state) {
case DAEMON_INIT:
return "STATUS=init";
case DAEMON_START:
@@ -188,17 +198,18 @@ sd_notify_status(void)
}
#ifdef USE_SYSTEMD
-static void do_sd_notify(enum daemon_status old_state)
+static void do_sd_notify(enum daemon_status old_state,
+ enum daemon_status new_state)
{
/*
* Checkerloop switches back and forth between idle and running state.
* No need to tell systemd each time.
* These notifications cause a lot of overhead on dbus.
*/
- if ((running_state == DAEMON_IDLE || running_state == DAEMON_RUNNING) &&
+ if ((new_state == DAEMON_IDLE || new_state == DAEMON_RUNNING) &&
(old_state == DAEMON_IDLE || old_state == DAEMON_RUNNING))
return;
- sd_notify(0, sd_notify_status());
+ sd_notify(0, sd_notify_status(new_state));
}
#endif
@@ -207,15 +218,16 @@ static void config_cleanup(void *arg)
pthread_mutex_unlock(&config_lock);
}
+/* must be called with config_lock held */
static void __post_config_state(enum daemon_status state)
{
- if (state != running_state && running_state != DAEMON_SHUTDOWN) {
- enum daemon_status old_state = running_state;
+ if (state != _running_state && _running_state != DAEMON_SHUTDOWN) {
+ enum daemon_status old_state = _running_state;
- running_state = state;
+ _running_state = state;
pthread_cond_broadcast(&config_cond);
#ifdef USE_SYSTEMD
- do_sd_notify(old_state);
+ do_sd_notify(old_state, state);
#endif
}
}
@@ -234,12 +246,12 @@ int set_config_state(enum daemon_status state)
pthread_cleanup_push(config_cleanup, NULL);
pthread_mutex_lock(&config_lock);
- if (running_state != state) {
- enum daemon_status old_state = running_state;
+ if (_running_state != state) {
+ enum daemon_status old_state = _running_state;
- if (running_state == DAEMON_SHUTDOWN)
+ if (_running_state == DAEMON_SHUTDOWN)
rc = EINVAL;
- else if (running_state != DAEMON_IDLE) {
+ else if (_running_state != DAEMON_IDLE) {
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
@@ -247,11 +259,11 @@ int set_config_state(enum daemon_status state)
rc = pthread_cond_timedwait(&config_cond,
&config_lock, &ts);
}
- if (!rc && (running_state != DAEMON_SHUTDOWN)) {
- running_state = state;
+ if (!rc && (_running_state != DAEMON_SHUTDOWN)) {
+ _running_state = state;
pthread_cond_broadcast(&config_cond);
#ifdef USE_SYSTEMD
- do_sd_notify(old_state);
+ do_sd_notify(old_state, state);
#endif
}
}
@@ -1405,17 +1417,20 @@ uev_trigger (struct uevent * uev, void * trigger_data)
int r = 0;
struct vectors * vecs;
struct uevent *merge_uev, *tmp;
+ enum daemon_status state;
vecs = (struct vectors *)trigger_data;
pthread_cleanup_push(config_cleanup, NULL);
pthread_mutex_lock(&config_lock);
- if (running_state != DAEMON_IDLE &&
- running_state != DAEMON_RUNNING)
+ while (_running_state != DAEMON_IDLE &&
+ _running_state != DAEMON_RUNNING &&
+ _running_state != DAEMON_SHUTDOWN)
pthread_cond_wait(&config_cond, &config_lock);
+ state = _running_state;
pthread_cleanup_pop(1);
- if (running_state == DAEMON_SHUTDOWN)
+ if (state == DAEMON_SHUTDOWN)
return 0;
/*
@@ -2661,6 +2676,7 @@ child (void * param)
struct config *conf;
char *envp;
int queue_without_daemon;
+ enum daemon_status state;
mlockall(MCL_CURRENT | MCL_FUTURE);
signal_init();
@@ -2756,8 +2772,9 @@ child (void * param)
rc = pthread_create(&uxlsnr_thr, &misc_attr, uxlsnrloop, vecs);
if (!rc) {
/* Wait for uxlsnr startup */
- while (running_state == DAEMON_IDLE)
+ while (_running_state == DAEMON_IDLE)
pthread_cond_wait(&config_cond, &config_lock);
+ state = _running_state;
}
pthread_cleanup_pop(1);
@@ -2765,7 +2782,7 @@ child (void * param)
condlog(0, "failed to create cli listener: %d", rc);
goto failed;
}
- else if (running_state != DAEMON_CONFIGURE) {
+ else if (state != DAEMON_CONFIGURE) {
condlog(0, "cli listener failed to start");
goto failed;
}
@@ -2805,15 +2822,17 @@ child (void * param)
}
pthread_attr_destroy(&misc_attr);
- while (running_state != DAEMON_SHUTDOWN) {
+ while (1) {
pthread_cleanup_push(config_cleanup, NULL);
pthread_mutex_lock(&config_lock);
- if (running_state != DAEMON_CONFIGURE &&
- running_state != DAEMON_SHUTDOWN) {
+ while (_running_state != DAEMON_CONFIGURE &&
+ _running_state != DAEMON_SHUTDOWN)
pthread_cond_wait(&config_cond, &config_lock);
- }
+ state = _running_state;
pthread_cleanup_pop(1);
- if (running_state == DAEMON_CONFIGURE) {
+ if (state == DAEMON_SHUTDOWN)
+ break;
+ if (state == DAEMON_CONFIGURE) {
pthread_cleanup_push(cleanup_lock, &vecs->lock);
lock(&vecs->lock);
pthread_testcancel();
@@ -2983,8 +3002,6 @@ main (int argc, char *argv[])
ANNOTATE_BENIGN_RACE_SIZED(&multipath_conf, sizeof(multipath_conf),
"Manipulated through RCU");
- ANNOTATE_BENIGN_RACE_SIZED(&running_state, sizeof(running_state),
- "Suppress complaints about unprotected running_state reads");
ANNOTATE_BENIGN_RACE_SIZED(&uxsock_timeout, sizeof(uxsock_timeout),
"Suppress complaints about this scalar variable");
--
2.19.2
More information about the dm-devel
mailing list