1 /*
2 * qemu_migration_params.c: QEMU migration parameters handling
3 *
4 * Copyright (C) 2006-2018 Red Hat, Inc.
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library. If not, see
18 * <http://www.gnu.org/licenses/>.
19 *
20 */
21
22 #include <config.h>
23
24 #include "virlog.h"
25 #include "virerror.h"
26 #include "viralloc.h"
27 #include "virstring.h"
28
29 #include "qemu_alias.h"
30 #include "qemu_hotplug.h"
31 #include "qemu_migration.h"
32 #include "qemu_migration_params.h"
33 #define LIBVIRT_QEMU_MIGRATION_PARAMSPRIV_H_ALLOW
34 #include "qemu_migration_paramspriv.h"
35 #include "qemu_monitor.h"
36
37 #define VIR_FROM_THIS VIR_FROM_QEMU
38
39 VIR_LOG_INIT("qemu.qemu_migration_params");
40
41 #define QEMU_MIGRATION_TLS_ALIAS_BASE "libvirt_migrate"
42
43 typedef enum {
44 QEMU_MIGRATION_PARAM_TYPE_INT,
45 QEMU_MIGRATION_PARAM_TYPE_ULL,
46 QEMU_MIGRATION_PARAM_TYPE_BOOL,
47 QEMU_MIGRATION_PARAM_TYPE_STRING,
48 } qemuMigrationParamType;
49
50 typedef struct _qemuMigrationParamValue qemuMigrationParamValue;
51 struct _qemuMigrationParamValue {
52 bool set;
53 union {
54 int i; /* exempt from syntax-check */
55 unsigned long long ull;
56 bool b;
57 char *s;
58 } value;
59 };
60
61 struct _qemuMigrationParams {
62 unsigned long long compMethods; /* bit-wise OR of qemuMigrationCompressMethod */
63 virBitmap *caps;
64 qemuMigrationParamValue params[QEMU_MIGRATION_PARAM_LAST];
65 virJSONValue *blockDirtyBitmapMapping;
66 };
67
68 typedef enum {
69 QEMU_MIGRATION_COMPRESS_XBZRLE = 0,
70 QEMU_MIGRATION_COMPRESS_MT,
71
72 QEMU_MIGRATION_COMPRESS_LAST
73 } qemuMigrationCompressMethod;
74 VIR_ENUM_DECL(qemuMigrationCompressMethod);
75 VIR_ENUM_IMPL(qemuMigrationCompressMethod,
76 QEMU_MIGRATION_COMPRESS_LAST,
77 "xbzrle",
78 "mt",
79 );
80
81 VIR_ENUM_IMPL(qemuMigrationCapability,
82 QEMU_MIGRATION_CAP_LAST,
83 "xbzrle",
84 "auto-converge",
85 "rdma-pin-all",
86 "events",
87 "postcopy-ram",
88 "compress",
89 "pause-before-switchover",
90 "late-block-activate",
91 "multifd",
92 "dirty-bitmaps",
93 );
94
95
96 VIR_ENUM_DECL(qemuMigrationParam);
97 VIR_ENUM_IMPL(qemuMigrationParam,
98 QEMU_MIGRATION_PARAM_LAST,
99 "compress-level",
100 "compress-threads",
101 "decompress-threads",
102 "cpu-throttle-initial",
103 "cpu-throttle-increment",
104 "tls-creds",
105 "tls-hostname",
106 "max-bandwidth",
107 "downtime-limit",
108 "block-incremental",
109 "xbzrle-cache-size",
110 "max-postcopy-bandwidth",
111 "multifd-channels",
112 );
113
114 typedef struct _qemuMigrationParamsAlwaysOnItem qemuMigrationParamsAlwaysOnItem;
115 struct _qemuMigrationParamsAlwaysOnItem {
116 qemuMigrationCapability cap;
117 int party; /* bit-wise OR of qemuMigrationParty */
118 };
119
120 typedef struct _qemuMigrationParamsFlagMapItem qemuMigrationParamsFlagMapItem;
121 struct _qemuMigrationParamsFlagMapItem {
122 virDomainMigrateFlags flag;
123 qemuMigrationCapability cap;
124 int party; /* bit-wise OR of qemuMigrationParty */
125 };
126
127 typedef struct _qemuMigrationParamsTPMapItem qemuMigrationParamsTPMapItem;
128 struct _qemuMigrationParamsTPMapItem {
129 const char *typedParam;
130 unsigned int unit;
131 qemuMigrationParam param;
132 int party; /* bit-wise OR of qemuMigrationParty */
133 };
134
135 /* Migration capabilities which should always be enabled as long as they
136 * are supported by QEMU. If the capability is supposed to be enabled on both
137 * sides of migration, it won't be enabled unless both sides support it.
138 */
139 static const qemuMigrationParamsAlwaysOnItem qemuMigrationParamsAlwaysOn[] = {
140 {QEMU_MIGRATION_CAP_PAUSE_BEFORE_SWITCHOVER,
141 QEMU_MIGRATION_SOURCE},
142
143 {QEMU_MIGRATION_CAP_LATE_BLOCK_ACTIVATE,
144 QEMU_MIGRATION_DESTINATION},
145 };
146
147 /* Translation from virDomainMigrateFlags to qemuMigrationCapability. */
148 static const qemuMigrationParamsFlagMapItem qemuMigrationParamsFlagMap[] = {
149 {VIR_MIGRATE_RDMA_PIN_ALL,
150 QEMU_MIGRATION_CAP_RDMA_PIN_ALL,
151 QEMU_MIGRATION_SOURCE | QEMU_MIGRATION_DESTINATION},
152
153 {VIR_MIGRATE_AUTO_CONVERGE,
154 QEMU_MIGRATION_CAP_AUTO_CONVERGE,
155 QEMU_MIGRATION_SOURCE},
156
157 {VIR_MIGRATE_POSTCOPY,
158 QEMU_MIGRATION_CAP_POSTCOPY,
159 QEMU_MIGRATION_SOURCE | QEMU_MIGRATION_DESTINATION},
160
161 {VIR_MIGRATE_PARALLEL,
162 QEMU_MIGRATION_CAP_MULTIFD,
163 QEMU_MIGRATION_SOURCE | QEMU_MIGRATION_DESTINATION},
164 };
165
166 /* Translation from VIR_MIGRATE_PARAM_* typed parameters to
167 * qemuMigrationParams. */
168 static const qemuMigrationParamsTPMapItem qemuMigrationParamsTPMap[] = {
169 {.typedParam = VIR_MIGRATE_PARAM_AUTO_CONVERGE_INITIAL,
170 .param = QEMU_MIGRATION_PARAM_THROTTLE_INITIAL,
171 .party = QEMU_MIGRATION_SOURCE},
172
173 {.typedParam = VIR_MIGRATE_PARAM_AUTO_CONVERGE_INCREMENT,
174 .param = QEMU_MIGRATION_PARAM_THROTTLE_INCREMENT,
175 .party = QEMU_MIGRATION_SOURCE},
176
177 {.typedParam = VIR_MIGRATE_PARAM_COMPRESSION_MT_LEVEL,
178 .param = QEMU_MIGRATION_PARAM_COMPRESS_LEVEL,
179 .party = QEMU_MIGRATION_SOURCE | QEMU_MIGRATION_DESTINATION},
180
181 {.typedParam = VIR_MIGRATE_PARAM_COMPRESSION_MT_THREADS,
182 .param = QEMU_MIGRATION_PARAM_COMPRESS_THREADS,
183 .party = QEMU_MIGRATION_SOURCE | QEMU_MIGRATION_DESTINATION},
184
185 {.typedParam = VIR_MIGRATE_PARAM_COMPRESSION_MT_DTHREADS,
186 .param = QEMU_MIGRATION_PARAM_DECOMPRESS_THREADS,
187 .party = QEMU_MIGRATION_SOURCE | QEMU_MIGRATION_DESTINATION},
188
189 {.typedParam = VIR_MIGRATE_PARAM_COMPRESSION_XBZRLE_CACHE,
190 .param = QEMU_MIGRATION_PARAM_XBZRLE_CACHE_SIZE,
191 .party = QEMU_MIGRATION_SOURCE | QEMU_MIGRATION_DESTINATION},
192
193 {.typedParam = VIR_MIGRATE_PARAM_BANDWIDTH_POSTCOPY,
194 .unit = 1024 * 1024, /* MiB/s */
195 .param = QEMU_MIGRATION_PARAM_MAX_POSTCOPY_BANDWIDTH,
196 .party = QEMU_MIGRATION_SOURCE | QEMU_MIGRATION_DESTINATION},
197
198 {.typedParam = VIR_MIGRATE_PARAM_PARALLEL_CONNECTIONS,
199 .param = QEMU_MIGRATION_PARAM_MULTIFD_CHANNELS,
200 .party = QEMU_MIGRATION_SOURCE | QEMU_MIGRATION_DESTINATION},
201
202 {.typedParam = VIR_MIGRATE_PARAM_TLS_DESTINATION,
203 .param = QEMU_MIGRATION_PARAM_TLS_HOSTNAME,
204 .party = QEMU_MIGRATION_SOURCE},
205 };
206
207 static const qemuMigrationParamType qemuMigrationParamTypes[] = {
208 [QEMU_MIGRATION_PARAM_COMPRESS_LEVEL] = QEMU_MIGRATION_PARAM_TYPE_INT,
209 [QEMU_MIGRATION_PARAM_COMPRESS_THREADS] = QEMU_MIGRATION_PARAM_TYPE_INT,
210 [QEMU_MIGRATION_PARAM_DECOMPRESS_THREADS] = QEMU_MIGRATION_PARAM_TYPE_INT,
211 [QEMU_MIGRATION_PARAM_THROTTLE_INITIAL] = QEMU_MIGRATION_PARAM_TYPE_INT,
212 [QEMU_MIGRATION_PARAM_THROTTLE_INCREMENT] = QEMU_MIGRATION_PARAM_TYPE_INT,
213 [QEMU_MIGRATION_PARAM_TLS_CREDS] = QEMU_MIGRATION_PARAM_TYPE_STRING,
214 [QEMU_MIGRATION_PARAM_TLS_HOSTNAME] = QEMU_MIGRATION_PARAM_TYPE_STRING,
215 [QEMU_MIGRATION_PARAM_MAX_BANDWIDTH] = QEMU_MIGRATION_PARAM_TYPE_ULL,
216 [QEMU_MIGRATION_PARAM_DOWNTIME_LIMIT] = QEMU_MIGRATION_PARAM_TYPE_ULL,
217 [QEMU_MIGRATION_PARAM_BLOCK_INCREMENTAL] = QEMU_MIGRATION_PARAM_TYPE_BOOL,
218 [QEMU_MIGRATION_PARAM_XBZRLE_CACHE_SIZE] = QEMU_MIGRATION_PARAM_TYPE_ULL,
219 [QEMU_MIGRATION_PARAM_MAX_POSTCOPY_BANDWIDTH] = QEMU_MIGRATION_PARAM_TYPE_ULL,
220 [QEMU_MIGRATION_PARAM_MULTIFD_CHANNELS] = QEMU_MIGRATION_PARAM_TYPE_INT,
221 };
222 G_STATIC_ASSERT(G_N_ELEMENTS(qemuMigrationParamTypes) == QEMU_MIGRATION_PARAM_LAST);
223
224
225 virBitmap *
qemuMigrationParamsGetAlwaysOnCaps(qemuMigrationParty party)226 qemuMigrationParamsGetAlwaysOnCaps(qemuMigrationParty party)
227 {
228 virBitmap *caps = virBitmapNew(QEMU_MIGRATION_CAP_LAST);
229 size_t i;
230
231 for (i = 0; i < G_N_ELEMENTS(qemuMigrationParamsAlwaysOn); i++) {
232 if (!(qemuMigrationParamsAlwaysOn[i].party & party))
233 continue;
234
235 ignore_value(virBitmapSetBit(caps, qemuMigrationParamsAlwaysOn[i].cap));
236 }
237
238 return caps;
239 }
240
241
242 qemuMigrationParams *
qemuMigrationParamsNew(void)243 qemuMigrationParamsNew(void)
244 {
245 g_autoptr(qemuMigrationParams) params = NULL;
246
247 params = g_new0(qemuMigrationParams, 1);
248
249 params->caps = virBitmapNew(QEMU_MIGRATION_CAP_LAST);
250
251 return g_steal_pointer(¶ms);
252 }
253
254
255 void
qemuMigrationParamsFree(qemuMigrationParams * migParams)256 qemuMigrationParamsFree(qemuMigrationParams *migParams)
257 {
258 size_t i;
259
260 if (!migParams)
261 return;
262
263 for (i = 0; i < QEMU_MIGRATION_PARAM_LAST; i++) {
264 if (qemuMigrationParamTypes[i] == QEMU_MIGRATION_PARAM_TYPE_STRING)
265 g_free(migParams->params[i].value.s);
266 }
267
268 virBitmapFree(migParams->caps);
269 virJSONValueFree(migParams->blockDirtyBitmapMapping);
270 g_free(migParams);
271 }
272
273
274 static int
qemuMigrationParamsCheckType(qemuMigrationParam param,qemuMigrationParamType type)275 qemuMigrationParamsCheckType(qemuMigrationParam param,
276 qemuMigrationParamType type)
277 {
278 if (qemuMigrationParamTypes[param] != type) {
279 virReportError(VIR_ERR_INTERNAL_ERROR,
280 _("Type mismatch for '%s' migration parameter"),
281 qemuMigrationParamTypeToString(param));
282 return -1;
283 }
284
285 return 0;
286 }
287
288
289 static int
qemuMigrationParamsGetTPInt(qemuMigrationParams * migParams,qemuMigrationParam param,virTypedParameterPtr params,int nparams,const char * name,unsigned int unit)290 qemuMigrationParamsGetTPInt(qemuMigrationParams *migParams,
291 qemuMigrationParam param,
292 virTypedParameterPtr params,
293 int nparams,
294 const char *name,
295 unsigned int unit)
296 {
297 int rc;
298
299 if (qemuMigrationParamsCheckType(param, QEMU_MIGRATION_PARAM_TYPE_INT) < 0)
300 return -1;
301
302 if (!params)
303 return 0;
304
305 if ((rc = virTypedParamsGetInt(params, nparams, name,
306 &migParams->params[param].value.i)) < 0)
307 return -1;
308
309 if (unit > 0) {
310 unsigned int max = UINT_MAX / unit;
311 if (migParams->params[param].value.i > max) {
312 virReportError(VIR_ERR_OVERFLOW,
313 _("migration parameter '%s' must be less than %u"),
314 name, max + 1);
315 return -1;
316 }
317 migParams->params[param].value.i *= unit;
318 }
319
320 migParams->params[param].set = !!rc;
321 return 0;
322 }
323
324
325 static int
qemuMigrationParamsSetTPInt(qemuMigrationParams * migParams,qemuMigrationParam param,virTypedParameterPtr * params,int * nparams,int * maxparams,const char * name,unsigned int unit)326 qemuMigrationParamsSetTPInt(qemuMigrationParams *migParams,
327 qemuMigrationParam param,
328 virTypedParameterPtr *params,
329 int *nparams,
330 int *maxparams,
331 const char *name,
332 unsigned int unit)
333 {
334 int value;
335
336 if (qemuMigrationParamsCheckType(param, QEMU_MIGRATION_PARAM_TYPE_INT) < 0)
337 return -1;
338
339 if (!migParams->params[param].set)
340 return 0;
341
342 value = migParams->params[param].value.i;
343 if (unit > 0)
344 value /= unit;
345
346 return virTypedParamsAddInt(params, nparams, maxparams, name, value);
347 }
348
349
350 static int
qemuMigrationParamsGetTPULL(qemuMigrationParams * migParams,qemuMigrationParam param,virTypedParameterPtr params,int nparams,const char * name,unsigned int unit)351 qemuMigrationParamsGetTPULL(qemuMigrationParams *migParams,
352 qemuMigrationParam param,
353 virTypedParameterPtr params,
354 int nparams,
355 const char *name,
356 unsigned int unit)
357 {
358 int rc;
359
360 if (qemuMigrationParamsCheckType(param, QEMU_MIGRATION_PARAM_TYPE_ULL) < 0)
361 return -1;
362
363 if (!params)
364 return 0;
365
366 if ((rc = virTypedParamsGetULLong(params, nparams, name,
367 &migParams->params[param].value.ull)) < 0)
368 return -1;
369
370 if (unit > 0) {
371 unsigned long long max = ULLONG_MAX / unit;
372 if (migParams->params[param].value.ull > max) {
373 virReportError(VIR_ERR_OVERFLOW,
374 _("migration parameter '%s' must be less than %llu"),
375 name, max + 1);
376 return -1;
377 }
378 migParams->params[param].value.ull *= unit;
379 }
380
381 migParams->params[param].set = !!rc;
382 return 0;
383 }
384
385
386 static int
qemuMigrationParamsSetTPULL(qemuMigrationParams * migParams,qemuMigrationParam param,virTypedParameterPtr * params,int * nparams,int * maxparams,const char * name,unsigned int unit)387 qemuMigrationParamsSetTPULL(qemuMigrationParams *migParams,
388 qemuMigrationParam param,
389 virTypedParameterPtr *params,
390 int *nparams,
391 int *maxparams,
392 const char *name,
393 unsigned int unit)
394 {
395 unsigned long long value;
396
397 if (qemuMigrationParamsCheckType(param, QEMU_MIGRATION_PARAM_TYPE_ULL) < 0)
398 return -1;
399
400 if (!migParams->params[param].set)
401 return 0;
402
403 value = migParams->params[param].value.ull;
404 if (unit > 0)
405 value /= unit;
406
407 return virTypedParamsAddULLong(params, nparams, maxparams, name, value);
408 }
409
410
411 static int
qemuMigrationParamsGetTPString(qemuMigrationParams * migParams,qemuMigrationParam param,virTypedParameterPtr params,int nparams,const char * name)412 qemuMigrationParamsGetTPString(qemuMigrationParams *migParams,
413 qemuMigrationParam param,
414 virTypedParameterPtr params,
415 int nparams,
416 const char *name)
417 {
418 const char *value = NULL;
419 int rc;
420
421 if (qemuMigrationParamsCheckType(param, QEMU_MIGRATION_PARAM_TYPE_STRING) < 0)
422 return -1;
423
424 if (!params)
425 return 0;
426
427 if ((rc = virTypedParamsGetString(params, nparams, name, &value)) < 0)
428 return -1;
429
430 migParams->params[param].value.s = g_strdup(value);
431 migParams->params[param].set = !!rc;
432 return 0;
433 }
434
435
436 static int
qemuMigrationParamsSetTPString(qemuMigrationParams * migParams,qemuMigrationParam param,virTypedParameterPtr * params,int * nparams,int * maxparams,const char * name)437 qemuMigrationParamsSetTPString(qemuMigrationParams *migParams,
438 qemuMigrationParam param,
439 virTypedParameterPtr *params,
440 int *nparams,
441 int *maxparams,
442 const char *name)
443 {
444 if (qemuMigrationParamsCheckType(param, QEMU_MIGRATION_PARAM_TYPE_STRING) < 0)
445 return -1;
446
447 if (!migParams->params[param].set)
448 return 0;
449
450 return virTypedParamsAddString(params, nparams, maxparams, name,
451 migParams->params[param].value.s);
452 }
453
454
455
456 static int
qemuMigrationParamsSetCompression(virTypedParameterPtr params,int nparams,unsigned long flags,qemuMigrationParams * migParams)457 qemuMigrationParamsSetCompression(virTypedParameterPtr params,
458 int nparams,
459 unsigned long flags,
460 qemuMigrationParams *migParams)
461 {
462 size_t i;
463 int method;
464 qemuMigrationCapability cap;
465
466 for (i = 0; i < nparams; i++) {
467 if (STRNEQ(params[i].field, VIR_MIGRATE_PARAM_COMPRESSION))
468 continue;
469
470 method = qemuMigrationCompressMethodTypeFromString(params[i].value.s);
471 if (method < 0) {
472 virReportError(VIR_ERR_INVALID_ARG,
473 _("Unsupported compression method '%s'"),
474 params[i].value.s);
475 return -1;
476 }
477
478 if (migParams->compMethods & (1ULL << method)) {
479 virReportError(VIR_ERR_INVALID_ARG,
480 _("Compression method '%s' is specified twice"),
481 params[i].value.s);
482 return -1;
483 }
484
485 migParams->compMethods |= 1ULL << method;
486
487 switch ((qemuMigrationCompressMethod) method) {
488 case QEMU_MIGRATION_COMPRESS_XBZRLE:
489 cap = QEMU_MIGRATION_CAP_XBZRLE;
490 break;
491
492 case QEMU_MIGRATION_COMPRESS_MT:
493 cap = QEMU_MIGRATION_CAP_COMPRESS;
494 break;
495
496 case QEMU_MIGRATION_COMPRESS_LAST:
497 default:
498 continue;
499 }
500 ignore_value(virBitmapSetBit(migParams->caps, cap));
501 }
502
503 if ((migParams->params[QEMU_MIGRATION_PARAM_COMPRESS_LEVEL].set ||
504 migParams->params[QEMU_MIGRATION_PARAM_COMPRESS_THREADS].set ||
505 migParams->params[QEMU_MIGRATION_PARAM_DECOMPRESS_THREADS].set) &&
506 !(migParams->compMethods & (1ULL << QEMU_MIGRATION_COMPRESS_MT))) {
507 virReportError(VIR_ERR_INVALID_ARG, "%s",
508 _("Turn multithread compression on to tune it"));
509 return -1;
510 }
511
512 if (migParams->params[QEMU_MIGRATION_PARAM_XBZRLE_CACHE_SIZE].set &&
513 !(migParams->compMethods & (1ULL << QEMU_MIGRATION_COMPRESS_XBZRLE))) {
514 virReportError(VIR_ERR_INVALID_ARG, "%s",
515 _("Turn xbzrle compression on to tune it"));
516 return -1;
517 }
518
519 if (!migParams->compMethods && (flags & VIR_MIGRATE_COMPRESSED)) {
520 migParams->compMethods = 1ULL << QEMU_MIGRATION_COMPRESS_XBZRLE;
521 ignore_value(virBitmapSetBit(migParams->caps,
522 QEMU_MIGRATION_CAP_XBZRLE));
523 }
524
525 return 0;
526 }
527
528
529 void
qemuMigrationParamsSetBlockDirtyBitmapMapping(qemuMigrationParams * migParams,virJSONValue ** params)530 qemuMigrationParamsSetBlockDirtyBitmapMapping(qemuMigrationParams *migParams,
531 virJSONValue **params)
532 {
533 virJSONValueFree(migParams->blockDirtyBitmapMapping);
534 migParams->blockDirtyBitmapMapping = g_steal_pointer(params);
535
536 if (migParams->blockDirtyBitmapMapping)
537 ignore_value(virBitmapSetBit(migParams->caps, QEMU_MIGRATION_CAP_BLOCK_DIRTY_BITMAPS));
538 else
539 ignore_value(virBitmapClearBit(migParams->caps, QEMU_MIGRATION_CAP_BLOCK_DIRTY_BITMAPS));
540 }
541
542
543 qemuMigrationParams *
qemuMigrationParamsFromFlags(virTypedParameterPtr params,int nparams,unsigned long flags,qemuMigrationParty party)544 qemuMigrationParamsFromFlags(virTypedParameterPtr params,
545 int nparams,
546 unsigned long flags,
547 qemuMigrationParty party)
548 {
549 g_autoptr(qemuMigrationParams) migParams = NULL;
550 size_t i;
551
552 if (!(migParams = qemuMigrationParamsNew()))
553 return NULL;
554
555 for (i = 0; i < G_N_ELEMENTS(qemuMigrationParamsFlagMap); i++) {
556 qemuMigrationCapability cap = qemuMigrationParamsFlagMap[i].cap;
557
558 if (qemuMigrationParamsFlagMap[i].party & party &&
559 flags & qemuMigrationParamsFlagMap[i].flag) {
560 VIR_DEBUG("Enabling migration capability '%s'",
561 qemuMigrationCapabilityTypeToString(cap));
562 ignore_value(virBitmapSetBit(migParams->caps, cap));
563 }
564 }
565
566 for (i = 0; i < G_N_ELEMENTS(qemuMigrationParamsTPMap); i++) {
567 const qemuMigrationParamsTPMapItem *item = &qemuMigrationParamsTPMap[i];
568
569 if (!(item->party & party))
570 continue;
571
572 VIR_DEBUG("Setting migration parameter '%s' from '%s'",
573 qemuMigrationParamTypeToString(item->param), item->typedParam);
574
575 switch (qemuMigrationParamTypes[item->param]) {
576 case QEMU_MIGRATION_PARAM_TYPE_INT:
577 if (qemuMigrationParamsGetTPInt(migParams, item->param, params,
578 nparams, item->typedParam,
579 item->unit) < 0)
580 return NULL;
581 break;
582
583 case QEMU_MIGRATION_PARAM_TYPE_ULL:
584 if (qemuMigrationParamsGetTPULL(migParams, item->param, params,
585 nparams, item->typedParam,
586 item->unit) < 0)
587 return NULL;
588 break;
589
590 case QEMU_MIGRATION_PARAM_TYPE_BOOL:
591 break;
592
593 case QEMU_MIGRATION_PARAM_TYPE_STRING:
594 if (qemuMigrationParamsGetTPString(migParams, item->param, params,
595 nparams, item->typedParam) < 0)
596 return NULL;
597 break;
598 }
599 }
600
601 if ((migParams->params[QEMU_MIGRATION_PARAM_THROTTLE_INITIAL].set ||
602 migParams->params[QEMU_MIGRATION_PARAM_THROTTLE_INCREMENT].set) &&
603 !(flags & VIR_MIGRATE_AUTO_CONVERGE)) {
604 virReportError(VIR_ERR_INVALID_ARG, "%s",
605 _("Turn auto convergence on to tune it"));
606 return NULL;
607 }
608
609 if (migParams->params[QEMU_MIGRATION_PARAM_MULTIFD_CHANNELS].set &&
610 !(flags & VIR_MIGRATE_PARALLEL)) {
611 virReportError(VIR_ERR_INVALID_ARG, "%s",
612 _("Turn parallel migration on to tune it"));
613 return NULL;
614 }
615
616 if (qemuMigrationParamsSetCompression(params, nparams, flags, migParams) < 0)
617 return NULL;
618
619 return g_steal_pointer(&migParams);
620 }
621
622
623 int
qemuMigrationParamsDump(qemuMigrationParams * migParams,virTypedParameterPtr * params,int * nparams,int * maxparams,unsigned long * flags)624 qemuMigrationParamsDump(qemuMigrationParams *migParams,
625 virTypedParameterPtr *params,
626 int *nparams,
627 int *maxparams,
628 unsigned long *flags)
629 {
630 size_t i;
631
632 if (migParams->compMethods == 1ULL << QEMU_MIGRATION_COMPRESS_XBZRLE &&
633 !migParams->params[QEMU_MIGRATION_PARAM_XBZRLE_CACHE_SIZE].set) {
634 *flags |= VIR_MIGRATE_COMPRESSED;
635 }
636
637 for (i = 0; i < QEMU_MIGRATION_COMPRESS_LAST; ++i) {
638 if ((migParams->compMethods & (1ULL << i)) &&
639 virTypedParamsAddString(params, nparams, maxparams,
640 VIR_MIGRATE_PARAM_COMPRESSION,
641 qemuMigrationCompressMethodTypeToString(i)) < 0)
642 return -1;
643 }
644
645 for (i = 0; i < G_N_ELEMENTS(qemuMigrationParamsTPMap); i++) {
646 const qemuMigrationParamsTPMapItem *item = &qemuMigrationParamsTPMap[i];
647
648 if (!(item->party & QEMU_MIGRATION_DESTINATION))
649 continue;
650
651 switch (qemuMigrationParamTypes[item->param]) {
652 case QEMU_MIGRATION_PARAM_TYPE_INT:
653 if (qemuMigrationParamsSetTPInt(migParams, item->param,
654 params, nparams, maxparams,
655 item->typedParam, item->unit) < 0)
656 return -1;
657 break;
658
659 case QEMU_MIGRATION_PARAM_TYPE_ULL:
660 if (qemuMigrationParamsSetTPULL(migParams, item->param,
661 params, nparams, maxparams,
662 item->typedParam, item->unit) < 0)
663 return -1;
664 break;
665
666 case QEMU_MIGRATION_PARAM_TYPE_BOOL:
667 break;
668
669 case QEMU_MIGRATION_PARAM_TYPE_STRING:
670 if (qemuMigrationParamsSetTPString(migParams, item->param,
671 params, nparams, maxparams,
672 item->typedParam) < 0)
673 return -1;
674 break;
675 }
676 }
677
678 return 0;
679 }
680
681
682 qemuMigrationParams *
qemuMigrationParamsFromJSON(virJSONValue * params)683 qemuMigrationParamsFromJSON(virJSONValue *params)
684 {
685 g_autoptr(qemuMigrationParams) migParams = NULL;
686 qemuMigrationParamValue *pv;
687 const char *name;
688 const char *str;
689 size_t i;
690
691 if (!(migParams = qemuMigrationParamsNew()))
692 return NULL;
693
694 if (!params)
695 return g_steal_pointer(&migParams);
696
697 for (i = 0; i < QEMU_MIGRATION_PARAM_LAST; i++) {
698 name = qemuMigrationParamTypeToString(i);
699 pv = &migParams->params[i];
700
701 switch (qemuMigrationParamTypes[i]) {
702 case QEMU_MIGRATION_PARAM_TYPE_INT:
703 if (virJSONValueObjectGetNumberInt(params, name, &pv->value.i) == 0)
704 pv->set = true;
705 break;
706
707 case QEMU_MIGRATION_PARAM_TYPE_ULL:
708 if (virJSONValueObjectGetNumberUlong(params, name, &pv->value.ull) == 0)
709 pv->set = true;
710 break;
711
712 case QEMU_MIGRATION_PARAM_TYPE_BOOL:
713 if (virJSONValueObjectGetBoolean(params, name, &pv->value.b) == 0)
714 pv->set = true;
715 break;
716
717 case QEMU_MIGRATION_PARAM_TYPE_STRING:
718 if ((str = virJSONValueObjectGetString(params, name))) {
719 pv->value.s = g_strdup(str);
720 pv->set = true;
721 }
722 break;
723 }
724 }
725
726 return g_steal_pointer(&migParams);
727 }
728
729
730 virJSONValue *
qemuMigrationParamsToJSON(qemuMigrationParams * migParams)731 qemuMigrationParamsToJSON(qemuMigrationParams *migParams)
732 {
733 g_autoptr(virJSONValue) params = virJSONValueNewObject();
734 size_t i;
735
736 for (i = 0; i < QEMU_MIGRATION_PARAM_LAST; i++) {
737 const char *name = qemuMigrationParamTypeToString(i);
738 qemuMigrationParamValue *pv = &migParams->params[i];
739 int rc = 0;
740
741 if (!pv->set)
742 continue;
743
744 switch (qemuMigrationParamTypes[i]) {
745 case QEMU_MIGRATION_PARAM_TYPE_INT:
746 rc = virJSONValueObjectAppendNumberInt(params, name, pv->value.i);
747 break;
748
749 case QEMU_MIGRATION_PARAM_TYPE_ULL:
750 rc = virJSONValueObjectAppendNumberUlong(params, name, pv->value.ull);
751 break;
752
753 case QEMU_MIGRATION_PARAM_TYPE_BOOL:
754 rc = virJSONValueObjectAppendBoolean(params, name, pv->value.b);
755 break;
756
757 case QEMU_MIGRATION_PARAM_TYPE_STRING:
758 rc = virJSONValueObjectAppendString(params, name, pv->value.s);
759 break;
760 }
761
762 if (rc < 0)
763 return NULL;
764 }
765
766 if (migParams->blockDirtyBitmapMapping) {
767 g_autoptr(virJSONValue) mapping = virJSONValueCopy(migParams->blockDirtyBitmapMapping);
768
769 if (!mapping)
770 return NULL;
771
772 if (virJSONValueObjectAppend(params, "block-bitmap-mapping", &mapping) < 0)
773 return NULL;
774 }
775
776 return g_steal_pointer(¶ms);
777 }
778
779
780 virJSONValue *
qemuMigrationCapsToJSON(virBitmap * caps,virBitmap * states)781 qemuMigrationCapsToJSON(virBitmap *caps,
782 virBitmap *states)
783 {
784 g_autoptr(virJSONValue) json = virJSONValueNewArray();
785 qemuMigrationCapability bit;
786
787 for (bit = 0; bit < QEMU_MIGRATION_CAP_LAST; bit++) {
788 g_autoptr(virJSONValue) cap = NULL;
789
790 if (!virBitmapIsBitSet(caps, bit))
791 continue;
792
793 if (virJSONValueObjectAdd(&cap,
794 "s:capability", qemuMigrationCapabilityTypeToString(bit),
795 "b:state", virBitmapIsBitSet(states, bit),
796 NULL) < 0)
797 return NULL;
798
799 if (virJSONValueArrayAppend(json, &cap) < 0)
800 return NULL;
801 }
802
803 return g_steal_pointer(&json);
804 }
805
806
807 /**
808 * qemuMigrationParamsApply
809 * @driver: qemu driver
810 * @vm: domain object
811 * @asyncJob: migration job
812 * @migParams: migration parameters to send to QEMU
813 *
814 * Send all parameters stored in @migParams to QEMU.
815 *
816 * Returns 0 on success, -1 on failure.
817 */
818 int
qemuMigrationParamsApply(virQEMUDriver * driver,virDomainObj * vm,int asyncJob,qemuMigrationParams * migParams)819 qemuMigrationParamsApply(virQEMUDriver *driver,
820 virDomainObj *vm,
821 int asyncJob,
822 qemuMigrationParams *migParams)
823 {
824 qemuDomainObjPrivate *priv = vm->privateData;
825 bool xbzrleCacheSize_old = false;
826 g_autoptr(virJSONValue) params = NULL;
827 g_autoptr(virJSONValue) caps = NULL;
828 qemuMigrationParam xbzrle = QEMU_MIGRATION_PARAM_XBZRLE_CACHE_SIZE;
829 int ret = -1;
830
831 if (qemuDomainObjEnterMonitorAsync(driver, vm, asyncJob) < 0)
832 return -1;
833
834 if (asyncJob == QEMU_ASYNC_JOB_NONE) {
835 if (!virBitmapIsAllClear(migParams->caps)) {
836 virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
837 _("Migration capabilities can only be set by "
838 "a migration job"));
839 goto cleanup;
840 }
841 } else {
842 if (!(caps = qemuMigrationCapsToJSON(priv->migrationCaps, migParams->caps)))
843 goto cleanup;
844
845 if (virJSONValueArraySize(caps) > 0 &&
846 qemuMonitorSetMigrationCapabilities(priv->mon, &caps) < 0)
847 goto cleanup;
848 }
849
850 /* If QEMU is too old to support xbzrle-cache-size migration parameter,
851 * we need to set it via migrate-set-cache-size and tell
852 * qemuMonitorSetMigrationParams to ignore this parameter.
853 */
854 if (migParams->params[xbzrle].set &&
855 !virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_MIGRATION_PARAM_XBZRLE_CACHE_SIZE)) {
856 if (qemuMonitorSetMigrationCacheSize(priv->mon,
857 migParams->params[xbzrle].value.ull) < 0)
858 goto cleanup;
859 xbzrleCacheSize_old = true;
860 migParams->params[xbzrle].set = false;
861 }
862
863 if (!(params = qemuMigrationParamsToJSON(migParams)))
864 goto cleanup;
865
866 if (virJSONValueObjectKeysNumber(params) > 0 &&
867 qemuMonitorSetMigrationParams(priv->mon, ¶ms) < 0)
868 goto cleanup;
869
870 ret = 0;
871
872 cleanup:
873 if (qemuDomainObjExitMonitor(driver, vm) < 0)
874 ret = -1;
875
876 if (xbzrleCacheSize_old)
877 migParams->params[xbzrle].set = true;
878
879 return ret;
880 }
881
882
883 /**
884 * qemuMigrationParamsSetString:
885 * @migrParams: migration parameter object
886 * @param: parameter to set
887 * @value: new value
888 *
889 * Enables and sets the migration parameter @param in @migrParams. Returns 0 on
890 * success and -1 on error. Libvirt error is reported.
891 */
892 static int
qemuMigrationParamsSetString(qemuMigrationParams * migParams,qemuMigrationParam param,const char * value)893 qemuMigrationParamsSetString(qemuMigrationParams *migParams,
894 qemuMigrationParam param,
895 const char *value)
896 {
897 if (qemuMigrationParamsCheckType(param, QEMU_MIGRATION_PARAM_TYPE_STRING) < 0)
898 return -1;
899
900 migParams->params[param].value.s = g_strdup(value);
901
902 migParams->params[param].set = true;
903
904 return 0;
905 }
906
907
908 /* qemuMigrationParamsEnableTLS
909 * @driver: pointer to qemu driver
910 * @vm: domain object
911 * @tlsListen: server or client
912 * @asyncJob: Migration job to join
913 * @tlsAlias: alias to be generated for TLS object
914 * @hostname: hostname of the migration destination
915 * @migParams: migration parameters to set
916 *
917 * Create the TLS objects for the migration and set the migParams value.
918 * If QEMU itself does not connect to the destination @hostname must be
919 * provided for certificate verification.
920 *
921 * Returns 0 on success, -1 on failure
922 */
923 int
qemuMigrationParamsEnableTLS(virQEMUDriver * driver,virDomainObj * vm,bool tlsListen,int asyncJob,char ** tlsAlias,const char * hostname,qemuMigrationParams * migParams)924 qemuMigrationParamsEnableTLS(virQEMUDriver *driver,
925 virDomainObj *vm,
926 bool tlsListen,
927 int asyncJob,
928 char **tlsAlias,
929 const char *hostname,
930 qemuMigrationParams *migParams)
931 {
932 qemuDomainObjPrivate *priv = vm->privateData;
933 qemuDomainJobPrivate *jobPriv = priv->job.privateData;
934 g_autoptr(virJSONValue) tlsProps = NULL;
935 g_autoptr(virJSONValue) secProps = NULL;
936 g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver);
937 const char *secAlias = NULL;
938
939 if (!cfg->migrateTLSx509certdir) {
940 virReportError(VIR_ERR_OPERATION_INVALID, "%s",
941 _("host migration TLS directory not configured"));
942 return -1;
943 }
944
945 if (!jobPriv->migParams->params[QEMU_MIGRATION_PARAM_TLS_CREDS].set) {
946 virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
947 _("TLS migration is not supported with this "
948 "QEMU binary"));
949 return -1;
950 }
951
952 /* If there's a secret, then grab/store it now using the connection */
953 if (cfg->migrateTLSx509secretUUID) {
954 if (!(priv->migSecinfo =
955 qemuDomainSecretInfoTLSNew(priv, QEMU_MIGRATION_TLS_ALIAS_BASE,
956 cfg->migrateTLSx509secretUUID)))
957 return -1;
958 secAlias = priv->migSecinfo->alias;
959 }
960
961 if (!(*tlsAlias = qemuAliasTLSObjFromSrcAlias(QEMU_MIGRATION_TLS_ALIAS_BASE)))
962 return -1;
963
964 if (qemuDomainGetTLSObjects(priv->migSecinfo,
965 cfg->migrateTLSx509certdir, tlsListen,
966 cfg->migrateTLSx509verify,
967 *tlsAlias, &tlsProps, &secProps) < 0)
968 return -1;
969
970 /* Ensure the domain doesn't already have the TLS objects defined...
971 * This should prevent any issues just in case some cleanup wasn't
972 * properly completed (both src and dst use the same alias) or
973 * some other error path between now and perform . */
974 qemuDomainDelTLSObjects(driver, vm, asyncJob, secAlias, *tlsAlias);
975
976 if (qemuDomainAddTLSObjects(driver, vm, asyncJob, &secProps, &tlsProps) < 0)
977 return -1;
978
979 if (qemuMigrationParamsSetString(migParams,
980 QEMU_MIGRATION_PARAM_TLS_CREDS,
981 *tlsAlias) < 0)
982 return -1;
983
984 if (!migParams->params[QEMU_MIGRATION_PARAM_TLS_HOSTNAME].set &&
985 qemuMigrationParamsSetString(migParams,
986 QEMU_MIGRATION_PARAM_TLS_HOSTNAME,
987 NULLSTR_EMPTY(hostname)) < 0)
988 return -1;
989
990 return 0;
991 }
992
993
994 /* qemuMigrationParamsDisableTLS
995 * @vm: domain object
996 * @migParams: Pointer to a migration parameters block
997 *
998 * If we support setting the tls-creds, then set both tls-creds and
999 * tls-hostname to the empty string ("") which indicates to not use
1000 * TLS on this migration.
1001 *
1002 * Returns 0 on success, -1 on failure
1003 */
1004 int
qemuMigrationParamsDisableTLS(virDomainObj * vm,qemuMigrationParams * migParams)1005 qemuMigrationParamsDisableTLS(virDomainObj *vm,
1006 qemuMigrationParams *migParams)
1007 {
1008 qemuDomainObjPrivate *priv = vm->privateData;
1009 qemuDomainJobPrivate *jobPriv = priv->job.privateData;
1010
1011 if (!jobPriv->migParams->params[QEMU_MIGRATION_PARAM_TLS_CREDS].set)
1012 return 0;
1013
1014 if (qemuMigrationParamsSetString(migParams,
1015 QEMU_MIGRATION_PARAM_TLS_CREDS, "") < 0 ||
1016 qemuMigrationParamsSetString(migParams,
1017 QEMU_MIGRATION_PARAM_TLS_HOSTNAME, "") < 0)
1018 return -1;
1019
1020 return 0;
1021 }
1022
1023
1024 bool
qemuMigrationParamsTLSHostnameIsSet(qemuMigrationParams * migParams)1025 qemuMigrationParamsTLSHostnameIsSet(qemuMigrationParams *migParams)
1026 {
1027 int param = QEMU_MIGRATION_PARAM_TLS_HOSTNAME;
1028 return (migParams->params[param].set &&
1029 STRNEQ(migParams->params[param].value.s, ""));
1030 }
1031
1032
1033 /* qemuMigrationParamsResetTLS
1034 * @driver: pointer to qemu driver
1035 * @vm: domain object
1036 * @asyncJob: migration job to join
1037 * @apiFlags: API flags used to start the migration
1038 *
1039 * Deconstruct all the setup possibly done for TLS - delete the TLS and
1040 * security objects and free the secinfo
1041 */
1042 static void
qemuMigrationParamsResetTLS(virQEMUDriver * driver,virDomainObj * vm,int asyncJob,qemuMigrationParams * origParams,unsigned long apiFlags)1043 qemuMigrationParamsResetTLS(virQEMUDriver *driver,
1044 virDomainObj *vm,
1045 int asyncJob,
1046 qemuMigrationParams *origParams,
1047 unsigned long apiFlags)
1048 {
1049 g_autofree char *tlsAlias = NULL;
1050 g_autofree char *secAlias = NULL;
1051
1052 /* There's nothing to do if QEMU does not support TLS migration or we were
1053 * not asked to enable it. */
1054 if (!origParams->params[QEMU_MIGRATION_PARAM_TLS_CREDS].set ||
1055 !(apiFlags & VIR_MIGRATE_TLS))
1056 return;
1057
1058 tlsAlias = qemuAliasTLSObjFromSrcAlias(QEMU_MIGRATION_TLS_ALIAS_BASE);
1059 secAlias = qemuAliasForSecret(QEMU_MIGRATION_TLS_ALIAS_BASE, NULL);
1060
1061 qemuDomainDelTLSObjects(driver, vm, asyncJob, secAlias, tlsAlias);
1062 g_clear_pointer(&QEMU_DOMAIN_PRIVATE(vm)->migSecinfo, qemuDomainSecretInfoFree);
1063 }
1064
1065
1066 int
qemuMigrationParamsFetch(virQEMUDriver * driver,virDomainObj * vm,int asyncJob,qemuMigrationParams ** migParams)1067 qemuMigrationParamsFetch(virQEMUDriver *driver,
1068 virDomainObj *vm,
1069 int asyncJob,
1070 qemuMigrationParams **migParams)
1071 {
1072 qemuDomainObjPrivate *priv = vm->privateData;
1073 g_autoptr(virJSONValue) jsonParams = NULL;
1074 int rc;
1075
1076 *migParams = NULL;
1077
1078 if (qemuDomainObjEnterMonitorAsync(driver, vm, asyncJob) < 0)
1079 return -1;
1080
1081 rc = qemuMonitorGetMigrationParams(priv->mon, &jsonParams);
1082
1083 if (qemuDomainObjExitMonitor(driver, vm) < 0 || rc < 0)
1084 return -1;
1085
1086 if (!(*migParams = qemuMigrationParamsFromJSON(jsonParams)))
1087 return -1;
1088
1089 return 0;
1090 }
1091
1092
1093 int
qemuMigrationParamsSetULL(qemuMigrationParams * migParams,qemuMigrationParam param,unsigned long long value)1094 qemuMigrationParamsSetULL(qemuMigrationParams *migParams,
1095 qemuMigrationParam param,
1096 unsigned long long value)
1097 {
1098 if (qemuMigrationParamsCheckType(param, QEMU_MIGRATION_PARAM_TYPE_ULL) < 0)
1099 return -1;
1100
1101 migParams->params[param].value.ull = value;
1102 migParams->params[param].set = true;
1103 return 0;
1104 }
1105
1106
1107 /**
1108 * Returns -1 on error,
1109 * 0 on success,
1110 * 1 if the parameter is not supported by QEMU.
1111 */
1112 int
qemuMigrationParamsGetULL(qemuMigrationParams * migParams,qemuMigrationParam param,unsigned long long * value)1113 qemuMigrationParamsGetULL(qemuMigrationParams *migParams,
1114 qemuMigrationParam param,
1115 unsigned long long *value)
1116 {
1117 if (qemuMigrationParamsCheckType(param, QEMU_MIGRATION_PARAM_TYPE_ULL) < 0)
1118 return -1;
1119
1120 if (!migParams->params[param].set)
1121 return 1;
1122
1123 *value = migParams->params[param].value.ull;
1124 return 0;
1125 }
1126
1127
1128 /**
1129 * qemuMigrationParamsCheck:
1130 *
1131 * Check supported migration parameters and keep their original values in
1132 * qemuDomainJobObj so that we can properly reset them at the end of migration.
1133 * Reports an error if any of the currently used capabilities in @migParams
1134 * are unsupported by QEMU.
1135 */
1136 int
qemuMigrationParamsCheck(virQEMUDriver * driver,virDomainObj * vm,int asyncJob,qemuMigrationParams * migParams,virBitmap * remoteCaps)1137 qemuMigrationParamsCheck(virQEMUDriver *driver,
1138 virDomainObj *vm,
1139 int asyncJob,
1140 qemuMigrationParams *migParams,
1141 virBitmap *remoteCaps)
1142 {
1143 qemuDomainObjPrivate *priv = vm->privateData;
1144 qemuDomainJobPrivate *jobPriv = priv->job.privateData;
1145 qemuMigrationCapability cap;
1146 qemuMigrationParty party;
1147 size_t i;
1148
1149 if (asyncJob == QEMU_ASYNC_JOB_MIGRATION_OUT)
1150 party = QEMU_MIGRATION_SOURCE;
1151 else
1152 party = QEMU_MIGRATION_DESTINATION;
1153
1154 for (cap = 0; cap < QEMU_MIGRATION_CAP_LAST; cap++) {
1155 bool state = false;
1156
1157 ignore_value(virBitmapGetBit(migParams->caps, cap, &state));
1158
1159 if (state && !qemuMigrationCapsGet(vm, cap)) {
1160 virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED,
1161 _("Migration option '%s' is not supported by QEMU binary"),
1162 qemuMigrationCapabilityTypeToString(cap));
1163 return -1;
1164 }
1165 }
1166
1167 for (i = 0; i < G_N_ELEMENTS(qemuMigrationParamsAlwaysOn); i++) {
1168 cap = qemuMigrationParamsAlwaysOn[i].cap;
1169
1170 if (qemuMigrationParamsAlwaysOn[i].party & party &&
1171 qemuMigrationCapsGet(vm, cap)) {
1172 if (qemuMigrationParamsAlwaysOn[i].party != party) {
1173 bool remote = false;
1174
1175 if (remoteCaps)
1176 ignore_value(virBitmapGetBit(remoteCaps, cap, &remote));
1177
1178 if (!remote) {
1179 VIR_DEBUG("Not enabling migration capability '%s'; it is "
1180 "not supported or automatically enabled by the "
1181 "other side of migration",
1182 qemuMigrationCapabilityTypeToString(cap));
1183 continue;
1184 }
1185 }
1186
1187 VIR_DEBUG("Enabling migration capability '%s'",
1188 qemuMigrationCapabilityTypeToString(cap));
1189 ignore_value(virBitmapSetBit(migParams->caps, cap));
1190 }
1191 }
1192
1193 /*
1194 * We want to disable all migration capabilities after migration, no need
1195 * to ask QEMU for their current settings.
1196 */
1197
1198 return qemuMigrationParamsFetch(driver, vm, asyncJob, &jobPriv->migParams);
1199 }
1200
1201
1202 /*
1203 * qemuMigrationParamsReset:
1204 *
1205 * Reset all migration parameters so that the next job which internally uses
1206 * migration (save, managedsave, snapshots, dump) will not try to use them.
1207 */
1208 void
qemuMigrationParamsReset(virQEMUDriver * driver,virDomainObj * vm,int asyncJob,qemuMigrationParams * origParams,unsigned long apiFlags)1209 qemuMigrationParamsReset(virQEMUDriver *driver,
1210 virDomainObj *vm,
1211 int asyncJob,
1212 qemuMigrationParams *origParams,
1213 unsigned long apiFlags)
1214 {
1215 virErrorPtr err;
1216
1217 virErrorPreserveLast(&err);
1218
1219 VIR_DEBUG("Resetting migration parameters %p, flags 0x%lx",
1220 origParams, apiFlags);
1221
1222 if (!virDomainObjIsActive(vm) || !origParams)
1223 goto cleanup;
1224
1225 if (qemuMigrationParamsApply(driver, vm, asyncJob, origParams) < 0)
1226 goto cleanup;
1227
1228 qemuMigrationParamsResetTLS(driver, vm, asyncJob, origParams, apiFlags);
1229 /* We don't reset 'block-bitmap-mapping' as it can't be unset */
1230
1231 cleanup:
1232 virErrorRestore(&err);
1233 }
1234
1235
1236 void
qemuMigrationParamsFormat(virBuffer * buf,qemuMigrationParams * migParams)1237 qemuMigrationParamsFormat(virBuffer *buf,
1238 qemuMigrationParams *migParams)
1239 {
1240 qemuMigrationParamValue *pv;
1241 size_t i;
1242
1243 virBufferAddLit(buf, "<migParams>\n");
1244 virBufferAdjustIndent(buf, 2);
1245
1246 for (i = 0; i < QEMU_MIGRATION_PARAM_LAST; i++) {
1247 pv = &migParams->params[i];
1248
1249 if (!pv->set)
1250 continue;
1251
1252 virBufferAsprintf(buf, "<param name='%s' ",
1253 qemuMigrationParamTypeToString(i));
1254
1255 switch (qemuMigrationParamTypes[i]) {
1256 case QEMU_MIGRATION_PARAM_TYPE_INT:
1257 virBufferAsprintf(buf, "value='%d'", pv->value.i);
1258 break;
1259
1260 case QEMU_MIGRATION_PARAM_TYPE_ULL:
1261 virBufferAsprintf(buf, "value='%llu'", pv->value.ull);
1262 break;
1263
1264 case QEMU_MIGRATION_PARAM_TYPE_BOOL:
1265 virBufferAsprintf(buf, "value='%s'", pv->value.b ? "yes" : "no");
1266 break;
1267
1268 case QEMU_MIGRATION_PARAM_TYPE_STRING:
1269 virBufferEscapeString(buf, "value='%s'", pv->value.s);
1270 break;
1271 }
1272
1273 virBufferAddLit(buf, "/>\n");
1274 }
1275
1276 virBufferAdjustIndent(buf, -2);
1277 virBufferAddLit(buf, "</migParams>\n");
1278 }
1279
1280
1281 int
qemuMigrationParamsParse(xmlXPathContextPtr ctxt,qemuMigrationParams ** migParams)1282 qemuMigrationParamsParse(xmlXPathContextPtr ctxt,
1283 qemuMigrationParams **migParams)
1284 {
1285 g_autoptr(qemuMigrationParams) params = NULL;
1286 qemuMigrationParamValue *pv;
1287 g_autofree xmlNodePtr *nodes = NULL;
1288 size_t i;
1289 int rc;
1290 int n;
1291
1292 *migParams = NULL;
1293
1294 if ((rc = virXPathBoolean("boolean(./migParams)", ctxt)) < 0)
1295 return -1;
1296
1297 if (rc == 0)
1298 return 0;
1299
1300 if ((n = virXPathNodeSet("./migParams[1]/param", ctxt, &nodes)) < 0)
1301 return -1;
1302
1303 if (!(params = qemuMigrationParamsNew()))
1304 return -1;
1305
1306 for (i = 0; i < n; i++) {
1307 g_autofree char *name = NULL;
1308 g_autofree char *value = NULL;
1309 int param;
1310
1311 if (!(name = virXMLPropString(nodes[i], "name"))) {
1312 virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
1313 _("missing migration parameter name"));
1314 return -1;
1315 }
1316
1317 if ((param = qemuMigrationParamTypeFromString(name)) < 0) {
1318 virReportError(VIR_ERR_INTERNAL_ERROR,
1319 _("unknown migration parameter '%s'"), name);
1320 return -1;
1321 }
1322 pv = ¶ms->params[param];
1323
1324 if (!(value = virXMLPropString(nodes[i], "value"))) {
1325 virReportError(VIR_ERR_INTERNAL_ERROR,
1326 _("missing value for migration parameter '%s'"),
1327 name);
1328 return -1;
1329 }
1330
1331 rc = 0;
1332 switch (qemuMigrationParamTypes[param]) {
1333 case QEMU_MIGRATION_PARAM_TYPE_INT:
1334 rc = virStrToLong_i(value, NULL, 10, &pv->value.i);
1335 break;
1336
1337 case QEMU_MIGRATION_PARAM_TYPE_ULL:
1338 rc = virStrToLong_ullp(value, NULL, 10, &pv->value.ull);
1339 break;
1340
1341 case QEMU_MIGRATION_PARAM_TYPE_BOOL:
1342 rc = virStringParseYesNo(value, &pv->value.b);
1343 break;
1344
1345 case QEMU_MIGRATION_PARAM_TYPE_STRING:
1346 pv->value.s = g_steal_pointer(&value);
1347 break;
1348 }
1349
1350 if (rc < 0) {
1351 virReportError(VIR_ERR_INTERNAL_ERROR,
1352 _("invalid value '%s' for migration parameter '%s'"),
1353 value, name);
1354 return -1;
1355 }
1356
1357 pv->set = true;
1358 }
1359
1360 *migParams = g_steal_pointer(¶ms);
1361
1362 return 0;
1363 }
1364
1365
1366 int
qemuMigrationCapsCheck(virQEMUDriver * driver,virDomainObj * vm,int asyncJob)1367 qemuMigrationCapsCheck(virQEMUDriver *driver,
1368 virDomainObj *vm,
1369 int asyncJob)
1370 {
1371 qemuDomainObjPrivate *priv = vm->privateData;
1372 g_autoptr(virBitmap) migEvent = NULL;
1373 g_autoptr(virJSONValue) json = NULL;
1374 g_auto(GStrv) caps = NULL;
1375 char **capStr;
1376 int rc;
1377
1378 if (qemuDomainObjEnterMonitorAsync(driver, vm, asyncJob) < 0)
1379 return -1;
1380
1381 rc = qemuMonitorGetMigrationCapabilities(priv->mon, &caps);
1382
1383 if (qemuDomainObjExitMonitor(driver, vm) < 0 || rc < 0)
1384 return -1;
1385
1386 if (!caps)
1387 return 0;
1388
1389 priv->migrationCaps = virBitmapNew(QEMU_MIGRATION_CAP_LAST);
1390
1391 for (capStr = caps; *capStr; capStr++) {
1392 int cap = qemuMigrationCapabilityTypeFromString(*capStr);
1393
1394 if (cap < 0) {
1395 VIR_DEBUG("Unknown migration capability: '%s'", *capStr);
1396 } else {
1397 ignore_value(virBitmapSetBit(priv->migrationCaps, cap));
1398 VIR_DEBUG("Found migration capability: '%s'", *capStr);
1399 }
1400 }
1401
1402 if (virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_MIGRATION_EVENT)) {
1403 migEvent = virBitmapNew(QEMU_MIGRATION_CAP_LAST);
1404
1405 ignore_value(virBitmapSetBit(migEvent, QEMU_MIGRATION_CAP_EVENTS));
1406
1407 if (!(json = qemuMigrationCapsToJSON(migEvent, migEvent)))
1408 return -1;
1409
1410 if (qemuDomainObjEnterMonitorAsync(driver, vm, asyncJob) < 0)
1411 return -1;
1412
1413 rc = qemuMonitorSetMigrationCapabilities(priv->mon, &json);
1414
1415 if (qemuDomainObjExitMonitor(driver, vm) < 0)
1416 return -1;
1417
1418 if (rc < 0) {
1419 virResetLastError();
1420 VIR_DEBUG("Cannot enable migration events; clearing capability");
1421 virQEMUCapsClear(priv->qemuCaps, QEMU_CAPS_MIGRATION_EVENT);
1422 }
1423 }
1424
1425 /* Migration events capability must always be enabled, clearing it from
1426 * migration capabilities bitmap makes sure it won't be touched anywhere
1427 * else.
1428 */
1429 ignore_value(virBitmapClearBit(priv->migrationCaps,
1430 QEMU_MIGRATION_CAP_EVENTS));
1431
1432 return 0;
1433 }
1434
1435
1436 bool
qemuMigrationCapsGet(virDomainObj * vm,qemuMigrationCapability cap)1437 qemuMigrationCapsGet(virDomainObj *vm,
1438 qemuMigrationCapability cap)
1439 {
1440 qemuDomainObjPrivate *priv = vm->privateData;
1441 bool enabled = false;
1442
1443 if (priv->migrationCaps)
1444 ignore_value(virBitmapGetBit(priv->migrationCaps, cap, &enabled));
1445
1446 return enabled;
1447 }
1448