]>
Commit | Line | Data |
---|---|---|
1f0776f1 JQ |
1 | /* |
2 | * QEMU migration capabilities | |
3 | * | |
4 | * Copyright (c) 2012-2023 Red Hat Inc | |
5 | * | |
6 | * Authors: | |
7 | * Orit Wasserman <owasserm@redhat.com> | |
8 | * Juan Quintela <quintela@redhat.com> | |
9 | * | |
10 | * This work is licensed under the terms of the GNU GPL, version 2 or later. | |
11 | * See the COPYING file in the top-level directory. | |
12 | */ | |
13 | ||
14 | #include "qemu/osdep.h" | |
77608706 | 15 | #include "qapi/error.h" |
4d0c6b69 | 16 | #include "qapi/qapi-commands-migration.h" |
f80196b7 | 17 | #include "qapi/qmp/qerror.h" |
77608706 | 18 | #include "sysemu/runstate.h" |
1f0776f1 | 19 | #include "migration.h" |
77608706 | 20 | #include "ram.h" |
1f0776f1 JQ |
21 | #include "options.h" |
22 | ||
23 | bool migrate_auto_converge(void) | |
24 | { | |
25 | MigrationState *s; | |
26 | ||
27 | s = migrate_get_current(); | |
28 | ||
29 | return s->capabilities[MIGRATION_CAPABILITY_AUTO_CONVERGE]; | |
30 | } | |
31 | ||
32 | bool migrate_background_snapshot(void) | |
33 | { | |
34 | MigrationState *s; | |
35 | ||
36 | s = migrate_get_current(); | |
37 | ||
38 | return s->capabilities[MIGRATION_CAPABILITY_BACKGROUND_SNAPSHOT]; | |
39 | } | |
40 | ||
9d4b1e5f JQ |
41 | bool migrate_block(void) |
42 | { | |
43 | MigrationState *s; | |
44 | ||
45 | s = migrate_get_current(); | |
46 | ||
47 | return s->capabilities[MIGRATION_CAPABILITY_BLOCK]; | |
48 | } | |
49 | ||
5e804644 JQ |
50 | bool migrate_colo(void) |
51 | { | |
52 | MigrationState *s = migrate_get_current(); | |
53 | return s->capabilities[MIGRATION_CAPABILITY_X_COLO]; | |
54 | } | |
55 | ||
a7a94d14 JQ |
56 | bool migrate_compress(void) |
57 | { | |
58 | MigrationState *s; | |
59 | ||
60 | s = migrate_get_current(); | |
61 | ||
62 | return s->capabilities[MIGRATION_CAPABILITY_COMPRESS]; | |
63 | } | |
64 | ||
1f0776f1 JQ |
65 | bool migrate_dirty_bitmaps(void) |
66 | { | |
67 | MigrationState *s; | |
68 | ||
69 | s = migrate_get_current(); | |
70 | ||
71 | return s->capabilities[MIGRATION_CAPABILITY_DIRTY_BITMAPS]; | |
72 | } | |
73 | ||
b890902c JQ |
74 | bool migrate_events(void) |
75 | { | |
76 | MigrationState *s; | |
77 | ||
78 | s = migrate_get_current(); | |
79 | ||
80 | return s->capabilities[MIGRATION_CAPABILITY_EVENTS]; | |
81 | } | |
82 | ||
1f0776f1 JQ |
83 | bool migrate_ignore_shared(void) |
84 | { | |
85 | MigrationState *s; | |
86 | ||
87 | s = migrate_get_current(); | |
88 | ||
89 | return s->capabilities[MIGRATION_CAPABILITY_X_IGNORE_SHARED]; | |
90 | } | |
91 | ||
92 | bool migrate_late_block_activate(void) | |
93 | { | |
94 | MigrationState *s; | |
95 | ||
96 | s = migrate_get_current(); | |
97 | ||
98 | return s->capabilities[MIGRATION_CAPABILITY_LATE_BLOCK_ACTIVATE]; | |
99 | } | |
100 | ||
51b07548 JQ |
101 | bool migrate_multifd(void) |
102 | { | |
103 | MigrationState *s; | |
104 | ||
105 | s = migrate_get_current(); | |
106 | ||
107 | return s->capabilities[MIGRATION_CAPABILITY_MULTIFD]; | |
108 | } | |
109 | ||
1f0776f1 JQ |
110 | bool migrate_pause_before_switchover(void) |
111 | { | |
112 | MigrationState *s; | |
113 | ||
114 | s = migrate_get_current(); | |
115 | ||
116 | return s->capabilities[MIGRATION_CAPABILITY_PAUSE_BEFORE_SWITCHOVER]; | |
117 | } | |
118 | ||
119 | bool migrate_postcopy_blocktime(void) | |
120 | { | |
121 | MigrationState *s; | |
122 | ||
123 | s = migrate_get_current(); | |
124 | ||
125 | return s->capabilities[MIGRATION_CAPABILITY_POSTCOPY_BLOCKTIME]; | |
126 | } | |
127 | ||
128 | bool migrate_postcopy_preempt(void) | |
129 | { | |
130 | MigrationState *s; | |
131 | ||
132 | s = migrate_get_current(); | |
133 | ||
134 | return s->capabilities[MIGRATION_CAPABILITY_POSTCOPY_PREEMPT]; | |
135 | } | |
136 | ||
137 | bool migrate_postcopy_ram(void) | |
138 | { | |
139 | MigrationState *s; | |
140 | ||
141 | s = migrate_get_current(); | |
142 | ||
143 | return s->capabilities[MIGRATION_CAPABILITY_POSTCOPY_RAM]; | |
144 | } | |
145 | ||
17cba690 JQ |
146 | bool migrate_rdma_pin_all(void) |
147 | { | |
148 | MigrationState *s = migrate_get_current(); | |
149 | ||
150 | return s->capabilities[MIGRATION_CAPABILITY_RDMA_PIN_ALL]; | |
151 | } | |
152 | ||
1f0776f1 JQ |
153 | bool migrate_release_ram(void) |
154 | { | |
155 | MigrationState *s; | |
156 | ||
157 | s = migrate_get_current(); | |
158 | ||
159 | return s->capabilities[MIGRATION_CAPABILITY_RELEASE_RAM]; | |
160 | } | |
161 | ||
38ad1110 JQ |
162 | bool migrate_return_path(void) |
163 | { | |
164 | MigrationState *s; | |
165 | ||
166 | s = migrate_get_current(); | |
167 | ||
168 | return s->capabilities[MIGRATION_CAPABILITY_RETURN_PATH]; | |
169 | } | |
170 | ||
1f0776f1 JQ |
171 | bool migrate_validate_uuid(void) |
172 | { | |
173 | MigrationState *s; | |
174 | ||
175 | s = migrate_get_current(); | |
176 | ||
177 | return s->capabilities[MIGRATION_CAPABILITY_VALIDATE_UUID]; | |
178 | } | |
179 | ||
87dca0c9 JQ |
180 | bool migrate_xbzrle(void) |
181 | { | |
182 | MigrationState *s; | |
183 | ||
184 | s = migrate_get_current(); | |
185 | ||
186 | return s->capabilities[MIGRATION_CAPABILITY_XBZRLE]; | |
187 | } | |
188 | ||
1f0776f1 JQ |
189 | bool migrate_zero_blocks(void) |
190 | { | |
191 | MigrationState *s; | |
192 | ||
193 | s = migrate_get_current(); | |
194 | ||
195 | return s->capabilities[MIGRATION_CAPABILITY_ZERO_BLOCKS]; | |
196 | } | |
b4bc342c JQ |
197 | |
198 | bool migrate_zero_copy_send(void) | |
199 | { | |
200 | MigrationState *s; | |
201 | ||
202 | s = migrate_get_current(); | |
203 | ||
204 | return s->capabilities[MIGRATION_CAPABILITY_ZERO_COPY_SEND]; | |
205 | } | |
77608706 JQ |
206 | typedef enum WriteTrackingSupport { |
207 | WT_SUPPORT_UNKNOWN = 0, | |
208 | WT_SUPPORT_ABSENT, | |
209 | WT_SUPPORT_AVAILABLE, | |
210 | WT_SUPPORT_COMPATIBLE | |
211 | } WriteTrackingSupport; | |
212 | ||
213 | static | |
214 | WriteTrackingSupport migrate_query_write_tracking(void) | |
215 | { | |
216 | /* Check if kernel supports required UFFD features */ | |
217 | if (!ram_write_tracking_available()) { | |
218 | return WT_SUPPORT_ABSENT; | |
219 | } | |
220 | /* | |
221 | * Check if current memory configuration is | |
222 | * compatible with required UFFD features. | |
223 | */ | |
224 | if (!ram_write_tracking_compatible()) { | |
225 | return WT_SUPPORT_AVAILABLE; | |
226 | } | |
227 | ||
228 | return WT_SUPPORT_COMPATIBLE; | |
229 | } | |
230 | ||
231 | /* Migration capabilities set */ | |
232 | struct MigrateCapsSet { | |
233 | int size; /* Capability set size */ | |
234 | MigrationCapability caps[]; /* Variadic array of capabilities */ | |
235 | }; | |
236 | typedef struct MigrateCapsSet MigrateCapsSet; | |
237 | ||
238 | /* Define and initialize MigrateCapsSet */ | |
239 | #define INITIALIZE_MIGRATE_CAPS_SET(_name, ...) \ | |
240 | MigrateCapsSet _name = { \ | |
241 | .size = sizeof((int []) { __VA_ARGS__ }) / sizeof(int), \ | |
242 | .caps = { __VA_ARGS__ } \ | |
243 | } | |
244 | ||
245 | /* Background-snapshot compatibility check list */ | |
246 | static const | |
247 | INITIALIZE_MIGRATE_CAPS_SET(check_caps_background_snapshot, | |
248 | MIGRATION_CAPABILITY_POSTCOPY_RAM, | |
249 | MIGRATION_CAPABILITY_DIRTY_BITMAPS, | |
250 | MIGRATION_CAPABILITY_POSTCOPY_BLOCKTIME, | |
251 | MIGRATION_CAPABILITY_LATE_BLOCK_ACTIVATE, | |
252 | MIGRATION_CAPABILITY_RETURN_PATH, | |
253 | MIGRATION_CAPABILITY_MULTIFD, | |
254 | MIGRATION_CAPABILITY_PAUSE_BEFORE_SWITCHOVER, | |
255 | MIGRATION_CAPABILITY_AUTO_CONVERGE, | |
256 | MIGRATION_CAPABILITY_RELEASE_RAM, | |
257 | MIGRATION_CAPABILITY_RDMA_PIN_ALL, | |
258 | MIGRATION_CAPABILITY_COMPRESS, | |
259 | MIGRATION_CAPABILITY_XBZRLE, | |
260 | MIGRATION_CAPABILITY_X_COLO, | |
261 | MIGRATION_CAPABILITY_VALIDATE_UUID, | |
262 | MIGRATION_CAPABILITY_ZERO_COPY_SEND); | |
263 | ||
264 | /** | |
265 | * @migration_caps_check - check capability compatibility | |
266 | * | |
267 | * @old_caps: old capability list | |
268 | * @new_caps: new capability list | |
269 | * @errp: set *errp if the check failed, with reason | |
270 | * | |
271 | * Returns true if check passed, otherwise false. | |
272 | */ | |
273 | bool migrate_caps_check(bool *old_caps, bool *new_caps, Error **errp) | |
274 | { | |
275 | MigrationIncomingState *mis = migration_incoming_get_current(); | |
276 | ||
277 | #ifndef CONFIG_LIVE_BLOCK_MIGRATION | |
278 | if (new_caps[MIGRATION_CAPABILITY_BLOCK]) { | |
279 | error_setg(errp, "QEMU compiled without old-style (blk/-b, inc/-i) " | |
280 | "block migration"); | |
281 | error_append_hint(errp, "Use drive_mirror+NBD instead.\n"); | |
282 | return false; | |
283 | } | |
284 | #endif | |
285 | ||
286 | #ifndef CONFIG_REPLICATION | |
287 | if (new_caps[MIGRATION_CAPABILITY_X_COLO]) { | |
288 | error_setg(errp, "QEMU compiled without replication module" | |
289 | " can't enable COLO"); | |
290 | error_append_hint(errp, "Please enable replication before COLO.\n"); | |
291 | return false; | |
292 | } | |
293 | #endif | |
294 | ||
295 | if (new_caps[MIGRATION_CAPABILITY_POSTCOPY_RAM]) { | |
296 | /* This check is reasonably expensive, so only when it's being | |
297 | * set the first time, also it's only the destination that needs | |
298 | * special support. | |
299 | */ | |
300 | if (!old_caps[MIGRATION_CAPABILITY_POSTCOPY_RAM] && | |
301 | runstate_check(RUN_STATE_INMIGRATE) && | |
302 | !postcopy_ram_supported_by_host(mis)) { | |
303 | /* postcopy_ram_supported_by_host will have emitted a more | |
304 | * detailed message | |
305 | */ | |
306 | error_setg(errp, "Postcopy is not supported"); | |
307 | return false; | |
308 | } | |
309 | ||
310 | if (new_caps[MIGRATION_CAPABILITY_X_IGNORE_SHARED]) { | |
311 | error_setg(errp, "Postcopy is not compatible with ignore-shared"); | |
312 | return false; | |
313 | } | |
314 | } | |
315 | ||
316 | if (new_caps[MIGRATION_CAPABILITY_BACKGROUND_SNAPSHOT]) { | |
317 | WriteTrackingSupport wt_support; | |
318 | int idx; | |
319 | /* | |
320 | * Check if 'background-snapshot' capability is supported by | |
321 | * host kernel and compatible with guest memory configuration. | |
322 | */ | |
323 | wt_support = migrate_query_write_tracking(); | |
324 | if (wt_support < WT_SUPPORT_AVAILABLE) { | |
325 | error_setg(errp, "Background-snapshot is not supported by host kernel"); | |
326 | return false; | |
327 | } | |
328 | if (wt_support < WT_SUPPORT_COMPATIBLE) { | |
329 | error_setg(errp, "Background-snapshot is not compatible " | |
330 | "with guest memory configuration"); | |
331 | return false; | |
332 | } | |
333 | ||
334 | /* | |
335 | * Check if there are any migration capabilities | |
336 | * incompatible with 'background-snapshot'. | |
337 | */ | |
338 | for (idx = 0; idx < check_caps_background_snapshot.size; idx++) { | |
339 | int incomp_cap = check_caps_background_snapshot.caps[idx]; | |
340 | if (new_caps[incomp_cap]) { | |
341 | error_setg(errp, | |
342 | "Background-snapshot is not compatible with %s", | |
343 | MigrationCapability_str(incomp_cap)); | |
344 | return false; | |
345 | } | |
346 | } | |
347 | } | |
348 | ||
349 | #ifdef CONFIG_LINUX | |
350 | if (new_caps[MIGRATION_CAPABILITY_ZERO_COPY_SEND] && | |
351 | (!new_caps[MIGRATION_CAPABILITY_MULTIFD] || | |
352 | new_caps[MIGRATION_CAPABILITY_COMPRESS] || | |
353 | new_caps[MIGRATION_CAPABILITY_XBZRLE] || | |
354 | migrate_multifd_compression() || | |
355 | migrate_use_tls())) { | |
356 | error_setg(errp, | |
357 | "Zero copy only available for non-compressed non-TLS multifd migration"); | |
358 | return false; | |
359 | } | |
360 | #else | |
361 | if (new_caps[MIGRATION_CAPABILITY_ZERO_COPY_SEND]) { | |
362 | error_setg(errp, | |
363 | "Zero copy currently only available on Linux"); | |
364 | return false; | |
365 | } | |
366 | #endif | |
367 | ||
368 | if (new_caps[MIGRATION_CAPABILITY_POSTCOPY_PREEMPT]) { | |
369 | if (!new_caps[MIGRATION_CAPABILITY_POSTCOPY_RAM]) { | |
370 | error_setg(errp, "Postcopy preempt requires postcopy-ram"); | |
371 | return false; | |
372 | } | |
373 | ||
374 | /* | |
375 | * Preempt mode requires urgent pages to be sent in separate | |
376 | * channel, OTOH compression logic will disorder all pages into | |
377 | * different compression channels, which is not compatible with the | |
378 | * preempt assumptions on channel assignments. | |
379 | */ | |
380 | if (new_caps[MIGRATION_CAPABILITY_COMPRESS]) { | |
381 | error_setg(errp, "Postcopy preempt not compatible with compress"); | |
382 | return false; | |
383 | } | |
384 | } | |
385 | ||
386 | if (new_caps[MIGRATION_CAPABILITY_MULTIFD]) { | |
387 | if (new_caps[MIGRATION_CAPABILITY_COMPRESS]) { | |
388 | error_setg(errp, "Multifd is not compatible with compress"); | |
389 | return false; | |
390 | } | |
391 | } | |
392 | ||
393 | return true; | |
394 | } | |
4d0c6b69 | 395 | |
f80196b7 JQ |
396 | bool migrate_cap_set(int cap, bool value, Error **errp) |
397 | { | |
398 | MigrationState *s = migrate_get_current(); | |
399 | bool new_caps[MIGRATION_CAPABILITY__MAX]; | |
400 | ||
401 | if (migration_is_running(s->state)) { | |
402 | error_setg(errp, QERR_MIGRATION_ACTIVE); | |
403 | return false; | |
404 | } | |
405 | ||
406 | memcpy(new_caps, s->capabilities, sizeof(new_caps)); | |
407 | new_caps[cap] = value; | |
408 | ||
409 | if (!migrate_caps_check(s->capabilities, new_caps, errp)) { | |
410 | return false; | |
411 | } | |
412 | s->capabilities[cap] = value; | |
413 | return true; | |
414 | } | |
415 | ||
4d0c6b69 JQ |
416 | MigrationCapabilityStatusList *qmp_query_migrate_capabilities(Error **errp) |
417 | { | |
418 | MigrationCapabilityStatusList *head = NULL, **tail = &head; | |
419 | MigrationCapabilityStatus *caps; | |
420 | MigrationState *s = migrate_get_current(); | |
421 | int i; | |
422 | ||
423 | for (i = 0; i < MIGRATION_CAPABILITY__MAX; i++) { | |
424 | #ifndef CONFIG_LIVE_BLOCK_MIGRATION | |
425 | if (i == MIGRATION_CAPABILITY_BLOCK) { | |
426 | continue; | |
427 | } | |
428 | #endif | |
429 | caps = g_malloc0(sizeof(*caps)); | |
430 | caps->capability = i; | |
431 | caps->state = s->capabilities[i]; | |
432 | QAPI_LIST_APPEND(tail, caps); | |
433 | } | |
434 | ||
435 | return head; | |
436 | } | |
45c1de13 JQ |
437 | |
438 | void qmp_migrate_set_capabilities(MigrationCapabilityStatusList *params, | |
439 | Error **errp) | |
440 | { | |
441 | MigrationState *s = migrate_get_current(); | |
442 | MigrationCapabilityStatusList *cap; | |
443 | bool new_caps[MIGRATION_CAPABILITY__MAX]; | |
444 | ||
445 | if (migration_is_running(s->state)) { | |
446 | error_setg(errp, QERR_MIGRATION_ACTIVE); | |
447 | return; | |
448 | } | |
449 | ||
450 | memcpy(new_caps, s->capabilities, sizeof(new_caps)); | |
451 | for (cap = params; cap; cap = cap->next) { | |
452 | new_caps[cap->value->capability] = cap->value->state; | |
453 | } | |
454 | ||
455 | if (!migrate_caps_check(s->capabilities, new_caps, errp)) { | |
456 | return; | |
457 | } | |
458 | ||
459 | for (cap = params; cap; cap = cap->next) { | |
460 | s->capabilities[cap->value->capability] = cap->value->state; | |
461 | } | |
462 | } | |
1dfc4b9e JQ |
463 | |
464 | /* parameters */ | |
465 | ||
6f8be708 JQ |
466 | bool migrate_block_incremental(void) |
467 | { | |
468 | MigrationState *s; | |
469 | ||
470 | s = migrate_get_current(); | |
471 | ||
472 | return s->parameters.block_incremental; | |
473 | } | |
474 | ||
f94a858f JQ |
475 | uint32_t migrate_checkpoint_delay(void) |
476 | { | |
477 | MigrationState *s; | |
478 | ||
479 | s = migrate_get_current(); | |
480 | ||
481 | return s->parameters.x_checkpoint_delay; | |
482 | } | |
483 | ||
1dfc4b9e JQ |
484 | int migrate_compress_level(void) |
485 | { | |
486 | MigrationState *s; | |
487 | ||
488 | s = migrate_get_current(); | |
489 | ||
490 | return s->parameters.compress_level; | |
491 | } | |
492 | ||
493 | int migrate_compress_threads(void) | |
494 | { | |
495 | MigrationState *s; | |
496 | ||
497 | s = migrate_get_current(); | |
498 | ||
499 | return s->parameters.compress_threads; | |
500 | } | |
501 | ||
502 | int migrate_compress_wait_thread(void) | |
503 | { | |
504 | MigrationState *s; | |
505 | ||
506 | s = migrate_get_current(); | |
507 | ||
508 | return s->parameters.compress_wait_thread; | |
509 | } | |
510 | ||
511 | int migrate_decompress_threads(void) | |
512 | { | |
513 | MigrationState *s; | |
514 | ||
515 | s = migrate_get_current(); | |
516 | ||
517 | return s->parameters.decompress_threads; | |
518 | } | |
519 | ||
520 | int64_t migrate_max_postcopy_bandwidth(void) | |
521 | { | |
522 | MigrationState *s; | |
523 | ||
524 | s = migrate_get_current(); | |
525 | ||
526 | return s->parameters.max_postcopy_bandwidth; | |
527 | } | |
528 | ||
529 | int migrate_multifd_channels(void) | |
530 | { | |
531 | MigrationState *s; | |
532 | ||
533 | s = migrate_get_current(); | |
534 | ||
535 | return s->parameters.multifd_channels; | |
536 | } | |
537 | ||
538 | MultiFDCompression migrate_multifd_compression(void) | |
539 | { | |
540 | MigrationState *s; | |
541 | ||
542 | s = migrate_get_current(); | |
543 | ||
544 | assert(s->parameters.multifd_compression < MULTIFD_COMPRESSION__MAX); | |
545 | return s->parameters.multifd_compression; | |
546 | } | |
547 | ||
548 | int migrate_multifd_zlib_level(void) | |
549 | { | |
550 | MigrationState *s; | |
551 | ||
552 | s = migrate_get_current(); | |
553 | ||
554 | return s->parameters.multifd_zlib_level; | |
555 | } | |
556 | ||
557 | int migrate_multifd_zstd_level(void) | |
558 | { | |
559 | MigrationState *s; | |
560 | ||
561 | s = migrate_get_current(); | |
562 | ||
563 | return s->parameters.multifd_zstd_level; | |
564 | } | |
565 | ||
6499efdb JQ |
566 | uint8_t migrate_throttle_trigger_threshold(void) |
567 | { | |
568 | MigrationState *s; | |
569 | ||
570 | s = migrate_get_current(); | |
571 | ||
572 | return s->parameters.throttle_trigger_threshold; | |
573 | } | |
574 | ||
1dfc4b9e JQ |
575 | uint64_t migrate_xbzrle_cache_size(void) |
576 | { | |
577 | MigrationState *s; | |
578 | ||
579 | s = migrate_get_current(); | |
580 | ||
581 | return s->parameters.xbzrle_cache_size; | |
582 | } |