[dm-devel] [RFC PATCH 2/6] multipathd: protect all access to running_state
Benjamin Marzinski
bmarzins at redhat.com
Thu Jan 17 00:06:17 UTC 2019
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 at redhat.com>
>
> 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