[libvirt] [PATCH sandbox 2/4] Sync and unmount filesystems during shutdown
Cedric Bosdonnat
cbosdonnat at suse.com
Mon Sep 7 11:43:08 UTC 2015
On Fri, 2015-09-04 at 12:40 +0100, Daniel P. Berrange wrote:
> To ensure that all pending I/O for filesytems backed by block
> devices is flushed to disk, it is important to sync and unmount
> the filesystems during shutdown. To avoid relying on the kernel
> reboot-on-panic behaviour, we also explicitly call reboot to
> power off the guest.
>
> Signed-off-by: Daniel P. Berrange <berrange at redhat.com>
> ---
> libvirt-sandbox/libvirt-sandbox-init-common.c | 166 ++++++++++++++++++++++++++
> libvirt-sandbox/libvirt-sandbox-init-qemu.c | 1 +
> 2 files changed, 167 insertions(+)
>
> diff --git a/libvirt-sandbox/libvirt-sandbox-init-common.c b/libvirt-sandbox/libvirt-sandbox-init-common.c
> index 760509f..a3b4687 100644
> --- a/libvirt-sandbox/libvirt-sandbox-init-common.c
> +++ b/libvirt-sandbox/libvirt-sandbox-init-common.c
> @@ -44,6 +44,8 @@
> #include <unistd.h>
> #include <limits.h>
> #include <grp.h>
> +#include <mntent.h>
> +#include <sys/reboot.h>
>
> #include "libvirt-sandbox-rpcpacket.h"
>
> @@ -52,6 +54,8 @@ static gboolean verbose = FALSE;
> static int sigwrite;
>
> #define ATTR_UNUSED __attribute__((__unused__))
> +static void sync_data(void);
> +static void umount_fs(void);
>
> static void sig_child(int sig ATTR_UNUSED)
> {
> @@ -598,6 +602,14 @@ static GVirSandboxRPCPacket *gvir_sandbox_encode_exit(int status,
> pkt->header.type = GVIR_SANDBOX_PROTOCOL_TYPE_MESSAGE;
> pkt->header.serial = serial;
>
> + /* The host may destroy the guest any time after receiving
> + * the exit code messages. So although the main() has code
> + * to sync + unmount we can't rely on that running. So we
> + * opportunistically sync + unmount here too.
> + */
> + sync_data();
> + umount_fs();
> +
> if (!gvir_sandbox_rpcpacket_encode_header(pkt, error))
> goto error;
> if (!gvir_sandbox_rpcpacket_encode_payload_msg(pkt,
> @@ -613,7 +625,151 @@ static GVirSandboxRPCPacket *gvir_sandbox_encode_exit(int status,
> return NULL;
> }
>
> +/* Copied & adapted from libguestfs daemon/sync.c under LGPLv2+ */
> +static void sync_data(void)
> +{
> + DIR *dir;
> + struct dirent *d;
> + char *dev_path;
> + int fd;
> +
> + if (debug)
> + fprintf(stderr, "Syncing data\n");
> + sync();
> +
> + /* On Linux, sync(2) doesn't perform a barrier, so qemu (which may
> + * have a writeback cache, even with cache=none) will still have
> + * some unwritten data. Force the data out of any qemu caches, by
> + * calling fsync on all block devices. Note we still need the
> + * call to sync above in order to schedule the writes.
> + * Thanks to: Avi Kivity, Kevin Wolf.
> + */
> +
> + if (!(dir = opendir("/dev"))) {
> + fprintf(stderr, "opendir: /dev failed %s\n", strerror(errno));
> + return;
> + }
> +
> + for (;;) {
> + errno = 0;
> + d = readdir(dir);
> + if (!d)
> + break;
> +
> + if (!(g_str_has_prefix(d->d_name, "sd") ||
> + g_str_has_prefix(d->d_name, "hd") ||
> + g_str_has_prefix(d->d_name, "ubd") ||
> + g_str_has_prefix(d->d_name, "vd") ||
> + g_str_has_prefix(d->d_name, "sr"))) {
> + continue;
> + }
> +
> + dev_path = g_strdup_printf("/dev/%s", d->d_name);
> +
> + if (debug)
> + fprintf(stderr, "Syncing fd %s\n", dev_path);
> + if ((fd = open(dev_path, O_RDONLY)) < 0) {
> + fprintf(stderr, "cannot open %s: %s\n", dev_path,
> + strerror(errno));
> + g_free(dev_path);
> + continue;
> + }
> +
> + /* fsync the device. */
> + if (debug) {
> + fprintf(stderr, "fsync %s\n", dev_path);
> + }
> +
> + if (fsync(fd) < 0) {
> + fprintf(stderr, "failed to fsync %s: %s\n",
> + dev_path, strerror(errno));
> + }
> + if (close(fd) < 0) {
> + fprintf(stderr, "failed to close %s: %s\n",
> + dev_path, strerror(errno));
> + }
> + g_free(dev_path);
> + }
> +
> + /* Check readdir didn't fail */
> + if (errno != 0) {
> + fprintf(stderr, "Failed to read /dev: %s\n",
> + strerror(errno));
> + }
> +
> + /* Close the directory handle */
> + if (closedir(dir) < 0) {
> + fprintf(stderr, "Failed to block /dev: %s\n",
> + strerror(errno));
> + }
> +
> + if (debug)
> + fprintf(stderr, "Syncing complete\n");
> +}
> +
> +
> +static int
> +compare_longest_first (gconstpointer vp1, gconstpointer vp2)
> +{
> + int n1 = strlen(vp1);
> + int n2 = strlen(vp2);
> + return n2 - n1;
> +}
> +
> +
> +/* Copied & adapted from libguestfs daemon/sync.c under LGPLv2+ */
> +static void umount_fs(void)
> +{
> + FILE *fp;
> + struct mntent *m;
> + GList *mounts = NULL, *tmp;
> +
> + if (debug)
> + fprintf(stderr, "Unmounting all filesystems\n");
> + if (!(fp = setmntent ("/proc/mounts", "r"))) {
> + fprintf(stderr, "Failed to open /proc/mounts: %s\n",
> + strerror(errno));
> + return;
> + }
> +
> + while ((m = getmntent (fp)) != NULL) {
> + if (debug)
> + fprintf(stderr, "Got fsname=%s dir=%s type=%s opts=%s freq=%d passno=%d\n",
> + m->mnt_fsname, m->mnt_dir, m->mnt_type, m->mnt_opts,
> + m->mnt_freq, m->mnt_passno);
> +
> + mounts = g_list_append(mounts, g_strdup(m->mnt_dir));
> + }
>
> + endmntent(fp);
> +
> + mounts = g_list_sort(mounts, compare_longest_first);
> +
> + /* Unmount them. */
> + tmp = mounts;
> + while (tmp) {
> + char *dir = tmp->data;
> +
> + if (debug)
> + fprintf(stderr, "Unmounting %s\n", dir);
> + if (umount(dir) < 0) {
> + /* We expect some failures, so don't pollute
> + * logs with them uneccessarily
> + */
> + if (debug || errno != EBUSY)
> + fprintf(stderr, "cannot unmount %s: %s\n",
> + dir, strerror(errno));
> + /* ignore failure */
> + }
> + g_free(dir);
> +
> + tmp = tmp->next;
> + }
> +
> + g_list_free(mounts);
> + if (debug)
> + fprintf(stderr, "Unmounting complete\n");
> +}
>
>
> static gssize read_data(int fd, char *buf, size_t len)
> @@ -1204,6 +1360,7 @@ static void libvirt_sandbox_version(void)
>
> int main(int argc, char **argv) {
> gchar *configfile = NULL;
> + gboolean poweroff = FALSE;
> GError *error = NULL;
> GOptionContext *context;
> GOptionEntry options[] = {
> @@ -1215,6 +1372,8 @@ int main(int argc, char **argv) {
> N_("display debugging information"), NULL },
> { "config", 'c', 0, G_OPTION_ARG_STRING, &configfile,
> N_("config file path"), "URI"},
> + { "poweroff", 'p', 0, G_OPTION_ARG_NONE, &poweroff,
> + N_("clean power off when exiting"), NULL},
> { NULL, 0, 0, G_OPTION_ARG_NONE, NULL, NULL, NULL }
> };
> const char *help_msg = N_("Run '" PACKAGE " --help' to see a full list of available command line options");
> @@ -1290,6 +1449,13 @@ int main(int argc, char **argv) {
> if (error)
> g_error_free(error);
>
> + sync_data();
> +
> + if (poweroff) {
> + umount_fs();
> + reboot(RB_POWER_OFF);
> + /* Should not be reached, but if it is, kernel will panic anyway */
> + }
> return ret;
>
> error:
> diff --git a/libvirt-sandbox/libvirt-sandbox-init-qemu.c b/libvirt-sandbox/libvirt-sandbox-init-qemu.c
> index cd6055a..8bde224 100644
> --- a/libvirt-sandbox/libvirt-sandbox-init-qemu.c
> +++ b/libvirt-sandbox/libvirt-sandbox-init-qemu.c
> @@ -424,6 +424,7 @@ main(int argc ATTR_UNUSED, char **argv ATTR_UNUSED)
>
> args[narg++] = SANDBOXCONFIGDIR "/.libs/ld.so";
> args[narg++] = SANDBOXCONFIGDIR "/.libs/libvirt-sandbox-init-common";
> + args[narg++] = "--poweroff";
> if (debug)
> args[narg++] = "-d";
>
ACK
--
Cedric
More information about the libvir-list
mailing list