Hi

On Thu, Sep 22, 2022 at 4:15 PM Alexander Ivanov <
alexander.iva...@virtuozzo.com> wrote:

> UFS supports FS freezing through ioctl UFSSUSPEND on /dev/ufssuspend.
> Freezed FS can be thawed by closing /dev/ufssuspend file descriptior.
>
> Use getmntinfo to get a list of mounted FS.
>
> Signed-off-by: Alexander Ivanov <alexander.iva...@virtuozzo.com>
> ---
>  qga/commands-bsd.c    | 109 +++++++++++++++++++++++++++++++++++++++---
>  qga/commands-common.h |  11 +++++
>  qga/main.c            |   6 +++
>  3 files changed, 120 insertions(+), 6 deletions(-)
>
> diff --git a/qga/commands-bsd.c b/qga/commands-bsd.c
> index c1e3ed13e9..5d3f46804a 100644
> --- a/qga/commands-bsd.c
> +++ b/qga/commands-bsd.c
> @@ -17,28 +17,125 @@
>  #include "qemu/queue.h"
>  #include "commands-common.h"
>
> +#include <sys/ioctl.h>
> +#include <sys/param.h>
> +#include <sys/ucred.h>
> +#include <sys/mount.h>
> +#include <paths.h>
> +
>  #if defined(CONFIG_FSFREEZE) || defined(CONFIG_FSTRIM)
>  bool build_fs_mount_list(FsMountList *mounts, Error **errp)
>  {
> -    error_setg(errp, QERR_UNSUPPORTED);
> -    return false;
> +    FsMount *mount;
> +    struct statfs *mntbuf, *mntp;
> +    struct stat statbuf;
> +    int i, count, ret;
> +
> +    count = getmntinfo(&mntbuf, MNT_NOWAIT);
> +    if (count == 0) {
> +        error_setg_errno(errp, errno, "getmntinfo failed");
> +        return false;
> +    }
> +
> +    for (i = 0; i < count; i++) {
> +        mntp = &mntbuf[i];
> +        ret = stat(mntp->f_mntonname, &statbuf);
> +        if (ret != 0) {
>

I am not sure we can simply ignore an error here. At least, there should be
a warning logged, no?


> +            continue;
> +        }
> +
> +        mount = g_new0(FsMount, 1);
> +
> +        mount->dirname = g_strdup(mntp->f_mntonname);
> +        mount->devtype = g_strdup(mntp->f_fstypename);
> +        mount->devmajor = major(mount->dev);
> +        mount->devminor = minor(mount->dev);
> +        mount->fsid = mntp->f_fsid;
> +        mount->dev = statbuf.st_dev;
> +
> +        QTAILQ_INSERT_TAIL(mounts, mount, next);
> +    }
> +    return true;
>  }
>  #endif
>
>  #if defined(CONFIG_FSFREEZE)
> +static int ufssuspend_fd = -1;
> +static int ufssuspend_cnt;
> +
>  int64_t qmp_guest_fsfreeze_do_freeze_list(bool has_mountpoints,
>                                            strList *mountpoints,
>                                            FsMountList mounts,
>                                            Error **errp)
>  {
> -    error_setg(errp, QERR_UNSUPPORTED);
> -    return 0;
> +    int ret;
> +    strList *list;
> +    struct FsMount *mount;
> +
> +    if (ufssuspend_fd != -1) {
> +        error_setg(errp, "filesystems have already frozen");
> +        return -1;
> +    }
> +
> +    ufssuspend_cnt = 0;
> +    ufssuspend_fd = qemu_open(_PATH_UFSSUSPEND, O_RDWR, errp);
> +    if (ufssuspend_fd == -1) {
> +        return -1;
> +    }
> +
> +    QTAILQ_FOREACH_REVERSE(mount, &mounts, next) {
> +        /*
> +         * To issue fsfreeze in the reverse order of mounts, check if the
> +         * mount is listed in the list here
> +         */
> +        if (has_mountpoints) {
> +            for (list = mountpoints; list; list = list->next) {
> +                if (strcmp(list->value, mount->dirname) == 0) {
>

nit: I prefer g_str_equal()


> +                    break;
> +                }
> +            }
> +            if (!list) {
> +                continue;
> +            }
> +        }
> +
> +        /* Only UFS supports suspend */
> +        if (strcmp(mount->devtype, "ufs") != 0) {
>

!g_str_equal()


> +            continue;
> +        }
> +
> +        ret = ioctl(ufssuspend_fd, UFSSUSPEND, &mount->fsid);
> +        if (ret == -1) {
> +            /*
> +             * ioctl returns EBUSY for all the FS except the first one
> +             * that was suspended
> +             */
> +            if (errno == EBUSY) {
> +                continue;
> +            }
> +            error_setg_errno(errp, errno, "failed to freeze %s",
> +                             mount->dirname);
> +            goto error;
> +        }
> +        ufssuspend_cnt++;
> +    }
> +    return ufssuspend_cnt;
> +error:
> +    close(ufssuspend_fd);
> +    ufssuspend_fd = -1;
> +    return -1;
> +
>  }
>
>  int qmp_guest_fsfreeze_do_thaw(Error **errp)
>  {
> -    error_setg(errp, QERR_UNSUPPORTED);
> -    return 0;
> +    int ret = ufssuspend_cnt;
> +    ufssuspend_cnt = 0;
> +    if (ufssuspend_fd != -1) {
> +        close(ufssuspend_fd);
> +        ufssuspend_fd = -1;
> +    }
>

Maybe leave a comment that UFSRESUME isn't necessary?


> +    return ret;
>  }
>  #endif
>
> diff --git a/qga/commands-common.h b/qga/commands-common.h
> index aa0472ea4c..c3be6db3a9 100644
> --- a/qga/commands-common.h
> +++ b/qga/commands-common.h
> @@ -41,11 +41,22 @@ void ga_wait_child(pid_t pid, int *status, Error
> **errp);
>  #endif
>  #endif /* __linux__*/
>
> +#ifdef __FreeBSD__
> +#include <ufs/ffs/fs.h>
> +#ifdef UFSSUSPEND
> +#define CONFIG_FSFREEZE
> +#endif
> +#endif /* __FreeBSD__ */
> +
>  #if defined(CONFIG_FSFREEZE) || defined(CONFIG_FSTRIM)
>  typedef struct FsMount {
>      char *dirname;
>      char *devtype;
>      unsigned int devmajor, devminor;
> +#if defined(__FreeBSD__)
> +    dev_t dev;
> +    fsid_t fsid;
> +#endif
>      QTAILQ_ENTRY(FsMount) next;
>  } FsMount;
>
> diff --git a/qga/main.c b/qga/main.c
> index 22b3c0df11..ab420051fb 100644
> --- a/qga/main.c
> +++ b/qga/main.c
> @@ -43,6 +43,12 @@
>  #define CONFIG_FSFREEZE
>  #endif
>  #endif
> +#ifdef __FreeBSD__
> +#include <ufs/ffs/fs.h>
> +#ifdef UFSSUSPEND
> +#define CONFIG_FSFREEZE
> +#endif
> +#endif
>
>  #ifndef _WIN32
>  #ifdef __FreeBSD__
> --
> 2.34.1
>
>
>

-- 
Marc-André Lureau

Reply via email to