diff mbox

kvm tools: Use overlayfs when mounting rootfs

Message ID 1312901862-7610-1-git-send-email-levinsasha928@gmail.com (mailing list archive)
State New, archived
Headers show

Commit Message

Sasha Levin Aug. 9, 2011, 2:57 p.m. UTC
This patch uses (the not yet merged) overlayfs to allow booting complex distribution rootfs and provide
a COW layer within the rootfs.

We use overlayfs for two reasons:
 - Overwrite the /etc/fstab file so that the mounter will use the virtio-9p device as root instead of
/dev/vda.
 - Provide a COW space so that changes aren't written back to the rootfs. This allows easy testing and
work without ruining your rootfs.

Usage:
'--overlayfs [root_dir]' - Use root_dir to boot the guest, COW is provided automatically.

Please note that host kernel support needs to have overlayfs support. This feature is not yet available
as part of the mainline kernel, therefore if you'd like to try out this patch you should pull overlayfs
into your kernel tree.

Latest version of overlayfs can be found here:
git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs.git overlayfs.v11

Cc: Miklos Szeredi <miklos@szeredi.hu>
Signed-off-by: Sasha Levin <levinsasha928@gmail.com>
---
 tools/kvm/Makefile                  |    2 +-
 tools/kvm/builtin-run.c             |   87 +++++++++++++++++++++++++++++++++--
 tools/kvm/include/kvm/builtin-run.h |    5 ++
 tools/kvm/kvm-cpu.c                 |    2 +
 4 files changed, 91 insertions(+), 5 deletions(-)

Comments

Pekka Enberg Aug. 9, 2011, 3:06 p.m. UTC | #1
On Tue, Aug 9, 2011 at 5:57 PM, Sasha Levin <levinsasha928@gmail.com> wrote:
> This patch uses (the not yet merged) overlayfs to allow booting complex distribution rootfs and provide
> a COW layer within the rootfs.
>
> We use overlayfs for two reasons:
>  - Overwrite the /etc/fstab file so that the mounter will use the virtio-9p device as root instead of
> /dev/vda.
>  - Provide a COW space so that changes aren't written back to the rootfs. This allows easy testing and
> work without ruining your rootfs.
>
> Usage:
> '--overlayfs [root_dir]' - Use root_dir to boot the guest, COW is provided automatically.
>
> Please note that host kernel support needs to have overlayfs support. This feature is not yet available
> as part of the mainline kernel, therefore if you'd like to try out this patch you should pull overlayfs
> into your kernel tree.
>
> Latest version of overlayfs can be found here:
> git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs.git overlayfs.v11
>
> Cc: Miklos Szeredi <miklos@szeredi.hu>
> Signed-off-by: Sasha Levin <levinsasha928@gmail.com>
> ---
>  tools/kvm/Makefile                  |    2 +-
>  tools/kvm/builtin-run.c             |   87 +++++++++++++++++++++++++++++++++--
>  tools/kvm/include/kvm/builtin-run.h |    5 ++
>  tools/kvm/kvm-cpu.c                 |    2 +
>  4 files changed, 91 insertions(+), 5 deletions(-)
>
> diff --git a/tools/kvm/Makefile b/tools/kvm/Makefile
> index 8d45ebf..acb3aca 100644
> --- a/tools/kvm/Makefile
> +++ b/tools/kvm/Makefile
> @@ -130,7 +130,7 @@ DEFINES     += -D_GNU_SOURCE
>  DEFINES        += -DKVMTOOLS_VERSION='"$(KVMTOOLS_VERSION)"'
>
>  KVM_INCLUDE := include
> -CFLAGS += $(CPPFLAGS) $(DEFINES) -I$(KVM_INCLUDE) -I../../include -I../../arch/$(ARCH)/include/ -Os -g
> +CFLAGS += $(CPPFLAGS) $(DEFINES) -I$(KVM_INCLUDE) -I../../include -I../../arch/$(ARCH)/include/ -O0 -g

Please drop this :-)

