1
2
3
4
5
6
7
8#include "qemu/osdep.h"
9#include "qapi/error.h"
10#include "hw/vfio/vfio-device.h"
11#include "migration/cpr.h"
12#include "migration/misc.h"
13#include "migration/options.h"
14#include "migration/qemu-file.h"
15#include "migration/savevm.h"
16#include "migration/vmstate.h"
17#include "monitor/monitor.h"
18#include "system/runstate.h"
19#include "trace.h"
20
21
22
23
24CprState cpr_state;
25
26
27
28typedef struct CprFd {
29 char *name;
30 unsigned int namelen;
31 int id;
32 int fd;
33 QLIST_ENTRY(CprFd) next;
34} CprFd;
35
36static const VMStateDescription vmstate_cpr_fd = {
37 .name = "cpr fd",
38 .version_id = 1,
39 .minimum_version_id = 1,
40 .fields = (VMStateField[]) {
41 VMSTATE_UINT32(namelen, CprFd),
42 VMSTATE_VBUFFER_ALLOC_UINT32(name, CprFd, 0, NULL, namelen),
43 VMSTATE_INT32(id, CprFd),
44 VMSTATE_FD(fd, CprFd),
45 VMSTATE_END_OF_LIST()
46 }
47};
48
49void cpr_save_fd(const char *name, int id, int fd)
50{
51 CprFd *elem = g_new0(CprFd, 1);
52
53 trace_cpr_save_fd(name, id, fd);
54 elem->name = g_strdup(name);
55 elem->namelen = strlen(name) + 1;
56 elem->id = id;
57 elem->fd = fd;
58 QLIST_INSERT_HEAD(&cpr_state.fds, elem, next);
59}
60
61static CprFd *find_fd(CprFdList *head, const char *name, int id)
62{
63 CprFd *elem;
64
65 QLIST_FOREACH(elem, head, next) {
66 if (!strcmp(elem->name, name) && elem->id == id) {
67 return elem;
68 }
69 }
70 return NULL;
71}
72
73void cpr_delete_fd(const char *name, int id)
74{
75 CprFd *elem = find_fd(&cpr_state.fds, name, id);
76
77 if (elem) {
78 QLIST_REMOVE(elem, next);
79 g_free(elem->name);
80 g_free(elem);
81 }
82
83 trace_cpr_delete_fd(name, id);
84}
85
86int cpr_find_fd(const char *name, int id)
87{
88 CprFd *elem = find_fd(&cpr_state.fds, name, id);
89 int fd = elem ? elem->fd : -1;
90
91 trace_cpr_find_fd(name, id, fd);
92 return fd;
93}
94
95void cpr_resave_fd(const char *name, int id, int fd)
96{
97 CprFd *elem = find_fd(&cpr_state.fds, name, id);
98 int old_fd = elem ? elem->fd : -1;
99
100 if (old_fd < 0) {
101 cpr_save_fd(name, id, fd);
102 } else if (old_fd != fd) {
103 error_setg(&error_fatal,
104 "internal error: cpr fd '%s' id %d value %d "
105 "already saved with a different value %d",
106 name, id, fd, old_fd);
107 }
108}
109
110int cpr_open_fd(const char *path, int flags, const char *name, int id,
111 Error **errp)
112{
113 int fd = cpr_find_fd(name, id);
114
115 if (fd < 0) {
116 fd = qemu_open(path, flags, errp);
117 if (fd >= 0) {
118 cpr_save_fd(name, id, fd);
119 }
120 }
121 return fd;
122}
123
124
125static const VMStateDescription vmstate_cpr_state = {
126 .name = CPR_STATE,
127 .version_id = 1,
128 .minimum_version_id = 1,
129 .fields = (VMStateField[]) {
130 VMSTATE_QLIST_V(fds, CprState, 1, vmstate_cpr_fd, CprFd, next),
131 VMSTATE_END_OF_LIST()
132 },
133 .subsections = (const VMStateDescription * const []) {
134 &vmstate_cpr_vfio_devices,
135 NULL
136 }
137};
138
139
140static QEMUFile *cpr_state_file;
141
142QIOChannel *cpr_state_ioc(void)
143{
144 return qemu_file_get_ioc(cpr_state_file);
145}
146
147static MigMode incoming_mode = MIG_MODE_NONE;
148
149MigMode cpr_get_incoming_mode(void)
150{
151 return incoming_mode;
152}
153
154void cpr_set_incoming_mode(MigMode mode)
155{
156 incoming_mode = mode;
157}
158
159bool cpr_is_incoming(void)
160{
161 return incoming_mode != MIG_MODE_NONE;
162}
163
164int cpr_state_save(MigrationChannel *channel, Error **errp)
165{
166 int ret;
167 QEMUFile *f;
168 MigMode mode = migrate_mode();
169
170 trace_cpr_state_save(MigMode_str(mode));
171
172 if (mode == MIG_MODE_CPR_TRANSFER) {
173 g_assert(channel);
174 f = cpr_transfer_output(channel, errp);
175 } else {
176 return 0;
177 }
178 if (!f) {
179 return -1;
180 }
181
182 qemu_put_be32(f, QEMU_CPR_FILE_MAGIC);
183 qemu_put_be32(f, QEMU_CPR_FILE_VERSION);
184
185 ret = vmstate_save_state(f, &vmstate_cpr_state, &cpr_state, 0);
186 if (ret) {
187 error_setg(errp, "vmstate_save_state error %d", ret);
188 qemu_fclose(f);
189 return ret;
190 }
191
192
193
194
195
196 qemu_fflush(f);
197 qio_channel_shutdown(qemu_file_get_ioc(f), QIO_CHANNEL_SHUTDOWN_WRITE,
198 NULL);
199 cpr_state_file = f;
200 return 0;
201}
202
203int cpr_state_load(MigrationChannel *channel, Error **errp)
204{
205 int ret;
206 uint32_t v;
207 QEMUFile *f;
208 MigMode mode = 0;
209
210 if (channel) {
211 mode = MIG_MODE_CPR_TRANSFER;
212 cpr_set_incoming_mode(mode);
213 f = cpr_transfer_input(channel, errp);
214 } else {
215 return 0;
216 }
217 if (!f) {
218 return -1;
219 }
220
221 trace_cpr_state_load(MigMode_str(mode));
222
223 v = qemu_get_be32(f);
224 if (v != QEMU_CPR_FILE_MAGIC) {
225 error_setg(errp, "Not a migration stream (bad magic %x)", v);
226 qemu_fclose(f);
227 return -EINVAL;
228 }
229 v = qemu_get_be32(f);
230 if (v != QEMU_CPR_FILE_VERSION) {
231 error_setg(errp, "Unsupported migration stream version %d", v);
232 qemu_fclose(f);
233 return -ENOTSUP;
234 }
235
236 ret = vmstate_load_state(f, &vmstate_cpr_state, &cpr_state, 1);
237 if (ret) {
238 error_setg(errp, "vmstate_load_state error %d", ret);
239 qemu_fclose(f);
240 return ret;
241 }
242
243
244
245
246
247 cpr_state_file = f;
248
249 return ret;
250}
251
252void cpr_state_close(void)
253{
254 if (cpr_state_file) {
255 qemu_fclose(cpr_state_file);
256 cpr_state_file = NULL;
257 }
258}
259
260bool cpr_incoming_needed(void *opaque)
261{
262 MigMode mode = migrate_mode();
263 return mode == MIG_MODE_CPR_TRANSFER;
264}
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280int cpr_get_fd_param(const char *name, const char *fdname, int index,
281 Error **errp)
282{
283 ERRP_GUARD();
284 int fd;
285
286 if (cpr_is_incoming()) {
287 fd = cpr_find_fd(name, index);
288 if (fd < 0) {
289 error_setg(errp, "cannot find saved value for fd %s", fdname);
290 }
291 } else {
292 fd = monitor_fd_param(monitor_cur(), fdname, errp);
293 if (fd >= 0) {
294 cpr_save_fd(name, index, fd);
295 } else {
296 error_prepend(errp, "Could not parse object fd %s:", fdname);
297 }
298 }
299 return fd;
300}
301