diff mbox series

[3/5] qga: Add UFS freeze/thaw support for FreeBSD

Message ID 20220922132001.940334-4-alexander.ivanov@virtuozzo.com (mailing list archive)
State New, archived
Headers show
Series qga: Add FreeBSD support | expand

Commit Message

Alexander Ivanov Sept. 22, 2022, 1:19 p.m. UTC
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.ivanov@virtuozzo.com>
---
 qga/commands-bsd.c    | 109 +++++++++++++++++++++++++++++++++++++++---
 qga/commands-common.h |  11 +++++
 qga/main.c            |   6 +++
 3 files changed, 120 insertions(+), 6 deletions(-)

Comments

Marc-André Lureau Sept. 23, 2022, 8:09 a.m. UTC | #1
Hi

On Thu, Sep 22, 2022 at 4:15 PM Alexander Ivanov <
alexander.ivanov@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.ivanov@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
>
>
>
diff mbox series

Patch

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) {
+            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) {
+                    break;
+                }
+            }
+            if (!list) {
+                continue;
+            }
+        }
+
+        /* Only UFS supports suspend */
+        if (strcmp(mount->devtype, "ufs") != 0) {
+            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;
+    }
+    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__