From: Kristina Hanicova Date: Mon, 5 Sep 2022 13:57:01 +0000 (+0200) Subject: move files: hypervisor/domain_job -> conf/virdomainjob X-Git-Url: http://xenbits.xensource.com/gitweb?a=commitdiff_plain;h=2378f9d86ebe12ca709e96ef0e6b9845d62d779a;p=libvirt.git move files: hypervisor/domain_job -> conf/virdomainjob The following patches move job object as a member into the domain object. Because of this, domain_conf (where the domain object is defined) needs to import the file with the job object. It makes sense to move jobs to the same level as the domain_conf: into src/conf/ Signed-off-by: Kristina Hanicova Reviewed-by: Ján Tomko Signed-off-by: Ján Tomko --- diff --git a/po/POTFILES b/po/POTFILES index b9577e840d..169e2a41dc 100644 --- a/po/POTFILES +++ b/po/POTFILES @@ -53,6 +53,7 @@ src/conf/storage_conf.c 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 @@ -90,7 +91,6 @@ src/hyperv/hyperv_util.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 diff --git a/src/ch/ch_domain.h b/src/ch/ch_domain.h index b3bebd6b9a..27efe2feed 100644 --- a/src/ch/ch_domain.h +++ b/src/ch/ch_domain.h @@ -24,7 +24,7 @@ #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) diff --git a/src/conf/meson.build b/src/conf/meson.build index 5ef494c3ba..5116c23fe3 100644 --- a/src/conf/meson.build +++ b/src/conf/meson.build @@ -20,6 +20,7 @@ domain_conf_sources = [ 'numa_conf.c', 'snapshot_conf.c', 'virdomaincheckpointobjlist.c', + 'virdomainjob.c', 'virdomainmomentobjlist.c', 'virdomainobjlist.c', 'virdomainsnapshotobjlist.c', diff --git a/src/conf/virdomainjob.c b/src/conf/virdomainjob.c new file mode 100644 index 0000000000..0515e1d507 --- /dev/null +++ b/src/conf/virdomainjob.c @@ -0,0 +1,500 @@ +/* + * virdomainjob.c: job functions shared between hypervisor drivers + * + * Copyright (C) 2022 Red Hat, Inc. + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include +#include + +#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; +} diff --git a/src/conf/virdomainjob.h b/src/conf/virdomainjob.h new file mode 100644 index 0000000000..bdfdc91935 --- /dev/null +++ b/src/conf/virdomainjob.h @@ -0,0 +1,244 @@ +/* + * 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); diff --git a/src/hypervisor/domain_job.c b/src/hypervisor/domain_job.c deleted file mode 100644 index 07ee5b4a3d..0000000000 --- a/src/hypervisor/domain_job.c +++ /dev/null @@ -1,500 +0,0 @@ -/* - * domain_job.c: job functions shared between hypervisor drivers - * - * Copyright (C) 2022 Red Hat, Inc. - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#include -#include - -#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; -} diff --git a/src/hypervisor/domain_job.h b/src/hypervisor/domain_job.h deleted file mode 100644 index d7409c05f0..0000000000 --- a/src/hypervisor/domain_job.h +++ /dev/null @@ -1,244 +0,0 @@ -/* - * 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); diff --git a/src/hypervisor/meson.build b/src/hypervisor/meson.build index 7532f30ee2..f35565b16b 100644 --- a/src/hypervisor/meson.build +++ b/src/hypervisor/meson.build @@ -3,7 +3,6 @@ hypervisor_sources = [ 'domain_driver.c', 'virclosecallbacks.c', 'virhostdev.c', - 'domain_job.c', ] stateful_driver_source_files += files(hypervisor_sources) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 08571cd4b4..5077db9c6b 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1175,6 +1175,28 @@ virDomainCheckpointUpdateRelations; 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; @@ -1585,28 +1607,6 @@ virDomainDriverParseBlkioDeviceStr; 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; diff --git a/src/libxl/libxl_domain.c b/src/libxl/libxl_domain.c index 6695ec670e..aadb13f461 100644 --- a/src/libxl/libxl_domain.c +++ b/src/libxl/libxl_domain.c @@ -37,7 +37,6 @@ #include "xen_common.h" #include "driver.h" #include "domain_validate.h" -#include "domain_job.h" #define VIR_FROM_THIS VIR_FROM_LIBXL diff --git a/src/libxl/libxl_domain.h b/src/libxl/libxl_domain.h index 8ad56f1e88..451e76e311 100644 --- a/src/libxl/libxl_domain.h +++ b/src/libxl/libxl_domain.h @@ -24,7 +24,7 @@ #include "libxl_conf.h" #include "virchrdev.h" -#include "domain_job.h" +#include "virdomainjob.h" typedef struct _libxlDomainObjPrivate libxlDomainObjPrivate; diff --git a/src/lxc/lxc_domain.c b/src/lxc/lxc_domain.c index 61e59ec726..f234aaf39c 100644 --- a/src/lxc/lxc_domain.c +++ b/src/lxc/lxc_domain.c @@ -29,7 +29,6 @@ #include "virsystemd.h" #include "virinitctl.h" #include "domain_driver.h" -#include "domain_job.h" #define VIR_FROM_THIS VIR_FROM_LXC diff --git a/src/lxc/lxc_domain.h b/src/lxc/lxc_domain.h index 82c36eb940..db622acc86 100644 --- a/src/lxc/lxc_domain.h +++ b/src/lxc/lxc_domain.h @@ -25,7 +25,7 @@ #include "lxc_conf.h" #include "lxc_monitor.h" #include "virenum.h" -#include "domain_job.h" +#include "virdomainjob.h" typedef enum { diff --git a/src/qemu/qemu_domainjob.h b/src/qemu/qemu_domainjob.h index bb3c7ede14..23eadc26a7 100644 --- a/src/qemu/qemu_domainjob.h +++ b/src/qemu/qemu_domainjob.h @@ -20,7 +20,7 @@ #include #include "qemu_monitor.h" -#include "domain_job.h" +#include "virdomainjob.h" typedef enum {