[dm-devel] [PATCH v2 3/5] libmultipath: drop mpp->nr_active field

Benjamin Marzinski bmarzins at redhat.com
Thu Dec 5 20:22:24 UTC 2019


On Wed, Nov 27, 2019 at 03:05:50PM +0000, Martin Wilck wrote:
> From: Martin Wilck <mwilck at suse.com>
> 
> The tracking of nr_active has turned out to be error prone and hard
> to verify. Calculating it on the fly is a quick operation, so
> do this rather than trying to track nr_active. Use a boolean
> field instead to track whether a map is currently in recovery mode.
> 
> Moreover, clean up the recovery logic by making set_no_path_retry()
> the place that checks the current configuration and state, sets
> "queue_if_no_path" if necessary, and enters or leaves recovery
> mode if necessary. This ensures that consistent logic is applied.
> 
> In the client handlers, we can't be sure that mpp->features is
> up-to-date. Also, users that change the queuing mode expect that
> the requested action is performed, regardless of state. Therefore,
> transform set_no_path_retry() into __set_no_path_retry(), which takes
> an additional parameter indicating whether the current state should
> be checked, and set this parameter to false when calling the function
> from the client handler code, true otherwise. Moreover, in the very
> unlikely case mpp->features is NULL, don't assume that queuing is off,
> just make no assumption about the current state.
> 

Thanks.

