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
					
				| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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