]> git.proxmox.com Git - mirror_qemu.git/blob - migration/postcopy-ram.c
postcopy: OS support test
[mirror_qemu.git] / migration / postcopy-ram.c
1 /*
2 * Postcopy migration for RAM
3 *
4 * Copyright 2013-2015 Red Hat, Inc. and/or its affiliates
5 *
6 * Authors:
7 * Dave Gilbert <dgilbert@redhat.com>
8 *
9 * This work is licensed under the terms of the GNU GPL, version 2 or later.
10 * See the COPYING file in the top-level directory.
11 *
12 */
13
14 /*
15 * Postcopy is a migration technique where the execution flips from the
16 * source to the destination before all the data has been copied.
17 */
18
19 #include <glib.h>
20 #include <stdio.h>
21 #include <unistd.h>
22
23 #include "qemu-common.h"
24 #include "migration/migration.h"
25 #include "migration/postcopy-ram.h"
26 #include "sysemu/sysemu.h"
27 #include "qemu/error-report.h"
28 #include "trace.h"
29
30 /* Postcopy needs to detect accesses to pages that haven't yet been copied
31 * across, and efficiently map new pages in, the techniques for doing this
32 * are target OS specific.
33 */
34 #if defined(__linux__)
35
36 #include <sys/mman.h>
37 #include <sys/ioctl.h>
38 #include <sys/syscall.h>
39 #include <sys/types.h>
40 #include <asm/types.h> /* for __u64 */
41 #endif
42
43 #if defined(__linux__) && defined(__NR_userfaultfd)
44 #include <linux/userfaultfd.h>
45
46 static bool ufd_version_check(int ufd)
47 {
48 struct uffdio_api api_struct;
49 uint64_t ioctl_mask;
50
51 api_struct.api = UFFD_API;
52 api_struct.features = 0;
53 if (ioctl(ufd, UFFDIO_API, &api_struct)) {
54 error_report("postcopy_ram_supported_by_host: UFFDIO_API failed: %s",
55 strerror(errno));
56 return false;
57 }
58
59 ioctl_mask = (__u64)1 << _UFFDIO_REGISTER |
60 (__u64)1 << _UFFDIO_UNREGISTER;
61 if ((api_struct.ioctls & ioctl_mask) != ioctl_mask) {
62 error_report("Missing userfault features: %" PRIx64,
63 (uint64_t)(~api_struct.ioctls & ioctl_mask));
64 return false;
65 }
66
67 return true;
68 }
69
70 bool postcopy_ram_supported_by_host(void)
71 {
72 long pagesize = getpagesize();
73 int ufd = -1;
74 bool ret = false; /* Error unless we change it */
75 void *testarea = NULL;
76 struct uffdio_register reg_struct;
77 struct uffdio_range range_struct;
78 uint64_t feature_mask;
79
80 if ((1ul << qemu_target_page_bits()) > pagesize) {
81 error_report("Target page size bigger than host page size");
82 goto out;
83 }
84
85 ufd = syscall(__NR_userfaultfd, O_CLOEXEC);
86 if (ufd == -1) {
87 error_report("%s: userfaultfd not available: %s", __func__,
88 strerror(errno));
89 goto out;
90 }
91
92 /* Version and features check */
93 if (!ufd_version_check(ufd)) {
94 goto out;
95 }
96
97 /*
98 * We need to check that the ops we need are supported on anon memory
99 * To do that we need to register a chunk and see the flags that
100 * are returned.
101 */
102 testarea = mmap(NULL, pagesize, PROT_READ | PROT_WRITE, MAP_PRIVATE |
103 MAP_ANONYMOUS, -1, 0);
104 if (testarea == MAP_FAILED) {
105 error_report("%s: Failed to map test area: %s", __func__,
106 strerror(errno));
107 goto out;
108 }
109 g_assert(((size_t)testarea & (pagesize-1)) == 0);
110
111 reg_struct.range.start = (uintptr_t)testarea;
112 reg_struct.range.len = pagesize;
113 reg_struct.mode = UFFDIO_REGISTER_MODE_MISSING;
114
115 if (ioctl(ufd, UFFDIO_REGISTER, &reg_struct)) {
116 error_report("%s userfault register: %s", __func__, strerror(errno));
117 goto out;
118 }
119
120 range_struct.start = (uintptr_t)testarea;
121 range_struct.len = pagesize;
122 if (ioctl(ufd, UFFDIO_UNREGISTER, &range_struct)) {
123 error_report("%s userfault unregister: %s", __func__, strerror(errno));
124 goto out;
125 }
126
127 feature_mask = (__u64)1 << _UFFDIO_WAKE |
128 (__u64)1 << _UFFDIO_COPY |
129 (__u64)1 << _UFFDIO_ZEROPAGE;
130 if ((reg_struct.ioctls & feature_mask) != feature_mask) {
131 error_report("Missing userfault map features: %" PRIx64,
132 (uint64_t)(~reg_struct.ioctls & feature_mask));
133 goto out;
134 }
135
136 /* Success! */
137 ret = true;
138 out:
139 if (testarea) {
140 munmap(testarea, pagesize);
141 }
142 if (ufd != -1) {
143 close(ufd);
144 }
145 return ret;
146 }
147
148 #else
149 /* No target OS support, stubs just fail */
150 bool postcopy_ram_supported_by_host(void)
151 {
152 error_report("%s: No OS support", __func__);
153 return false;
154 }
155
156 #endif
157