On Fri, Jan 04, 2019 at 06:59:10PM +0100, Martin Wilck wrote: > 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. Reviewed-by: Benjamin Marzinski <bmarzins@xxxxxxxxxx> > > Signed-off-by: Martin Wilck <mwilck@xxxxxxxx> > --- > 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 -- dm-devel mailing list dm-devel@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/dm-devel