[dm-devel] [PATCH] libmultipath: simplify failed wwid code
Benjamin Marzinski
bmarzins at redhat.com
Fri May 8 16:32:00 UTC 2020
On Fri, May 08, 2020 at 09:15:32AM +0000, Martin Wilck wrote:
> On Fri, 2020-05-01 at 17:39 -0500, Benjamin Marzinski wrote:
> > The (is|mark|unmark)_failed_wwid code is needlessly complicated.
> > Locking a file is necssary if multiple processes could otherwise be
> > writing to it at the same time. That is not the case with the
> > failed_wwids files. They can simply be empty files in a
> > directory. Even
> > with all the locking in place, two processes accessing or modifying a
> > file at the same time will still race. And even without the locking,
> > if
> > two processes try to access or modify a file at the same time, they
> > will
> > both see a reasonable result, and will leave the files in a valid
> > state
> > afterwards.
> >
> > Instead of doing all the locking work (which made it necessary to
> > write
> > a file, even just to check if a file existed), simply check for files
> > with lstat(), create them with open(), and remove them with unlink().
> >
> > Signed-off-by: Benjamin Marzinski <bmarzins at redhat.com>
> > ---
> > libmultipath/wwids.c | 131 ++++++++++++++++++-----------------------
> > --
> > 1 file changed, 56 insertions(+), 75 deletions(-)
>
> I agree, almost :-)
>
> Please consider adding the attached patch on top, which switches back
> to atomic creation of the failed_wwids file, with just a little bit
> more compexity. I prefer the creation of the file to be atomic on the
> file system level. IMO that's how "flag" files like this should be
> handled in principle, and doing it correctly makes me feel better, even
> though I have to concur that an actual race is hardly possible.
Reviewed-by: Benjamin Marzinski <bmarzins at redhat.com>
Or where you looking for me to respin with this included? Either way is
fine.
> Regards,
> Martin
>
> From d0e4669c92863f98cdbe7b41a8a9b64223958bec Mon Sep 17 00:00:00 2001
> From: Martin Wilck <mwilck at suse.com>
> Date: Fri, 8 May 2020 00:51:50 +0200
> Subject: [PATCH] libmultipath: use atomic linkat() in mark_failed_wwid()
>
> This keeps (almost) the simplicity of the previous patch, while
> making sure that the return value of mark_failed_wwid()
> (WWID_FAILED_CHANGED vs. WWID_FAILED_UNCHANGED) is correct, even
> if several processes access this WWID at the same time.
>
> Signed-off-by: Martin Wilck <mwilck at suse.com>
> ---
> libmultipath/wwids.c | 42 +++++++++++++++++++++++++++++-------------
> 1 file changed, 29 insertions(+), 13 deletions(-)
>
> diff --git a/libmultipath/wwids.c b/libmultipath/wwids.c
> index b15b146b..ccdd13d2 100644
> --- a/libmultipath/wwids.c
> +++ b/libmultipath/wwids.c
> @@ -374,7 +374,7 @@ int is_failed_wwid(const char *wwid)
>
> if (safe_sprintf(path, "%s/%s", shm_dir, wwid)) {
> condlog(1, "%s: path name overflow", __func__);
> - return -1;
> + return WWID_FAILED_ERROR;
> }
>
> if (lstat(path, &st) == 0)
> @@ -390,27 +390,43 @@ int is_failed_wwid(const char *wwid)
>
> int mark_failed_wwid(const char *wwid)
> {
> - char path[PATH_MAX];
> - int r, fd;
> + char tmpfile[WWID_SIZE + 2 * sizeof(long) + 1];
> + int r = WWID_FAILED_ERROR, fd, dfd;
>
> - if (safe_sprintf(path, "%s/%s", shm_dir, wwid)) {
> - condlog(1, "%s: path name overflow", __func__);
> - return -1;
> + dfd = open(shm_dir, O_RDONLY|O_DIRECTORY);
> + if (dfd == -1 && errno == ENOENT) {
> + char path[sizeof(shm_dir) + 2];
> +
> + /* arg for ensure_directories_exist() must not end with "/" */
> + safe_sprintf(path, "%s/_", shm_dir);
> + ensure_directories_exist(path, 0700);
> + dfd = open(shm_dir, O_RDONLY|O_DIRECTORY);
> }
> - if (ensure_directories_exist(path, 0700) < 0) {
> - condlog(1, "%s: can't setup directories", __func__);
> - return -1;
> + if (dfd == -1) {
> + condlog(1, "%s: can't setup %s: %m", __func__, shm_dir);
> + return WWID_FAILED_ERROR;
> }
>
> - fd = open(path, O_RDONLY | O_CREAT | O_EXCL, S_IRUSR);
> - if (fd >= 0) {
> + safe_sprintf(tmpfile, "%s.%lx", wwid, (long)getpid());
> + fd = openat(dfd, tmpfile, O_RDONLY | O_CREAT | O_EXCL, S_IRUSR);
> + if (fd >= 0)
> close(fd);
> + else
> + goto out_closedir;
> +
> + if (linkat(dfd, tmpfile, dfd, wwid, 0) == 0)
> r = WWID_FAILED_CHANGED;
> - } else if (errno == EEXIST)
> + else if (errno == EEXIST)
> r = WWID_FAILED_UNCHANGED;
> else
> r = WWID_FAILED_ERROR;
>
> + if (unlinkat(dfd, tmpfile, 0) == -1)
> + condlog(2, "%s: failed to unlink %s/%s: %m",
> + __func__, shm_dir, tmpfile);
> +
> +out_closedir:
> + close(dfd);
> print_failed_wwid_result("mark_failed", wwid, r);
> return r;
> }
> @@ -422,7 +438,7 @@ int unmark_failed_wwid(const char *wwid)
>
> if (safe_sprintf(path, "%s/%s", shm_dir, wwid)) {
> condlog(1, "%s: path name overflow", __func__);
> - return -1;
> + return WWID_FAILED_ERROR;
> }
>
> if (unlink(path) == 0)
> --
> 2.26.2
>
More information about the dm-devel
mailing list