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                                   &params, &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