/* * Postcopy migration for RAM * * Copyright 2013-2015 Red Hat, Inc. and/or its affiliates * * Authors: * Dave Gilbert * * 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 #include #include #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 #include #include #include #include /* for __u64 */ #endif #if defined(__linux__) && defined(__NR_userfaultfd) #include 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