[dm-devel] [PATCH 57/57] Allow specific CLI commands to run unlocked

Benjamin Marzinski bmarzins at redhat.com
Wed May 4 03:02:23 UTC 2016


On Wed, Apr 27, 2016 at 01:10:58PM +0200, Hannes Reinecke wrote:
> When multipath is busy with checking paths or processing udev
> events it'll take the vector lock, causing the CLI
> to become unresponsive.
> This patch allows certain CLI commands to not wait for the vector
> lock, so that those commands will always succeed.

In parse_cmd(), you call pthread_mutex_timedlock(), which might not lock
the mutex, but then you unconditionally unlock it later on. Also "show
config" and "show blacklist" dereference conf, and without holding the
vecs lock, there is nothing to keep a reconfigure from happening at the
same time, and setting conf to NULL.

To elaborate on my comment on the last patch: One way to fix these two
patches is to add a read/write lock that all the threads except the
child thread grab in read mode when they are active, and drop before
they sleep, and the child holds in write mode from when it deletes conf
till it finishes load_config.  We'd need to make reloading the config
happens before grabbing the vecs lock in reconfigure, to make sure that
we keep the lock ordering consistent. But then it would be safe to
dereference conf everywhere. It would also allow you to get rid of the
DAEMON_IDLE state, and cut down on the number of things the vecs lock is
protecting, which can only help.

Another option would be to add reference counts to the config structure,
and have each thread grab a reference when they are active, and drop it
before they sleep.  That way, when reconfigure happened, the old config
wouldn't be freed until all of the threads using it slept. Doing this
would involve each thread keeping track of which version of conf they
were using. That would either require passing conf as an argument to
every function that used it (which would end up being most of the
functions), or using the __thread specifier to have a thread-local conf.

-Ben


