1 /*
2 * qemu_domainjob.c: helper functions for QEMU domain jobs
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library. If not, see
16 * <http://www.gnu.org/licenses/>.
17 */
18
19 #include <config.h>
20
21 #include "qemu_domain.h"
22 #include "qemu_migration.h"
23 #include "qemu_domainjob.h"
24 #include "viralloc.h"
25 #include "virlog.h"
26 #include "virerror.h"
27 #include "virtime.h"
28 #include "virthreadjob.h"
29
30 #define VIR_FROM_THIS VIR_FROM_QEMU
31
32 VIR_LOG_INIT("qemu.qemu_domainjob");
33
34 VIR_ENUM_IMPL(qemuDomainJob,
35 QEMU_JOB_LAST,
36 "none",
37 "query",
38 "destroy",
39 "suspend",
40 "modify",
41 "abort",
42 "migration operation",
43 "none", /* async job is never stored in job.active */
44 "async nested",
45 );
46
47 VIR_ENUM_IMPL(qemuDomainAgentJob,
48 QEMU_AGENT_JOB_LAST,
49 "none",
50 "query",
51 "modify",
52 );
53
54 VIR_ENUM_IMPL(qemuDomainAsyncJob,
55 QEMU_ASYNC_JOB_LAST,
56 "none",
57 "migration out",
58 "migration in",
59 "save",
60 "dump",
61 "snapshot",
62 "start",
63 "backup",
64 );
65
66 const char *
qemuDomainAsyncJobPhaseToString(qemuDomainAsyncJob job,int phase G_GNUC_UNUSED)67 qemuDomainAsyncJobPhaseToString(qemuDomainAsyncJob job,
68 int phase G_GNUC_UNUSED)
69 {
70 switch (job) {
71 case QEMU_ASYNC_JOB_MIGRATION_OUT:
72 case QEMU_ASYNC_JOB_MIGRATION_IN:
73 return qemuMigrationJobPhaseTypeToString(phase);
74
75 case QEMU_ASYNC_JOB_SAVE:
76 case QEMU_ASYNC_JOB_DUMP:
77 case QEMU_ASYNC_JOB_SNAPSHOT:
78 case QEMU_ASYNC_JOB_START:
79 case QEMU_ASYNC_JOB_NONE:
80 case QEMU_ASYNC_JOB_BACKUP:
81 G_GNUC_FALLTHROUGH;
82 case QEMU_ASYNC_JOB_LAST:
83 break;
84 }
85
86 return "none";
87 }
88
89 int
qemuDomainAsyncJobPhaseFromString(qemuDomainAsyncJob job,const char * phase)90 qemuDomainAsyncJobPhaseFromString(qemuDomainAsyncJob job,
91 const char *phase)
92 {
93 if (!phase)
94 return 0;
95
96 switch (job) {
97 case QEMU_ASYNC_JOB_MIGRATION_OUT:
98 case QEMU_ASYNC_JOB_MIGRATION_IN:
99 return qemuMigrationJobPhaseTypeFromString(phase);
100
101 case QEMU_ASYNC_JOB_SAVE:
102 case QEMU_ASYNC_JOB_DUMP:
103 case QEMU_ASYNC_JOB_SNAPSHOT:
104 case QEMU_ASYNC_JOB_START:
105 case QEMU_ASYNC_JOB_NONE:
106 case QEMU_ASYNC_JOB_BACKUP:
107 G_GNUC_FALLTHROUGH;
108 case QEMU_ASYNC_JOB_LAST:
109 break;
110 }
111
112 if (STREQ(phase, "none"))
113 return 0;
114 else
115 return -1;
116 }
117
118
119 void
qemuDomainJobInfoFree(qemuDomainJobInfo * info)120 qemuDomainJobInfoFree(qemuDomainJobInfo *info)
121 {
122 g_free(info->errmsg);
123 g_free(info);
124 }
125
126
127 qemuDomainJobInfo *
qemuDomainJobInfoCopy(qemuDomainJobInfo * info)128 qemuDomainJobInfoCopy(qemuDomainJobInfo *info)
129 {
130 qemuDomainJobInfo *ret = g_new0(qemuDomainJobInfo, 1);
131
132 memcpy(ret, info, sizeof(*info));
133
134 ret->errmsg = g_strdup(info->errmsg);
135
136 return ret;
137 }
138
139 void
qemuDomainEventEmitJobCompleted(virQEMUDriver * driver,virDomainObj * vm)140 qemuDomainEventEmitJobCompleted(virQEMUDriver *driver,
141 virDomainObj *vm)
142 {
143 qemuDomainObjPrivate *priv = vm->privateData;
144 virObjectEvent *event;
145 virTypedParameterPtr params = NULL;
146 int nparams = 0;
147 int type;
148
149 if (!priv->job.completed)
150 return;
151
152 if (qemuDomainJobInfoToParams(priv->job.completed, &type,
153 ¶ms, &nparams) < 0) {
154 VIR_WARN("Could not get stats for completed job; domain %s",
155 vm->def->name);
156 }
157
158 event = virDomainEventJobCompletedNewFromObj(vm, params, nparams);
159 virObjectEventStateQueue(driver->domainEventState, event);
160 }
161
162
163 int
qemuDomainObjInitJob(qemuDomainJobObj * job,qemuDomainObjPrivateJobCallbacks * cb)164 qemuDomainObjInitJob(qemuDomainJobObj *job,
165 qemuDomainObjPrivateJobCallbacks *cb)
166 {
167 memset(job, 0, sizeof(*job));
168 job->cb = cb;
169
170 if (!(job->privateData = job->cb->allocJobPrivate()))
171 return -1;
172
173 if (virCondInit(&job->cond) < 0) {
174 job->cb->freeJobPrivate(job->privateData);
175 return -1;
176 }
177
178 if (virCondInit(&job->asyncCond) < 0) {
179 job->cb->freeJobPrivate(job->privateData);
180 virCondDestroy(&job->cond);
181 return -1;
182 }
183
184 return 0;
185 }
186
187
188 static void
qemuDomainObjResetJob(qemuDomainJobObj * job)189 qemuDomainObjResetJob(qemuDomainJobObj *job)
190 {
191 job->active = QEMU_JOB_NONE;
192 job->owner = 0;
193 g_clear_pointer(&job->ownerAPI, g_free);
194 job->started = 0;
195 }
196
197
198 static void
qemuDomainObjResetAgentJob(qemuDomainJobObj * job)199 qemuDomainObjResetAgentJob(qemuDomainJobObj *job)
200 {
201 job->agentActive = QEMU_AGENT_JOB_NONE;
202 job->agentOwner = 0;
203 g_clear_pointer(&job->agentOwnerAPI, g_free);
204 job->agentStarted = 0;
205 }
206
207
208 static void
qemuDomainObjResetAsyncJob(qemuDomainJobObj * job)209 qemuDomainObjResetAsyncJob(qemuDomainJobObj *job)
210 {
211 job->asyncJob = QEMU_ASYNC_JOB_NONE;
212 job->asyncOwner = 0;
213 g_clear_pointer(&job->asyncOwnerAPI, g_free);
214 job->asyncStarted = 0;
215 job->phase = 0;
216 job->mask = QEMU_JOB_DEFAULT_MASK;
217 job->abortJob = false;
218 VIR_FREE(job->error);
219 g_clear_pointer(&job->current, qemuDomainJobInfoFree);
220 job->cb->resetJobPrivate(job->privateData);
221 job->apiFlags = 0;
222 }
223
224 int
qemuDomainObjRestoreJob(virDomainObj * obj,qemuDomainJobObj * job)225 qemuDomainObjRestoreJob(virDomainObj *obj,
226 qemuDomainJobObj *job)
227 {
228 qemuDomainObjPrivate *priv = obj->privateData;
229
230 memset(job, 0, sizeof(*job));
231 job->active = priv->job.active;
232 job->owner = priv->job.owner;
233 job->asyncJob = priv->job.asyncJob;
234 job->asyncOwner = priv->job.asyncOwner;
235 job->phase = priv->job.phase;
236 job->privateData = g_steal_pointer(&priv->job.privateData);
237 job->apiFlags = priv->job.apiFlags;
238
239 if (!(priv->job.privateData = priv->job.cb->allocJobPrivate()))
240 return -1;
241 job->cb = priv->job.cb;
242
243 qemuDomainObjResetJob(&priv->job);
244 qemuDomainObjResetAsyncJob(&priv->job);
245 return 0;
246 }
247
248 void
qemuDomainObjClearJob(qemuDomainJobObj * job)249 qemuDomainObjClearJob(qemuDomainJobObj *job)
250 {
251 if (!job->cb)
252 return;
253
254 qemuDomainObjResetJob(job);
255 qemuDomainObjResetAsyncJob(job);
256 g_clear_pointer(&job->privateData, job->cb->freeJobPrivate);
257 g_clear_pointer(&job->current, qemuDomainJobInfoFree);
258 g_clear_pointer(&job->completed, qemuDomainJobInfoFree);
259 virCondDestroy(&job->cond);
260 virCondDestroy(&job->asyncCond);
261 }
262
263 bool
qemuDomainTrackJob(qemuDomainJob job)264 qemuDomainTrackJob(qemuDomainJob job)
265 {
266 return (QEMU_DOMAIN_TRACK_JOBS & JOB_MASK(job)) != 0;
267 }
268
269
270 int
qemuDomainJobInfoUpdateTime(qemuDomainJobInfo * jobInfo)271 qemuDomainJobInfoUpdateTime(qemuDomainJobInfo *jobInfo)
272 {
273 unsigned long long now;
274
275 if (!jobInfo->started)
276 return 0;
277
278 if (virTimeMillisNow(&now) < 0)
279 return -1;
280
281 if (now < jobInfo->started) {
282 VIR_WARN("Async job starts in the future");
283 jobInfo->started = 0;
284 return 0;
285 }
286
287 jobInfo->timeElapsed = now - jobInfo->started;
288 return 0;
289 }
290
291 int
qemuDomainJobInfoUpdateDowntime(qemuDomainJobInfo * jobInfo)292 qemuDomainJobInfoUpdateDowntime(qemuDomainJobInfo *jobInfo)
293 {
294 unsigned long long now;
295
296 if (!jobInfo->stopped)
297 return 0;
298
299 if (virTimeMillisNow(&now) < 0)
300 return -1;
301
302 if (now < jobInfo->stopped) {
303 VIR_WARN("Guest's CPUs stopped in the future");
304 jobInfo->stopped = 0;
305 return 0;
306 }
307
308 jobInfo->stats.mig.downtime = now - jobInfo->stopped;
309 jobInfo->stats.mig.downtime_set = true;
310 return 0;
311 }
312
313 static virDomainJobType
qemuDomainJobStatusToType(qemuDomainJobStatus status)314 qemuDomainJobStatusToType(qemuDomainJobStatus status)
315 {
316 switch (status) {
317 case QEMU_DOMAIN_JOB_STATUS_NONE:
318 break;
319
320 case QEMU_DOMAIN_JOB_STATUS_ACTIVE:
321 case QEMU_DOMAIN_JOB_STATUS_MIGRATING:
322 case QEMU_DOMAIN_JOB_STATUS_QEMU_COMPLETED:
323 case QEMU_DOMAIN_JOB_STATUS_POSTCOPY:
324 case QEMU_DOMAIN_JOB_STATUS_PAUSED:
325 return VIR_DOMAIN_JOB_UNBOUNDED;
326
327 case QEMU_DOMAIN_JOB_STATUS_COMPLETED:
328 return VIR_DOMAIN_JOB_COMPLETED;
329
330 case QEMU_DOMAIN_JOB_STATUS_FAILED:
331 return VIR_DOMAIN_JOB_FAILED;
332
333 case QEMU_DOMAIN_JOB_STATUS_CANCELED:
334 return VIR_DOMAIN_JOB_CANCELLED;
335 }
336
337 return VIR_DOMAIN_JOB_NONE;
338 }
339
340 int
qemuDomainJobInfoToInfo(qemuDomainJobInfo * jobInfo,virDomainJobInfoPtr info)341 qemuDomainJobInfoToInfo(qemuDomainJobInfo *jobInfo,
342 virDomainJobInfoPtr info)
343 {
344 info->type = qemuDomainJobStatusToType(jobInfo->status);
345 info->timeElapsed = jobInfo->timeElapsed;
346
347 switch (jobInfo->statsType) {
348 case QEMU_DOMAIN_JOB_STATS_TYPE_MIGRATION:
349 info->memTotal = jobInfo->stats.mig.ram_total;
350 info->memRemaining = jobInfo->stats.mig.ram_remaining;
351 info->memProcessed = jobInfo->stats.mig.ram_transferred;
352 info->fileTotal = jobInfo->stats.mig.disk_total +
353 jobInfo->mirrorStats.total;
354 info->fileRemaining = jobInfo->stats.mig.disk_remaining +
355 (jobInfo->mirrorStats.total -
356 jobInfo->mirrorStats.transferred);
357 info->fileProcessed = jobInfo->stats.mig.disk_transferred +
358 jobInfo->mirrorStats.transferred;
359 break;
360
361 case QEMU_DOMAIN_JOB_STATS_TYPE_SAVEDUMP:
362 info->memTotal = jobInfo->stats.mig.ram_total;
363 info->memRemaining = jobInfo->stats.mig.ram_remaining;
364 info->memProcessed = jobInfo->stats.mig.ram_transferred;
365 break;
366
367 case QEMU_DOMAIN_JOB_STATS_TYPE_MEMDUMP:
368 info->memTotal = jobInfo->stats.dump.total;
369 info->memProcessed = jobInfo->stats.dump.completed;
370 info->memRemaining = info->memTotal - info->memProcessed;
371 break;
372
373 case QEMU_DOMAIN_JOB_STATS_TYPE_BACKUP:
374 info->fileTotal = jobInfo->stats.backup.total;
375 info->fileProcessed = jobInfo->stats.backup.transferred;
376 info->fileRemaining = info->fileTotal - info->fileProcessed;
377 break;
378
379 case QEMU_DOMAIN_JOB_STATS_TYPE_NONE:
380 break;
381 }
382
383 info->dataTotal = info->memTotal + info->fileTotal;
384 info->dataRemaining = info->memRemaining + info->fileRemaining;
385 info->dataProcessed = info->memProcessed + info->fileProcessed;
386
387 return 0;
388 }
389
390
391 static int
qemuDomainMigrationJobInfoToParams(qemuDomainJobInfo * jobInfo,int * type,virTypedParameterPtr * params,int * nparams)392 qemuDomainMigrationJobInfoToParams(qemuDomainJobInfo *jobInfo,
393 int *type,
394 virTypedParameterPtr *params,
395 int *nparams)
396 {
397 qemuMonitorMigrationStats *stats = &jobInfo->stats.mig;
398 qemuDomainMirrorStats *mirrorStats = &jobInfo->mirrorStats;
399 virTypedParameterPtr par = NULL;
400 int maxpar = 0;
401 int npar = 0;
402 unsigned long long mirrorRemaining = mirrorStats->total -
403 mirrorStats->transferred;
404
405 if (virTypedParamsAddInt(&par, &npar, &maxpar,
406 VIR_DOMAIN_JOB_OPERATION,
407 jobInfo->operation) < 0)
408 goto error;
409
410 if (virTypedParamsAddULLong(&par, &npar, &maxpar,
411 VIR_DOMAIN_JOB_TIME_ELAPSED,
412 jobInfo->timeElapsed) < 0)
413 goto error;
414
415 if (jobInfo->timeDeltaSet &&
416 jobInfo->timeElapsed > jobInfo->timeDelta &&
417 virTypedParamsAddULLong(&par, &npar, &maxpar,
418 VIR_DOMAIN_JOB_TIME_ELAPSED_NET,
419 jobInfo->timeElapsed - jobInfo->timeDelta) < 0)
420 goto error;
421
422 if (stats->downtime_set &&
423 virTypedParamsAddULLong(&par, &npar, &maxpar,
424 VIR_DOMAIN_JOB_DOWNTIME,
425 stats->downtime) < 0)
426 goto error;
427
428 if (stats->downtime_set &&
429 jobInfo->timeDeltaSet &&
430 stats->downtime > jobInfo->timeDelta &&
431 virTypedParamsAddULLong(&par, &npar, &maxpar,
432 VIR_DOMAIN_JOB_DOWNTIME_NET,
433 stats->downtime - jobInfo->timeDelta) < 0)
434 goto error;
435
436 if (stats->setup_time_set &&
437 virTypedParamsAddULLong(&par, &npar, &maxpar,
438 VIR_DOMAIN_JOB_SETUP_TIME,
439 stats->setup_time) < 0)
440 goto error;
441
442 if (virTypedParamsAddULLong(&par, &npar, &maxpar,
443 VIR_DOMAIN_JOB_DATA_TOTAL,
444 stats->ram_total +
445 stats->disk_total +
446 mirrorStats->total) < 0 ||
447 virTypedParamsAddULLong(&par, &npar, &maxpar,
448 VIR_DOMAIN_JOB_DATA_PROCESSED,
449 stats->ram_transferred +
450 stats->disk_transferred +
451 mirrorStats->transferred) < 0 ||
452 virTypedParamsAddULLong(&par, &npar, &maxpar,
453 VIR_DOMAIN_JOB_DATA_REMAINING,
454 stats->ram_remaining +
455 stats->disk_remaining +
456 mirrorRemaining) < 0)
457 goto error;
458
459 if (virTypedParamsAddULLong(&par, &npar, &maxpar,
460 VIR_DOMAIN_JOB_MEMORY_TOTAL,
461 stats->ram_total) < 0 ||
462 virTypedParamsAddULLong(&par, &npar, &maxpar,
463 VIR_DOMAIN_JOB_MEMORY_PROCESSED,
464 stats->ram_transferred) < 0 ||
465 virTypedParamsAddULLong(&par, &npar, &maxpar,
466 VIR_DOMAIN_JOB_MEMORY_REMAINING,
467 stats->ram_remaining) < 0)
468 goto error;
469
470 if (stats->ram_bps &&
471 virTypedParamsAddULLong(&par, &npar, &maxpar,
472 VIR_DOMAIN_JOB_MEMORY_BPS,
473 stats->ram_bps) < 0)
474 goto error;
475
476 if (stats->ram_duplicate_set) {
477 if (virTypedParamsAddULLong(&par, &npar, &maxpar,
478 VIR_DOMAIN_JOB_MEMORY_CONSTANT,
479 stats->ram_duplicate) < 0 ||
480 virTypedParamsAddULLong(&par, &npar, &maxpar,
481 VIR_DOMAIN_JOB_MEMORY_NORMAL,
482 stats->ram_normal) < 0 ||
483 virTypedParamsAddULLong(&par, &npar, &maxpar,
484 VIR_DOMAIN_JOB_MEMORY_NORMAL_BYTES,
485 stats->ram_normal_bytes) < 0)
486 goto error;
487 }
488
489 if (virTypedParamsAddULLong(&par, &npar, &maxpar,
490 VIR_DOMAIN_JOB_MEMORY_DIRTY_RATE,
491 stats->ram_dirty_rate) < 0 ||
492 virTypedParamsAddULLong(&par, &npar, &maxpar,
493 VIR_DOMAIN_JOB_MEMORY_ITERATION,
494 stats->ram_iteration) < 0 ||
495 virTypedParamsAddULLong(&par, &npar, &maxpar,
496 VIR_DOMAIN_JOB_MEMORY_POSTCOPY_REQS,
497 stats->ram_postcopy_reqs) < 0)
498 goto error;
499
500 if (stats->ram_page_size > 0 &&
501 virTypedParamsAddULLong(&par, &npar, &maxpar,
502 VIR_DOMAIN_JOB_MEMORY_PAGE_SIZE,
503 stats->ram_page_size) < 0)
504 goto error;
505
506 /* The remaining stats are disk, mirror, or migration specific
507 * so if this is a SAVEDUMP, we can just skip them */
508 if (jobInfo->statsType == QEMU_DOMAIN_JOB_STATS_TYPE_SAVEDUMP)
509 goto done;
510
511 if (virTypedParamsAddULLong(&par, &npar, &maxpar,
512 VIR_DOMAIN_JOB_DISK_TOTAL,
513 stats->disk_total +
514 mirrorStats->total) < 0 ||
515 virTypedParamsAddULLong(&par, &npar, &maxpar,
516 VIR_DOMAIN_JOB_DISK_PROCESSED,
517 stats->disk_transferred +
518 mirrorStats->transferred) < 0 ||
519 virTypedParamsAddULLong(&par, &npar, &maxpar,
520 VIR_DOMAIN_JOB_DISK_REMAINING,
521 stats->disk_remaining +
522 mirrorRemaining) < 0)
523 goto error;
524
525 if (stats->disk_bps &&
526 virTypedParamsAddULLong(&par, &npar, &maxpar,
527 VIR_DOMAIN_JOB_DISK_BPS,
528 stats->disk_bps) < 0)
529 goto error;
530
531 if (stats->xbzrle_set) {
532 if (virTypedParamsAddULLong(&par, &npar, &maxpar,
533 VIR_DOMAIN_JOB_COMPRESSION_CACHE,
534 stats->xbzrle_cache_size) < 0 ||
535 virTypedParamsAddULLong(&par, &npar, &maxpar,
536 VIR_DOMAIN_JOB_COMPRESSION_BYTES,
537 stats->xbzrle_bytes) < 0 ||
538 virTypedParamsAddULLong(&par, &npar, &maxpar,
539 VIR_DOMAIN_JOB_COMPRESSION_PAGES,
540 stats->xbzrle_pages) < 0 ||
541 virTypedParamsAddULLong(&par, &npar, &maxpar,
542 VIR_DOMAIN_JOB_COMPRESSION_CACHE_MISSES,
543 stats->xbzrle_cache_miss) < 0 ||
544 virTypedParamsAddULLong(&par, &npar, &maxpar,
545 VIR_DOMAIN_JOB_COMPRESSION_OVERFLOW,
546 stats->xbzrle_overflow) < 0)
547 goto error;
548 }
549
550 if (stats->cpu_throttle_percentage &&
551 virTypedParamsAddInt(&par, &npar, &maxpar,
552 VIR_DOMAIN_JOB_AUTO_CONVERGE_THROTTLE,
553 stats->cpu_throttle_percentage) < 0)
554 goto error;
555
556 done:
557 *type = qemuDomainJobStatusToType(jobInfo->status);
558 *params = par;
559 *nparams = npar;
560 return 0;
561
562 error:
563 virTypedParamsFree(par, npar);
564 return -1;
565 }
566
567
568 static int
qemuDomainDumpJobInfoToParams(qemuDomainJobInfo * jobInfo,int * type,virTypedParameterPtr * params,int * nparams)569 qemuDomainDumpJobInfoToParams(qemuDomainJobInfo *jobInfo,
570 int *type,
571 virTypedParameterPtr *params,
572 int *nparams)
573 {
574 qemuMonitorDumpStats *stats = &jobInfo->stats.dump;
575 virTypedParameterPtr par = NULL;
576 int maxpar = 0;
577 int npar = 0;
578
579 if (virTypedParamsAddInt(&par, &npar, &maxpar,
580 VIR_DOMAIN_JOB_OPERATION,
581 jobInfo->operation) < 0)
582 goto error;
583
584 if (virTypedParamsAddULLong(&par, &npar, &maxpar,
585 VIR_DOMAIN_JOB_TIME_ELAPSED,
586 jobInfo->timeElapsed) < 0)
587 goto error;
588
589 if (virTypedParamsAddULLong(&par, &npar, &maxpar,
590 VIR_DOMAIN_JOB_MEMORY_TOTAL,
591 stats->total) < 0 ||
592 virTypedParamsAddULLong(&par, &npar, &maxpar,
593 VIR_DOMAIN_JOB_MEMORY_PROCESSED,
594 stats->completed) < 0 ||
595 virTypedParamsAddULLong(&par, &npar, &maxpar,
596 VIR_DOMAIN_JOB_MEMORY_REMAINING,
597 stats->total - stats->completed) < 0)
598 goto error;
599
600 *type = qemuDomainJobStatusToType(jobInfo->status);
601 *params = par;
602 *nparams = npar;
603 return 0;
604
605 error:
606 virTypedParamsFree(par, npar);
607 return -1;
608 }
609
610
611 static int
qemuDomainBackupJobInfoToParams(qemuDomainJobInfo * jobInfo,int * type,virTypedParameterPtr * params,int * nparams)612 qemuDomainBackupJobInfoToParams(qemuDomainJobInfo *jobInfo,
613 int *type,
614 virTypedParameterPtr *params,
615 int *nparams)
616 {
617 qemuDomainBackupStats *stats = &jobInfo->stats.backup;
618 g_autoptr(virTypedParamList) par = g_new0(virTypedParamList, 1);
619
620 if (virTypedParamListAddInt(par, jobInfo->operation,
621 VIR_DOMAIN_JOB_OPERATION) < 0)
622 return -1;
623
624 if (virTypedParamListAddULLong(par, jobInfo->timeElapsed,
625 VIR_DOMAIN_JOB_TIME_ELAPSED) < 0)
626 return -1;
627
628 if (stats->transferred > 0 || stats->total > 0) {
629 if (virTypedParamListAddULLong(par, stats->total,
630 VIR_DOMAIN_JOB_DISK_TOTAL) < 0)
631 return -1;
632
633 if (virTypedParamListAddULLong(par, stats->transferred,
634 VIR_DOMAIN_JOB_DISK_PROCESSED) < 0)
635 return -1;
636
637 if (virTypedParamListAddULLong(par, stats->total - stats->transferred,
638 VIR_DOMAIN_JOB_DISK_REMAINING) < 0)
639 return -1;
640 }
641
642 if (stats->tmp_used > 0 || stats->tmp_total > 0) {
643 if (virTypedParamListAddULLong(par, stats->tmp_used,
644 VIR_DOMAIN_JOB_DISK_TEMP_USED) < 0)
645 return -1;
646
647 if (virTypedParamListAddULLong(par, stats->tmp_total,
648 VIR_DOMAIN_JOB_DISK_TEMP_TOTAL) < 0)
649 return -1;
650 }
651
652 if (jobInfo->status != QEMU_DOMAIN_JOB_STATUS_ACTIVE &&
653 virTypedParamListAddBoolean(par,
654 jobInfo->status == QEMU_DOMAIN_JOB_STATUS_COMPLETED,
655 VIR_DOMAIN_JOB_SUCCESS) < 0)
656 return -1;
657
658 if (jobInfo->errmsg &&
659 virTypedParamListAddString(par, jobInfo->errmsg, VIR_DOMAIN_JOB_ERRMSG) < 0)
660 return -1;
661
662 *nparams = virTypedParamListStealParams(par, params);
663 *type = qemuDomainJobStatusToType(jobInfo->status);
664 return 0;
665 }
666
667
668 int
qemuDomainJobInfoToParams(qemuDomainJobInfo * jobInfo,int * type,virTypedParameterPtr * params,int * nparams)669 qemuDomainJobInfoToParams(qemuDomainJobInfo *jobInfo,
670 int *type,
671 virTypedParameterPtr *params,
672 int *nparams)
673 {
674 switch (jobInfo->statsType) {
675 case QEMU_DOMAIN_JOB_STATS_TYPE_MIGRATION:
676 case QEMU_DOMAIN_JOB_STATS_TYPE_SAVEDUMP:
677 return qemuDomainMigrationJobInfoToParams(jobInfo, type, params, nparams);
678
679 case QEMU_DOMAIN_JOB_STATS_TYPE_MEMDUMP:
680 return qemuDomainDumpJobInfoToParams(jobInfo, type, params, nparams);
681
682 case QEMU_DOMAIN_JOB_STATS_TYPE_BACKUP:
683 return qemuDomainBackupJobInfoToParams(jobInfo, type, params, nparams);
684
685 case QEMU_DOMAIN_JOB_STATS_TYPE_NONE:
686 virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
687 _("invalid job statistics type"));
688 break;
689
690 default:
691 virReportEnumRangeError(qemuDomainJobStatsType, jobInfo->statsType);
692 break;
693 }
694
695 return -1;
696 }
697
698
699 void
qemuDomainObjSetJobPhase(virQEMUDriver * driver,virDomainObj * obj,int phase)700 qemuDomainObjSetJobPhase(virQEMUDriver *driver,
701 virDomainObj *obj,
702 int phase)
703 {
704 qemuDomainObjPrivate *priv = obj->privateData;
705 unsigned long long me = virThreadSelfID();
706
707 if (!priv->job.asyncJob)
708 return;
709
710 VIR_DEBUG("Setting '%s' phase to '%s'",
711 qemuDomainAsyncJobTypeToString(priv->job.asyncJob),
712 qemuDomainAsyncJobPhaseToString(priv->job.asyncJob, phase));
713
714 if (priv->job.asyncOwner == 0) {
715 priv->job.asyncOwnerAPI = g_strdup(virThreadJobGet());
716 } else if (me != priv->job.asyncOwner) {
717 VIR_WARN("'%s' async job is owned by thread %llu",
718 qemuDomainAsyncJobTypeToString(priv->job.asyncJob),
719 priv->job.asyncOwner);
720 }
721
722 priv->job.phase = phase;
723 priv->job.asyncOwner = me;
724 qemuDomainObjSaveStatus(driver, obj);
725 }
726
727 void
qemuDomainObjSetAsyncJobMask(virDomainObj * obj,unsigned long long allowedJobs)728 qemuDomainObjSetAsyncJobMask(virDomainObj *obj,
729 unsigned long long allowedJobs)
730 {
731 qemuDomainObjPrivate *priv = obj->privateData;
732
733 if (!priv->job.asyncJob)
734 return;
735
736 priv->job.mask = allowedJobs | JOB_MASK(QEMU_JOB_DESTROY);
737 }
738
739 void
qemuDomainObjDiscardAsyncJob(virQEMUDriver * driver,virDomainObj * obj)740 qemuDomainObjDiscardAsyncJob(virQEMUDriver *driver, virDomainObj *obj)
741 {
742 qemuDomainObjPrivate *priv = obj->privateData;
743
744 if (priv->job.active == QEMU_JOB_ASYNC_NESTED)
745 qemuDomainObjResetJob(&priv->job);
746 qemuDomainObjResetAsyncJob(&priv->job);
747 qemuDomainObjSaveStatus(driver, obj);
748 }
749
750 void
qemuDomainObjReleaseAsyncJob(virDomainObj * obj)751 qemuDomainObjReleaseAsyncJob(virDomainObj *obj)
752 {
753 qemuDomainObjPrivate *priv = obj->privateData;
754
755 VIR_DEBUG("Releasing ownership of '%s' async job",
756 qemuDomainAsyncJobTypeToString(priv->job.asyncJob));
757
758 if (priv->job.asyncOwner != virThreadSelfID()) {
759 VIR_WARN("'%s' async job is owned by thread %llu",
760 qemuDomainAsyncJobTypeToString(priv->job.asyncJob),
761 priv->job.asyncOwner);
762 }
763 priv->job.asyncOwner = 0;
764 }
765
766 static bool
qemuDomainNestedJobAllowed(qemuDomainJobObj * jobs,qemuDomainJob newJob)767 qemuDomainNestedJobAllowed(qemuDomainJobObj *jobs, qemuDomainJob newJob)
768 {
769 return !jobs->asyncJob ||
770 newJob == QEMU_JOB_NONE ||
771 (jobs->mask & JOB_MASK(newJob)) != 0;
772 }
773
774 bool
qemuDomainJobAllowed(qemuDomainJobObj * jobs,qemuDomainJob newJob)775 qemuDomainJobAllowed(qemuDomainJobObj *jobs, qemuDomainJob newJob)
776 {
777 return !jobs->active && qemuDomainNestedJobAllowed(jobs, newJob);
778 }
779
780 static bool
qemuDomainObjCanSetJob(qemuDomainJobObj * job,qemuDomainJob newJob,qemuDomainAgentJob newAgentJob)781 qemuDomainObjCanSetJob(qemuDomainJobObj *job,
782 qemuDomainJob newJob,
783 qemuDomainAgentJob newAgentJob)
784 {
785 return ((newJob == QEMU_JOB_NONE ||
786 job->active == QEMU_JOB_NONE) &&
787 (newAgentJob == QEMU_AGENT_JOB_NONE ||
788 job->agentActive == QEMU_AGENT_JOB_NONE));
789 }
790
791 /* Give up waiting for mutex after 30 seconds */
792 #define QEMU_JOB_WAIT_TIME (1000ull * 30)
793
794 /**
795 * qemuDomainObjBeginJobInternal:
796 * @driver: qemu driver
797 * @obj: domain object
798 * @job: qemuDomainJob to start
799 * @asyncJob: qemuDomainAsyncJob to start
800 * @nowait: don't wait trying to acquire @job
801 *
802 * Acquires job for a domain object which must be locked before
803 * calling. If there's already a job running waits up to
804 * QEMU_JOB_WAIT_TIME after which the functions fails reporting
805 * an error unless @nowait is set.
806 *
807 * If @nowait is true this function tries to acquire job and if
808 * it fails, then it returns immediately without waiting. No
809 * error is reported in this case.
810 *
811 * Returns: 0 on success,
812 * -2 if unable to start job because of timeout or
813 * maxQueuedJobs limit,
814 * -1 otherwise.
815 */
816 static int ATTRIBUTE_NONNULL(1)
qemuDomainObjBeginJobInternal(virQEMUDriver * driver,virDomainObj * obj,qemuDomainJob job,qemuDomainAgentJob agentJob,qemuDomainAsyncJob asyncJob,bool nowait)817 qemuDomainObjBeginJobInternal(virQEMUDriver *driver,
818 virDomainObj *obj,
819 qemuDomainJob job,
820 qemuDomainAgentJob agentJob,
821 qemuDomainAsyncJob asyncJob,
822 bool nowait)
823 {
824 qemuDomainObjPrivate *priv = obj->privateData;
825 unsigned long long now;
826 unsigned long long then;
827 bool nested = job == QEMU_JOB_ASYNC_NESTED;
828 bool async = job == QEMU_JOB_ASYNC;
829 g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver);
830 const char *blocker = NULL;
831 const char *agentBlocker = NULL;
832 int ret = -1;
833 unsigned long long duration = 0;
834 unsigned long long agentDuration = 0;
835 unsigned long long asyncDuration = 0;
836
837 VIR_DEBUG("Starting job: job=%s agentJob=%s asyncJob=%s "
838 "(vm=%p name=%s, current job=%s agentJob=%s async=%s)",
839 qemuDomainJobTypeToString(job),
840 qemuDomainAgentJobTypeToString(agentJob),
841 qemuDomainAsyncJobTypeToString(asyncJob),
842 obj, obj->def->name,
843 qemuDomainJobTypeToString(priv->job.active),
844 qemuDomainAgentJobTypeToString(priv->job.agentActive),
845 qemuDomainAsyncJobTypeToString(priv->job.asyncJob));
846
847 if (virTimeMillisNow(&now) < 0)
848 return -1;
849
850 priv->jobs_queued++;
851 then = now + QEMU_JOB_WAIT_TIME;
852
853 retry:
854 if ((!async && job != QEMU_JOB_DESTROY) &&
855 cfg->maxQueuedJobs &&
856 priv->jobs_queued > cfg->maxQueuedJobs) {
857 goto error;
858 }
859
860 while (!nested && !qemuDomainNestedJobAllowed(&priv->job, job)) {
861 if (nowait)
862 goto cleanup;
863
864 VIR_DEBUG("Waiting for async job (vm=%p name=%s)", obj, obj->def->name);
865 if (virCondWaitUntil(&priv->job.asyncCond, &obj->parent.lock, then) < 0)
866 goto error;
867 }
868
869 while (!qemuDomainObjCanSetJob(&priv->job, job, agentJob)) {
870 if (nowait)
871 goto cleanup;
872
873 VIR_DEBUG("Waiting for job (vm=%p name=%s)", obj, obj->def->name);
874 if (virCondWaitUntil(&priv->job.cond, &obj->parent.lock, then) < 0)
875 goto error;
876 }
877
878 /* No job is active but a new async job could have been started while obj
879 * was unlocked, so we need to recheck it. */
880 if (!nested && !qemuDomainNestedJobAllowed(&priv->job, job))
881 goto retry;
882
883 ignore_value(virTimeMillisNow(&now));
884
885 if (job) {
886 qemuDomainObjResetJob(&priv->job);
887
888 if (job != QEMU_JOB_ASYNC) {
889 VIR_DEBUG("Started job: %s (async=%s vm=%p name=%s)",
890 qemuDomainJobTypeToString(job),
891 qemuDomainAsyncJobTypeToString(priv->job.asyncJob),
892 obj, obj->def->name);
893 priv->job.active = job;
894 priv->job.owner = virThreadSelfID();
895 priv->job.ownerAPI = g_strdup(virThreadJobGet());
896 priv->job.started = now;
897 } else {
898 VIR_DEBUG("Started async job: %s (vm=%p name=%s)",
899 qemuDomainAsyncJobTypeToString(asyncJob),
900 obj, obj->def->name);
901 qemuDomainObjResetAsyncJob(&priv->job);
902 priv->job.current = g_new0(qemuDomainJobInfo, 1);
903 priv->job.current->status = QEMU_DOMAIN_JOB_STATUS_ACTIVE;
904 priv->job.asyncJob = asyncJob;
905 priv->job.asyncOwner = virThreadSelfID();
906 priv->job.asyncOwnerAPI = g_strdup(virThreadJobGet());
907 priv->job.asyncStarted = now;
908 priv->job.current->started = now;
909 }
910 }
911
912 if (agentJob) {
913 qemuDomainObjResetAgentJob(&priv->job);
914
915 VIR_DEBUG("Started agent job: %s (vm=%p name=%s job=%s async=%s)",
916 qemuDomainAgentJobTypeToString(agentJob),
917 obj, obj->def->name,
918 qemuDomainJobTypeToString(priv->job.active),
919 qemuDomainAsyncJobTypeToString(priv->job.asyncJob));
920 priv->job.agentActive = agentJob;
921 priv->job.agentOwner = virThreadSelfID();
922 priv->job.agentOwnerAPI = g_strdup(virThreadJobGet());
923 priv->job.agentStarted = now;
924 }
925
926 if (qemuDomainTrackJob(job))
927 qemuDomainObjSaveStatus(driver, obj);
928
929 return 0;
930
931 error:
932 ignore_value(virTimeMillisNow(&now));
933 if (priv->job.active && priv->job.started)
934 duration = now - priv->job.started;
935 if (priv->job.agentActive && priv->job.agentStarted)
936 agentDuration = now - priv->job.agentStarted;
937 if (priv->job.asyncJob && priv->job.asyncStarted)
938 asyncDuration = now - priv->job.asyncStarted;
939
940 VIR_WARN("Cannot start job (%s, %s, %s) for domain %s; "
941 "current job is (%s, %s, %s) "
942 "owned by (%llu %s, %llu %s, %llu %s (flags=0x%lx)) "
943 "for (%llus, %llus, %llus)",
944 qemuDomainJobTypeToString(job),
945 qemuDomainAgentJobTypeToString(agentJob),
946 qemuDomainAsyncJobTypeToString(asyncJob),
947 obj->def->name,
948 qemuDomainJobTypeToString(priv->job.active),
949 qemuDomainAgentJobTypeToString(priv->job.agentActive),
950 qemuDomainAsyncJobTypeToString(priv->job.asyncJob),
951 priv->job.owner, NULLSTR(priv->job.ownerAPI),
952 priv->job.agentOwner, NULLSTR(priv->job.agentOwnerAPI),
953 priv->job.asyncOwner, NULLSTR(priv->job.asyncOwnerAPI),
954 priv->job.apiFlags,
955 duration / 1000, agentDuration / 1000, asyncDuration / 1000);
956
957 if (job) {
958 if (nested || qemuDomainNestedJobAllowed(&priv->job, job))
959 blocker = priv->job.ownerAPI;
960 else
961 blocker = priv->job.asyncOwnerAPI;
962 }
963
964 if (agentJob)
965 agentBlocker = priv->job.agentOwnerAPI;
966
967 if (errno == ETIMEDOUT) {
968 if (blocker && agentBlocker) {
969 virReportError(VIR_ERR_OPERATION_TIMEOUT,
970 _("cannot acquire state change "
971 "lock (held by monitor=%s agent=%s)"),
972 blocker, agentBlocker);
973 } else if (blocker) {
974 virReportError(VIR_ERR_OPERATION_TIMEOUT,
975 _("cannot acquire state change "
976 "lock (held by monitor=%s)"),
977 blocker);
978 } else if (agentBlocker) {
979 virReportError(VIR_ERR_OPERATION_TIMEOUT,
980 _("cannot acquire state change "
981 "lock (held by agent=%s)"),
982 agentBlocker);
983 } else {
984 virReportError(VIR_ERR_OPERATION_TIMEOUT, "%s",
985 _("cannot acquire state change lock"));
986 }
987 ret = -2;
988 } else if (cfg->maxQueuedJobs &&
989 priv->jobs_queued > cfg->maxQueuedJobs) {
990 if (blocker && agentBlocker) {
991 virReportError(VIR_ERR_OPERATION_FAILED,
992 _("cannot acquire state change "
993 "lock (held by monitor=%s agent=%s) "
994 "due to max_queued limit"),
995 blocker, agentBlocker);
996 } else if (blocker) {
997 virReportError(VIR_ERR_OPERATION_FAILED,
998 _("cannot acquire state change "
999 "lock (held by monitor=%s) "
1000 "due to max_queued limit"),
1001 blocker);
1002 } else if (agentBlocker) {
1003 virReportError(VIR_ERR_OPERATION_FAILED,
1004 _("cannot acquire state change "
1005 "lock (held by agent=%s) "
1006 "due to max_queued limit"),
1007 agentBlocker);
1008 } else {
1009 virReportError(VIR_ERR_OPERATION_FAILED, "%s",
1010 _("cannot acquire state change lock "
1011 "due to max_queued limit"));
1012 }
1013 ret = -2;
1014 } else {
1015 virReportSystemError(errno, "%s", _("cannot acquire job mutex"));
1016 }
1017
1018 cleanup:
1019 priv->jobs_queued--;
1020 return ret;
1021 }
1022
1023 /*
1024 * obj must be locked before calling
1025 *
1026 * This must be called by anything that will change the VM state
1027 * in any way, or anything that will use the QEMU monitor.
1028 *
1029 * Successful calls must be followed by EndJob eventually
1030 */
qemuDomainObjBeginJob(virQEMUDriver * driver,virDomainObj * obj,qemuDomainJob job)1031 int qemuDomainObjBeginJob(virQEMUDriver *driver,
1032 virDomainObj *obj,
1033 qemuDomainJob job)
1034 {
1035 if (qemuDomainObjBeginJobInternal(driver, obj, job,
1036 QEMU_AGENT_JOB_NONE,
1037 QEMU_ASYNC_JOB_NONE, false) < 0)
1038 return -1;
1039 return 0;
1040 }
1041
1042 /**
1043 * qemuDomainObjBeginAgentJob:
1044 *
1045 * Grabs agent type of job. Use if caller talks to guest agent only.
1046 *
1047 * To end job call qemuDomainObjEndAgentJob.
1048 */
1049 int
qemuDomainObjBeginAgentJob(virQEMUDriver * driver,virDomainObj * obj,qemuDomainAgentJob agentJob)1050 qemuDomainObjBeginAgentJob(virQEMUDriver *driver,
1051 virDomainObj *obj,
1052 qemuDomainAgentJob agentJob)
1053 {
1054 return qemuDomainObjBeginJobInternal(driver, obj, QEMU_JOB_NONE,
1055 agentJob,
1056 QEMU_ASYNC_JOB_NONE, false);
1057 }
1058
qemuDomainObjBeginAsyncJob(virQEMUDriver * driver,virDomainObj * obj,qemuDomainAsyncJob asyncJob,virDomainJobOperation operation,unsigned long apiFlags)1059 int qemuDomainObjBeginAsyncJob(virQEMUDriver *driver,
1060 virDomainObj *obj,
1061 qemuDomainAsyncJob asyncJob,
1062 virDomainJobOperation operation,
1063 unsigned long apiFlags)
1064 {
1065 qemuDomainObjPrivate *priv;
1066
1067 if (qemuDomainObjBeginJobInternal(driver, obj, QEMU_JOB_ASYNC,
1068 QEMU_AGENT_JOB_NONE,
1069 asyncJob, false) < 0)
1070 return -1;
1071
1072 priv = obj->privateData;
1073 priv->job.current->operation = operation;
1074 priv->job.apiFlags = apiFlags;
1075 return 0;
1076 }
1077
1078 int
qemuDomainObjBeginNestedJob(virQEMUDriver * driver,virDomainObj * obj,qemuDomainAsyncJob asyncJob)1079 qemuDomainObjBeginNestedJob(virQEMUDriver *driver,
1080 virDomainObj *obj,
1081 qemuDomainAsyncJob asyncJob)
1082 {
1083 qemuDomainObjPrivate *priv = obj->privateData;
1084
1085 if (asyncJob != priv->job.asyncJob) {
1086 virReportError(VIR_ERR_INTERNAL_ERROR,
1087 _("unexpected async job %d type expected %d"),
1088 asyncJob, priv->job.asyncJob);
1089 return -1;
1090 }
1091
1092 if (priv->job.asyncOwner != virThreadSelfID()) {
1093 VIR_WARN("This thread doesn't seem to be the async job owner: %llu",
1094 priv->job.asyncOwner);
1095 }
1096
1097 return qemuDomainObjBeginJobInternal(driver, obj,
1098 QEMU_JOB_ASYNC_NESTED,
1099 QEMU_AGENT_JOB_NONE,
1100 QEMU_ASYNC_JOB_NONE,
1101 false);
1102 }
1103
1104 /**
1105 * qemuDomainObjBeginJobNowait:
1106 *
1107 * @driver: qemu driver
1108 * @obj: domain object
1109 * @job: qemuDomainJob to start
1110 *
1111 * Acquires job for a domain object which must be locked before
1112 * calling. If there's already a job running it returns
1113 * immediately without any error reported.
1114 *
1115 * Returns: see qemuDomainObjBeginJobInternal
1116 */
1117 int
qemuDomainObjBeginJobNowait(virQEMUDriver * driver,virDomainObj * obj,qemuDomainJob job)1118 qemuDomainObjBeginJobNowait(virQEMUDriver *driver,
1119 virDomainObj *obj,
1120 qemuDomainJob job)
1121 {
1122 return qemuDomainObjBeginJobInternal(driver, obj, job,
1123 QEMU_AGENT_JOB_NONE,
1124 QEMU_ASYNC_JOB_NONE, true);
1125 }
1126
1127 /*
1128 * obj must be locked and have a reference before calling
1129 *
1130 * To be called after completing the work associated with the
1131 * earlier qemuDomainBeginJob() call
1132 */
1133 void
qemuDomainObjEndJob(virQEMUDriver * driver,virDomainObj * obj)1134 qemuDomainObjEndJob(virQEMUDriver *driver, virDomainObj *obj)
1135 {
1136 qemuDomainObjPrivate *priv = obj->privateData;
1137 qemuDomainJob job = priv->job.active;
1138
1139 priv->jobs_queued--;
1140
1141 VIR_DEBUG("Stopping job: %s (async=%s vm=%p name=%s)",
1142 qemuDomainJobTypeToString(job),
1143 qemuDomainAsyncJobTypeToString(priv->job.asyncJob),
1144 obj, obj->def->name);
1145
1146 qemuDomainObjResetJob(&priv->job);
1147 if (qemuDomainTrackJob(job))
1148 qemuDomainObjSaveStatus(driver, obj);
1149 /* We indeed need to wake up ALL threads waiting because
1150 * grabbing a job requires checking more variables. */
1151 virCondBroadcast(&priv->job.cond);
1152 }
1153
1154 void
qemuDomainObjEndAgentJob(virDomainObj * obj)1155 qemuDomainObjEndAgentJob(virDomainObj *obj)
1156 {
1157 qemuDomainObjPrivate *priv = obj->privateData;
1158 qemuDomainAgentJob agentJob = priv->job.agentActive;
1159
1160 priv->jobs_queued--;
1161
1162 VIR_DEBUG("Stopping agent job: %s (async=%s vm=%p name=%s)",
1163 qemuDomainAgentJobTypeToString(agentJob),
1164 qemuDomainAsyncJobTypeToString(priv->job.asyncJob),
1165 obj, obj->def->name);
1166
1167 qemuDomainObjResetAgentJob(&priv->job);
1168 /* We indeed need to wake up ALL threads waiting because
1169 * grabbing a job requires checking more variables. */
1170 virCondBroadcast(&priv->job.cond);
1171 }
1172
1173 void
qemuDomainObjEndAsyncJob(virQEMUDriver * driver,virDomainObj * obj)1174 qemuDomainObjEndAsyncJob(virQEMUDriver *driver, virDomainObj *obj)
1175 {
1176 qemuDomainObjPrivate *priv = obj->privateData;
1177
1178 priv->jobs_queued--;
1179
1180 VIR_DEBUG("Stopping async job: %s (vm=%p name=%s)",
1181 qemuDomainAsyncJobTypeToString(priv->job.asyncJob),
1182 obj, obj->def->name);
1183
1184 qemuDomainObjResetAsyncJob(&priv->job);
1185 qemuDomainObjSaveStatus(driver, obj);
1186 virCondBroadcast(&priv->job.asyncCond);
1187 }
1188
1189 void
qemuDomainObjAbortAsyncJob(virDomainObj * obj)1190 qemuDomainObjAbortAsyncJob(virDomainObj *obj)
1191 {
1192 qemuDomainObjPrivate *priv = obj->privateData;
1193
1194 VIR_DEBUG("Requesting abort of async job: %s (vm=%p name=%s)",
1195 qemuDomainAsyncJobTypeToString(priv->job.asyncJob),
1196 obj, obj->def->name);
1197
1198 priv->job.abortJob = true;
1199 virDomainObjBroadcast(obj);
1200 }
1201
1202 int
qemuDomainObjPrivateXMLFormatJob(virBuffer * buf,virDomainObj * vm)1203 qemuDomainObjPrivateXMLFormatJob(virBuffer *buf,
1204 virDomainObj *vm)
1205 {
1206 qemuDomainObjPrivate *priv = vm->privateData;
1207 g_auto(virBuffer) attrBuf = VIR_BUFFER_INITIALIZER;
1208 g_auto(virBuffer) childBuf = VIR_BUFFER_INIT_CHILD(buf);
1209 qemuDomainJob job = priv->job.active;
1210
1211 if (!qemuDomainTrackJob(job))
1212 job = QEMU_JOB_NONE;
1213
1214 if (job == QEMU_JOB_NONE &&
1215 priv->job.asyncJob == QEMU_ASYNC_JOB_NONE)
1216 return 0;
1217
1218 virBufferAsprintf(&attrBuf, " type='%s' async='%s'",
1219 qemuDomainJobTypeToString(job),
1220 qemuDomainAsyncJobTypeToString(priv->job.asyncJob));
1221
1222 if (priv->job.phase) {
1223 virBufferAsprintf(&attrBuf, " phase='%s'",
1224 qemuDomainAsyncJobPhaseToString(priv->job.asyncJob,
1225 priv->job.phase));
1226 }
1227
1228 if (priv->job.asyncJob != QEMU_ASYNC_JOB_NONE)
1229 virBufferAsprintf(&attrBuf, " flags='0x%lx'", priv->job.apiFlags);
1230
1231 if (priv->job.cb->formatJob(&childBuf, &priv->job, vm) < 0)
1232 return -1;
1233
1234 virXMLFormatElement(buf, "job", &attrBuf, &childBuf);
1235
1236 return 0;
1237 }
1238
1239
1240 int
qemuDomainObjPrivateXMLParseJob(virDomainObj * vm,xmlXPathContextPtr ctxt)1241 qemuDomainObjPrivateXMLParseJob(virDomainObj *vm,
1242 xmlXPathContextPtr ctxt)
1243 {
1244 qemuDomainObjPrivate *priv = vm->privateData;
1245 qemuDomainJobObj *job = &priv->job;
1246 VIR_XPATH_NODE_AUTORESTORE(ctxt)
1247 g_autofree char *tmp = NULL;
1248
1249 if (!(ctxt->node = virXPathNode("./job[1]", ctxt)))
1250 return 0;
1251
1252 if ((tmp = virXPathString("string(@type)", ctxt))) {
1253 int type;
1254
1255 if ((type = qemuDomainJobTypeFromString(tmp)) < 0) {
1256 virReportError(VIR_ERR_INTERNAL_ERROR,
1257 _("Unknown job type %s"), tmp);
1258 return -1;
1259 }
1260 VIR_FREE(tmp);
1261 priv->job.active = type;
1262 }
1263
1264 if ((tmp = virXPathString("string(@async)", ctxt))) {
1265 int async;
1266
1267 if ((async = qemuDomainAsyncJobTypeFromString(tmp)) < 0) {
1268 virReportError(VIR_ERR_INTERNAL_ERROR,
1269 _("Unknown async job type %s"), tmp);
1270 return -1;
1271 }
1272 VIR_FREE(tmp);
1273 priv->job.asyncJob = async;
1274
1275 if ((tmp = virXPathString("string(@phase)", ctxt))) {
1276 priv->job.phase = qemuDomainAsyncJobPhaseFromString(async, tmp);
1277 if (priv->job.phase < 0) {
1278 virReportError(VIR_ERR_INTERNAL_ERROR,
1279 _("Unknown job phase %s"), tmp);
1280 return -1;
1281 }
1282 VIR_FREE(tmp);
1283 }
1284 }
1285
1286 if (virXPathULongHex("string(@flags)", ctxt, &priv->job.apiFlags) == -2) {
1287 virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Invalid job flags"));
1288 return -1;
1289 }
1290
1291 if (priv->job.cb->parseJob(ctxt, job, vm) < 0)
1292 return -1;
1293
1294 return 0;
1295 }
1296