Re: [PATCH v2 18/37] libmultipath: keep bindings in memory

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Mon, Sep 11, 2023 at 06:38:27PM +0200, mwilck@xxxxxxxx wrote:
> From: Martin Wilck <mwilck@xxxxxxxx>
> 
> Rather than opening the bindings file every time we must retrieve
> a binding, keep the contents in memory and write the file only
> if additions have been made. This simplifies the code, and should speed up
> alias lookups significantly. As a side effect, the aliases will be stored
> sorted by alias, which changes the way aliases are allocated if there are
> unused "holes" in the sequence of aliases. For example, if the bindings file
> contains mpathb, mpathy, and mpatha, in this order, the next new alias used to
> be mpathz and is now mpathc.
> 
> Another side effect is that multipathd will not automatically pick up changes
> to the bindings file at runtime without a reconfigure operation. It is
> questionable whether these on-the-fly changes were a good idea in the first
> place, as inconsistent configurations may easily come to pass. It desired,
> it would be feasible to implement automatic update of the bindings using the
> existing inotify approach.
> 
> The new implementation of get_user_friendly_alias() is slightly different
> than before. The logic is summarized in a comment in the code. Unit tests
> will be provided that illustrate the changes.
> 
Reviewed-by: Benjamin Marzinski <bmarzins@xxxxxxxxxx>
> Signed-off-by: Martin Wilck <mwilck@xxxxxxxx>
> ---
>  libmultipath/alias.c     | 359 ++++++++++++++++-----------------------
>  libmultipath/alias.h     |   2 +-
>  libmultipath/configure.c |   3 +-
>  3 files changed, 148 insertions(+), 216 deletions(-)
> 
> diff --git a/libmultipath/alias.c b/libmultipath/alias.c
> index ad83ca0..d656374 100644
> --- a/libmultipath/alias.c
> +++ b/libmultipath/alias.c
> @@ -50,8 +50,6 @@
>  "# alias wwid\n" \
>  "#\n"
>  
> -static const char bindings_file_header[] = BINDINGS_FILE_HEADER;
> -
>  struct binding {
>  	char *alias;
>  	char *wwid;
> @@ -80,6 +78,45 @@ static void _free_binding(struct binding *bdg)
>  	free(bdg);
>  }
>  
> +static const struct binding *get_binding_for_alias(const Bindings *bindings,
> +						   const char *alias)
> +{
> +	const struct binding *bdg;
> +	int i;
> +
> +	if (!alias)
> +		return NULL;
> +	vector_foreach_slot(bindings, bdg, i) {
> +		if (!strncmp(bdg->alias, alias, WWID_SIZE)) {
> +			condlog(3, "Found matching alias [%s] in bindings file."
> +				" Setting wwid to %s", alias, bdg->wwid);
> +			return bdg;
> +		}
> +	}
> +
> +	condlog(3, "No matching alias [%s] in bindings file.", alias);
> +	return NULL;
> +}
> +
> +static const struct binding *get_binding_for_wwid(const Bindings *bindings,
> +						  const char *wwid)
> +{
> +	const struct binding *bdg;
> +	int i;
> +
> +	if (!wwid)
> +		return NULL;
> +	vector_foreach_slot(bindings, bdg, i) {
> +		if (!strncmp(bdg->wwid, wwid, WWID_SIZE)) {
> +			condlog(3, "Found matching wwid [%s] in bindings file."
> +				" Setting alias to %s", wwid, bdg->alias);
> +			return bdg;
> +		}
> +	}
> +	condlog(3, "No matching wwid [%s] in bindings file.", wwid);
> +	return NULL;
> +}
> +
>  static int add_binding(Bindings *bindings, const char *alias, const char *wwid)
>  {
>  	struct binding *bdg;
> @@ -115,6 +152,24 @@ static int add_binding(Bindings *bindings, const char *alias, const char *wwid)
>  	return BINDING_ERROR;
>  }
>  
> +static int delete_binding(Bindings *bindings, const char *wwid)
> +{
> +	struct binding *bdg;
> +	int i;
> +
> +	vector_foreach_slot(bindings, bdg, i) {
> +		if (!strncmp(bdg->wwid, wwid, WWID_SIZE)) {
> +			_free_binding(bdg);
> +			break;
> +		}
> +	}
> +	if (i >= VECTOR_SIZE(bindings))
> +		return BINDING_NOTFOUND;
> +
> +	vector_del_slot(bindings, i);
> +	return BINDING_DELETED;
> +}
> +
>  static int write_bindings_file(const Bindings *bindings, int fd)
>  {
>  	struct binding *bnd;
> @@ -267,38 +322,15 @@ static bool id_already_taken(int id, const char *prefix, const char *map_wwid)
>  	return alias_already_taken(alias, map_wwid);
>  }
>  
> -/*
> - * Returns: 0   if matching entry in WWIDs file found
> - *         -1   if an error occurs
> - *         >0   a free ID that could be used for the WWID at hand
> - * *map_alias is set to a freshly allocated string with the matching alias if
> - * the function returns 0, or to NULL otherwise.
> - */
> -static int
> -lookup_binding(FILE *f, const char *map_wwid, char **map_alias,
> -	       const char *prefix, int check_if_taken)
> +int get_free_id(const Bindings *bindings, const char *prefix, const char *map_wwid)
>  {
> -	char buf[LINE_MAX];
> -	unsigned int line_nr = 0;
> -	int id = 1;
> +	const struct binding *bdg;
> +	int i, id = 1;
>  	int biggest_id = 1;
>  	int smallest_bigger_id = INT_MAX;
>  
> -	*map_alias = NULL;
> -
> -	rewind(f);
> -	while (fgets(buf, LINE_MAX, f)) {
> -		const char *alias, *wwid;
> -		char *c, *saveptr;
> -		int curr_id;
> -
> -		line_nr++;
> -		c = strpbrk(buf, "#\n\r");
> -		if (c)
> -			*c = '\0';
> -		alias = strtok_r(buf, " \t", &saveptr);
> -		if (!alias) /* blank line */
> -			continue;
> +	vector_foreach_slot(bindings, bdg, i) {
> +		int curr_id = scan_devname(bdg->alias, prefix);
>  
>  		/*
>  		 * Find an unused index - explanation of the algorithm
> @@ -333,8 +365,6 @@ lookup_binding(FILE *f, const char *map_wwid, char **map_alias,
>  		 * biggest_id is always > smallest_bigger_id, except in the
>  		 * "perfectly ordered" case.
>  		 */
> -
> -		curr_id = scan_devname(alias, prefix);
>  		if (curr_id == id) {
>  			if (id < INT_MAX)
>  				id++;
> @@ -345,36 +375,15 @@ lookup_binding(FILE *f, const char *map_wwid, char **map_alias,
>  		}
>  		if (curr_id > biggest_id)
>  			biggest_id = curr_id;
> +
>  		if (curr_id > id && curr_id < smallest_bigger_id)
>  			smallest_bigger_id = curr_id;
> -		wwid = strtok_r(NULL, " \t", &saveptr);
> -		if (!wwid){
> -			condlog(3,
> -				"Ignoring malformed line %u in bindings file",
> -				line_nr);
> -			continue;
> -		}
> -		if (strcmp(wwid, map_wwid) == 0){
> -			condlog(3, "Found matching wwid [%s] in bindings file."
> -				" Setting alias to %s", wwid, alias);
> -			*map_alias = strdup(alias);
> -			if (*map_alias == NULL) {
> -				condlog(0, "Cannot copy alias from bindings "
> -					"file: out of memory");
> -				return -1;
> -			}
> -			return 0;
> -		}
>  	}
> -	if (!prefix && check_if_taken)
> -		id = -1;
> -	if (id >= smallest_bigger_id) {
> -		if (biggest_id < INT_MAX)
> -			id = biggest_id + 1;
> -		else
> -			id = -1;
> -	}
> -	if (id > 0 && check_if_taken) {
> +
> +	if (id >= smallest_bigger_id)
> +		id = biggest_id < INT_MAX ? biggest_id + 1 : -1;
> +
> +	if (id > 0) {
>  		while(id_already_taken(id, prefix, map_wwid)) {
>  			if (id == INT_MAX) {
>  				id = -1;
> @@ -391,64 +400,17 @@ lookup_binding(FILE *f, const char *map_wwid, char **map_alias,
>  			}
>  		}
>  	}
> -	if (id < 0) {
> +
> +	if (id < 0)
>  		condlog(0, "no more available user_friendly_names");
> -		return -1;
> -	} else
> -		condlog(3, "No matching wwid [%s] in bindings file.", map_wwid);
>  	return id;
>  }
>  
> -static int
> -rlookup_binding(FILE *f, char *buff, const char *map_alias)
> -{
> -	char line[LINE_MAX];
> -	unsigned int line_nr = 0;
> -
> -	buff[0] = '\0';
> -
> -	while (fgets(line, LINE_MAX, f)) {
> -		char *c, *saveptr;
> -		const char *alias, *wwid;
> -
> -		line_nr++;
> -		c = strpbrk(line, "#\n\r");
> -		if (c)
> -			*c = '\0';
> -		alias = strtok_r(line, " \t", &saveptr);
> -		if (!alias) /* blank line */
> -			continue;
> -		wwid = strtok_r(NULL, " \t", &saveptr);
> -		if (!wwid){
> -			condlog(3,
> -				"Ignoring malformed line %u in bindings file",
> -				line_nr);
> -			continue;
> -		}
> -		if (strlen(wwid) > WWID_SIZE - 1) {
> -			condlog(3,
> -				"Ignoring too large wwid at %u in bindings file", line_nr);
> -			continue;
> -		}
> -		if (strcmp(alias, map_alias) == 0){
> -			condlog(3, "Found matching alias [%s] in bindings file."
> -				" Setting wwid to %s", alias, wwid);
> -			strlcpy(buff, wwid, WWID_SIZE);
> -			return 0;
> -		}
> -	}
> -	condlog(3, "No matching alias [%s] in bindings file.", map_alias);
> -
> -	return -1;
> -}
> -
>  static char *
> -allocate_binding(int fd, const char *wwid, int id, const char *prefix)
> +allocate_binding(const char *filename, const char *wwid, int id, const char *prefix)
>  {
>  	STRBUF_ON_STACK(buf);
> -	off_t offset;
> -	ssize_t len;
> -	char *alias, *c;
> +	char *alias;
>  
>  	if (id <= 0) {
>  		condlog(0, "%s: cannot allocate new binding for id %d",
> @@ -460,164 +422,135 @@ allocate_binding(int fd, const char *wwid, int id, const char *prefix)
>  	    format_devname(&buf, id) == -1)
>  		return NULL;
>  
> -	if (print_strbuf(&buf, " %s\n", wwid) < 0)
> -		return NULL;
> -
> -	offset = lseek(fd, 0, SEEK_END);
> -	if (offset < 0){
> -		condlog(0, "Cannot seek to end of bindings file : %s",
> -			strerror(errno));
> -		return NULL;
> -	}
> -
> -	len = get_strbuf_len(&buf);
>  	alias = steal_strbuf_str(&buf);
>  
> -	if (write(fd, alias, len) != len) {
> -		condlog(0, "Cannot write binding to bindings file : %s",
> -			strerror(errno));
> -		/* clear partial write */
> -		if (ftruncate(fd, offset))
> -			condlog(0, "Cannot truncate the header : %s",
> -				strerror(errno));
> +	if (add_binding(&global_bindings, alias, wwid) != BINDING_ADDED) {
> +		condlog(0, "%s: cannot allocate new binding %s for %s",
> +			__func__, alias, wwid);
> +		free(alias);
> +		return NULL;
> +	}
> +
> +	if (update_bindings_file(&global_bindings, filename) == -1) {
> +		condlog(1, "%s: deleting binding %s for %s", __func__, alias, wwid);
> +		delete_binding(&global_bindings, wwid);
>  		free(alias);
>  		return NULL;
>  	}
> -	c = strchr(alias, ' ');
> -	if (c)
> -		*c = '\0';
>  
>  	condlog(3, "Created new binding [%s] for WWID [%s]", alias, wwid);
>  	return alias;
>  }
>  
> +/*
> + * get_user_friendly_alias() action table
> + *
> + * The table shows the various cases, the actions taken, and the CI
> + * functions from tests/alias.c that represent them.
> + *
> + *  - O: old alias given
> + *  - A: old alias in table (y: yes, correct WWID; X: yes, wrong WWID)
> + *  - W: wwid in table
> + *
> + *  | No | O | A | W | action                                     | function gufa_X              |
> + *  |----+---+---+---+--------------------------------------------+------------------------------|
> + *  |  1 | n | - | n | get new alias                              | nomatch_Y                    |
> + *  |  2 | n | - | y | use alias from bindings                    | match_a_Y                    |
> + *  |  3 | y | n | n | add binding for old alias                  | old_nomatch_nowwidmatch      |
> + *  |  4 | y | n | y | use alias from bindings (avoid duplicates) | old_nomatch_wwidmatch        |
> + *  |  5 | y | y | n | [ impossible ]                             | -                            |
> + *  |  6 | y | y | y | use old alias == alias from bindings       | old_match                    |
> + *  |  7 | y | X | n | get new alias                              | old_match_other              |
> + *  |  8 | y | X | y | use alias from bindings                    | old_match_other_wwidmatch    |
> + *
> + * Notes:
> + *  - "use alias from bindings" means that the alias from the bindings file will
> + *    be tried; if it is in use, the alias selection will fail. No other
> + *    bindings will be attempted.
> + *  - "get new alias" fails if all aliases are used up, or if writing the
> + *    bindings file fails.
> + *  - if "alias_old" is set, it can't be bound to a different map. alias_old is
> + *    initialized in find_existing_alias() by scanning the mpvec. We trust
> + *    that the mpvec corrcectly represents kernel state.
> + */
> +
>  char *get_user_friendly_alias(const char *wwid, const char *file, const char *alias_old,
>  			      const char *prefix, bool bindings_read_only)
>  {
>  	char *alias = NULL;
>  	int id = 0;
> -	int fd, can_write;
>  	bool new_binding = false;
> -	char buff[WWID_SIZE];
> -	FILE *f;
> +	const struct binding *bdg;
>  
> -	fd = open_file(file, &can_write, bindings_file_header);
> -	if (fd < 0)
> -		return NULL;
> -
> -	f = fdopen(fd, "r");
> -	if (!f) {
> -		condlog(0, "cannot fdopen on bindings file descriptor");
> -		close(fd);
> -		return NULL;
> -	}
> -
> -	if (!strlen(alias_old))
> +	if (!*alias_old)
>  		goto new_alias;
>  
> -	/* lookup the binding. if it exists, the wwid will be in buff
> -	 * either way, id contains the id for the alias
> -	 */
> -	rlookup_binding(f, buff, alias_old);
> -
> -	if (strlen(buff) > 0) {
> -		/* If buff is our wwid, it's already allocated correctly. */
> -		if (strcmp(buff, wwid) == 0) {
> +	/* See if there's a binding matching both alias_old and wwid */
> +	bdg = get_binding_for_alias(&global_bindings, alias_old);
> +	if (bdg) {
> +		if (!strcmp(bdg->wwid, wwid)) {
>  			alias = strdup(alias_old);
>  			goto out;
> -
>  		} else {
>  			condlog(0, "alias %s already bound to wwid %s, cannot reuse",
> -				alias_old, buff);
> -			goto new_alias;		     ;
> +				alias_old, bdg->wwid);
> +			goto new_alias;
>  		}
>  	}
>  
> -	/*
> -	 * Look for an existing alias in the bindings file.
> -	 * Pass prefix = NULL, so lookup_binding() won't try to allocate a new id.
> -	 */
> -	lookup_binding(f, wwid, &alias, NULL, 0);
> -	if (alias) {
> -		if (alias_already_taken(alias, wwid)) {
> -			free(alias);
> -			alias = NULL;
> -		} else
> -			condlog(3, "Use existing binding [%s] for WWID [%s]",
> -				alias, wwid);
> -		goto out;
> -	}
> -
> -	/* alias_old is already taken by our WWID, update bindings file. */
> +	/* allocate the existing alias in the bindings file */
>  	id = scan_devname(alias_old, prefix);
>  
>  new_alias:
> +	/* Check for existing binding of WWID */
> +	bdg = get_binding_for_wwid(&global_bindings, wwid);
> +	if (bdg) {
> +		if (!alias_already_taken(bdg->alias, wwid)) {
> +			condlog(3, "Use existing binding [%s] for WWID [%s]",
> +				bdg->alias, wwid);
> +			alias = strdup(bdg->alias);
> +		}
> +		goto out;
> +	}
> +
>  	if (id <= 0) {
>  		/*
>  		 * no existing alias was provided, or allocating it
>  		 * failed. Try a new one.
>  		 */
> -		id = lookup_binding(f, wwid, &alias, prefix, 1);
> -		if (id == 0 && alias_already_taken(alias, wwid)) {
> -			free(alias);
> -			alias = NULL;
> -		}
> +		id = get_free_id(&global_bindings, prefix, wwid);
>  		if (id <= 0)
>  			goto out;
>  		else
>  			new_binding = true;
>  	}
>  
> -	if (fflush(f) != 0) {
> -		condlog(0, "cannot fflush bindings file stream : %s",
> -			strerror(errno));
> -		goto out;
> -	}
> +	if (!bindings_read_only && id > 0)
> +		alias = allocate_binding(file, wwid, id, prefix);
>  
> -	if (can_write && !bindings_read_only) {
> -		alias = allocate_binding(fd, wwid, id, prefix);
> -		if (alias && !new_binding)
> -			condlog(2, "Allocated existing binding [%s] for WWID [%s]",
> -				alias, wwid);
> -	}
> +	if (alias && !new_binding)
> +		condlog(2, "Allocated existing binding [%s] for WWID [%s]",
> +			alias, wwid);
>  
>  out:
> -	pthread_cleanup_push(free, alias);
> -	fclose(f);
> -	pthread_cleanup_pop(0);
>  	return alias;
>  }
>  
> -int
> -get_user_friendly_wwid(const char *alias, char *buff, const char *file)
> +int get_user_friendly_wwid(const char *alias, char *buff)
>  {
> -	int fd, unused;
> -	FILE *f;
> +	const struct binding *bdg;
>  
>  	if (!alias || *alias == '\0') {
>  		condlog(3, "Cannot find binding for empty alias");
>  		return -1;
>  	}
>  
> -	fd = open_file(file, &unused, bindings_file_header);
> -	if (fd < 0)
> -		return -1;
> -
> -	f = fdopen(fd, "r");
> -	if (!f) {
> -		condlog(0, "cannot fdopen on bindings file descriptor : %s",
> -			strerror(errno));
> -		close(fd);
> +	bdg = get_binding_for_alias(&global_bindings, alias);
> +	if (!bdg) {
> +		*buff = '\0';
>  		return -1;
>  	}
> -
> -	rlookup_binding(f, buff, alias);
> -	if (!strlen(buff)) {
> -		fclose(f);
> -		return -1;
> -	}
> -
> -	fclose(f);
> +	strlcpy(buff, bdg->wwid, WWID_SIZE);
>  	return 0;
>  }
>  
> diff --git a/libmultipath/alias.h b/libmultipath/alias.h
> index 37b49d9..5ef6720 100644
> --- a/libmultipath/alias.h
> +++ b/libmultipath/alias.h
> @@ -2,7 +2,7 @@
>  #define _ALIAS_H
>  
>  int valid_alias(const char *alias);
> -int get_user_friendly_wwid(const char *alias, char *buff, const char *file);
> +int get_user_friendly_wwid(const char *alias, char *buff);
>  char *get_user_friendly_alias(const char *wwid, const char *file,
>  			      const char *alias_old,
>  			      const char *prefix, bool bindings_read_only);
> diff --git a/libmultipath/configure.c b/libmultipath/configure.c
> index 029fbbd..d809490 100644
> --- a/libmultipath/configure.c
> +++ b/libmultipath/configure.c
> @@ -1378,8 +1378,7 @@ static int _get_refwwid(enum mpath_cmds cmd, const char *dev,
>  			refwwid = tmpwwid;
>  
>  		/* or may be a binding */
> -		else if (get_user_friendly_wwid(dev, tmpwwid,
> -						conf->bindings_file) == 0)
> +		else if (get_user_friendly_wwid(dev, tmpwwid) == 0)
>  			refwwid = tmpwwid;
>  
>  		/* or may be an alias */
> -- 
> 2.42.0
--
dm-devel mailing list
dm-devel@xxxxxxxxxx
https://listman.redhat.com/mailman/listinfo/dm-devel




[Index of Archives]     [DM Crypt]     [Fedora Desktop]     [ATA RAID]     [Fedora Marketing]     [Fedora Packaging]     [Fedora SELinux]     [Yosemite Discussion]     [KDE Users]     [Fedora Docs]

  Powered by Linux