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