On Mon, Sep 11, 2023 at 06:38:27PM +0200, mwi...@suse.com wrote:
> From: Martin Wilck <mwi...@suse.com>
> 
> 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 <bmarz...@redhat.com>
> Signed-off-by: Martin Wilck <mwi...@suse.com>
> ---
>  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@redhat.com
https://listman.redhat.com/mailman/listinfo/dm-devel

Reply via email to