From: "Dr. David Alan Gilbert" <dgilb...@redhat.com> Provide a check to see if the OS we're running on has all the bits needed for postcopy.
Creates postcopy-ram.c which will get most of the other helpers we need. Signed-off-by: Dr. David Alan Gilbert <dgilb...@redhat.com> --- Makefile.objs | 2 +- include/migration/postcopy-ram.h | 19 +++++ postcopy-ram.c | 160 +++++++++++++++++++++++++++++++++++++++ savevm.c | 6 ++ 4 files changed, 186 insertions(+), 1 deletion(-) create mode 100644 include/migration/postcopy-ram.h create mode 100644 postcopy-ram.c diff --git a/Makefile.objs b/Makefile.objs index 97db978..fa0a3a0 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -54,7 +54,7 @@ common-obj-y += qemu-file.o common-obj-$(CONFIG_RDMA) += migration-rdma.o common-obj-y += qemu-char.o #aio.o common-obj-y += block-migration.o -common-obj-y += page_cache.o xbzrle.o +common-obj-y += page_cache.o xbzrle.o postcopy-ram.o common-obj-$(CONFIG_POSIX) += migration-exec.o migration-unix.o migration-fd.o diff --git a/include/migration/postcopy-ram.h b/include/migration/postcopy-ram.h new file mode 100644 index 0000000..dcd1afa --- /dev/null +++ b/include/migration/postcopy-ram.h @@ -0,0 +1,19 @@ +/* + * Postcopy migration for RAM + * + * Copyright 2013 Red Hat, Inc. and/or its affiliates + * + * Authors: + * Dave Gilbert <dgilb...@redhat.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ +#ifndef QEMU_POSTCOPY_RAM_H +#define QEMU_POSTCOPY_RAM_H + +/* Return 0 if the host supports everything we need to do postcopy-ram */ +int postcopy_ram_hosttest(void); + +#endif diff --git a/postcopy-ram.c b/postcopy-ram.c new file mode 100644 index 0000000..bba5c71 --- /dev/null +++ b/postcopy-ram.c @@ -0,0 +1,160 @@ +/* + * Postcopy migration for RAM + * + * Copyright 2013-2014 Red Hat, Inc. and/or its affiliates + * + * Authors: + * Dave Gilbert <dgilb...@redhat.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +/* + * Postcopy is a migration technique where the execution flips from the + * source to the destination before all the data has been copied. + */ + +#include <glib.h> +#include <stdio.h> +#include <unistd.h> + +#include "qemu-common.h" +#include "migration/migration.h" +#include "migration/postcopy-ram.h" + +//#define DEBUG_POSTCOPY + +#ifdef DEBUG_POSTCOPY +#define DPRINTF(fmt, ...) \ + do { fprintf(stderr, "postcopy@%" PRId64 " " fmt "\n", \ + qemu_clock_get_ms(QEMU_CLOCK_REALTIME), \ + ## __VA_ARGS__); } while (0) +#else +#define DPRINTF(fmt, ...) \ + do { } while (0) +#endif + +/* Postcopy needs to detect accesses to pages that haven't yet been copied + * across, and efficiently map new pages in, the techniques for doing this + * are target OS specific. + */ +#if defined(__linux__) + +/* On Linux we use: + * madvise MADV_USERFAULT - to mark an area of anonymous memory such + * that userspace is notifed of accesses to + * unallocated areas. + * userfaultfd - opens a socket to receive USERFAULT messages + * remap_anon_pages - to shuffle mapped pages into previously unallocated + * areas without creating loads of VMAs. + */ + +#include <sys/mman.h> +#include <sys/types.h> + +/* TODO remove once we have libc defs */ + +#ifdef HOST_X86_64 + /* NOTE: These are Andrea's 3.15.0 world */ +#ifndef MADV_USERFAULT +#define MADV_USERFAULT 18 +#define MADV_NOUSERFAULT 19 +#endif + +#ifndef __NR_remap_anon_pages +#define __NR_remap_anon_pages 321 +#endif + +#ifndef __NR_userfaultfd +#define __NR_userfaultfd 322 +#endif + +#endif + +#ifndef USERFAULTFD_PROTOCOL +#define USERFAULTFD_PROTOCOL (uint64_t)0xaa +#endif + +#endif + +#if defined(__linux__) && defined(MADV_USERFAULT) && \ + defined(__NR_remap_anon_pages) + +int postcopy_ram_hosttest(void) +{ + /* TODO: Needs guarding with CONFIG_ once we have libc's that have the defs + * + * Try each syscall we need, but this isn't a testbench, + * just enough to see that we have the calls + */ + void *testarea = NULL, *testarea2 = NULL; + long pagesize = getpagesize(); + int ufd = -1; + int ret = -1; /* Error unless we change it */ + + testarea = mmap(NULL, pagesize, PROT_READ | PROT_WRITE, MAP_PRIVATE | + MAP_ANONYMOUS, -1, 0); + if (!testarea) { + perror("postcopy_ram_hosttest: Failed to map test area"); + goto out; + } + g_assert(((size_t)testarea & (pagesize-1)) == 0); + + ufd = syscall(__NR_userfaultfd, O_CLOEXEC); + if (ufd == -1) { + perror("postcopy_ram_hosttest: userfaultfd not available"); + goto out; + } + + if (madvise(testarea, pagesize, MADV_USERFAULT)) { + perror("postcopy_ram_hosttest: MADV_USERFAULT not available"); + goto out; + } + + if (madvise(testarea, pagesize, MADV_NOUSERFAULT)) { + perror("postcopy_ram_hosttest: MADV_NOUSERFAULT not available"); + goto out; + } + + testarea2 = mmap(NULL, pagesize, PROT_READ | PROT_WRITE, MAP_PRIVATE | + MAP_ANONYMOUS, -1, 0); + if (!testarea2) { + perror("postcopy_ram_hosttest: Failed to map second test area"); + goto out; + } + g_assert(((size_t)testarea2 & (pagesize-1)) == 0); + *(char *)testarea = 0; /* Force the map of the new page */ + if (syscall(__NR_remap_anon_pages, testarea2, testarea, pagesize, 0) != + pagesize) { + perror("postcopy_ram_hosttest: remap_anon_pages not available"); + goto out; + } + + /* Success! */ + ret = 0; +out: + if (testarea) { + munmap(testarea, pagesize); + } + if (testarea2) { + munmap(testarea2, pagesize); + } + if (ufd != -1) { + close(ufd); + } + return ret; +} + +#else +/* No target OS support, stubs just fail */ + +int postcopy_ram_hosttest(void) +{ + error_report("postcopy_ram_hosttest: No OS support"); + return -1; +} + +#endif + diff --git a/savevm.c b/savevm.c index 1642a59..a0cb88b 100644 --- a/savevm.c +++ b/savevm.c @@ -33,6 +33,7 @@ #include "qemu/timer.h" #include "audio/audio.h" #include "migration/migration.h" +#include "migration/postcopy-ram.h" #include "qemu/sockets.h" #include "qemu/queue.h" #include "sysemu/cpus.h" @@ -1087,6 +1088,11 @@ static int loadvm_postcopy_ram_handle_advise(MigrationIncomingState *mis, return -1; } + /* Check this host can do it */ + if (postcopy_ram_hosttest()) { + return -1; + } + if (remote_hps != sysconf(_SC_PAGESIZE)) { /* * Some combinations of mismatch are probably possible but it gets -- 1.9.3