diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index da1f57fef4..7f1f8eef59 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -147,6 +147,7 @@ qemuDomainObjResetJob(qemuDomainObjPrivatePtr priv) struct qemuDomainJobObj *job = &priv->job; job->active = QEMU_JOB_NONE; + job->owner = 0; } static void @@ -155,6 +156,7 @@ qemuDomainObjResetAsyncJob(qemuDomainObjPrivatePtr priv) struct qemuDomainJobObj *job = &priv->job; job->asyncJob = QEMU_ASYNC_JOB_NONE; + job->asyncOwner = 0; job->phase = 0; job->mask = DEFAULT_JOB_MASK; job->start = 0; @@ -169,13 +171,25 @@ qemuDomainObjRestoreJob(virDomainObjPtr obj, memset(job, 0, sizeof(*job)); job->active = priv->job.active; + job->owner = priv->job.owner; job->asyncJob = priv->job.asyncJob; + job->asyncOwner = priv->job.asyncOwner; job->phase = priv->job.phase; qemuDomainObjResetJob(priv); qemuDomainObjResetAsyncJob(priv); } +void +qemuDomainObjTransferJob(virDomainObjPtr obj) +{ + qemuDomainObjPrivatePtr priv = obj->privateData; + + VIR_DEBUG("Changing job owner from %d to %d", + priv->job.owner, virThreadSelfID()); + priv->job.owner = virThreadSelfID(); +} + static void qemuDomainObjFreeJob(qemuDomainObjPrivatePtr priv) { @@ -664,11 +678,23 @@ qemuDomainObjSetJobPhase(struct qemud_driver *driver, int phase) { qemuDomainObjPrivatePtr priv = obj->privateData; + int me = virThreadSelfID(); if (!priv->job.asyncJob) return; + VIR_DEBUG("Setting '%s' phase to '%s'", + qemuDomainAsyncJobTypeToString(priv->job.asyncJob), + qemuDomainAsyncJobPhaseToString(priv->job.asyncJob, phase)); + + if (priv->job.asyncOwner && me != priv->job.asyncOwner) { + VIR_WARN("'%s' async job is owned by thread %d", + qemuDomainAsyncJobTypeToString(priv->job.asyncJob), + priv->job.asyncOwner); + } + priv->job.phase = phase; + priv->job.asyncOwner = me; qemuDomainObjSaveJob(driver, obj); } @@ -695,6 +721,22 @@ qemuDomainObjDiscardAsyncJob(struct qemud_driver *driver, virDomainObjPtr obj) qemuDomainObjSaveJob(driver, obj); } +void +qemuDomainObjReleaseAsyncJob(virDomainObjPtr obj) +{ + qemuDomainObjPrivatePtr priv = obj->privateData; + + VIR_DEBUG("Releasing ownership of '%s' async job", + qemuDomainAsyncJobTypeToString(priv->job.asyncJob)); + + if (priv->job.asyncOwner != virThreadSelfID()) { + VIR_WARN("'%s' async job is owned by thread %d", + qemuDomainAsyncJobTypeToString(priv->job.asyncJob), + priv->job.asyncOwner); + } + priv->job.asyncOwner = 0; +} + static bool qemuDomainNestedJobAllowed(qemuDomainObjPrivatePtr priv, enum qemuDomainJob job) { @@ -764,11 +806,13 @@ retry: qemuDomainJobTypeToString(job), qemuDomainAsyncJobTypeToString(priv->job.asyncJob)); priv->job.active = job; + priv->job.owner = virThreadSelfID(); } else { VIR_DEBUG("Starting async job: %s", qemuDomainAsyncJobTypeToString(asyncJob)); qemuDomainObjResetAsyncJob(priv); priv->job.asyncJob = asyncJob; + priv->job.asyncOwner = virThreadSelfID(); priv->job.start = now; } @@ -784,6 +828,15 @@ retry: return 0; error: + VIR_WARN("Cannot start job (%s, %s) for domain %s;" + " current job is (%s, %s) owned by (%d, %d)", + qemuDomainJobTypeToString(job), + qemuDomainAsyncJobTypeToString(asyncJob), + obj->def->name, + qemuDomainJobTypeToString(priv->job.active), + qemuDomainAsyncJobTypeToString(priv->job.asyncJob), + priv->job.owner, priv->job.asyncOwner); + if (errno == ETIMEDOUT) qemuReportError(VIR_ERR_OPERATION_TIMEOUT, "%s", _("cannot acquire state change lock")); diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h index b3eecd38b4..ce52569f5a 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h @@ -96,9 +96,11 @@ VIR_ENUM_DECL(qemuDomainAsyncJob) struct qemuDomainJobObj { virCond cond; /* Use to coordinate jobs */ enum qemuDomainJob active; /* Currently running job */ + int owner; /* Thread which set current job */ virCond asyncCond; /* Use to coordinate with async jobs */ enum qemuDomainAsyncJob asyncJob; /* Currently active async job */ + int asyncOwner; /* Thread which set current async job */ int phase; /* Job phase (mainly for migrations) */ unsigned long long mask; /* Jobs allowed during async job */ unsigned long long start; /* When the async job started */ @@ -203,8 +205,10 @@ void qemuDomainObjSetAsyncJobMask(virDomainObjPtr obj, unsigned long long allowedJobs); void qemuDomainObjRestoreJob(virDomainObjPtr obj, struct qemuDomainJobObj *job); +void qemuDomainObjTransferJob(virDomainObjPtr obj); void qemuDomainObjDiscardAsyncJob(struct qemud_driver *driver, virDomainObjPtr obj); +void qemuDomainObjReleaseAsyncJob(virDomainObjPtr obj); void qemuDomainObjEnterMonitor(struct qemud_driver *driver, virDomainObjPtr obj) diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index ff6f273044..c1bb93a908 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -3242,6 +3242,7 @@ qemuMigrationJobStartPhase(struct qemud_driver *driver, int qemuMigrationJobContinue(virDomainObjPtr vm) { + qemuDomainObjReleaseAsyncJob(vm); return virDomainObjUnref(vm); } diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 96f39e8800..19569cf01e 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -3038,8 +3038,8 @@ qemuProcessReconnect(void *opaque) priv = obj->privateData; - /* Set fake job so that EnterMonitor* doesn't want to start a new one */ - priv->job.active = QEMU_JOB_MODIFY; + /* Job was started by the caller for us */ + qemuDomainObjTransferJob(obj); /* Hold an extra reference because we can't allow 'vm' to be * deleted if qemuConnectMonitor() failed */ @@ -3119,8 +3119,6 @@ qemuProcessReconnect(void *opaque) if (qemuProcessRecoverJob(driver, obj, conn, &oldjob) < 0) goto error; - priv->job.active = QEMU_JOB_NONE; - /* update domain state XML with possibly updated state in virDomainObj */ if (virDomainSaveStatus(driver->caps, driver->stateDir, obj) < 0) goto error;