[dm-devel] [PATCH 55/57] multipathd: asynchronous configuration

Benjamin Marzinski bmarzins at redhat.com
Tue May 3 18:25:34 UTC 2016


On Wed, Apr 27, 2016 at 01:10:56PM +0200, Hannes Reinecke wrote:

Oh, I did have one other thought.

If you're not changing the signal mask in uevent_listen, can't you just
use poll?  Otherwise people are going to try and figure out what's going
on with the signals that they're missing (at least that's what I did).


> For initial configuration multipathd waits until it has synchronized
> with the existing setup. On larger systems this takes up quite
> some time (I've measured 80 seconds on a system with 1024 paths)
> causing systemd to stall and the system to fail booting.
> This patch makes the initial configuration asynchronous, and
> using the same codepath as the existing 'reconfigure' CLI
> command.
> 
> Signed-off-by: Hannes Reinecke <hare at suse.de>
> ---
>  libmultipath/uevent.c     |  10 +--
>  multipathd/cli_handlers.c |  14 ++-
>  multipathd/main.c         | 219 ++++++++++++++++++++++++++++++----------------
>  multipathd/main.h         |   2 +
>  4 files changed, 157 insertions(+), 88 deletions(-)
> 
> diff --git a/libmultipath/uevent.c b/libmultipath/uevent.c
> index 478c6ce..fbe9c44 100644
> --- a/libmultipath/uevent.c
> +++ b/libmultipath/uevent.c
> @@ -529,8 +529,6 @@ int uevent_listen(struct udev *udev)
>  	}
>  
>  	pthread_sigmask(SIG_SETMASK, NULL, &mask);
> -	sigdelset(&mask, SIGHUP);
> -	sigdelset(&mask, SIGUSR1);
>  	events = 0;
>  	while (1) {
>  		struct uevent *uev;
> @@ -561,9 +559,11 @@ int uevent_listen(struct udev *udev)
>  			continue;
>  		}
>  		if (fdcount < 0) {
> -			if (errno != EINTR)
> -				condlog(0, "error receiving "
> -					"uevent message: %m");
> +			if (errno == EINTR)
> +				continue;
> +
> +			condlog(0, "error receiving "
> +				"uevent message: %m");
>  			err = -errno;
>  			break;
>  		}
> diff --git a/multipathd/cli_handlers.c b/multipathd/cli_handlers.c
> index dbdcbc2..0397353 100644
> --- a/multipathd/cli_handlers.c
> +++ b/multipathd/cli_handlers.c
> @@ -909,17 +909,13 @@ cli_switch_group(void * v, char ** reply, int * len, void * data)
>  int
>  cli_reconfigure(void * v, char ** reply, int * len, void * data)
>  {
> -	struct vectors * vecs = (struct vectors *)data;
> -
> -	if (need_to_delay_reconfig(vecs)) {
> -		conf->delayed_reconfig = 1;
> -		condlog(2, "delaying reconfigure (operator)");
> -		return 0;
> -	}
> -
>  	condlog(2, "reconfigure (operator)");
>  
> -	return reconfigure(vecs);
> +	if (set_config_state(DAEMON_CONFIGURE) == ETIMEDOUT) {
> +		condlog(2, "timeout starting reconfiguration");
> +		return 1;
> +	}
> +	return 0;
>  }
>  
>  int
> diff --git a/multipathd/main.c b/multipathd/main.c
> index 77eb498..41b5a49 100644
> --- a/multipathd/main.c
> +++ b/multipathd/main.c
> @@ -97,10 +97,11 @@ struct mpath_event_param
>  unsigned int mpath_mx_alloc_len;
>  
>  int logsink;
> -enum daemon_status running_state;
> +enum daemon_status running_state = DAEMON_INIT;
>  pid_t daemon_pid;
> +pthread_mutex_t config_lock = PTHREAD_MUTEX_INITIALIZER;
> +pthread_cond_t config_cond = PTHREAD_COND_INITIALIZER;
>  
> -static sem_t exit_sem;
>  /*
>   * global copy of vecs for use in sig handlers
>   */
> @@ -108,6 +109,94 @@ struct vectors * gvecs;
>  
>  struct udev * udev;
>  
> +const char *
> +daemon_status(void)
> +{
> +	switch (running_state) {
> +	case DAEMON_INIT:
> +		return "init";
> +	case DAEMON_START:
> +		return "startup";
> +	case DAEMON_CONFIGURE:
> +		return "configure";
> +	case DAEMON_IDLE:
> +		return "idle";
> +	case DAEMON_RUNNING:
> +		return "running";
> +	case DAEMON_SHUTDOWN:
> +		return "shutdown";
> +	}
> +	return NULL;
> +}
> +
> +/*
> + * I love you too, systemd ...
> + */
> +const char *
> +sd_notify_status(void)
> +{
> +	switch (running_state) {
> +	case DAEMON_INIT:
> +		return "STATUS=init";
> +	case DAEMON_START:
> +		return "STATUS=startup";
> +	case DAEMON_CONFIGURE:
> +		return "STATUS=configure";
> +	case DAEMON_IDLE:
> +		return "STATUS=idle";
> +	case DAEMON_RUNNING:
> +		return "STATUS=running";
> +	case DAEMON_SHUTDOWN:
> +		return "STATUS=shutdown";
> +	}
> +	return NULL;
> +}
> +
> +static void config_cleanup(void *arg)
> +{
> +	pthread_mutex_unlock(&config_lock);
> +}
> +
> +void post_config_state(enum daemon_status state)
> +{
> +	pthread_mutex_lock(&config_lock);
> +	if (state != running_state) {
> +		running_state = state;
> +		pthread_cond_broadcast(&config_cond);
> +#ifdef USE_SYSTEMD
> +		sd_notify(0, sd_notify_status());
> +#endif
> +	}
> +	pthread_mutex_unlock(&config_lock);
> +}
> +
> +int set_config_state(enum daemon_status state)
> +{
> +	int rc = 0;
> +
> +	pthread_cleanup_push(config_cleanup, NULL);
> +	pthread_mutex_lock(&config_lock);
> +	if (running_state != state) {
> +		if (running_state != DAEMON_IDLE) {
> +			struct timespec ts;
> +
> +			clock_gettime(CLOCK_REALTIME, &ts);
> +			ts.tv_sec += 1;
> +			rc = pthread_cond_timedwait(&config_cond,
> +						    &config_lock, &ts);
> +		}
> +		if (!rc) {
> +			running_state = state;
> +			pthread_cond_broadcast(&config_cond);
> +#ifdef USE_SYSTEMD
> +			sd_notify(0, sd_notify_status());
> +#endif
> +		}
> +	}
> +	pthread_cleanup_pop(1);
> +	return rc;
> +}
> +
>  static int
>  need_switch_pathgroup (struct multipath * mpp, int refresh)
>  {
> @@ -352,7 +441,7 @@ ev_add_map (char * dev, char * alias, struct vectors * vecs)
>  	if (mpp) {
>  		if (mpp->wait_for_udev > 1) {
>  			if (update_map(mpp, vecs))
> -			/* setup multipathd removed the map */
> +				/* setup multipathd removed the map */
>  				return 1;
>  		}
>  		if (mpp->wait_for_udev) {
> @@ -360,7 +449,7 @@ ev_add_map (char * dev, char * alias, struct vectors * vecs)
>  			if (conf->delayed_reconfig &&
>  			    !need_to_delay_reconfig(vecs)) {
>  				condlog(2, "reconfigure (delayed)");
> -				reconfigure(vecs);
> +				set_config_state(DAEMON_CONFIGURE);
>  				return 0;
>  			}
>  		}
> @@ -903,6 +992,16 @@ uev_trigger (struct uevent * uev, void * trigger_data)
>  	if (uev_discard(uev->devpath))
>  		return 0;
>  
> +	pthread_cleanup_push(config_cleanup, NULL);
> +	pthread_mutex_lock(&config_lock);
> +	if (running_state != DAEMON_IDLE &&
> +	    running_state != DAEMON_RUNNING)
> +		pthread_cond_wait(&config_cond, &config_lock);
> +	pthread_cleanup_pop(1);
> +
> +	if (running_state == DAEMON_SHUTDOWN)
> +		return 0;
> +
>  	pthread_cleanup_push(cleanup_lock, &vecs->lock);
>  	lock(vecs->lock);
>  	pthread_testcancel();
> @@ -1031,25 +1130,7 @@ uxlsnrloop (void * ap)
>  void
>  exit_daemon (void)
>  {
> -	sem_post(&exit_sem);
> -}
> -
> -const char *
> -daemon_status(void)
> -{
> -	switch (running_state) {
> -	case DAEMON_INIT:
> -		return "init";
> -	case DAEMON_START:
> -		return "startup";
> -	case DAEMON_CONFIGURE:
> -		return "configure";
> -	case DAEMON_RUNNING:
> -		return "running";
> -	case DAEMON_SHUTDOWN:
> -		return "shutdown";
> -	}
> -	return NULL;
> +	post_config_state(DAEMON_SHUTDOWN);
>  }
>  
>  static void
> @@ -1178,7 +1259,7 @@ missing_uev_wait_tick(struct vectors *vecs)
>  	if (timed_out && conf->delayed_reconfig &&
>  	    !need_to_delay_reconfig(vecs)) {
>  		condlog(2, "reconfigure (delayed)");
> -		reconfigure(vecs);
> +		set_config_state(DAEMON_CONFIGURE);
>  	}
>  }
>  
> @@ -1541,11 +1622,18 @@ checkerloop (void *ap)
>  
>  	while (1) {
>  		struct timeval diff_time, start_time, end_time;
> -		int num_paths = 0, ticks = 0, signo, strict_timing;
> +		int num_paths = 0, ticks = 0, signo, strict_timing, rc = 0;
>  		sigset_t mask;
>  
>  		if (gettimeofday(&start_time, NULL) != 0)
>  			start_time.tv_sec = 0;
> +
> +		rc = set_config_state(DAEMON_RUNNING);
> +		if (rc == ETIMEDOUT) {
> +			condlog(4, "timeout waiting for DAEMON_IDLE");
> +			continue;
> +		}
> +
>  		pthread_cleanup_push(cleanup_lock, &vecs->lock);
>  		lock(vecs->lock);
>  		pthread_testcancel();
> @@ -1600,6 +1688,7 @@ checkerloop (void *ap)
>  			}
>  		}
>  
> +		post_config_state(DAEMON_IDLE);
>  		if (!strict_timing)
>  			sleep(1);
>  		else {
> @@ -1734,8 +1823,6 @@ reconfigure (struct vectors * vecs)
>  	struct config * old = conf;
>  	int retval = 1;
>  
> -	running_state = DAEMON_CONFIGURE;
> -
>  	/*
>  	 * free old map and path vectors ... they use old conf state
>  	 */
> @@ -1765,8 +1852,6 @@ reconfigure (struct vectors * vecs)
>  	}
>  	uxsock_timeout = conf->uxsock_timeout;
>  
> -	running_state = DAEMON_RUNNING;
> -
>  	return retval;
>  }
>  
> @@ -1819,20 +1904,9 @@ signal_set(int signo, void (*func) (int))
>  void
>  handle_signals(void)
>  {
> -	if (reconfig_sig && running_state == DAEMON_RUNNING) {
> -		pthread_cleanup_push(cleanup_lock,
> -				&gvecs->lock);
> -		lock(gvecs->lock);
> -		pthread_testcancel();
> -		if (need_to_delay_reconfig(gvecs)) {
> -			conf->delayed_reconfig = 1;
> -			condlog(2, "delaying reconfigure (signal)");
> -		}
> -		else {
> -			condlog(2, "reconfigure (signal)");
> -			reconfigure(gvecs);
> -		}
> -		lock_cleanup_pop(gvecs->lock);
> +	if (reconfig_sig) {
> +		condlog(2, "reconfigure (signal)");
> +		set_config_state(DAEMON_CONFIGURE);
>  	}
>  	if (log_reset_sig) {
>  		condlog(2, "reset log (signal)");
> @@ -1966,7 +2040,6 @@ child (void * param)
>  	char *envp;
>  
>  	mlockall(MCL_CURRENT | MCL_FUTURE);
> -	sem_init(&exit_sem, 0, 0);
>  	signal_init();
>  
>  	udev = udev_new();
> @@ -1987,11 +2060,8 @@ child (void * param)
>  		exit(1);
>  	}
>  
> -	running_state = DAEMON_START;
> +	post_config_state(DAEMON_START);
>  
> -#ifdef USE_SYSTEMD
> -	sd_notify(0, "STATUS=startup");
> -#endif
>  	condlog(2, "--------start up--------");
>  	condlog(2, "read " DEFAULT_CONFIGFILE);
>  
> @@ -2067,6 +2137,11 @@ child (void * param)
>  	}
>  #endif
>  	/*
> +	 * Signal start of configuration
> +	 */
> +	post_config_state(DAEMON_CONFIGURE);
> +
> +	/*
>  	 * Start uevent listener early to catch events
>  	 */
>  	if ((rc = pthread_create(&uevent_thr, &uevent_attr, ueventloop, udev))) {
> @@ -2078,21 +2153,6 @@ child (void * param)
>  		condlog(0, "failed to create cli listener: %d", rc);
>  		goto failed;
>  	}
> -	/*
> -	 * fetch and configure both paths and multipaths
> -	 */
> -#ifdef USE_SYSTEMD
> -	sd_notify(0, "STATUS=configure");
> -#endif
> -	running_state = DAEMON_CONFIGURE;
> -
> -	lock(vecs->lock);
> -	if (configure(vecs, 1)) {
> -		unlock(vecs->lock);
> -		condlog(0, "failure during configuration");
> -		goto failed;
> -	}
> -	unlock(vecs->lock);
>  
>  	/*
>  	 * start threads
> @@ -2107,20 +2167,32 @@ child (void * param)
>  	}
>  	pthread_attr_destroy(&misc_attr);
>  
> -	running_state = DAEMON_RUNNING;
>  #ifdef USE_SYSTEMD
> -	sd_notify(0, "READY=1\nSTATUS=running");
> +	sd_notify(0, "READY=1");
>  #endif
>  
> -	/*
> -	 * exit path
> -	 */
> -	while(sem_wait(&exit_sem) != 0); /* Do nothing */
> +	while (running_state != DAEMON_SHUTDOWN) {
> +		pthread_cleanup_push(config_cleanup, NULL);
> +		pthread_mutex_lock(&config_lock);
> +		if (running_state != DAEMON_CONFIGURE &&
> +		    running_state != DAEMON_SHUTDOWN) {
> +			pthread_cond_wait(&config_cond, &config_lock);
> +		}
> +		pthread_cleanup_pop(1);
> +		if (running_state == DAEMON_CONFIGURE) {
> +			pthread_cleanup_push(cleanup_lock, &vecs->lock);
> +			lock(vecs->lock);
> +			pthread_testcancel();
> +			if (!need_to_delay_reconfig(vecs)) {
> +				reconfigure(vecs);
> +			} else {
> +				conf->delayed_reconfig = 1;
> +			}
> +			lock_cleanup_pop(vecs->lock);
> +			post_config_state(DAEMON_IDLE);
> +		}
> +	}
>  
> -#ifdef USE_SYSTEMD
> -	sd_notify(0, "STATUS=shutdown");
> -#endif
> -	running_state = DAEMON_SHUTDOWN;
>  	lock(vecs->lock);
>  	if (conf->queue_without_daemon == QUE_NO_DAEMON_OFF)
>  		vector_foreach_slot(vecs->mpvec, mpp, i)
> @@ -2253,7 +2325,6 @@ main (int argc, char *argv[])
>  	int foreground = 0;
>  
>  	logsink = 1;
> -	running_state = DAEMON_INIT;
>  	dm_init();
>  
>  	if (getuid() != 0) {
> diff --git a/multipathd/main.h b/multipathd/main.h
> index d1a6d71..10b3a6d 100644
> --- a/multipathd/main.h
> +++ b/multipathd/main.h
> @@ -7,6 +7,7 @@ enum daemon_status {
>      DAEMON_INIT,
>      DAEMON_START,
>      DAEMON_CONFIGURE,
> +    DAEMON_IDLE,
>      DAEMON_RUNNING,
>      DAEMON_SHUTDOWN,
>  };
> @@ -26,6 +27,7 @@ int ev_remove_path (struct path *, struct vectors *);
>  int ev_add_map (char *, char *, struct vectors *);
>  int ev_remove_map (char *, char *, int, struct vectors *);
>  void sync_map_state (struct multipath *);
> +int set_config_state(enum daemon_status);
>  void * mpath_alloc_prin_response(int prin_sa);
>  int prin_do_scsi_ioctl(char *, int rq_servact, struct prin_resp * resp,
>         int noisy);
> -- 
> 2.6.6




More information about the dm-devel mailing list