postcopy: OS support test
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 <dgilbert@redhat.com> Reviewed-by: Amit Shah <amit.shah@redhat.com> Reviewed-by: Juan Quintela <quintela@redhat.com> Signed-off-by: Juan Quintela <quintela@redhat.com>
This commit is contained in:
parent
c31b098f64
commit
eb59db53a4
19
include/migration/postcopy-ram.h
Normal file
19
include/migration/postcopy-ram.h
Normal file
@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Postcopy migration for RAM
|
||||
*
|
||||
* Copyright 2013 Red Hat, Inc. and/or its affiliates
|
||||
*
|
||||
* Authors:
|
||||
* Dave Gilbert <dgilbert@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 true if the host supports everything we need to do postcopy-ram */
|
||||
bool postcopy_ram_supported_by_host(void);
|
||||
|
||||
#endif
|
@ -1,7 +1,7 @@
|
||||
common-obj-y += migration.o tcp.o
|
||||
common-obj-y += vmstate.o
|
||||
common-obj-y += qemu-file.o qemu-file-buf.o qemu-file-unix.o qemu-file-stdio.o
|
||||
common-obj-y += xbzrle.o
|
||||
common-obj-y += xbzrle.o postcopy-ram.o
|
||||
|
||||
common-obj-$(CONFIG_RDMA) += rdma.o
|
||||
common-obj-$(CONFIG_POSIX) += exec.o unix.o fd.o
|
||||
|
157
migration/postcopy-ram.c
Normal file
157
migration/postcopy-ram.c
Normal file
@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Postcopy migration for RAM
|
||||
*
|
||||
* Copyright 2013-2015 Red Hat, Inc. and/or its affiliates
|
||||
*
|
||||
* Authors:
|
||||
* Dave Gilbert <dgilbert@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"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "trace.h"
|
||||
|
||||
/* 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__)
|
||||
|
||||
#include <sys/mman.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/types.h>
|
||||
#include <asm/types.h> /* for __u64 */
|
||||
#endif
|
||||
|
||||
#if defined(__linux__) && defined(__NR_userfaultfd)
|
||||
#include <linux/userfaultfd.h>
|
||||
|
||||
static bool ufd_version_check(int ufd)
|
||||
{
|
||||
struct uffdio_api api_struct;
|
||||
uint64_t ioctl_mask;
|
||||
|
||||
api_struct.api = UFFD_API;
|
||||
api_struct.features = 0;
|
||||
if (ioctl(ufd, UFFDIO_API, &api_struct)) {
|
||||
error_report("postcopy_ram_supported_by_host: UFFDIO_API failed: %s",
|
||||
strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
ioctl_mask = (__u64)1 << _UFFDIO_REGISTER |
|
||||
(__u64)1 << _UFFDIO_UNREGISTER;
|
||||
if ((api_struct.ioctls & ioctl_mask) != ioctl_mask) {
|
||||
error_report("Missing userfault features: %" PRIx64,
|
||||
(uint64_t)(~api_struct.ioctls & ioctl_mask));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool postcopy_ram_supported_by_host(void)
|
||||
{
|
||||
long pagesize = getpagesize();
|
||||
int ufd = -1;
|
||||
bool ret = false; /* Error unless we change it */
|
||||
void *testarea = NULL;
|
||||
struct uffdio_register reg_struct;
|
||||
struct uffdio_range range_struct;
|
||||
uint64_t feature_mask;
|
||||
|
||||
if ((1ul << qemu_target_page_bits()) > pagesize) {
|
||||
error_report("Target page size bigger than host page size");
|
||||
goto out;
|
||||
}
|
||||
|
||||
ufd = syscall(__NR_userfaultfd, O_CLOEXEC);
|
||||
if (ufd == -1) {
|
||||
error_report("%s: userfaultfd not available: %s", __func__,
|
||||
strerror(errno));
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Version and features check */
|
||||
if (!ufd_version_check(ufd)) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* We need to check that the ops we need are supported on anon memory
|
||||
* To do that we need to register a chunk and see the flags that
|
||||
* are returned.
|
||||
*/
|
||||
testarea = mmap(NULL, pagesize, PROT_READ | PROT_WRITE, MAP_PRIVATE |
|
||||
MAP_ANONYMOUS, -1, 0);
|
||||
if (testarea == MAP_FAILED) {
|
||||
error_report("%s: Failed to map test area: %s", __func__,
|
||||
strerror(errno));
|
||||
goto out;
|
||||
}
|
||||
g_assert(((size_t)testarea & (pagesize-1)) == 0);
|
||||
|
||||
reg_struct.range.start = (uintptr_t)testarea;
|
||||
reg_struct.range.len = pagesize;
|
||||
reg_struct.mode = UFFDIO_REGISTER_MODE_MISSING;
|
||||
|
||||
if (ioctl(ufd, UFFDIO_REGISTER, ®_struct)) {
|
||||
error_report("%s userfault register: %s", __func__, strerror(errno));
|
||||
goto out;
|
||||
}
|
||||
|
||||
range_struct.start = (uintptr_t)testarea;
|
||||
range_struct.len = pagesize;
|
||||
if (ioctl(ufd, UFFDIO_UNREGISTER, &range_struct)) {
|
||||
error_report("%s userfault unregister: %s", __func__, strerror(errno));
|
||||
goto out;
|
||||
}
|
||||
|
||||
feature_mask = (__u64)1 << _UFFDIO_WAKE |
|
||||
(__u64)1 << _UFFDIO_COPY |
|
||||
(__u64)1 << _UFFDIO_ZEROPAGE;
|
||||
if ((reg_struct.ioctls & feature_mask) != feature_mask) {
|
||||
error_report("Missing userfault map features: %" PRIx64,
|
||||
(uint64_t)(~reg_struct.ioctls & feature_mask));
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Success! */
|
||||
ret = true;
|
||||
out:
|
||||
if (testarea) {
|
||||
munmap(testarea, pagesize);
|
||||
}
|
||||
if (ufd != -1) {
|
||||
close(ufd);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
#else
|
||||
/* No target OS support, stubs just fail */
|
||||
bool postcopy_ram_supported_by_host(void)
|
||||
{
|
||||
error_report("%s: No OS support", __func__);
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -37,6 +37,7 @@
|
||||
#include "qemu/timer.h"
|
||||
#include "audio/audio.h"
|
||||
#include "migration/migration.h"
|
||||
#include "migration/postcopy-ram.h"
|
||||
#include "qapi/qmp/qerror.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/sockets.h"
|
||||
@ -1205,6 +1206,10 @@ static int loadvm_postcopy_handle_advise(MigrationIncomingState *mis)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!postcopy_ram_supported_by_host()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
remote_hps = qemu_get_be64(mis->from_src_file);
|
||||
if (remote_hps != getpagesize()) {
|
||||
/*
|
||||
|
Loading…
Reference in New Issue
Block a user