[PATCH] um: hostfs: catch EINTR and partial read/write

Benjamin Berg benjamin at sipsolutions.net
Fri Jan 5 02:12:24 PST 2024


On Thu, 2024-01-04 at 23:27 +0100, Richard Weinberger wrote:
> On Fri, Nov 10, 2023 at 10:44 AM <benjamin at sipsolutions.net> wrote:
> > 
> > From: Benjamin Berg <benjamin.berg at intel.com>
> > 
> > The UM kernel uses signals for various purposes (SIGALRM for
> > scheduling
> > for example). These signals are interrupts for the UM kernel, which
> > should not affect file system operations from userspace processes.
> > 
> > Said differently, in hostfs all operations are directly forwarded
> > to the
> > host and will block the entire UM kernel. As such, hostfs does not
> > permit other tasks to be scheduled while operations are ongoing and
> > these operations are fundamentally synchronous and not
> > interruptible.
> > 
> > With all this, the only reasonable thing to do is to catch all
> > EINTR
> > situations and retry them. This includes retrying short read/write
> > operations in order to ensure they finish.
> 
> I'm confused. Your patches makes hostfs robust to EINTR but it
> doesn't remove the blocking, right?

The intention was to really do all I/O blocking rather than letting us
be interrupted by signals.

For me, the motivation was consistency. However, without the change, a
userspace process that does not expect signals might behave
incorrectly. For me more important though was that a correctly behaving
userspace process that retries could also affect the reproducability of
runs when using time-travel mode.

> In what scenarios do you see hostfs failing due to signals right now?

Usually userspace will retry and there is no failure. But I could
imagine a simple userspace process that does not retry fail if
something else happens at the same time (SIGALRM or SIGIO for console
input).

I agree that refactoring OS helpers a bit does seem like a nicer
solution overall.

Benjamin

