mirror of
https://github.com/qemu/qemu.git
synced 2024-12-14 06:53:43 +08:00
158 lines
4.2 KiB
C
158 lines
4.2 KiB
C
|
/*
|
||
|
* 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
|
||
|
|