> 
> Signed-off-by: Hannes Reinecke <hare at suse.de>
> ---
>  multipathd/cli.c  | 46 ++++++++++++++++++++++++++++++++++++++++++++--
>  multipathd/cli.h  |  4 +++-
>  multipathd/main.c | 26 ++++++++++++--------------
>  3 files changed, 59 insertions(+), 17 deletions(-)
> 
> diff --git a/multipathd/cli.c b/multipathd/cli.c
> index 3c9cdbf..d991cd0 100644
> --- a/multipathd/cli.c
> +++ b/multipathd/cli.c
> @@ -1,9 +1,13 @@
>  /*
>   * Copyright (c) 2005 Christophe Varoqui
>   */
> +#include <sys/time.h>
>  #include <errno.h>
> +#include <pthread.h>
>  #include <memory.h>
>  #include <vector.h>
> +#include <structs.h>
> +#include <structs_vec.h>
>  #include <parser.h>
>  #include <util.h>
>  #include <version.h>
> @@ -100,6 +104,19 @@ set_handler_callback (uint64_t fp, int (*fn)(void *, char **, int *, void *))
>  	if (!h)
>  		return 1;
>  	h->fn = fn;
> +	h->locked = 1;
> +	return 0;
> +}
> +
> +int
> +set_unlocked_handler_callback (unsigned long fp,int (*fn)(void *, char **, int *, void *))
> +{
> +	struct handler * h = find_handler(fp);
> +
> +	if (!h)
> +		return 1;
> +	h->fn = fn;
> +	h->locked = 0;
>  	return 0;
>  }
>  
> @@ -430,11 +447,13 @@ genhelp_handler (const char *cmd, int error)
>  }
>  
>  int
> -parse_cmd (char * cmd, char ** reply, int * len, void * data)
> +parse_cmd (char * cmd, char ** reply, int * len, void * data, int timeout )
>  {
>  	int r;
>  	struct handler * h;
>  	vector cmdvec = NULL;
> +	struct timespec tmo;
> +	struct timeval now;
>  
>  	r = get_cmdvec(cmd, &cmdvec);
>  
> @@ -456,7 +475,30 @@ parse_cmd (char * cmd, char ** reply, int * len, void * data)
>  	/*
>  	 * execute handler
>  	 */
> -	r = h->fn(cmdvec, reply, len, data);
> +	if (gettimeofday(&now, NULL) == 0) {
> +		tmo.tv_sec = now.tv_sec + timeout;
> +		tmo.tv_nsec = now.tv_usec * 1000;
> +	} else {
> +		tmo.tv_sec = 0;
> +	}
> +	if (h->locked) {
> +		struct vectors * vecs = (struct vectors *)data;
> +
> +		pthread_cleanup_push(cleanup_lock, &vecs->lock);
> +		if (tmo.tv_sec) {
> +			vecs->lock.depth++;
> +			r = pthread_mutex_timedlock(vecs->lock.mutex, &tmo);
> +		} else {
> +			lock(vecs->lock);
> +			r = 0;
> +		}
> +		if (r == 0) {
> +			pthread_testcancel();
> +			r = h->fn(cmdvec, reply, len, data);
> +		}
> +		lock_cleanup_pop(vecs->lock);
> +	} else
> +		r = h->fn(cmdvec, reply, len, data);
>  	free_keys(cmdvec);
>  
>  	return r;
> diff --git a/multipathd/cli.h b/multipathd/cli.h
> index 2aa19d5..84ca40f 100644
> --- a/multipathd/cli.h
> +++ b/multipathd/cli.h
> @@ -100,13 +100,15 @@ struct key {
>  
>  struct handler {
>  	uint64_t fingerprint;
> +	int locked;
>  	int (*fn)(void *, char **, int *, void *);
>  };
>  
>  int alloc_handlers (void);
>  int add_handler (uint64_t fp, int (*fn)(void *, char **, int *, void *));
>  int set_handler_callback (uint64_t fp, int (*fn)(void *, char **, int *, void *));
> -int parse_cmd (char * cmd, char ** reply, int * len, void *);
> +int set_unlocked_handler_callback (uint64_t fp, int (*fn)(void *, char **, int *, void *));
> +int parse_cmd (char * cmd, char ** reply, int * len, void *, int);
>  int load_keys (void);
>  char * get_keyparam (vector v, uint64_t code);
>  void free_keys (vector vec);
> diff --git a/multipathd/main.c b/multipathd/main.c
> index 132101f..dc788c5 100644
> --- a/multipathd/main.c
> +++ b/multipathd/main.c
> @@ -974,14 +974,13 @@ uxsock_trigger (char * str, char ** reply, int * len, void * trigger_data)
>  	*len = 0;
>  	vecs = (struct vectors *)trigger_data;
>  
> -	pthread_cleanup_push(cleanup_lock, &vecs->lock);
> -	lock(vecs->lock);
> -	pthread_testcancel();
> -
> -	r = parse_cmd(str, reply, len, vecs);
> +	r = parse_cmd(str, reply, len, vecs, uxsock_timeout / 1000);
>  
>  	if (r > 0) {
> -		*reply = STRDUP("fail\n");
> +		if (r == ETIMEDOUT)
> +			*reply = STRDUP("timeout\n");
> +		else
> +			*reply = STRDUP("fail\n");
>  		*len = strlen(*reply) + 1;
>  		r = 1;
>  	}
> @@ -992,7 +991,6 @@ uxsock_trigger (char * str, char ** reply, int * len, void * trigger_data)
>  	}
>  	/* else if (r < 0) leave *reply alone */
>  
> -	lock_cleanup_pop(vecs->lock);
>  	return r;
>  }
>  
> @@ -1112,8 +1110,8 @@ uxlsnrloop (void * ap)
>  	set_handler_callback(LIST+PATHS+RAW+FMT, cli_list_paths_raw);
>  	set_handler_callback(LIST+PATH, cli_list_path);
>  	set_handler_callback(LIST+MAPS, cli_list_maps);
> -	set_handler_callback(LIST+STATUS, cli_list_status);
> -	set_handler_callback(LIST+DAEMON, cli_list_daemon);
> +	set_unlocked_handler_callback(LIST+STATUS, cli_list_status);
> +	set_unlocked_handler_callback(LIST+DAEMON, cli_list_daemon);
>  	set_handler_callback(LIST+MAPS+STATUS, cli_list_maps_status);
>  	set_handler_callback(LIST+MAPS+STATS, cli_list_maps_stats);
>  	set_handler_callback(LIST+MAPS+FMT, cli_list_maps_fmt);
> @@ -1123,8 +1121,8 @@ uxlsnrloop (void * ap)
>  	set_handler_callback(LIST+MAP+TOPOLOGY, cli_list_map_topology);
>  	set_handler_callback(LIST+MAP+FMT, cli_list_map_fmt);
>  	set_handler_callback(LIST+MAP+RAW+FMT, cli_list_map_fmt);
> -	set_handler_callback(LIST+CONFIG, cli_list_config);
> -	set_handler_callback(LIST+BLACKLIST, cli_list_blacklist);
> +	set_unlocked_handler_callback(LIST+CONFIG, cli_list_config);
> +	set_unlocked_handler_callback(LIST+BLACKLIST, cli_list_blacklist);
>  	set_handler_callback(LIST+DEVICES, cli_list_devices);
>  	set_handler_callback(LIST+WILDCARDS, cli_list_wildcards);
>  	set_handler_callback(ADD+PATH, cli_add_path);
> @@ -1132,7 +1130,7 @@ uxlsnrloop (void * ap)
>  	set_handler_callback(ADD+MAP, cli_add_map);
>  	set_handler_callback(DEL+MAP, cli_del_map);
>  	set_handler_callback(SWITCH+MAP+GROUP, cli_switch_group);
> -	set_handler_callback(RECONFIGURE, cli_reconfigure);
> +	set_unlocked_handler_callback(RECONFIGURE, cli_reconfigure);
>  	set_handler_callback(SUSPEND+MAP, cli_suspend);
>  	set_handler_callback(RESUME+MAP, cli_resume);
>  	set_handler_callback(RESIZE+MAP, cli_resize);
> @@ -1144,8 +1142,8 @@ uxlsnrloop (void * ap)
>  	set_handler_callback(RESTOREQ+MAP, cli_restore_queueing);
>  	set_handler_callback(DISABLEQ+MAPS, cli_disable_all_queueing);
>  	set_handler_callback(RESTOREQ+MAPS, cli_restore_all_queueing);
> -	set_handler_callback(QUIT, cli_quit);
> -	set_handler_callback(SHUTDOWN, cli_shutdown);
> +	set_unlocked_handler_callback(QUIT, cli_quit);
> +	set_unlocked_handler_callback(SHUTDOWN, cli_shutdown);
>  	set_handler_callback(GETPRSTATUS+MAP, cli_getprstatus);
>  	set_handler_callback(SETPRSTATUS+MAP, cli_setprstatus);
>  	set_handler_callback(UNSETPRSTATUS+MAP, cli_unsetprstatus);
> -- 
> 2.6.6




More information about the dm-devel mailing list