> 
> > If, at any point, hostfs becomes able to push operations into the
> > background in order to schedule another task, then an abortion
> > mechanism
> > may be needed.
> > 
> > Signed-off-by: Benjamin Berg <benjamin.berg at intel.com>
> > ---
> >  arch/um/include/shared/os.h |   7 ++-
> >  fs/hostfs/hostfs_user.c     | 115 +++++++++++++++++++++++---------
> > ----
> >  2 files changed, 80 insertions(+), 42 deletions(-)
> > 
> > diff --git a/arch/um/include/shared/os.h
> > b/arch/um/include/shared/os.h
> > index 0df646c6651e..7c5564ebc5bb 100644
> > --- a/arch/um/include/shared/os.h
> > +++ b/arch/um/include/shared/os.h
> > @@ -18,7 +18,12 @@
> >  #include <sys/types.h>
> >  #endif
> > 
> > -#define CATCH_EINTR(expr) while ((errno = 0, ((expr) < 0)) &&
> > (errno == EINTR))
> > +#define CATCH_EINTR(expr)
> > ({                                           \
> > +               int
> > __result;                                           \
> > +               while ((errno = 0, ((__result = (expr)) < 0))
> > &&        \
> > +                      (errno ==
> > EINTR));                               \
> > +              
> > __result;                                               \
> > +       })
> 
> This needs to be a patch on it's own.
> 
> >  #define OS_TYPE_FILE 1
> >  #define OS_TYPE_DIR 2
> > diff --git a/fs/hostfs/hostfs_user.c b/fs/hostfs/hostfs_user.c
> > index 840619e39a1a..dd30bcc6d58f 100644
> > --- a/fs/hostfs/hostfs_user.c
> > +++ b/fs/hostfs/hostfs_user.c
> > @@ -17,6 +17,7 @@
> >  #include <sys/syscall.h>
> >  #include "hostfs.h"
> >  #include <utime.h>
> > +#include <os.h>
> > 
> >  static void stat64_to_hostfs(const struct stat64 *buf, struct
> > hostfs_stat *p)
> >  {
> > @@ -44,9 +45,9 @@ int stat_file(const char *path, struct
> > hostfs_stat *p, int fd)
> >         struct stat64 buf;
> > 
> >         if (fd >= 0) {
> > -               if (fstat64(fd, &buf) < 0)
> > +               if (CATCH_EINTR(fstat64(fd, &buf)) < 0)
> >                         return -errno;
> > -       } else if (lstat64(path, &buf) < 0) {
> > +       } else if (CATCH_EINTR(lstat64(path, &buf)) < 0) {
> >                 return -errno;
> >         }
> >         stat64_to_hostfs(&buf, p);
> > @@ -63,7 +64,7 @@ int access_file(char *path, int r, int w, int x)
> >                 mode |= W_OK;
> >         if (x)
> >                 mode |= X_OK;
> > -       if (access(path, mode) != 0)
> > +       if (CATCH_EINTR(access(path, mode)) != 0)
> >                 return -errno;
> >         else return 0;
> >  }
> > @@ -82,7 +83,7 @@ int open_file(char *path, int r, int w, int
> > append)
> > 
> >         if (append)
> >                 mode |= O_APPEND;
> > -       fd = open64(path, mode);
> > +       fd = CATCH_EINTR(open64(path, mode));
> >         if (fd < 0)
> >                 return -errno;
> >         else return fd;
> > @@ -92,7 +93,11 @@ void *open_dir(char *path, int *err_out)
> >  {
> >         DIR *dir;
> > 
> > -       dir = opendir(path);
> > +       do {
> > +               errno = 0;
> > +               dir = opendir(path);
> > +       } while (dir == NULL && errno == -EINTR);
> > +
> >         *err_out = errno;
> > 
> >         return dir;
> > @@ -112,9 +117,14 @@ char *read_dir(void *stream, unsigned long
> > long *pos_out,
> >         DIR *dir = stream;
> >         struct dirent *ent;
> > 
> > -       ent = readdir(dir);
> > +       do {
> > +               errno = 0;
> > +               ent = readdir(dir);
> > +       } while (ent == 0 && errno == EINTR);
> > +
> >         if (ent == NULL)
> >                 return NULL;
> > +
> >         *len_out = strlen(ent->d_name);
> >         *ino_out = ent->d_ino;
> >         *type_out = ent->d_type;
> > @@ -125,30 +135,52 @@ char *read_dir(void *stream, unsigned long
> > long *pos_out,
> >  int read_file(int fd, unsigned long long *offset, char *buf, int
> > len)
> >  {
> >         int n;
> > +       int read = 0;
> > +
> > +       do {
> > +               errno = 0;
> > +               n = pread64(fd, buf, len, *offset);
> > +               if (n > 0) {
> > +                       read += n;
> > +                       *offset += n;
> > +                       len -= n;
> > +                       continue;
> > +               }
> > +       } while (n < 0 && errno == EINTR);
> > 
> > -       n = pread64(fd, buf, len, *offset);
> > -       if (n < 0)
> > -               return -errno;
> > -       *offset += n;
> > -       return n;
> > +       if (read > 0)
> > +               return read;
> > +
> > +       return -errno;
> >  }
> > 
> >  int write_file(int fd, unsigned long long *offset, const char
> > *buf, int len)
> >  {
> >         int n;
> > +       int written = 0;
> > +
> > +       do {
> > +               errno = 0;
> > +               n = pwrite64(fd, buf, len, *offset);
> > +               if (n > 0) {
> > +                       written += n;
> > +                       *offset += n;
> > +                       len -= n;
> > +                       continue;
> > +               }
> > +       } while (n < 0 && errno == EINTR);
> > 
> > -       n = pwrite64(fd, buf, len, *offset);
> > -       if (n < 0)
> > -               return -errno;
> > -       *offset += n;
> > -       return n;
> > +       if (written > 0)
> > +               return written;
> > +
> > +       return -errno;
> >  }
> > 
> >  int lseek_file(int fd, long long offset, int whence)
> >  {
> >         int ret;
> > 
> > -       ret = lseek64(fd, offset, whence);
> > +       ret = CATCH_EINTR(lseek64(fd, offset, whence));
> >         if (ret < 0)
> >                 return -errno;
> >         return 0;
> > @@ -158,9 +190,9 @@ int fsync_file(int fd, int datasync)
> >  {
> >         int ret;
> >         if (datasync)
> > -               ret = fdatasync(fd);
> > +               ret = CATCH_EINTR(fdatasync(fd));
> >         else
> > -               ret = fsync(fd);
> > +               ret = CATCH_EINTR(fsync(fd));
> > 
> >         if (ret < 0)
> >                 return -errno;
> > @@ -169,24 +201,24 @@ int fsync_file(int fd, int datasync)
> > 
> >  int replace_file(int oldfd, int fd)
> >  {
> > -       return dup2(oldfd, fd);
> > +       return CATCH_EINTR(dup2(oldfd, fd));
> >  }
> > 
> >  void close_file(void *stream)
> >  {
> > -       close(*((int *) stream));
> > +       CATCH_EINTR(close(*((int *) stream)));
> >  }
> > 
> >  void close_dir(void *stream)
> >  {
> > -       closedir(stream);
> > +       CATCH_EINTR(closedir(stream));
> >  }
> > 
> >  int file_create(char *name, int mode)
> >  {
> >         int fd;
> > 
> > -       fd = open64(name, O_CREAT | O_RDWR, mode);
> > +       fd = CATCH_EINTR(open64(name, O_CREAT | O_RDWR, mode));
> >         if (fd < 0)
> >                 return -errno;
> >         return fd;
> > @@ -200,33 +232,33 @@ int set_attr(const char *file, struct
> > hostfs_iattr *attrs, int fd)
> > 
> >         if (attrs->ia_valid & HOSTFS_ATTR_MODE) {
> >                 if (fd >= 0) {
> > -                       if (fchmod(fd, attrs->ia_mode) != 0)
> > +                       if (CATCH_EINTR(fchmod(fd, attrs->ia_mode))
> > != 0)
> >                                 return -errno;
> > -               } else if (chmod(file, attrs->ia_mode) != 0) {
> > +               } else if (CATCH_EINTR(chmod(file, attrs->ia_mode))
> > != 0) {
> >                         return -errno;
> >                 }
> >         }
> >         if (attrs->ia_valid & HOSTFS_ATTR_UID) {
> >                 if (fd >= 0) {
> > -                       if (fchown(fd, attrs->ia_uid, -1))
> > +                       if (CATCH_EINTR(fchown(fd, attrs->ia_uid, -
> > 1)))
> >                                 return -errno;
> > -               } else if (chown(file, attrs->ia_uid, -1)) {
> > +               } else if (CATCH_EINTR(chown(file, attrs->ia_uid, -
> > 1))) {
> >                         return -errno;
> >                 }
> >         }
> >         if (attrs->ia_valid & HOSTFS_ATTR_GID) {
> >                 if (fd >= 0) {
> > -                       if (fchown(fd, -1, attrs->ia_gid))
> > +                       if (CATCH_EINTR(fchown(fd, -1, attrs-
> > >ia_gid)))
> >                                 return -errno;
> > -               } else if (chown(file, -1, attrs->ia_gid)) {
> > +               } else if (CATCH_EINTR(chown(file, -1, attrs-
> > >ia_gid))) {
> >                         return -errno;
> >                 }
> >         }
> >         if (attrs->ia_valid & HOSTFS_ATTR_SIZE) {
> >                 if (fd >= 0) {
> > -                       if (ftruncate(fd, attrs->ia_size))
> > +                       if (CATCH_EINTR(ftruncate(fd, attrs-
> > >ia_size)))
> >                                 return -errno;
> > -               } else if (truncate(file, attrs->ia_size)) {
> > +               } else if (CATCH_EINTR(truncate(file, attrs-
> > >ia_size))) {
> >                         return -errno;
> >                 }
> >         }
> > @@ -279,7 +311,7 @@ int make_symlink(const char *from, const char
> > *to)
> >  {
> >         int err;
> > 
> > -       err = symlink(to, from);
> > +       err = CATCH_EINTR(symlink(to, from));
> >         if (err)
> >                 return -errno;
> >         return 0;
> > @@ -289,7 +321,7 @@ int unlink_file(const char *file)
> >  {
> >         int err;
> > 
> > -       err = unlink(file);
> > +       err = CATCH_EINTR(unlink(file));
> >         if (err)
> >                 return -errno;
> >         return 0;
> > @@ -299,7 +331,7 @@ int do_mkdir(const char *file, int mode)
> >  {
> >         int err;
> > 
> > -       err = mkdir(file, mode);
> > +       err = CATCH_EINTR(mkdir(file, mode));
> >         if (err)
> >                 return -errno;
> >         return 0;
> > @@ -309,7 +341,7 @@ int hostfs_do_rmdir(const char *file)
> >  {
> >         int err;
> > 
> > -       err = rmdir(file);
> > +       err = CATCH_EINTR(rmdir(file));
> >         if (err)
> >                 return -errno;
> >         return 0;
> > @@ -319,7 +351,7 @@ int do_mknod(const char *file, int mode,
> > unsigned int major, unsigned int minor)
> >  {
> >         int err;
> > 
> > -       err = mknod(file, mode, os_makedev(major, minor));
> > +       err = CATCH_EINTR(mknod(file, mode, os_makedev(major,
> > minor)));
> >         if (err)
> >                 return -errno;
> >         return 0;
> > @@ -329,7 +361,7 @@ int link_file(const char *to, const char *from)
> >  {
> >         int err;
> > 
> > -       err = link(to, from);
> > +       err = CATCH_EINTR(link(to, from));
> >         if (err)
> >                 return -errno;
> >         return 0;
> > @@ -339,7 +371,7 @@ int hostfs_do_readlink(char *file, char *buf,
> > int size)
> >  {
> >         int n;
> > 
> > -       n = readlink(file, buf, size);
> > +       n = CATCH_EINTR(readlink(file, buf, size));
> >         if (n < 0)
> >                 return -errno;
> >         if (n < size)
> > @@ -351,7 +383,7 @@ int rename_file(char *from, char *to)
> >  {
> >         int err;
> > 
> > -       err = rename(from, to);
> > +       err = CATCH_EINTR(rename(from, to));
> >         if (err < 0)
> >                 return -errno;
> >         return 0;
> > @@ -371,7 +403,8 @@ int rename2_file(char *from, char *to, unsigned
> > int flags)
> >  #endif
> > 
> >  #ifdef SYS_renameat2
> > -       err = syscall(SYS_renameat2, AT_FDCWD, from, AT_FDCWD, to,
> > flags);
> > +       err = CATCH_EINTR(syscall(SYS_renameat2, AT_FDCWD, from,
> > +                                 AT_FDCWD, to, flags));
> >         if (err < 0) {
> >                 if (errno != ENOSYS)
> >                         return -errno;
> > @@ -392,7 +425,7 @@ int do_statfs(char *root, long *bsize_out, long
> > long *blocks_out,
> >         struct statfs64 buf;
> >         int err;
> > 
> > -       err = statfs64(root, &buf);
> > +       err = CATCH_EINTR(statfs64(root, &buf));
> >         if (err < 0)
> >                 return -errno;
> 
> In general I'm not so happy with adding CATCH_EINTR() to all call
> sites.
> As Anton suggested, a new helper would be nice for each file method.
> 



More information about the linux-um mailing list