>
>  ifneq ($(WERROR),0)
>        WARNINGS += -Werror
> diff --git a/tools/kvm/builtin-run.c b/tools/kvm/builtin-run.c
> index c8539cc..19f7d6b 100644
> --- a/tools/kvm/builtin-run.c
> +++ b/tools/kvm/builtin-run.c
> @@ -76,6 +76,7 @@ static const char *guest_mac;
>  static const char *host_mac;
>  static const char *script;
>  static const char *guest_name;
> +static const char *overlayfs;
>  static bool single_step;
>  static bool readonly_image[MAX_DISK_IMAGES];
>  static bool vnc;
> @@ -91,6 +92,13 @@ bool do_debug_print = false;
>  static int nrcpus;
>  static int vidmode = -1;
>
> +/* Apperantly <linux/fs.h> and <sys/mount.h> don't work well together */
> +int mount(const char *source, const char *target,
> +         const char *filesystemtype, unsigned long mountflags,
> +         const void *data);
> +int umount(const char *target);
> +
> +
>  static const char * const run_usage[] = {
>        "kvm run [<options>] [<kernel image>]",
>        NULL
> @@ -169,6 +177,7 @@ static const struct option options[] = {
>        OPT_BOOLEAN('\0', "balloon", &balloon, "Enable virtio balloon"),
>        OPT_BOOLEAN('\0', "vnc", &vnc, "Enable VNC framebuffer"),
>        OPT_BOOLEAN('\0', "sdl", &sdl, "Enable SDL framebuffer"),
> +       OPT_STRING('\0', "overlayfs", &overlayfs, "overlayfs", "Root FS"),
>
>        OPT_GROUP("Kernel options:"),
>        OPT_STRING('k', "kernel", &kernel_filename, "kernel",
> @@ -491,6 +500,71 @@ void kvm_run_help(void)
>        usage_with_options(run_usage, options);
>  }
>
> +int kvm_create_overlayfs(struct kvm* kvm)
> +{
> +       char tmp[PATH_MAX];
> +       char cow[PATH_MAX], overlay[PATH_MAX], rootfs[PATH_MAX];
> +
> +       /*
> +        * Create 3 directories for building our mount.
> +        * Read rootfs -> modification layer -> cow layer -> new root
> +        */
> +
> +       sprintf(cow, "%s-cow", guest_name);
> +       sprintf(rootfs, "%s-rootfs", guest_name);
> +       sprintf(overlay, "%s-overlay", guest_name);
> +
> +       if (mkdir(cow, 0777) < 0) {
> +               /* If the COW dir is already there it's ok - just use it */
> +               if (errno != EEXIST)
> +                       goto cleanup;
> +       }
> +
> +       if (mkdir(rootfs, 0777) < 0)
> +               goto cleanup;
> +
> +       if (mkdir(overlay, 0777) < 0)
> +               goto cleanup;
> +
> +       sprintf(tmp, "lowerdir=%s,upperdir=virt", overlayfs);
> +       if (mount("overlayfs", overlay, "overlayfs", MS_MGC_VAL, tmp) < 0) {
> +               printf("%d\n", errno);
> +               goto cleanup;
> +
> +       }
> +
> +       sprintf(tmp, "lowerdir=%s,upperdir=%s", overlay, cow);
> +       if (mount("overlayfs", rootfs, "overlayfs", MS_MGC_VAL, tmp) < 0)
> +               goto cleanup;
> +
> +       if (realpath(rootfs, tmp) == 0 ||
> +           virtio_9p__init(kvm, tmp, "/dev/root") < 0)
> +               die("Unable to initialize virtio 9p");
> +       using_rootfs = 1;
> +
> +       return 0;
> +
> +cleanup:
> +       kvm_remove_overlayfs();
> +
> +       return -1;
> +}
> +
> +void kvm_remove_overlayfs(void)
> +{
> +       char cow[PATH_MAX], overlay[PATH_MAX], rootfs[PATH_MAX];
> +
> +       sprintf(cow, "%s-cow", guest_name);
> +       sprintf(rootfs, "%s-rootfs", guest_name);
> +       sprintf(overlay, "%s-overlay", guest_name);
> +
> +       umount(rootfs);
> +       umount(overlay);
> +       rmdir(cow);
> +       rmdir(overlay);
> +       rmdir(rootfs);
> +}

I guess this is OK for this patch but for the long-term I think we
should make the guests persistent so that

  kvm run

picks up the previous copy-on-write overlayfs automatically. That'd
probably mean that 'kvm run' will always attempt to use a guest named
'default' that's automatically created upon the first time 'kvm run'
is invoked.

We should also make 'kvm list' print out something like

  default [powered off]

Ingo probably has some ideas on this as well.

> +
>  int kvm_cmd_run(int argc, const char **argv, const char *prefix)
>  {
>        struct virtio_net_parameters net_params;
> @@ -634,7 +708,7 @@ int kvm_cmd_run(int argc, const char **argv, const char *prefix)
>                strlcat(real_cmdline, kernel_cmdline, sizeof(real_cmdline));
>
>        hi = NULL;
> -       if (!using_rootfs && !image_filename[0]) {
> +       if (!overlayfs && !using_rootfs && !image_filename[0]) {
>                hi = host_image(real_cmdline, sizeof(real_cmdline));
>                if (hi) {
>                        image_filename[0] = hi;
> @@ -643,12 +717,17 @@ int kvm_cmd_run(int argc, const char **argv, const char *prefix)
>                }
>        }
>
> -       if (!strstr(real_cmdline, "root="))
> -               strlcat(real_cmdline, " root=/dev/vda rw ", sizeof(real_cmdline));
> +       if (overlayfs) {
> +               if (kvm_create_overlayfs(kvm) < 0)
> +                       die("Failed creating overlayfs - did you remember to apply the overlayfs patches?");
> +       }
>
> -       if (using_rootfs)
> +       if (using_rootfs || overlayfs)
>                strcat(real_cmdline, " root=/dev/root rootflags=rw,trans=virtio,version=9p2000.u rootfstype=9p");
>
> +       if (!strstr(real_cmdline, "root="))
> +               strlcat(real_cmdline, " root=/dev/vda rw ", sizeof(real_cmdline));
> +
>        if (image_count) {
>                kvm->nr_disks = image_count;
>                kvm->disks    = disk_image__open_all(image_filename, readonly_image, image_count);
> diff --git a/tools/kvm/include/kvm/builtin-run.h b/tools/kvm/include/kvm/builtin-run.h
> index d056ad4..aa12a42 100644
> --- a/tools/kvm/include/kvm/builtin-run.h
> +++ b/tools/kvm/include/kvm/builtin-run.h
> @@ -1,6 +1,11 @@
>  #ifndef __KVM_RUN_H__
>  #define __KVM_RUN_H__
>
> +struct kvm;
> +
> +void kvm_remove_overlayfs(void);
> +int kvm_create_overlayfs(struct kvm* kvm);
> +
>  int kvm_cmd_run(int argc, const char **argv, const char *prefix);
>  void kvm_run_help(void);
>
> diff --git a/tools/kvm/kvm-cpu.c b/tools/kvm/kvm-cpu.c
> index 2f5d23c..6f60138 100644
> --- a/tools/kvm/kvm-cpu.c
> +++ b/tools/kvm/kvm-cpu.c
> @@ -3,6 +3,7 @@
>  #include "kvm/symbol.h"
>  #include "kvm/util.h"
>  #include "kvm/kvm.h"
> +#include "kvm/builtin-run.h"
>
>  #include <asm/msr-index.h>
>
> @@ -422,6 +423,7 @@ static void kvm_cpu__handle_coalesced_mmio(struct kvm_cpu *cpu)
>  void kvm_cpu__reboot(void)
>  {
>        pthread_kill(kvm_cpus[0]->thread, SIGKVMEXIT);
> +       kvm_remove_overlayfs();
>  }
>
>  int kvm_cpu__start(struct kvm_cpu *cpu)
> --
> 1.7.6
>
> --
> To unsubscribe from this list: send the line "unsubscribe kvm" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>
--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/tools/kvm/Makefile b/tools/kvm/Makefile
index 8d45ebf..acb3aca 100644
--- a/tools/kvm/Makefile
+++ b/tools/kvm/Makefile
@@ -130,7 +130,7 @@  DEFINES	+= -D_GNU_SOURCE
 DEFINES	+= -DKVMTOOLS_VERSION='"$(KVMTOOLS_VERSION)"'
 
 KVM_INCLUDE := include
-CFLAGS	+= $(CPPFLAGS) $(DEFINES) -I$(KVM_INCLUDE) -I../../include -I../../arch/$(ARCH)/include/ -Os -g
+CFLAGS	+= $(CPPFLAGS) $(DEFINES) -I$(KVM_INCLUDE) -I../../include -I../../arch/$(ARCH)/include/ -O0 -g
 
 ifneq ($(WERROR),0)
 	WARNINGS += -Werror
diff --git a/tools/kvm/builtin-run.c b/tools/kvm/builtin-run.c
index c8539cc..19f7d6b 100644
--- a/tools/kvm/builtin-run.c
+++ b/tools/kvm/builtin-run.c
@@ -76,6 +76,7 @@  static const char *guest_mac;
 static const char *host_mac;
 static const char *script;
 static const char *guest_name;
+static const char *overlayfs;
 static bool single_step;
 static bool readonly_image[MAX_DISK_IMAGES];
 static bool vnc;
@@ -91,6 +92,13 @@  bool do_debug_print = false;
 static int nrcpus;
 static int vidmode = -1;
 
+/* Apperantly <linux/fs.h> and <sys/mount.h> don't work well together */
+int mount(const char *source, const char *target,
+	  const char *filesystemtype, unsigned long mountflags,
+	  const void *data);
+int umount(const char *target);
+
+
 static const char * const run_usage[] = {
 	"kvm run [<options>] [<kernel image>]",
 	NULL
@@ -169,6 +177,7 @@  static const struct option options[] = {
 	OPT_BOOLEAN('\0', "balloon", &balloon, "Enable virtio balloon"),
 	OPT_BOOLEAN('\0', "vnc", &vnc, "Enable VNC framebuffer"),
 	OPT_BOOLEAN('\0', "sdl", &sdl, "Enable SDL framebuffer"),
+	OPT_STRING('\0', "overlayfs", &overlayfs, "overlayfs", "Root FS"),
 
 	OPT_GROUP("Kernel options:"),
 	OPT_STRING('k', "kernel", &kernel_filename, "kernel",
@@ -491,6 +500,71 @@  void kvm_run_help(void)
 	usage_with_options(run_usage, options);
 }
 
+int kvm_create_overlayfs(struct kvm* kvm)
+{
+	char tmp[PATH_MAX];
+	char cow[PATH_MAX], overlay[PATH_MAX], rootfs[PATH_MAX];
+
+	/*
+	 * Create 3 directories for building our mount.
+	 * Read rootfs -> modification layer -> cow layer -> new root
+	 */
+
+	sprintf(cow, "%s-cow", guest_name);
+	sprintf(rootfs, "%s-rootfs", guest_name);
+	sprintf(overlay, "%s-overlay", guest_name);
+
+	if (mkdir(cow, 0777) < 0) {
+		/* If the COW dir is already there it's ok - just use it */
+		if (errno != EEXIST)
+			goto cleanup;
+	}
+
+	if (mkdir(rootfs, 0777) < 0)
+		goto cleanup;
+
+	if (mkdir(overlay, 0777) < 0)
+		goto cleanup;
+
+	sprintf(tmp, "lowerdir=%s,upperdir=virt", overlayfs);
+	if (mount("overlayfs", overlay, "overlayfs", MS_MGC_VAL, tmp) < 0) {
+		printf("%d\n", errno);
+		goto cleanup;
+		
+	}
+
+	sprintf(tmp, "lowerdir=%s,upperdir=%s", overlay, cow);
+	if (mount("overlayfs", rootfs, "overlayfs", MS_MGC_VAL, tmp) < 0)
+		goto cleanup;
+
+	if (realpath(rootfs, tmp) == 0 ||
+	    virtio_9p__init(kvm, tmp, "/dev/root") < 0)
+		die("Unable to initialize virtio 9p");
+	using_rootfs = 1;
+
+	return 0;
+		
+cleanup:
+	kvm_remove_overlayfs();
+
+	return -1;
+}
+
+void kvm_remove_overlayfs(void)
+{
+	char cow[PATH_MAX], overlay[PATH_MAX], rootfs[PATH_MAX];
+
+	sprintf(cow, "%s-cow", guest_name);
+	sprintf(rootfs, "%s-rootfs", guest_name);
+	sprintf(overlay, "%s-overlay", guest_name);
+
+	umount(rootfs);
+	umount(overlay);
+	rmdir(cow);
+	rmdir(overlay);
+	rmdir(rootfs);
+}
+
 int kvm_cmd_run(int argc, const char **argv, const char *prefix)
 {
 	struct virtio_net_parameters net_params;
@@ -634,7 +708,7 @@  int kvm_cmd_run(int argc, const char **argv, const char *prefix)
 		strlcat(real_cmdline, kernel_cmdline, sizeof(real_cmdline));
 
 	hi = NULL;
-	if (!using_rootfs && !image_filename[0]) {
+	if (!overlayfs && !using_rootfs && !image_filename[0]) {
 		hi = host_image(real_cmdline, sizeof(real_cmdline));
 		if (hi) {
 			image_filename[0] = hi;
@@ -643,12 +717,17 @@  int kvm_cmd_run(int argc, const char **argv, const char *prefix)
 		}
 	}
 
-	if (!strstr(real_cmdline, "root="))
-		strlcat(real_cmdline, " root=/dev/vda rw ", sizeof(real_cmdline));
+	if (overlayfs) {
+		if (kvm_create_overlayfs(kvm) < 0)
+			die("Failed creating overlayfs - did you remember to apply the overlayfs patches?");
+	}
 
-	if (using_rootfs)
+	if (using_rootfs || overlayfs)
 		strcat(real_cmdline, " root=/dev/root rootflags=rw,trans=virtio,version=9p2000.u rootfstype=9p");
 
+	if (!strstr(real_cmdline, "root="))
+		strlcat(real_cmdline, " root=/dev/vda rw ", sizeof(real_cmdline));
+
 	if (image_count) {
 		kvm->nr_disks = image_count;
 		kvm->disks    = disk_image__open_all(image_filename, readonly_image, image_count);
diff --git a/tools/kvm/include/kvm/builtin-run.h b/tools/kvm/include/kvm/builtin-run.h
index d056ad4..aa12a42 100644
--- a/tools/kvm/include/kvm/builtin-run.h
+++ b/tools/kvm/include/kvm/builtin-run.h
@@ -1,6 +1,11 @@ 
 #ifndef __KVM_RUN_H__
 #define __KVM_RUN_H__
 
+struct kvm;
+
+void kvm_remove_overlayfs(void);
+int kvm_create_overlayfs(struct kvm* kvm);
+
 int kvm_cmd_run(int argc, const char **argv, const char *prefix);
 void kvm_run_help(void);
 
diff --git a/tools/kvm/kvm-cpu.c b/tools/kvm/kvm-cpu.c
index 2f5d23c..6f60138 100644
--- a/tools/kvm/kvm-cpu.c
+++ b/tools/kvm/kvm-cpu.c
@@ -3,6 +3,7 @@ 
 #include "kvm/symbol.h"
 #include "kvm/util.h"
 #include "kvm/kvm.h"
+#include "kvm/builtin-run.h"
 
 #include <asm/msr-index.h>
 
@@ -422,6 +423,7 @@  static void kvm_cpu__handle_coalesced_mmio(struct kvm_cpu *cpu)
 void kvm_cpu__reboot(void)
 {
 	pthread_kill(kvm_cpus[0]->thread, SIGKVMEXIT);
+	kvm_remove_overlayfs();
 }
 
 int kvm_cpu__start(struct kvm_cpu *cpu)