src/conf/storage_encryption_conf.c
src/conf/storage_source_conf.c
src/conf/virchrdev.c
+src/conf/virdomainjob.c
src/conf/virdomainmomentobjlist.c
src/conf/virdomainobjlist.c
src/conf/virnetworkobj.c
src/hyperv/hyperv_wmi.c
src/hypervisor/domain_cgroup.c
src/hypervisor/domain_driver.c
-src/hypervisor/domain_job.c
src/hypervisor/virclosecallbacks.c
src/hypervisor/virhostdev.c
src/interface/interface_backend_netcf.c
#include "ch_monitor.h"
#include "virchrdev.h"
#include "vircgroup.h"
-#include "domain_job.h"
+#include "virdomainjob.h"
/* Give up waiting for mutex after 30 seconds */
#define CH_JOB_WAIT_TIME (1000ull * 30)
'numa_conf.c',
'snapshot_conf.c',
'virdomaincheckpointobjlist.c',
+ 'virdomainjob.c',
'virdomainmomentobjlist.c',
'virdomainobjlist.c',
'virdomainsnapshotobjlist.c',
--- /dev/null
+/*
+ * virdomainjob.c: job functions shared between hypervisor drivers
+ *
+ * Copyright (C) 2022 Red Hat, Inc.
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#include <config.h>
+#include <string.h>
+
+#include "virdomainjob.h"
+#include "viralloc.h"
+#include "virthreadjob.h"
+#include "virlog.h"
+#include "virtime.h"
+
+#define VIR_FROM_THIS VIR_FROM_NONE
+
+VIR_LOG_INIT("conf.virdomainjob");
+
+
+VIR_ENUM_IMPL(virDomainJob,
+ VIR_JOB_LAST,
+ "none",
+ "query",
+ "destroy",
+ "suspend",
+ "modify",
+ "abort",
+ "migration operation",
+ "modify migration safe",
+ "none", /* async job is never stored in job.active */
+ "async nested",
+);
+
+VIR_ENUM_IMPL(virDomainAgentJob,
+ VIR_AGENT_JOB_LAST,
+ "none",
+ "query",
+ "modify",
+);
+
+VIR_ENUM_IMPL(virDomainAsyncJob,
+ VIR_ASYNC_JOB_LAST,
+ "none",
+ "migration out",
+ "migration in",
+ "save",
+ "dump",
+ "snapshot",
+ "start",
+ "backup",
+);
+
+virDomainJobData *
+virDomainJobDataInit(virDomainJobDataPrivateDataCallbacks *cb)
+{
+ virDomainJobData *ret = g_new0(virDomainJobData, 1);
+
+ ret->privateDataCb = cb;
+
+ if (ret->privateDataCb)
+ ret->privateData = ret->privateDataCb->allocPrivateData();
+
+ return ret;
+}
+
+virDomainJobData *
+virDomainJobDataCopy(virDomainJobData *data)
+{
+ virDomainJobData *ret = g_new0(virDomainJobData, 1);
+
+ memcpy(ret, data, sizeof(*data));
+
+ if (ret->privateDataCb)
+ ret->privateData = data->privateDataCb->copyPrivateData(data->privateData);
+
+ ret->errmsg = g_strdup(data->errmsg);
+
+ return ret;
+}
+
+void
+virDomainJobDataFree(virDomainJobData *data)
+{
+ if (!data)
+ return;
+
+ if (data->privateDataCb)
+ data->privateDataCb->freePrivateData(data->privateData);
+
+ g_free(data->errmsg);
+ g_free(data);
+}
+
+virDomainJobType
+virDomainJobStatusToType(virDomainJobStatus status)
+{
+ switch (status) {
+ case VIR_DOMAIN_JOB_STATUS_NONE:
+ break;
+
+ case VIR_DOMAIN_JOB_STATUS_ACTIVE:
+ case VIR_DOMAIN_JOB_STATUS_MIGRATING:
+ case VIR_DOMAIN_JOB_STATUS_HYPERVISOR_COMPLETED:
+ case VIR_DOMAIN_JOB_STATUS_POSTCOPY:
+ case VIR_DOMAIN_JOB_STATUS_POSTCOPY_PAUSED:
+ case VIR_DOMAIN_JOB_STATUS_PAUSED:
+ return VIR_DOMAIN_JOB_UNBOUNDED;
+
+ case VIR_DOMAIN_JOB_STATUS_COMPLETED:
+ return VIR_DOMAIN_JOB_COMPLETED;
+
+ case VIR_DOMAIN_JOB_STATUS_FAILED:
+ return VIR_DOMAIN_JOB_FAILED;
+
+ case VIR_DOMAIN_JOB_STATUS_CANCELED:
+ return VIR_DOMAIN_JOB_CANCELLED;
+ }
+
+ return VIR_DOMAIN_JOB_NONE;
+}
+
+int
+virDomainObjInitJob(virDomainJobObj *job,
+ virDomainObjPrivateJobCallbacks *cb,
+ virDomainJobDataPrivateDataCallbacks *jobDataPrivateCb)
+{
+ memset(job, 0, sizeof(*job));
+ job->cb = cb;
+ job->jobDataPrivateCb = jobDataPrivateCb;
+
+ if (virCondInit(&job->cond) < 0)
+ return -1;
+
+ if (virCondInit(&job->asyncCond) < 0) {
+ virCondDestroy(&job->cond);
+ return -1;
+ }
+
+ if (job->cb &&
+ !(job->privateData = job->cb->allocJobPrivate())) {
+ virCondDestroy(&job->cond);
+ virCondDestroy(&job->asyncCond);
+ return -1;
+ }
+
+ return 0;
+}
+
+void
+virDomainObjResetJob(virDomainJobObj *job)
+{
+ job->active = VIR_JOB_NONE;
+ job->owner = 0;
+ g_clear_pointer(&job->ownerAPI, g_free);
+ job->started = 0;
+}
+
+void
+virDomainObjResetAgentJob(virDomainJobObj *job)
+{
+ job->agentActive = VIR_AGENT_JOB_NONE;
+ job->agentOwner = 0;
+ g_clear_pointer(&job->agentOwnerAPI, g_free);
+ job->agentStarted = 0;
+}
+
+void
+virDomainObjResetAsyncJob(virDomainJobObj *job)
+{
+ job->asyncJob = VIR_ASYNC_JOB_NONE;
+ job->asyncOwner = 0;
+ g_clear_pointer(&job->asyncOwnerAPI, g_free);
+ job->asyncStarted = 0;
+ job->phase = 0;
+ job->mask = VIR_JOB_DEFAULT_MASK;
+ job->abortJob = false;
+ VIR_FREE(job->error);
+ g_clear_pointer(&job->current, virDomainJobDataFree);
+ job->apiFlags = 0;
+
+ if (job->cb)
+ job->cb->resetJobPrivate(job->privateData);
+}
+
+/**
+ * virDomainObjPreserveJob
+ * @param currJob structure is a job that needs to be preserved
+ * @param job structure where to store job details from @currJob
+ *
+ * Saves the current job details from @currJob to @job and resets the job in @currJob.
+ *
+ * Returns 0 on success, -1 on failure.
+ */
+int
+virDomainObjPreserveJob(virDomainJobObj *currJob,
+ virDomainJobObj *job)
+{
+ memset(job, 0, sizeof(*job));
+ job->active = currJob->active;
+ job->owner = currJob->owner;
+ job->asyncJob = currJob->asyncJob;
+ job->asyncOwner = currJob->asyncOwner;
+ job->phase = currJob->phase;
+ job->privateData = g_steal_pointer(&currJob->privateData);
+ job->apiFlags = currJob->apiFlags;
+
+ if (currJob->cb &&
+ !(currJob->privateData = currJob->cb->allocJobPrivate()))
+ return -1;
+ job->cb = currJob->cb;
+
+ virDomainObjResetJob(currJob);
+ virDomainObjResetAsyncJob(currJob);
+ return 0;
+}
+
+void
+virDomainObjClearJob(virDomainJobObj *job)
+{
+ virDomainObjResetJob(job);
+ virDomainObjResetAsyncJob(job);
+ g_clear_pointer(&job->current, virDomainJobDataFree);
+ g_clear_pointer(&job->completed, virDomainJobDataFree);
+ virCondDestroy(&job->cond);
+ virCondDestroy(&job->asyncCond);
+
+ if (job->cb)
+ g_clear_pointer(&job->privateData, job->cb->freeJobPrivate);
+}
+
+bool
+virDomainTrackJob(virDomainJob job)
+{
+ return (VIR_DOMAIN_TRACK_JOBS & JOB_MASK(job)) != 0;
+}
+
+bool
+virDomainNestedJobAllowed(virDomainJobObj *jobs, virDomainJob newJob)
+{
+ return !jobs->asyncJob ||
+ newJob == VIR_JOB_NONE ||
+ (jobs->mask & JOB_MASK(newJob));
+}
+
+bool
+virDomainObjCanSetJob(virDomainJobObj *job,
+ virDomainJob newJob,
+ virDomainAgentJob newAgentJob)
+{
+ return ((newJob == VIR_JOB_NONE ||
+ job->active == VIR_JOB_NONE) &&
+ (newAgentJob == VIR_AGENT_JOB_NONE ||
+ job->agentActive == VIR_AGENT_JOB_NONE));
+}
+
+/* Give up waiting for mutex after 30 seconds */
+#define VIR_JOB_WAIT_TIME (1000ull * 30)
+
+/**
+ * virDomainObjBeginJobInternal:
+ * @obj: virDomainObj = domain object
+ * @jobObj: virDomainJobObj = domain job object
+ * @job: virDomainJob to start
+ * @agentJob: virDomainAgentJob to start
+ * @asyncJob: virDomainAsyncJob to start
+ * @nowait: don't wait trying to acquire @job
+ *
+ * Acquires job for a domain object which must be locked before
+ * calling. If there's already a job running waits up to
+ * VIR_JOB_WAIT_TIME after which the functions fails reporting
+ * an error unless @nowait is set.
+ *
+ * If @nowait is true this function tries to acquire job and if
+ * it fails, then it returns immediately without waiting. No
+ * error is reported in this case.
+ *
+ * Returns: 0 on success,
+ * -2 if unable to start job because of timeout or
+ * maxQueuedJobs limit,
+ * -1 otherwise.
+ */
+int
+virDomainObjBeginJobInternal(virDomainObj *obj,
+ virDomainJobObj *jobObj,
+ virDomainJob job,
+ virDomainAgentJob agentJob,
+ virDomainAsyncJob asyncJob,
+ bool nowait)
+{
+ unsigned long long now = 0;
+ unsigned long long then = 0;
+ bool nested = job == VIR_JOB_ASYNC_NESTED;
+ const char *blocker = NULL;
+ const char *agentBlocker = NULL;
+ int ret = -1;
+ unsigned long long duration = 0;
+ unsigned long long agentDuration = 0;
+ unsigned long long asyncDuration = 0;
+ const char *currentAPI = virThreadJobGet();
+
+ VIR_DEBUG("Starting job: API=%s job=%s agentJob=%s asyncJob=%s "
+ "(vm=%p name=%s, current job=%s agentJob=%s async=%s)",
+ NULLSTR(currentAPI),
+ virDomainJobTypeToString(job),
+ virDomainAgentJobTypeToString(agentJob),
+ virDomainAsyncJobTypeToString(asyncJob),
+ obj, obj->def->name,
+ virDomainJobTypeToString(jobObj->active),
+ virDomainAgentJobTypeToString(jobObj->agentActive),
+ virDomainAsyncJobTypeToString(jobObj->asyncJob));
+
+ if (virTimeMillisNow(&now) < 0)
+ return -1;
+
+ jobObj->jobsQueued++;
+ then = now + VIR_JOB_WAIT_TIME;
+
+ retry:
+ if (job != VIR_JOB_ASYNC &&
+ job != VIR_JOB_DESTROY &&
+ jobObj->maxQueuedJobs &&
+ jobObj->jobsQueued > jobObj->maxQueuedJobs) {
+ goto error;
+ }
+
+ while (!nested && !virDomainNestedJobAllowed(jobObj, job)) {
+ if (nowait)
+ goto cleanup;
+
+ VIR_DEBUG("Waiting for async job (vm=%p name=%s)", obj, obj->def->name);
+ if (virCondWaitUntil(&jobObj->asyncCond, &obj->parent.lock, then) < 0)
+ goto error;
+ }
+
+ while (!virDomainObjCanSetJob(jobObj, job, agentJob)) {
+ if (nowait)
+ goto cleanup;
+
+ VIR_DEBUG("Waiting for job (vm=%p name=%s)", obj, obj->def->name);
+ if (virCondWaitUntil(&jobObj->cond, &obj->parent.lock, then) < 0)
+ goto error;
+ }
+
+ /* No job is active but a new async job could have been started while obj
+ * was unlocked, so we need to recheck it. */
+ if (!nested && !virDomainNestedJobAllowed(jobObj, job))
+ goto retry;
+
+ if (obj->removing) {
+ char uuidstr[VIR_UUID_STRING_BUFLEN];
+
+ virUUIDFormat(obj->def->uuid, uuidstr);
+ virReportError(VIR_ERR_NO_DOMAIN,
+ _("no domain with matching uuid '%s' (%s)"),
+ uuidstr, obj->def->name);
+ goto cleanup;
+ }
+
+ ignore_value(virTimeMillisNow(&now));
+
+ if (job) {
+ virDomainObjResetJob(jobObj);
+
+ if (job != VIR_JOB_ASYNC) {
+ VIR_DEBUG("Started job: %s (async=%s vm=%p name=%s)",
+ virDomainJobTypeToString(job),
+ virDomainAsyncJobTypeToString(jobObj->asyncJob),
+ obj, obj->def->name);
+ jobObj->active = job;
+ jobObj->owner = virThreadSelfID();
+ jobObj->ownerAPI = g_strdup(virThreadJobGet());
+ jobObj->started = now;
+ } else {
+ VIR_DEBUG("Started async job: %s (vm=%p name=%s)",
+ virDomainAsyncJobTypeToString(asyncJob),
+ obj, obj->def->name);
+ virDomainObjResetAsyncJob(jobObj);
+ jobObj->current = virDomainJobDataInit(jobObj->jobDataPrivateCb);
+ jobObj->current->status = VIR_DOMAIN_JOB_STATUS_ACTIVE;
+ jobObj->asyncJob = asyncJob;
+ jobObj->asyncOwner = virThreadSelfID();
+ jobObj->asyncOwnerAPI = g_strdup(virThreadJobGet());
+ jobObj->asyncStarted = now;
+ jobObj->current->started = now;
+ }
+ }
+
+ if (agentJob) {
+ virDomainObjResetAgentJob(jobObj);
+ VIR_DEBUG("Started agent job: %s (vm=%p name=%s job=%s async=%s)",
+ virDomainAgentJobTypeToString(agentJob),
+ obj, obj->def->name,
+ virDomainJobTypeToString(jobObj->active),
+ virDomainAsyncJobTypeToString(jobObj->asyncJob));
+ jobObj->agentActive = agentJob;
+ jobObj->agentOwner = virThreadSelfID();
+ jobObj->agentOwnerAPI = g_strdup(virThreadJobGet());
+ jobObj->agentStarted = now;
+ }
+
+ if (virDomainTrackJob(job) && jobObj->cb &&
+ jobObj->cb->saveStatusPrivate)
+ jobObj->cb->saveStatusPrivate(obj);
+
+ return 0;
+
+ error:
+ ignore_value(virTimeMillisNow(&now));
+ if (jobObj->active && jobObj->started)
+ duration = now - jobObj->started;
+ if (jobObj->agentActive && jobObj->agentStarted)
+ agentDuration = now - jobObj->agentStarted;
+ if (jobObj->asyncJob && jobObj->asyncStarted)
+ asyncDuration = now - jobObj->asyncStarted;
+
+ VIR_WARN("Cannot start job (%s, %s, %s) in API %s for domain %s; "
+ "current job is (%s, %s, %s) "
+ "owned by (%llu %s, %llu %s, %llu %s (flags=0x%lx)) "
+ "for (%llus, %llus, %llus)",
+ virDomainJobTypeToString(job),
+ virDomainAgentJobTypeToString(agentJob),
+ virDomainAsyncJobTypeToString(asyncJob),
+ NULLSTR(currentAPI),
+ obj->def->name,
+ virDomainJobTypeToString(jobObj->active),
+ virDomainAgentJobTypeToString(jobObj->agentActive),
+ virDomainAsyncJobTypeToString(jobObj->asyncJob),
+ jobObj->owner, NULLSTR(jobObj->ownerAPI),
+ jobObj->agentOwner, NULLSTR(jobObj->agentOwnerAPI),
+ jobObj->asyncOwner, NULLSTR(jobObj->asyncOwnerAPI),
+ jobObj->apiFlags,
+ duration / 1000, agentDuration / 1000, asyncDuration / 1000);
+
+ if (job) {
+ if (nested || virDomainNestedJobAllowed(jobObj, job))
+ blocker = jobObj->ownerAPI;
+ else
+ blocker = jobObj->asyncOwnerAPI;
+ }
+
+ if (agentJob)
+ agentBlocker = jobObj->agentOwnerAPI;
+
+ if (errno == ETIMEDOUT) {
+ if (blocker && agentBlocker) {
+ virReportError(VIR_ERR_OPERATION_TIMEOUT,
+ _("cannot acquire state change "
+ "lock (held by monitor=%s agent=%s)"),
+ blocker, agentBlocker);
+ } else if (blocker) {
+ virReportError(VIR_ERR_OPERATION_TIMEOUT,
+ _("cannot acquire state change "
+ "lock (held by monitor=%s)"),
+ blocker);
+ } else if (agentBlocker) {
+ virReportError(VIR_ERR_OPERATION_TIMEOUT,
+ _("cannot acquire state change "
+ "lock (held by agent=%s)"),
+ agentBlocker);
+ } else {
+ virReportError(VIR_ERR_OPERATION_TIMEOUT, "%s",
+ _("cannot acquire state change lock"));
+ }
+ ret = -2;
+ } else if (jobObj->maxQueuedJobs &&
+ jobObj->jobsQueued > jobObj->maxQueuedJobs) {
+ if (blocker && agentBlocker) {
+ virReportError(VIR_ERR_OPERATION_FAILED,
+ _("cannot acquire state change "
+ "lock (held by monitor=%s agent=%s) "
+ "due to max_queued limit"),
+ blocker, agentBlocker);
+ } else if (blocker) {
+ virReportError(VIR_ERR_OPERATION_FAILED,
+ _("cannot acquire state change "
+ "lock (held by monitor=%s) "
+ "due to max_queued limit"),
+ blocker);
+ } else if (agentBlocker) {
+ virReportError(VIR_ERR_OPERATION_FAILED,
+ _("cannot acquire state change "
+ "lock (held by agent=%s) "
+ "due to max_queued limit"),
+ agentBlocker);
+ } else {
+ virReportError(VIR_ERR_OPERATION_FAILED, "%s",
+ _("cannot acquire state change lock "
+ "due to max_queued limit"));
+ }
+ ret = -2;
+ } else {
+ virReportSystemError(errno, "%s", _("cannot acquire job mutex"));
+ }
+
+ cleanup:
+ jobObj->jobsQueued--;
+ return ret;
+}
--- /dev/null
+/*
+ * virdomainjob.h: job functions shared between hypervisor drivers
+ *
+ * Copyright (C) 2022 Red Hat, Inc.
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#pragma once
+
+#include "internal.h"
+#include "virenum.h"
+#include "virthread.h"
+#include "virbuffer.h"
+#include "domain_conf.h"
+
+#define JOB_MASK(job) (job == 0 ? 0 : 1 << (job - 1))
+#define VIR_JOB_DEFAULT_MASK \
+ (JOB_MASK(VIR_JOB_QUERY) | \
+ JOB_MASK(VIR_JOB_DESTROY) | \
+ JOB_MASK(VIR_JOB_ABORT))
+
+/* Jobs which have to be tracked in domain state XML. */
+#define VIR_DOMAIN_TRACK_JOBS \
+ (JOB_MASK(VIR_JOB_DESTROY) | \
+ JOB_MASK(VIR_JOB_ASYNC))
+
+
+/* Only 1 job is allowed at any time
+ * A job includes *all* monitor commands / hypervisor.so api,
+ * even those just querying information, not merely actions */
+typedef enum {
+ VIR_JOB_NONE = 0, /* Always set to 0 for easy if (jobActive) conditions */
+ VIR_JOB_QUERY, /* Doesn't change any state */
+ VIR_JOB_DESTROY, /* Destroys the domain (cannot be masked out) */
+ VIR_JOB_SUSPEND, /* Suspends (stops vCPUs) the domain */
+ VIR_JOB_MODIFY, /* May change state */
+ VIR_JOB_ABORT, /* Abort current async job */
+ VIR_JOB_MIGRATION_OP, /* Operation influencing outgoing migration */
+ VIR_JOB_MODIFY_MIGRATION_SAFE, /* Internal only job for event handlers which
+ need to be processed even during migration.
+ The code may only change state in a way
+ that does not affect migration. */
+
+ /* The following two items must always be the last items before JOB_LAST */
+ VIR_JOB_ASYNC, /* Asynchronous job */
+ VIR_JOB_ASYNC_NESTED, /* Normal job within an async job */
+
+ VIR_JOB_LAST
+} virDomainJob;
+VIR_ENUM_DECL(virDomainJob);
+
+
+/* Currently only QEMU driver uses agent jobs */
+typedef enum {
+ VIR_AGENT_JOB_NONE = 0, /* No agent job. */
+ VIR_AGENT_JOB_QUERY, /* Does not change state of domain */
+ VIR_AGENT_JOB_MODIFY, /* May change state of domain */
+
+ VIR_AGENT_JOB_LAST
+} virDomainAgentJob;
+VIR_ENUM_DECL(virDomainAgentJob);
+
+
+/* Async job consists of a series of jobs that may change state. Independent
+ * jobs that do not change state (and possibly others if explicitly allowed by
+ * current async job) are allowed to be run even if async job is active.
+ * Currently supported by QEMU only. */
+typedef enum {
+ VIR_ASYNC_JOB_NONE = 0,
+ VIR_ASYNC_JOB_MIGRATION_OUT,
+ VIR_ASYNC_JOB_MIGRATION_IN,
+ VIR_ASYNC_JOB_SAVE,
+ VIR_ASYNC_JOB_DUMP,
+ VIR_ASYNC_JOB_SNAPSHOT,
+ VIR_ASYNC_JOB_START,
+ VIR_ASYNC_JOB_BACKUP,
+
+ VIR_ASYNC_JOB_LAST
+} virDomainAsyncJob;
+VIR_ENUM_DECL(virDomainAsyncJob);
+
+
+typedef enum {
+ VIR_DOMAIN_JOB_STATUS_NONE = 0,
+ VIR_DOMAIN_JOB_STATUS_ACTIVE,
+ VIR_DOMAIN_JOB_STATUS_MIGRATING,
+ VIR_DOMAIN_JOB_STATUS_HYPERVISOR_COMPLETED,
+ VIR_DOMAIN_JOB_STATUS_PAUSED,
+ VIR_DOMAIN_JOB_STATUS_POSTCOPY,
+ VIR_DOMAIN_JOB_STATUS_POSTCOPY_PAUSED,
+ VIR_DOMAIN_JOB_STATUS_COMPLETED,
+ VIR_DOMAIN_JOB_STATUS_FAILED,
+ VIR_DOMAIN_JOB_STATUS_CANCELED,
+} virDomainJobStatus;
+
+typedef void *(*virDomainJobDataPrivateDataAlloc) (void);
+typedef void *(*virDomainJobDataPrivateDataCopy) (void *);
+typedef void (*virDomainJobDataPrivateDataFree) (void *);
+
+typedef struct _virDomainJobDataPrivateDataCallbacks virDomainJobDataPrivateDataCallbacks;
+struct _virDomainJobDataPrivateDataCallbacks {
+ virDomainJobDataPrivateDataAlloc allocPrivateData;
+ virDomainJobDataPrivateDataCopy copyPrivateData;
+ virDomainJobDataPrivateDataFree freePrivateData;
+};
+
+
+typedef struct _virDomainJobData virDomainJobData;
+struct _virDomainJobData {
+ virDomainJobType jobType;
+
+ virDomainJobStatus status;
+ virDomainJobOperation operation;
+ unsigned long long started; /* When the async job started */
+ unsigned long long stopped; /* When the domain's CPUs were stopped */
+ unsigned long long sent; /* When the source sent status info to the
+ destination (only for migrations). */
+ unsigned long long received; /* When the destination host received status
+ info from the source (migrations only). */
+ /* Computed values */
+ unsigned long long timeElapsed;
+ long long timeDelta; /* delta = received - sent, i.e., the difference between
+ the source and the destination time plus the time
+ between the end of Perform phase on the source and
+ the beginning of Finish phase on the destination. */
+ bool timeDeltaSet;
+
+ char *errmsg; /* optional error message for failed completed jobs */
+
+ void *privateData; /* private data of hypervisors */
+ virDomainJobDataPrivateDataCallbacks *privateDataCb; /* callbacks of private data,
+ hypervisor based */
+};
+
+
+virDomainJobData *
+virDomainJobDataInit(virDomainJobDataPrivateDataCallbacks *cb);
+
+void
+virDomainJobDataFree(virDomainJobData *data);
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(virDomainJobData, virDomainJobDataFree);
+
+virDomainJobData *
+virDomainJobDataCopy(virDomainJobData *data);
+
+virDomainJobType
+virDomainJobStatusToType(virDomainJobStatus status);
+
+
+typedef struct _virDomainObjPrivateJobCallbacks virDomainObjPrivateJobCallbacks;
+
+typedef struct _virDomainJobObj virDomainJobObj;
+struct _virDomainJobObj {
+ virCond cond; /* Use to coordinate jobs */
+
+ int jobsQueued;
+ unsigned int maxQueuedJobs;
+
+ /* The following members are for VIR_JOB_* */
+ virDomainJob active; /* currently running job */
+ unsigned long long owner; /* Thread id which set current job */
+ char *ownerAPI; /* The API which owns the job */
+ unsigned long long started; /* When the current job started */
+
+ /* The following members are for VIR_AGENT_JOB_* */
+ virDomainAgentJob agentActive; /* Currently running agent job */
+ unsigned long long agentOwner; /* Thread id which set current agent job */
+ char *agentOwnerAPI; /* The API which owns the agent job */
+ unsigned long long agentStarted; /* When the current agent job started */
+
+ /* The following members are for VIR_ASYNC_JOB_* */
+ virCond asyncCond; /* Use to coordinate with async jobs */
+ virDomainAsyncJob asyncJob; /* Currently active async job */
+ unsigned long long asyncOwner; /* Thread which set current async job */
+ char *asyncOwnerAPI; /* The API which owns the async job */
+ unsigned long long asyncStarted; /* When the current async job started */
+ int phase; /* Job phase (mainly for migrations) */
+ unsigned long long mask; /* Jobs allowed during async job */
+ virDomainJobData *current; /* async job progress data */
+ virDomainJobData *completed; /* statistics data of a recently completed job */
+ bool abortJob; /* abort of the job requested */
+ char *error; /* job event completion error */
+ unsigned long apiFlags; /* flags passed to the API which started the async job */
+
+ void *privateData; /* job specific collection of data */
+ virDomainObjPrivateJobCallbacks *cb;
+ virDomainJobDataPrivateDataCallbacks *jobDataPrivateCb; /* callbacks for privateData of
+ virDomainJobData, can be NULL */
+};
+
+
+typedef void *(*virDomainObjPrivateJobAlloc)(void);
+typedef void (*virDomainObjPrivateJobFree)(void *);
+typedef void (*virDomainObjPrivateJobReset)(void *);
+typedef int (*virDomainObjPrivateJobFormat)(virBuffer *,
+ virDomainJobObj *,
+ virDomainObj *);
+typedef int (*virDomainObjPrivateJobParse)(xmlXPathContextPtr,
+ virDomainJobObj *,
+ virDomainObj *);
+typedef void (*virDomainObjPrivateSaveStatus)(virDomainObj *obj);
+
+struct _virDomainObjPrivateJobCallbacks {
+ virDomainObjPrivateJobAlloc allocJobPrivate;
+ virDomainObjPrivateJobFree freeJobPrivate;
+ virDomainObjPrivateJobReset resetJobPrivate;
+ virDomainObjPrivateJobFormat formatJobPrivate;
+ virDomainObjPrivateJobParse parseJobPrivate;
+ virDomainObjPrivateSaveStatus saveStatusPrivate;
+};
+
+
+int virDomainObjInitJob(virDomainJobObj *job,
+ virDomainObjPrivateJobCallbacks *cb,
+ virDomainJobDataPrivateDataCallbacks *jobDataPrivateCb);
+
+void virDomainObjResetJob(virDomainJobObj *job);
+
+void virDomainObjResetAgentJob(virDomainJobObj *job);
+
+void virDomainObjResetAsyncJob(virDomainJobObj *job);
+
+int virDomainObjPreserveJob(virDomainJobObj *currJob,
+ virDomainJobObj *job);
+
+void virDomainObjClearJob(virDomainJobObj *job);
+G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(virDomainJobObj, virDomainObjClearJob);
+
+bool virDomainTrackJob(virDomainJob job);
+
+bool virDomainNestedJobAllowed(virDomainJobObj *jobs, virDomainJob newJob);
+
+bool virDomainObjCanSetJob(virDomainJobObj *job,
+ virDomainJob newJob,
+ virDomainAgentJob newAgentJob);
+
+int virDomainObjBeginJobInternal(virDomainObj *obj,
+ virDomainJobObj *jobObj,
+ virDomainJob job,
+ virDomainAgentJob agentJob,
+ virDomainAsyncJob asyncJob,
+ bool nowait)
+ ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3);
+++ /dev/null
-/*
- * domain_job.c: job functions shared between hypervisor drivers
- *
- * Copyright (C) 2022 Red Hat, Inc.
- * SPDX-License-Identifier: LGPL-2.1-or-later
- */
-
-#include <config.h>
-#include <string.h>
-
-#include "domain_job.h"
-#include "viralloc.h"
-#include "virthreadjob.h"
-#include "virlog.h"
-#include "virtime.h"
-
-#define VIR_FROM_THIS VIR_FROM_NONE
-
-VIR_LOG_INIT("hypervisor.domain_job");
-
-
-VIR_ENUM_IMPL(virDomainJob,
- VIR_JOB_LAST,
- "none",
- "query",
- "destroy",
- "suspend",
- "modify",
- "abort",
- "migration operation",
- "modify migration safe",
- "none", /* async job is never stored in job.active */
- "async nested",
-);
-
-VIR_ENUM_IMPL(virDomainAgentJob,
- VIR_AGENT_JOB_LAST,
- "none",
- "query",
- "modify",
-);
-
-VIR_ENUM_IMPL(virDomainAsyncJob,
- VIR_ASYNC_JOB_LAST,
- "none",
- "migration out",
- "migration in",
- "save",
- "dump",
- "snapshot",
- "start",
- "backup",
-);
-
-virDomainJobData *
-virDomainJobDataInit(virDomainJobDataPrivateDataCallbacks *cb)
-{
- virDomainJobData *ret = g_new0(virDomainJobData, 1);
-
- ret->privateDataCb = cb;
-
- if (ret->privateDataCb)
- ret->privateData = ret->privateDataCb->allocPrivateData();
-
- return ret;
-}
-
-virDomainJobData *
-virDomainJobDataCopy(virDomainJobData *data)
-{
- virDomainJobData *ret = g_new0(virDomainJobData, 1);
-
- memcpy(ret, data, sizeof(*data));
-
- if (ret->privateDataCb)
- ret->privateData = data->privateDataCb->copyPrivateData(data->privateData);
-
- ret->errmsg = g_strdup(data->errmsg);
-
- return ret;
-}
-
-void
-virDomainJobDataFree(virDomainJobData *data)
-{
- if (!data)
- return;
-
- if (data->privateDataCb)
- data->privateDataCb->freePrivateData(data->privateData);
-
- g_free(data->errmsg);
- g_free(data);
-}
-
-virDomainJobType
-virDomainJobStatusToType(virDomainJobStatus status)
-{
- switch (status) {
- case VIR_DOMAIN_JOB_STATUS_NONE:
- break;
-
- case VIR_DOMAIN_JOB_STATUS_ACTIVE:
- case VIR_DOMAIN_JOB_STATUS_MIGRATING:
- case VIR_DOMAIN_JOB_STATUS_HYPERVISOR_COMPLETED:
- case VIR_DOMAIN_JOB_STATUS_POSTCOPY:
- case VIR_DOMAIN_JOB_STATUS_POSTCOPY_PAUSED:
- case VIR_DOMAIN_JOB_STATUS_PAUSED:
- return VIR_DOMAIN_JOB_UNBOUNDED;
-
- case VIR_DOMAIN_JOB_STATUS_COMPLETED:
- return VIR_DOMAIN_JOB_COMPLETED;
-
- case VIR_DOMAIN_JOB_STATUS_FAILED:
- return VIR_DOMAIN_JOB_FAILED;
-
- case VIR_DOMAIN_JOB_STATUS_CANCELED:
- return VIR_DOMAIN_JOB_CANCELLED;
- }
-
- return VIR_DOMAIN_JOB_NONE;
-}
-
-int
-virDomainObjInitJob(virDomainJobObj *job,
- virDomainObjPrivateJobCallbacks *cb,
- virDomainJobDataPrivateDataCallbacks *jobDataPrivateCb)
-{
- memset(job, 0, sizeof(*job));
- job->cb = cb;
- job->jobDataPrivateCb = jobDataPrivateCb;
-
- if (virCondInit(&job->cond) < 0)
- return -1;
-
- if (virCondInit(&job->asyncCond) < 0) {
- virCondDestroy(&job->cond);
- return -1;
- }
-
- if (job->cb &&
- !(job->privateData = job->cb->allocJobPrivate())) {
- virCondDestroy(&job->cond);
- virCondDestroy(&job->asyncCond);
- return -1;
- }
-
- return 0;
-}
-
-void
-virDomainObjResetJob(virDomainJobObj *job)
-{
- job->active = VIR_JOB_NONE;
- job->owner = 0;
- g_clear_pointer(&job->ownerAPI, g_free);
- job->started = 0;
-}
-
-void
-virDomainObjResetAgentJob(virDomainJobObj *job)
-{
- job->agentActive = VIR_AGENT_JOB_NONE;
- job->agentOwner = 0;
- g_clear_pointer(&job->agentOwnerAPI, g_free);
- job->agentStarted = 0;
-}
-
-void
-virDomainObjResetAsyncJob(virDomainJobObj *job)
-{
- job->asyncJob = VIR_ASYNC_JOB_NONE;
- job->asyncOwner = 0;
- g_clear_pointer(&job->asyncOwnerAPI, g_free);
- job->asyncStarted = 0;
- job->phase = 0;
- job->mask = VIR_JOB_DEFAULT_MASK;
- job->abortJob = false;
- VIR_FREE(job->error);
- g_clear_pointer(&job->current, virDomainJobDataFree);
- job->apiFlags = 0;
-
- if (job->cb)
- job->cb->resetJobPrivate(job->privateData);
-}
-
-/**
- * virDomainObjPreserveJob
- * @param currJob structure is a job that needs to be preserved
- * @param job structure where to store job details from @currJob
- *
- * Saves the current job details from @currJob to @job and resets the job in @currJob.
- *
- * Returns 0 on success, -1 on failure.
- */
-int
-virDomainObjPreserveJob(virDomainJobObj *currJob,
- virDomainJobObj *job)
-{
- memset(job, 0, sizeof(*job));
- job->active = currJob->active;
- job->owner = currJob->owner;
- job->asyncJob = currJob->asyncJob;
- job->asyncOwner = currJob->asyncOwner;
- job->phase = currJob->phase;
- job->privateData = g_steal_pointer(&currJob->privateData);
- job->apiFlags = currJob->apiFlags;
-
- if (currJob->cb &&
- !(currJob->privateData = currJob->cb->allocJobPrivate()))
- return -1;
- job->cb = currJob->cb;
-
- virDomainObjResetJob(currJob);
- virDomainObjResetAsyncJob(currJob);
- return 0;
-}
-
-void
-virDomainObjClearJob(virDomainJobObj *job)
-{
- virDomainObjResetJob(job);
- virDomainObjResetAsyncJob(job);
- g_clear_pointer(&job->current, virDomainJobDataFree);
- g_clear_pointer(&job->completed, virDomainJobDataFree);
- virCondDestroy(&job->cond);
- virCondDestroy(&job->asyncCond);
-
- if (job->cb)
- g_clear_pointer(&job->privateData, job->cb->freeJobPrivate);
-}
-
-bool
-virDomainTrackJob(virDomainJob job)
-{
- return (VIR_DOMAIN_TRACK_JOBS & JOB_MASK(job)) != 0;
-}
-
-bool
-virDomainNestedJobAllowed(virDomainJobObj *jobs, virDomainJob newJob)
-{
- return !jobs->asyncJob ||
- newJob == VIR_JOB_NONE ||
- (jobs->mask & JOB_MASK(newJob));
-}
-
-bool
-virDomainObjCanSetJob(virDomainJobObj *job,
- virDomainJob newJob,
- virDomainAgentJob newAgentJob)
-{
- return ((newJob == VIR_JOB_NONE ||
- job->active == VIR_JOB_NONE) &&
- (newAgentJob == VIR_AGENT_JOB_NONE ||
- job->agentActive == VIR_AGENT_JOB_NONE));
-}
-
-/* Give up waiting for mutex after 30 seconds */
-#define VIR_JOB_WAIT_TIME (1000ull * 30)
-
-/**
- * virDomainObjBeginJobInternal:
- * @obj: virDomainObj = domain object
- * @jobObj: virDomainJobObj = domain job object
- * @job: virDomainJob to start
- * @agentJob: virDomainAgentJob to start
- * @asyncJob: virDomainAsyncJob to start
- * @nowait: don't wait trying to acquire @job
- *
- * Acquires job for a domain object which must be locked before
- * calling. If there's already a job running waits up to
- * VIR_JOB_WAIT_TIME after which the functions fails reporting
- * an error unless @nowait is set.
- *
- * If @nowait is true this function tries to acquire job and if
- * it fails, then it returns immediately without waiting. No
- * error is reported in this case.
- *
- * Returns: 0 on success,
- * -2 if unable to start job because of timeout or
- * maxQueuedJobs limit,
- * -1 otherwise.
- */
-int
-virDomainObjBeginJobInternal(virDomainObj *obj,
- virDomainJobObj *jobObj,
- virDomainJob job,
- virDomainAgentJob agentJob,
- virDomainAsyncJob asyncJob,
- bool nowait)
-{
- unsigned long long now = 0;
- unsigned long long then = 0;
- bool nested = job == VIR_JOB_ASYNC_NESTED;
- const char *blocker = NULL;
- const char *agentBlocker = NULL;
- int ret = -1;
- unsigned long long duration = 0;
- unsigned long long agentDuration = 0;
- unsigned long long asyncDuration = 0;
- const char *currentAPI = virThreadJobGet();
-
- VIR_DEBUG("Starting job: API=%s job=%s agentJob=%s asyncJob=%s "
- "(vm=%p name=%s, current job=%s agentJob=%s async=%s)",
- NULLSTR(currentAPI),
- virDomainJobTypeToString(job),
- virDomainAgentJobTypeToString(agentJob),
- virDomainAsyncJobTypeToString(asyncJob),
- obj, obj->def->name,
- virDomainJobTypeToString(jobObj->active),
- virDomainAgentJobTypeToString(jobObj->agentActive),
- virDomainAsyncJobTypeToString(jobObj->asyncJob));
-
- if (virTimeMillisNow(&now) < 0)
- return -1;
-
- jobObj->jobsQueued++;
- then = now + VIR_JOB_WAIT_TIME;
-
- retry:
- if (job != VIR_JOB_ASYNC &&
- job != VIR_JOB_DESTROY &&
- jobObj->maxQueuedJobs &&
- jobObj->jobsQueued > jobObj->maxQueuedJobs) {
- goto error;
- }
-
- while (!nested && !virDomainNestedJobAllowed(jobObj, job)) {
- if (nowait)
- goto cleanup;
-
- VIR_DEBUG("Waiting for async job (vm=%p name=%s)", obj, obj->def->name);
- if (virCondWaitUntil(&jobObj->asyncCond, &obj->parent.lock, then) < 0)
- goto error;
- }
-
- while (!virDomainObjCanSetJob(jobObj, job, agentJob)) {
- if (nowait)
- goto cleanup;
-
- VIR_DEBUG("Waiting for job (vm=%p name=%s)", obj, obj->def->name);
- if (virCondWaitUntil(&jobObj->cond, &obj->parent.lock, then) < 0)
- goto error;
- }
-
- /* No job is active but a new async job could have been started while obj
- * was unlocked, so we need to recheck it. */
- if (!nested && !virDomainNestedJobAllowed(jobObj, job))
- goto retry;
-
- if (obj->removing) {
- char uuidstr[VIR_UUID_STRING_BUFLEN];
-
- virUUIDFormat(obj->def->uuid, uuidstr);
- virReportError(VIR_ERR_NO_DOMAIN,
- _("no domain with matching uuid '%s' (%s)"),
- uuidstr, obj->def->name);
- goto cleanup;
- }
-
- ignore_value(virTimeMillisNow(&now));
-
- if (job) {
- virDomainObjResetJob(jobObj);
-
- if (job != VIR_JOB_ASYNC) {
- VIR_DEBUG("Started job: %s (async=%s vm=%p name=%s)",
- virDomainJobTypeToString(job),
- virDomainAsyncJobTypeToString(jobObj->asyncJob),
- obj, obj->def->name);
- jobObj->active = job;
- jobObj->owner = virThreadSelfID();
- jobObj->ownerAPI = g_strdup(virThreadJobGet());
- jobObj->started = now;
- } else {
- VIR_DEBUG("Started async job: %s (vm=%p name=%s)",
- virDomainAsyncJobTypeToString(asyncJob),
- obj, obj->def->name);
- virDomainObjResetAsyncJob(jobObj);
- jobObj->current = virDomainJobDataInit(jobObj->jobDataPrivateCb);
- jobObj->current->status = VIR_DOMAIN_JOB_STATUS_ACTIVE;
- jobObj->asyncJob = asyncJob;
- jobObj->asyncOwner = virThreadSelfID();
- jobObj->asyncOwnerAPI = g_strdup(virThreadJobGet());
- jobObj->asyncStarted = now;
- jobObj->current->started = now;
- }
- }
-
- if (agentJob) {
- virDomainObjResetAgentJob(jobObj);
- VIR_DEBUG("Started agent job: %s (vm=%p name=%s job=%s async=%s)",
- virDomainAgentJobTypeToString(agentJob),
- obj, obj->def->name,
- virDomainJobTypeToString(jobObj->active),
- virDomainAsyncJobTypeToString(jobObj->asyncJob));
- jobObj->agentActive = agentJob;
- jobObj->agentOwner = virThreadSelfID();
- jobObj->agentOwnerAPI = g_strdup(virThreadJobGet());
- jobObj->agentStarted = now;
- }
-
- if (virDomainTrackJob(job) && jobObj->cb &&
- jobObj->cb->saveStatusPrivate)
- jobObj->cb->saveStatusPrivate(obj);
-
- return 0;
-
- error:
- ignore_value(virTimeMillisNow(&now));
- if (jobObj->active && jobObj->started)
- duration = now - jobObj->started;
- if (jobObj->agentActive && jobObj->agentStarted)
- agentDuration = now - jobObj->agentStarted;
- if (jobObj->asyncJob && jobObj->asyncStarted)
- asyncDuration = now - jobObj->asyncStarted;
-
- VIR_WARN("Cannot start job (%s, %s, %s) in API %s for domain %s; "
- "current job is (%s, %s, %s) "
- "owned by (%llu %s, %llu %s, %llu %s (flags=0x%lx)) "
- "for (%llus, %llus, %llus)",
- virDomainJobTypeToString(job),
- virDomainAgentJobTypeToString(agentJob),
- virDomainAsyncJobTypeToString(asyncJob),
- NULLSTR(currentAPI),
- obj->def->name,
- virDomainJobTypeToString(jobObj->active),
- virDomainAgentJobTypeToString(jobObj->agentActive),
- virDomainAsyncJobTypeToString(jobObj->asyncJob),
- jobObj->owner, NULLSTR(jobObj->ownerAPI),
- jobObj->agentOwner, NULLSTR(jobObj->agentOwnerAPI),
- jobObj->asyncOwner, NULLSTR(jobObj->asyncOwnerAPI),
- jobObj->apiFlags,
- duration / 1000, agentDuration / 1000, asyncDuration / 1000);
-
- if (job) {
- if (nested || virDomainNestedJobAllowed(jobObj, job))
- blocker = jobObj->ownerAPI;
- else
- blocker = jobObj->asyncOwnerAPI;
- }
-
- if (agentJob)
- agentBlocker = jobObj->agentOwnerAPI;
-
- if (errno == ETIMEDOUT) {
- if (blocker && agentBlocker) {
- virReportError(VIR_ERR_OPERATION_TIMEOUT,
- _("cannot acquire state change "
- "lock (held by monitor=%s agent=%s)"),
- blocker, agentBlocker);
- } else if (blocker) {
- virReportError(VIR_ERR_OPERATION_TIMEOUT,
- _("cannot acquire state change "
- "lock (held by monitor=%s)"),
- blocker);
- } else if (agentBlocker) {
- virReportError(VIR_ERR_OPERATION_TIMEOUT,
- _("cannot acquire state change "
- "lock (held by agent=%s)"),
- agentBlocker);
- } else {
- virReportError(VIR_ERR_OPERATION_TIMEOUT, "%s",
- _("cannot acquire state change lock"));
- }
- ret = -2;
- } else if (jobObj->maxQueuedJobs &&
- jobObj->jobsQueued > jobObj->maxQueuedJobs) {
- if (blocker && agentBlocker) {
- virReportError(VIR_ERR_OPERATION_FAILED,
- _("cannot acquire state change "
- "lock (held by monitor=%s agent=%s) "
- "due to max_queued limit"),
- blocker, agentBlocker);
- } else if (blocker) {
- virReportError(VIR_ERR_OPERATION_FAILED,
- _("cannot acquire state change "
- "lock (held by monitor=%s) "
- "due to max_queued limit"),
- blocker);
- } else if (agentBlocker) {
- virReportError(VIR_ERR_OPERATION_FAILED,
- _("cannot acquire state change "
- "lock (held by agent=%s) "
- "due to max_queued limit"),
- agentBlocker);
- } else {
- virReportError(VIR_ERR_OPERATION_FAILED, "%s",
- _("cannot acquire state change lock "
- "due to max_queued limit"));
- }
- ret = -2;
- } else {
- virReportSystemError(errno, "%s", _("cannot acquire job mutex"));
- }
-
- cleanup:
- jobObj->jobsQueued--;
- return ret;
-}
+++ /dev/null
-/*
- * domain_job.h: job functions shared between hypervisor drivers
- *
- * Copyright (C) 2022 Red Hat, Inc.
- * SPDX-License-Identifier: LGPL-2.1-or-later
- */
-
-#pragma once
-
-#include "internal.h"
-#include "virenum.h"
-#include "virthread.h"
-#include "virbuffer.h"
-#include "domain_conf.h"
-
-#define JOB_MASK(job) (job == 0 ? 0 : 1 << (job - 1))
-#define VIR_JOB_DEFAULT_MASK \
- (JOB_MASK(VIR_JOB_QUERY) | \
- JOB_MASK(VIR_JOB_DESTROY) | \
- JOB_MASK(VIR_JOB_ABORT))
-
-/* Jobs which have to be tracked in domain state XML. */
-#define VIR_DOMAIN_TRACK_JOBS \
- (JOB_MASK(VIR_JOB_DESTROY) | \
- JOB_MASK(VIR_JOB_ASYNC))
-
-
-/* Only 1 job is allowed at any time
- * A job includes *all* monitor commands / hypervisor.so api,
- * even those just querying information, not merely actions */
-typedef enum {
- VIR_JOB_NONE = 0, /* Always set to 0 for easy if (jobActive) conditions */
- VIR_JOB_QUERY, /* Doesn't change any state */
- VIR_JOB_DESTROY, /* Destroys the domain (cannot be masked out) */
- VIR_JOB_SUSPEND, /* Suspends (stops vCPUs) the domain */
- VIR_JOB_MODIFY, /* May change state */
- VIR_JOB_ABORT, /* Abort current async job */
- VIR_JOB_MIGRATION_OP, /* Operation influencing outgoing migration */
- VIR_JOB_MODIFY_MIGRATION_SAFE, /* Internal only job for event handlers which
- need to be processed even during migration.
- The code may only change state in a way
- that does not affect migration. */
-
- /* The following two items must always be the last items before JOB_LAST */
- VIR_JOB_ASYNC, /* Asynchronous job */
- VIR_JOB_ASYNC_NESTED, /* Normal job within an async job */
-
- VIR_JOB_LAST
-} virDomainJob;
-VIR_ENUM_DECL(virDomainJob);
-
-
-/* Currently only QEMU driver uses agent jobs */
-typedef enum {
- VIR_AGENT_JOB_NONE = 0, /* No agent job. */
- VIR_AGENT_JOB_QUERY, /* Does not change state of domain */
- VIR_AGENT_JOB_MODIFY, /* May change state of domain */
-
- VIR_AGENT_JOB_LAST
-} virDomainAgentJob;
-VIR_ENUM_DECL(virDomainAgentJob);
-
-
-/* Async job consists of a series of jobs that may change state. Independent
- * jobs that do not change state (and possibly others if explicitly allowed by
- * current async job) are allowed to be run even if async job is active.
- * Currently supported by QEMU only. */
-typedef enum {
- VIR_ASYNC_JOB_NONE = 0,
- VIR_ASYNC_JOB_MIGRATION_OUT,
- VIR_ASYNC_JOB_MIGRATION_IN,
- VIR_ASYNC_JOB_SAVE,
- VIR_ASYNC_JOB_DUMP,
- VIR_ASYNC_JOB_SNAPSHOT,
- VIR_ASYNC_JOB_START,
- VIR_ASYNC_JOB_BACKUP,
-
- VIR_ASYNC_JOB_LAST
-} virDomainAsyncJob;
-VIR_ENUM_DECL(virDomainAsyncJob);
-
-
-typedef enum {
- VIR_DOMAIN_JOB_STATUS_NONE = 0,
- VIR_DOMAIN_JOB_STATUS_ACTIVE,
- VIR_DOMAIN_JOB_STATUS_MIGRATING,
- VIR_DOMAIN_JOB_STATUS_HYPERVISOR_COMPLETED,
- VIR_DOMAIN_JOB_STATUS_PAUSED,
- VIR_DOMAIN_JOB_STATUS_POSTCOPY,
- VIR_DOMAIN_JOB_STATUS_POSTCOPY_PAUSED,
- VIR_DOMAIN_JOB_STATUS_COMPLETED,
- VIR_DOMAIN_JOB_STATUS_FAILED,
- VIR_DOMAIN_JOB_STATUS_CANCELED,
-} virDomainJobStatus;
-
-typedef void *(*virDomainJobDataPrivateDataAlloc) (void);
-typedef void *(*virDomainJobDataPrivateDataCopy) (void *);
-typedef void (*virDomainJobDataPrivateDataFree) (void *);
-
-typedef struct _virDomainJobDataPrivateDataCallbacks virDomainJobDataPrivateDataCallbacks;
-struct _virDomainJobDataPrivateDataCallbacks {
- virDomainJobDataPrivateDataAlloc allocPrivateData;
- virDomainJobDataPrivateDataCopy copyPrivateData;
- virDomainJobDataPrivateDataFree freePrivateData;
-};
-
-
-typedef struct _virDomainJobData virDomainJobData;
-struct _virDomainJobData {
- virDomainJobType jobType;
-
- virDomainJobStatus status;
- virDomainJobOperation operation;
- unsigned long long started; /* When the async job started */
- unsigned long long stopped; /* When the domain's CPUs were stopped */
- unsigned long long sent; /* When the source sent status info to the
- destination (only for migrations). */
- unsigned long long received; /* When the destination host received status
- info from the source (migrations only). */
- /* Computed values */
- unsigned long long timeElapsed;
- long long timeDelta; /* delta = received - sent, i.e., the difference between
- the source and the destination time plus the time
- between the end of Perform phase on the source and
- the beginning of Finish phase on the destination. */
- bool timeDeltaSet;
-
- char *errmsg; /* optional error message for failed completed jobs */
-
- void *privateData; /* private data of hypervisors */
- virDomainJobDataPrivateDataCallbacks *privateDataCb; /* callbacks of private data,
- hypervisor based */
-};
-
-
-virDomainJobData *
-virDomainJobDataInit(virDomainJobDataPrivateDataCallbacks *cb);
-
-void
-virDomainJobDataFree(virDomainJobData *data);
-
-G_DEFINE_AUTOPTR_CLEANUP_FUNC(virDomainJobData, virDomainJobDataFree);
-
-virDomainJobData *
-virDomainJobDataCopy(virDomainJobData *data);
-
-virDomainJobType
-virDomainJobStatusToType(virDomainJobStatus status);
-
-
-typedef struct _virDomainObjPrivateJobCallbacks virDomainObjPrivateJobCallbacks;
-
-typedef struct _virDomainJobObj virDomainJobObj;
-struct _virDomainJobObj {
- virCond cond; /* Use to coordinate jobs */
-
- int jobsQueued;
- unsigned int maxQueuedJobs;
-
- /* The following members are for VIR_JOB_* */
- virDomainJob active; /* currently running job */
- unsigned long long owner; /* Thread id which set current job */
- char *ownerAPI; /* The API which owns the job */
- unsigned long long started; /* When the current job started */
-
- /* The following members are for VIR_AGENT_JOB_* */
- virDomainAgentJob agentActive; /* Currently running agent job */
- unsigned long long agentOwner; /* Thread id which set current agent job */
- char *agentOwnerAPI; /* The API which owns the agent job */
- unsigned long long agentStarted; /* When the current agent job started */
-
- /* The following members are for VIR_ASYNC_JOB_* */
- virCond asyncCond; /* Use to coordinate with async jobs */
- virDomainAsyncJob asyncJob; /* Currently active async job */
- unsigned long long asyncOwner; /* Thread which set current async job */
- char *asyncOwnerAPI; /* The API which owns the async job */
- unsigned long long asyncStarted; /* When the current async job started */
- int phase; /* Job phase (mainly for migrations) */
- unsigned long long mask; /* Jobs allowed during async job */
- virDomainJobData *current; /* async job progress data */
- virDomainJobData *completed; /* statistics data of a recently completed job */
- bool abortJob; /* abort of the job requested */
- char *error; /* job event completion error */
- unsigned long apiFlags; /* flags passed to the API which started the async job */
-
- void *privateData; /* job specific collection of data */
- virDomainObjPrivateJobCallbacks *cb;
- virDomainJobDataPrivateDataCallbacks *jobDataPrivateCb; /* callbacks for privateData of
- virDomainJobData, can be NULL */
-};
-
-
-typedef void *(*virDomainObjPrivateJobAlloc)(void);
-typedef void (*virDomainObjPrivateJobFree)(void *);
-typedef void (*virDomainObjPrivateJobReset)(void *);
-typedef int (*virDomainObjPrivateJobFormat)(virBuffer *,
- virDomainJobObj *,
- virDomainObj *);
-typedef int (*virDomainObjPrivateJobParse)(xmlXPathContextPtr,
- virDomainJobObj *,
- virDomainObj *);
-typedef void (*virDomainObjPrivateSaveStatus)(virDomainObj *obj);
-
-struct _virDomainObjPrivateJobCallbacks {
- virDomainObjPrivateJobAlloc allocJobPrivate;
- virDomainObjPrivateJobFree freeJobPrivate;
- virDomainObjPrivateJobReset resetJobPrivate;
- virDomainObjPrivateJobFormat formatJobPrivate;
- virDomainObjPrivateJobParse parseJobPrivate;
- virDomainObjPrivateSaveStatus saveStatusPrivate;
-};
-
-
-int virDomainObjInitJob(virDomainJobObj *job,
- virDomainObjPrivateJobCallbacks *cb,
- virDomainJobDataPrivateDataCallbacks *jobDataPrivateCb);
-
-void virDomainObjResetJob(virDomainJobObj *job);
-
-void virDomainObjResetAgentJob(virDomainJobObj *job);
-
-void virDomainObjResetAsyncJob(virDomainJobObj *job);
-
-int virDomainObjPreserveJob(virDomainJobObj *currJob,
- virDomainJobObj *job);
-
-void virDomainObjClearJob(virDomainJobObj *job);
-G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(virDomainJobObj, virDomainObjClearJob);
-
-bool virDomainTrackJob(virDomainJob job);
-
-bool virDomainNestedJobAllowed(virDomainJobObj *jobs, virDomainJob newJob);
-
-bool virDomainObjCanSetJob(virDomainJobObj *job,
- virDomainJob newJob,
- virDomainAgentJob newAgentJob);
-
-int virDomainObjBeginJobInternal(virDomainObj *obj,
- virDomainJobObj *jobObj,
- virDomainJob job,
- virDomainAgentJob agentJob,
- virDomainAsyncJob asyncJob,
- bool nowait)
- ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3);
'domain_driver.c',
'virclosecallbacks.c',
'virhostdev.c',
- 'domain_job.c',
]
stateful_driver_source_files += files(hypervisor_sources)
virDomainListCheckpoints;
+#conf/virdomainjob.h
+virDomainAgentJobTypeToString;
+virDomainAsyncJobTypeFromString;
+virDomainAsyncJobTypeToString;
+virDomainJobDataCopy;
+virDomainJobDataFree;
+virDomainJobDataInit;
+virDomainJobStatusToType;
+virDomainJobTypeFromString;
+virDomainJobTypeToString;
+virDomainNestedJobAllowed;
+virDomainObjBeginJobInternal;
+virDomainObjCanSetJob;
+virDomainObjClearJob;
+virDomainObjInitJob;
+virDomainObjPreserveJob;
+virDomainObjResetAgentJob;
+virDomainObjResetAsyncJob;
+virDomainObjResetJob;
+virDomainTrackJob;
+
+
# conf/virdomainmomentobjlist.h
virDomainMomentDropChildren;
virDomainMomentDropParent;
virDomainDriverSetupPersistentDefBlkioParams;
-# hypervisor/domain_job.h
-virDomainAgentJobTypeToString;
-virDomainAsyncJobTypeFromString;
-virDomainAsyncJobTypeToString;
-virDomainJobDataCopy;
-virDomainJobDataFree;
-virDomainJobDataInit;
-virDomainJobStatusToType;
-virDomainJobTypeFromString;
-virDomainJobTypeToString;
-virDomainNestedJobAllowed;
-virDomainObjBeginJobInternal;
-virDomainObjCanSetJob;
-virDomainObjClearJob;
-virDomainObjInitJob;
-virDomainObjPreserveJob;
-virDomainObjResetAgentJob;
-virDomainObjResetAsyncJob;
-virDomainObjResetJob;
-virDomainTrackJob;
-
-
# hypervisor/virclosecallbacks.h
virCloseCallbacksGet;
virCloseCallbacksNew;
#include "xen_common.h"
#include "driver.h"
#include "domain_validate.h"
-#include "domain_job.h"
#define VIR_FROM_THIS VIR_FROM_LIBXL
#include "libxl_conf.h"
#include "virchrdev.h"
-#include "domain_job.h"
+#include "virdomainjob.h"
typedef struct _libxlDomainObjPrivate libxlDomainObjPrivate;
#include "virsystemd.h"
#include "virinitctl.h"
#include "domain_driver.h"
-#include "domain_job.h"
#define VIR_FROM_THIS VIR_FROM_LXC
#include "lxc_conf.h"
#include "lxc_monitor.h"
#include "virenum.h"
-#include "domain_job.h"
+#include "virdomainjob.h"
typedef enum {
#include <glib-object.h>
#include "qemu_monitor.h"
-#include "domain_job.h"
+#include "virdomainjob.h"
typedef enum {