Reviewed-by: Benjamin Marzinski <bmarzins at redhat.com>
> Signed-off-by: Martin Wilck <mwilck at suse.com>
> ---
>  libmultipath/configure.c   |  5 +--
>  libmultipath/devmapper.c   |  2 +-
>  libmultipath/io_err_stat.c |  4 +-
>  libmultipath/print.c       |  5 ++-
>  libmultipath/structs.c     | 19 +++++++++
>  libmultipath/structs.h     |  4 +-
>  libmultipath/structs_vec.c | 81 ++++++++++++++++++++++++++------------
>  libmultipath/structs_vec.h |  4 +-
>  multipathd/cli_handlers.c  | 41 ++++++++-----------
>  multipathd/main.c          | 19 +++------
>  10 files changed, 110 insertions(+), 74 deletions(-)
> 
> diff --git a/libmultipath/configure.c b/libmultipath/configure.c
> index 5ac7d903..c95848a0 100644
> --- a/libmultipath/configure.c
> +++ b/libmultipath/configure.c
> @@ -401,7 +401,6 @@ int setup_map(struct multipath *mpp, char *params, int params_size,
>  			condlog(2, "%s: setting up map with %d/%d path checkers pending",
>  				mpp->alias, n_pending, n_paths);
>  	}
> -	mpp->nr_active = pathcount(mpp, PATH_UP) + pathcount(mpp, PATH_GHOST);
>  
>  	/*
>  	 * ponders each path group and determine highest prio pg
> @@ -934,8 +933,8 @@ int domap(struct multipath *mpp, char *params, int is_daemon)
>  		}
>  
>  		sysfs_set_max_sectors_kb(mpp, 0);
> -		if (is_daemon && mpp->ghost_delay > 0 && mpp->nr_active &&
> -		    pathcount(mpp, PATH_GHOST) == mpp->nr_active)
> +		if (is_daemon && mpp->ghost_delay > 0 && count_active_paths(mpp) &&
> +		    pathcount(mpp, PATH_UP) == 0)
>  			mpp->ghost_delay_tick = mpp->ghost_delay;
>  		r = dm_addmap_create(mpp, params);
>  
> diff --git a/libmultipath/devmapper.c b/libmultipath/devmapper.c
> index acf576aa..bed8ddc6 100644
> --- a/libmultipath/devmapper.c
> +++ b/libmultipath/devmapper.c
> @@ -403,7 +403,7 @@ static uint16_t build_udev_flags(const struct multipath *mpp, int reload)
>  	/* DM_UDEV_DISABLE_LIBRARY_FALLBACK is added in dm_addmap */
>  	return	(mpp->skip_kpartx == SKIP_KPARTX_ON ?
>  		 MPATH_UDEV_NO_KPARTX_FLAG : 0) |
> -		((mpp->nr_active == 0 || mpp->ghost_delay_tick > 0)?
> +		((count_active_paths(mpp) == 0 || mpp->ghost_delay_tick > 0) ?
>  		 MPATH_UDEV_NO_PATHS_FLAG : 0) |
>  		(reload && !mpp->force_udev_reload ?
>  		 MPATH_UDEV_RELOAD_FLAG : 0);
> diff --git a/libmultipath/io_err_stat.c b/libmultipath/io_err_stat.c
> index dcc8690d..1b9cd6c0 100644
> --- a/libmultipath/io_err_stat.c
> +++ b/libmultipath/io_err_stat.c
> @@ -383,7 +383,7 @@ int need_io_err_check(struct path *pp)
>  
>  	if (uatomic_read(&io_err_thread_running) == 0)
>  		return 0;
> -	if (pp->mpp->nr_active <= 0) {
> +	if (count_active_paths(pp->mpp) <= 0) {
>  		io_err_stat_log(2, "%s: recover path early", pp->dev);
>  		goto recover;
>  	}
> @@ -481,7 +481,7 @@ static int poll_io_err_stat(struct vectors *vecs, struct io_err_stat_path *pp)
>  		 */
>  		path->tick = 1;
>  
> -	} else if (path->mpp && path->mpp->nr_active > 0) {
> +	} else if (path->mpp && count_active_paths(path->mpp) > 0) {
>  		io_err_stat_log(3, "%s: keep failing the dm path %s",
>  				path->mpp->alias, path->dev);
>  		path->io_err_pathfail_cnt = PATH_IO_ERR_WAITING_TO_CHECK;
> diff --git a/libmultipath/print.c b/libmultipath/print.c
> index b98e9bda..e1ef4d3f 100644
> --- a/libmultipath/print.c
> +++ b/libmultipath/print.c
> @@ -181,9 +181,10 @@ snprint_queueing (char * buff, size_t len, const struct multipath * mpp)
>  		return snprintf(buff, len, "-");
>  	else if (mpp->no_path_retry > 0) {
>  		if (mpp->retry_tick > 0)
> +
>  			return snprintf(buff, len, "%i sec",
>  					mpp->retry_tick);
> -		else if (mpp->retry_tick == 0 && mpp->nr_active > 0)
> +		else if (mpp->retry_tick == 0 && count_active_paths(mpp) > 0)
>  			return snprintf(buff, len, "%i chk",
>  					mpp->no_path_retry);
>  		else
> @@ -195,7 +196,7 @@ snprint_queueing (char * buff, size_t len, const struct multipath * mpp)
>  static int
>  snprint_nb_paths (char * buff, size_t len, const struct multipath * mpp)
>  {
> -	return snprint_int(buff, len, mpp->nr_active);
> +	return snprint_int(buff, len, count_active_paths(mpp));
>  }
>  
>  static int
> diff --git a/libmultipath/structs.c b/libmultipath/structs.c
> index f420ee5c..cc931e4e 100644
> --- a/libmultipath/structs.c
> +++ b/libmultipath/structs.c
> @@ -478,6 +478,25 @@ int pathcount(const struct multipath *mpp, int state)
>  	return count;
>  }
>  
> +int count_active_paths(const struct multipath *mpp)
> +{
> +	struct pathgroup *pgp;
> +	struct path *pp;
> +	int count = 0;
> +	int i, j;
> +
> +	if (!mpp->pg)
> +		return 0;
> +
> +	vector_foreach_slot (mpp->pg, pgp, i) {
> +		vector_foreach_slot (pgp->paths, pp, j) {
> +			if (pp->state == PATH_UP || pp->state == PATH_GHOST)
> +				count++;
> +		}
> +	}
> +	return count;
> +}
> +
>  int pathcmp(const struct pathgroup *pgp, const struct pathgroup *cpgp)
>  {
>  	int i, j;
> diff --git a/libmultipath/structs.h b/libmultipath/structs.h
> index 3665b89a..da4b6ca0 100644
> --- a/libmultipath/structs.h
> +++ b/libmultipath/structs.h
> @@ -3,6 +3,7 @@
>  
>  #include <sys/types.h>
>  #include <inttypes.h>
> +#include <stdbool.h>
>  
>  #include "prio.h"
>  #include "byteorder.h"
> @@ -309,7 +310,6 @@ struct multipath {
>  	int pgfailback;
>  	int failback_tick;
>  	int rr_weight;
> -	int nr_active;     /* current available(= not known as failed) paths */
>  	int no_path_retry; /* number of retries after all paths are down */
>  	int retry_tick;    /* remaining times for retries */
>  	int disable_queueing;
> @@ -319,6 +319,7 @@ struct multipath {
>  	int fast_io_fail;
>  	int retain_hwhandler;
>  	int deferred_remove;
> +	bool in_recovery;
>  	int san_path_err_threshold;
>  	int san_path_err_forget_rate;
>  	int san_path_err_recovery_time;
> @@ -449,6 +450,7 @@ struct path * first_path (const struct multipath *mpp);
>  
>  int pathcountgr (const struct pathgroup *, int);
>  int pathcount (const struct multipath *, int);
> +int count_active_paths(const struct multipath *);
>  int pathcmp (const struct pathgroup *, const struct pathgroup *);
>  int add_feature (char **, const char *);
>  int remove_feature (char **, const char *);
> diff --git a/libmultipath/structs_vec.c b/libmultipath/structs_vec.c
> index fbe97662..3dbbaa0f 100644
> --- a/libmultipath/structs_vec.c
> +++ b/libmultipath/structs_vec.c
> @@ -290,10 +290,15 @@ update_multipath_strings(struct multipath *mpp, vector pathvec, int is_daemon)
>  	return 0;
>  }
>  
> -void enter_recovery_mode(struct multipath *mpp)
> +static void enter_recovery_mode(struct multipath *mpp)
>  {
>  	unsigned int checkint;
> -	struct config *conf = get_multipath_config();
> +	struct config *conf;
> +
> +	if (mpp->in_recovery || mpp->no_path_retry <= 0)
> +		return;
> +
> +	conf = get_multipath_config();
>  	checkint = conf->checkint;
>  	put_multipath_config(conf);
>  
> @@ -302,37 +307,63 @@ void enter_recovery_mode(struct multipath *mpp)
>  	 * meaning of +1: retry_tick may be decremented in checkerloop before
>  	 * starting retry.
>  	 */
> +	mpp->in_recovery = true;
>  	mpp->stat_queueing_timeouts++;
>  	mpp->retry_tick = mpp->no_path_retry * checkint + 1;
>  	condlog(1, "%s: Entering recovery mode: max_retries=%d",
>  		mpp->alias, mpp->no_path_retry);
>  }
>  
> -void set_no_path_retry(struct multipath *mpp)
> +static void leave_recovery_mode(struct multipath *mpp)
> +{
> +	bool recovery = mpp->in_recovery;
> +
> +	mpp->in_recovery = false;
> +	mpp->retry_tick = 0;
> +
> +	/*
> +	 * in_recovery is only ever set if mpp->no_path_retry > 0
> +	 * (see enter_recovery_mode()). But no_path_retry may have been
> +	 * changed while the map was recovering, so test it here again.
> +	 */
> +	if (recovery && (mpp->no_path_retry == NO_PATH_RETRY_QUEUE ||
> +			 mpp->no_path_retry > 0)) {
> +		dm_queue_if_no_path(mpp->alias, 1);
> +		condlog(2, "%s: queue_if_no_path enabled", mpp->alias);
> +		condlog(1, "%s: Recovered to normal mode", mpp->alias);
> +	}
> +}
> +
> +void __set_no_path_retry(struct multipath *mpp, bool check_features)
>  {
> -	char is_queueing = 0;
> +	bool is_queueing;
>  
> -	mpp->nr_active = pathcount(mpp, PATH_UP) + pathcount(mpp, PATH_GHOST);
> -	if (mpp->features && strstr(mpp->features, "queue_if_no_path"))
> -		is_queueing = 1;
> +	check_features = check_features && mpp->features != NULL;
> +	if (check_features)
> +		is_queueing = strstr(mpp->features, "queue_if_no_path");
>  
>  	switch (mpp->no_path_retry) {
>  	case NO_PATH_RETRY_UNDEF:
>  		break;
>  	case NO_PATH_RETRY_FAIL:
> -		if (is_queueing)
> +		if (!check_features || is_queueing)
>  			dm_queue_if_no_path(mpp->alias, 0);
>  		break;
>  	case NO_PATH_RETRY_QUEUE:
> -		if (!is_queueing)
> +		if (!check_features || !is_queueing)
>  			dm_queue_if_no_path(mpp->alias, 1);
>  		break;
>  	default:
> -		if (mpp->nr_active > 0) {
> -			mpp->retry_tick = 0;
> -			if (!is_queueing)
> +		if (count_active_paths(mpp) > 0) {
> +			/*
> +			 * If in_recovery is set, leave_recovery_mode() takes
> +			 * care of dm_queue_if_no_path. Otherwise, do it here.
> +			 */
> +			if ((!check_features || !is_queueing) &&
> +			    !mpp->in_recovery)
>  				dm_queue_if_no_path(mpp->alias, 1);
> -		} else if (is_queueing && mpp->retry_tick == 0)
> +			leave_recovery_mode(mpp);
> +		} else
>  			enter_recovery_mode(mpp);
>  		break;
>  	}
> @@ -480,25 +511,23 @@ int verify_paths(struct multipath *mpp, struct vectors *vecs)
>   */
>  void update_queue_mode_del_path(struct multipath *mpp)
>  {
> -	if (--mpp->nr_active == 0) {
> -		if (mpp->no_path_retry > 0)
> -			enter_recovery_mode(mpp);
> -		else if (mpp->no_path_retry != NO_PATH_RETRY_QUEUE)
> +	int active = count_active_paths(mpp);
> +
> +	if (active == 0) {
> +		enter_recovery_mode(mpp);
> +		if (mpp->no_path_retry != NO_PATH_RETRY_QUEUE)
>  			mpp->stat_map_failures++;
>  	}
> -	condlog(2, "%s: remaining active paths: %d", mpp->alias, mpp->nr_active);
> +	condlog(2, "%s: remaining active paths: %d", mpp->alias, active);
>  }
>  
>  void update_queue_mode_add_path(struct multipath *mpp)
>  {
> -	if (mpp->nr_active++ == 0 && mpp->no_path_retry > 0) {
> -		/* come back to normal mode from retry mode */
> -		mpp->retry_tick = 0;
> -		dm_queue_if_no_path(mpp->alias, 1);
> -		condlog(2, "%s: queue_if_no_path enabled", mpp->alias);
> -		condlog(1, "%s: Recovered to normal mode", mpp->alias);
> -	}
> -	condlog(2, "%s: remaining active paths: %d", mpp->alias, mpp->nr_active);
> +	int active = count_active_paths(mpp);
> +
> +	if (active > 0)
> +		leave_recovery_mode(mpp);
> +	condlog(2, "%s: remaining active paths: %d", mpp->alias, active);
>  }
>  
>  vector get_used_hwes(const struct _vector *pathvec)
> diff --git a/libmultipath/structs_vec.h b/libmultipath/structs_vec.h
> index d3219278..2a5e3d60 100644
> --- a/libmultipath/structs_vec.h
> +++ b/libmultipath/structs_vec.h
> @@ -11,8 +11,8 @@ struct vectors {
>  	vector mpvec;
>  };
>  
> -void set_no_path_retry(struct multipath *mpp);
> -void enter_recovery_mode(struct multipath *mpp);
> +void __set_no_path_retry(struct multipath *mpp, bool check_features);
> +#define set_no_path_retry(mpp) __set_no_path_retry(mpp, true)
>  
>  int adopt_paths (vector pathvec, struct multipath * mpp);
>  void orphan_paths(vector pathvec, struct multipath *mpp,
> diff --git a/multipathd/cli_handlers.c b/multipathd/cli_handlers.c
> index 371b0a79..14447c3c 100644
> --- a/multipathd/cli_handlers.c
> +++ b/multipathd/cli_handlers.c
> @@ -1024,16 +1024,16 @@ cli_restore_queueing(void *v, char **reply, int *len, void *data)
>  	select_no_path_retry(conf, mpp);
>  	pthread_cleanup_pop(1);
>  
> -	if (mpp->no_path_retry != NO_PATH_RETRY_UNDEF &&
> -			mpp->no_path_retry != NO_PATH_RETRY_FAIL) {
> -		dm_queue_if_no_path(mpp->alias, 1);
> -		if (mpp->no_path_retry > 0) {
> -			if (mpp->nr_active > 0)
> -				mpp->retry_tick = 0;
> -			else
> -				enter_recovery_mode(mpp);
> -		}
> -	}
> +	/*
> +	 * Don't call set_no_path_retry() for the NO_PATH_RETRY_FAIL case.
> +	 * That would disable queueing when "restorequeueing" is called,
> +	 * and the code never behaved that way. Users might not expect it.
> +	 * In almost all cases, queueing will be disabled anyway when we
> +	 * are here.
> +	 */
> +	if (mpp->no_path_retry != NO_PATH_RETRY_FAIL)
> +		__set_no_path_retry(mpp, false);
> +
>  	return 0;
>  }
>  
> @@ -1051,16 +1051,9 @@ cli_restore_all_queueing(void *v, char **reply, int *len, void *data)
>  		pthread_cleanup_push(put_multipath_config, conf);
>  		select_no_path_retry(conf, mpp);
>  		pthread_cleanup_pop(1);
> -		if (mpp->no_path_retry != NO_PATH_RETRY_UNDEF &&
> -		    mpp->no_path_retry != NO_PATH_RETRY_FAIL) {
> -			dm_queue_if_no_path(mpp->alias, 1);
> -			if (mpp->no_path_retry > 0) {
> -				if (mpp->nr_active > 0)
> -					mpp->retry_tick = 0;
> -				else
> -					enter_recovery_mode(mpp);
> -			}
> -		}
> +		/* See comment in cli_restore_queueing() */
> +		if (mpp->no_path_retry != NO_PATH_RETRY_FAIL)
> +			__set_no_path_retry(mpp, false);
>  	}
>  	return 0;
>  }
> @@ -1085,12 +1078,12 @@ cli_disable_queueing(void *v, char **reply, int *len, void *data)
>  		return 1;
>  	}
>  
> -	if (mpp->nr_active == 0)
> +	if (count_active_paths(mpp) == 0)
>  		mpp->stat_map_failures++;
>  	mpp->retry_tick = 0;
>  	mpp->no_path_retry = NO_PATH_RETRY_FAIL;
>  	mpp->disable_queueing = 1;
> -	dm_queue_if_no_path(mpp->alias, 0);
> +	__set_no_path_retry(mpp, false);
>  	return 0;
>  }
>  
> @@ -1103,12 +1096,12 @@ cli_disable_all_queueing(void *v, char **reply, int *len, void *data)
>  
>  	condlog(2, "disable queueing (operator)");
>  	vector_foreach_slot(vecs->mpvec, mpp, i) {
> -		if (mpp->nr_active == 0)
> +		if (count_active_paths(mpp) == 0)
>  			mpp->stat_map_failures++;
>  		mpp->retry_tick = 0;
>  		mpp->no_path_retry = NO_PATH_RETRY_FAIL;
>  		mpp->disable_queueing = 1;
> -		dm_queue_if_no_path(mpp->alias, 0);
> +		__set_no_path_retry(mpp, false);
>  	}
>  	return 0;
>  }
> diff --git a/multipathd/main.c b/multipathd/main.c
> index a21d96e4..c0423602 100644
> --- a/multipathd/main.c
> +++ b/multipathd/main.c
> @@ -1616,7 +1616,7 @@ fail_path (struct path * pp, int del_active)
>   * caller must have locked the path list before calling that function
>   */
>  static int
> -reinstate_path (struct path * pp, int add_active)
> +reinstate_path (struct path * pp)
>  {
>  	int ret = 0;
>  
> @@ -1628,8 +1628,7 @@ reinstate_path (struct path * pp, int add_active)
>  		ret = 1;
>  	} else {
>  		condlog(2, "%s: reinstated", pp->dev_t);
> -		if (add_active)
> -			update_queue_mode_add_path(pp->mpp);
> +		update_queue_mode_add_path(pp->mpp);
>  	}
>  	return ret;
>  }
> @@ -1861,7 +1860,7 @@ static int check_path_reinstate_state(struct path * pp) {
>  
>  	if (pp->disable_reinstate) {
>  		/* If there are no other usable paths, reinstate the path */
> -		if (pp->mpp->nr_active == 0) {
> +		if (count_active_paths(pp->mpp) == 0) {
>  			condlog(2, "%s : reinstating path early", pp->dev);
>  			goto reinstate_path;
>  		}
> @@ -1960,7 +1959,6 @@ check_path (struct vectors * vecs, struct path * pp, unsigned int ticks)
>  	int newstate;
>  	int new_path_up = 0;
>  	int chkr_new_path_up = 0;
> -	int add_active;
>  	int disable_reinstate = 0;
>  	int oldchkrstate = pp->chkrstate;
>  	int retrigger_tries, verbosity;
> @@ -2134,7 +2132,7 @@ check_path (struct vectors * vecs, struct path * pp, unsigned int ticks)
>  	 * paths if there are no other active paths in map.
>  	 */
>  	disable_reinstate = (newstate == PATH_GHOST &&
> -			     pp->mpp->nr_active == 0 &&
> +			     count_active_paths(pp->mpp) == 0 &&
>  			     path_get_tpgs(pp) == TPGS_IMPLICIT) ? 1 : 0;
>  
>  	pp->chkrstate = newstate;
> @@ -2185,12 +2183,7 @@ check_path (struct vectors * vecs, struct path * pp, unsigned int ticks)
>  		/*
>  		 * reinstate this path
>  		 */
> -		if (oldstate != PATH_UP &&
> -		    oldstate != PATH_GHOST)
> -			add_active = 1;
> -		else
> -			add_active = 0;
> -		if (!disable_reinstate && reinstate_path(pp, add_active)) {
> +		if (!disable_reinstate && reinstate_path(pp)) {
>  			condlog(3, "%s: reload map", pp->dev);
>  			ev_add_path(pp, vecs, 1);
>  			pp->tick = 1;
> @@ -2213,7 +2206,7 @@ check_path (struct vectors * vecs, struct path * pp, unsigned int ticks)
>  		    pp->dmstate == PSTATE_UNDEF) &&
>  		    !disable_reinstate) {
>  			/* Clear IO errors */
> -			if (reinstate_path(pp, 0)) {
> +			if (reinstate_path(pp)) {
>  				condlog(3, "%s: reload map", pp->dev);
>  				ev_add_path(pp, vecs, 1);
>  				pp->tick = 1;
> -- 
> 2.24.0




More information about the dm-devel mailing list