On Tue, Jun 19, 2018 at 08:38:01 +0200, Michal Privoznik wrote:
The point is to break QEMU_JOB_* into smaller pieces which
enables us to achieve higher throughput. For instance, if there
are two threads, one is trying to query something on qemu
monitor while the other is trying to query something on agent
monitor these two threads would serialize. There is not much
reason for that.
Signed-off-by: Michal Privoznik <mprivozn(a)redhat.com>
---
src/qemu/THREADS.txt | 112 ++++++++++++++++++++++++++---
src/qemu/qemu_domain.c | 187 +++++++++++++++++++++++++++++++++++++++----------
src/qemu/qemu_domain.h | 12 ++++
3 files changed, 264 insertions(+), 47 deletions(-)
...
diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c
index 91f3c6d236..30a06a1575 100644
--- a/src/qemu/qemu_domain.c
+++ b/src/qemu/qemu_domain.c
...
@@ -6356,6 +6369,15 @@ qemuDomainJobAllowed(qemuDomainObjPrivatePtr
priv, qemuDomainJob job)
return !priv->job.active && qemuDomainNestedJobAllowed(priv, job);
}
+static bool
+qemuDomainObjCanSetJob(qemuDomainObjPrivatePtr priv,
+ qemuDomainJob job,
+ qemuDomainAgentJob agentJob)
+{
+ return (job == QEMU_JOB_NONE || !priv->job.active) &&
+ (agentJob == QEMU_AGENT_JOB_NONE || !priv->job.agentActive);
This is pretty strange, everything you compare here is an enum, either
qemuDomainJob or qemuDomainAgentJob and mixing ! with an explicit
comparison with *_NONE is confusing. It's not incorrect, but I think
(job == QEMU_JOB_NONE || priv->job.active == QEMU_JOB_NONE) &&
(agentJob == QEMU_AGENT_JOB_NONE || priv->job.agentActive == QEMU_AGENT_JOB_NONE)
would be easier to read. Or alternatively you could use ! everywhere.
+}
+
/* Give up waiting for mutex after 30 seconds */
#define QEMU_JOB_WAIT_TIME (1000ull * 30)
...
@@ -6434,10 +6456,9 @@ qemuDomainObjBeginJobInternal(virQEMUDriverPtr
driver,
goto error;
}
- while (priv->job.active) {
+ while (!qemuDomainObjCanSetJob(priv, job, agentJob)) {
You're now checking more variables for a single condition which means
waking up a single thread with virCondSignal could easily wake the one
which cannot currently run. We need to use virCondBroadcast instead and
I see you did the change, which is nice. However, it might be useful to
add a comment to the code explaining why we use virCondBroadcast to
avoid any future confusion or even blind attempts to replace it with
virCondSignal.
if (nowait)
goto cleanup;
-
Drop this line removal, it's most likely a rebase artifact anyway.
VIR_DEBUG("Waiting for job (vm=%p name=%s)", obj,
obj->def->name);
if (virCondWaitUntil(&priv->job.cond, &obj->parent.lock, then)
< 0)
goto error;
@@ -6448,32 +6469,48 @@ qemuDomainObjBeginJobInternal(virQEMUDriverPtr driver,
if (!nested && !qemuDomainNestedJobAllowed(priv, job))
goto retry;
- qemuDomainObjResetJob(priv);
-
ignore_value(virTimeMillisNow(&now));
- if (job != QEMU_JOB_ASYNC) {
- VIR_DEBUG("Started job: %s (async=%s vm=%p name=%s)",
- qemuDomainJobTypeToString(job),
- qemuDomainAsyncJobTypeToString(priv->job.asyncJob),
- obj, obj->def->name);
- priv->job.active = job;
- priv->job.owner = virThreadSelfID();
- priv->job.ownerAPI = virThreadJobGet();
+ if (job) {
+ qemuDomainObjResetJob(priv);
+
+ if (job != QEMU_JOB_ASYNC) {
+ VIR_DEBUG("Started job: %s (async=%s vm=%p name=%s)",
+ qemuDomainJobTypeToString(job),
+ qemuDomainAsyncJobTypeToString(priv->job.asyncJob),
+ obj, obj->def->name);
+ priv->job.active = job;
+ priv->job.owner = virThreadSelfID();
+ priv->job.ownerAPI = virThreadJobGet();
+ priv->job.started = now;
+ } else {
+ VIR_DEBUG("Started async job: %s (vm=%p name=%s)",
+ qemuDomainAsyncJobTypeToString(asyncJob),
+ obj, obj->def->name);
+ qemuDomainObjResetAsyncJob(priv);
+ if (VIR_ALLOC(priv->job.current) < 0)
+ goto cleanup;
+ priv->job.current->status = QEMU_DOMAIN_JOB_STATUS_ACTIVE;
+ priv->job.asyncJob = asyncJob;
+ priv->job.asyncOwner = virThreadSelfID();
+ priv->job.asyncOwnerAPI = virThreadJobGet();
+ priv->job.asyncStarted = now;
+ priv->job.current->started = now;
+ }
+ }
+
+ if (agentJob) {
+ qemuDomainObjResetAgentJob(priv);
+
+ VIR_DEBUG("Started agent job: %s (vm=%p name=%s job=%s async=%s)",
+ qemuDomainAgentJobTypeToString(agentJob),
+ obj, obj->def->name,
+ qemuDomainJobTypeToString(priv->job.active),
+ qemuDomainAsyncJobTypeToString(priv->job.asyncJob));
+ priv->job.agentActive = agentJob;
+ priv->job.agentOwner = virThreadSelfID();
+ priv->job.agentOwnerAPI = virThreadJobGet();
priv->job.started = now;
This should be priv->job.agentStarted = now
- } else {
- VIR_DEBUG("Started async job: %s (vm=%p name=%s)",
- qemuDomainAsyncJobTypeToString(asyncJob),
- obj, obj->def->name);
- qemuDomainObjResetAsyncJob(priv);
- if (VIR_ALLOC(priv->job.current) < 0)
- goto cleanup;
- priv->job.current->status = QEMU_DOMAIN_JOB_STATUS_ACTIVE;
- priv->job.asyncJob = asyncJob;
- priv->job.asyncOwner = virThreadSelfID();
- priv->job.asyncOwnerAPI = virThreadJobGet();
- priv->job.asyncStarted = now;
- priv->job.current->started = now;
}
if (qemuDomainTrackJob(job))
@@ -6554,12 +6591,46 @@ int qemuDomainObjBeginJob(virQEMUDriverPtr driver,
qemuDomainJob job)
{
if (qemuDomainObjBeginJobInternal(driver, obj, job,
+ QEMU_AGENT_JOB_NONE,
QEMU_ASYNC_JOB_NONE, false) < 0)
return -1;
else
return 0;
}
+/**
+ * qemuDomainObjBeginAgentJob:
+ *
+ * Grabs agent type of job. Use if caller talks to guest agent only.
+ *
+ * To end job call qemuDomainObjEndAgentJob.
+ */
+int qemuDomainObjBeginAgentJob(virQEMUDriverPtr driver,
+ virDomainObjPtr obj,
+ qemuDomainAgentJob agentJob)
I know this file is pretty inconsistent and you're not making it a lot
worse, but s/int /int
/ and reindent the rest.
+{
+ return qemuDomainObjBeginJobInternal(driver, obj, QEMU_JOB_NONE,
+ agentJob,
+ QEMU_ASYNC_JOB_NONE, false);
+}
+
+/**
+ * qemuDomainObjBeginJobWithAgent:
+ *
+ * Grabs both monitor and agent types of job. Use if caller talks to both
+ * monitor and guest agent.
+ *
+ * To end job call qemuDomainObjEndJobWithAgent.
+ */
+int qemuDomainObjBeginJobWithAgent(virQEMUDriverPtr driver,
+ virDomainObjPtr obj,
+ qemuDomainJob job,
+ qemuDomainAgentJob agentJob)
s/int /int
/ again
+{
+ return qemuDomainObjBeginJobInternal(driver, obj, job,
+ agentJob, QEMU_ASYNC_JOB_NONE, false);
Strange indentation. And since you're going to touch the code here, the
following formatting would look a bit better:
return qemuDomainObjBeginJobInternal(driver, obj, job, agentJob,
QEMU_ASYNC_JOB_NONE, false);
+}
+
int qemuDomainObjBeginAsyncJob(virQEMUDriverPtr driver,
virDomainObjPtr obj,
qemuDomainAsyncJob asyncJob,
@@ -6569,6 +6640,7 @@ int qemuDomainObjBeginAsyncJob(virQEMUDriverPtr driver,
qemuDomainObjPrivatePtr priv;
if (qemuDomainObjBeginJobInternal(driver, obj, QEMU_JOB_ASYNC,
+ QEMU_AGENT_JOB_NONE,
asyncJob, false) < 0)
return -1;
@@ -6598,6 +6670,7 @@ qemuDomainObjBeginNestedJob(virQEMUDriverPtr driver,
return qemuDomainObjBeginJobInternal(driver, obj,
QEMU_JOB_ASYNC_NESTED,
+ QEMU_AGENT_JOB_NONE,
QEMU_ASYNC_JOB_NONE,
false);
}
@@ -6621,6 +6694,7 @@ qemuDomainObjBeginJobNowait(virQEMUDriverPtr driver,
qemuDomainJob job)
{
return qemuDomainObjBeginJobInternal(driver, obj, job,
+ QEMU_AGENT_JOB_NONE,
QEMU_ASYNC_JOB_NONE, true);
}
@@ -6646,7 +6720,47 @@ qemuDomainObjEndJob(virQEMUDriverPtr driver, virDomainObjPtr obj)
qemuDomainObjResetJob(priv);
if (qemuDomainTrackJob(job))
qemuDomainObjSaveJob(driver, obj);
- virCondSignal(&priv->job.cond);
+ virCondBroadcast(&priv->job.cond);
+}
+
+void
+qemuDomainObjEndAgentJob(virDomainObjPtr obj)
+{
+ qemuDomainObjPrivatePtr priv = obj->privateData;
+ qemuDomainAgentJob agentJob = priv->job.agentActive;
+
+ priv->jobs_queued--;
+
+ VIR_DEBUG("Stopping agent job: %s (async=%s vm=%p name=%s)",
+ qemuDomainAgentJobTypeToString(agentJob),
+ qemuDomainAsyncJobTypeToString(priv->job.asyncJob),
+ obj, obj->def->name);
+
+ qemuDomainObjResetAgentJob(priv);
+ virCondBroadcast(&priv->job.cond);
+}
+
+void
+qemuDomainObjEndJobWithAgent(virQEMUDriverPtr driver,
+ virDomainObjPtr obj)
+{
+ qemuDomainObjPrivatePtr priv = obj->privateData;
+ qemuDomainJob job = priv->job.active;
+ qemuDomainAgentJob agentJob = priv->job.agentActive;
+
+ priv->jobs_queued--;
+
+ VIR_DEBUG("Stopping both jobs: %s %s (async=%s vm=%p name=%s)",
+ qemuDomainJobTypeToString(job),
+ qemuDomainAgentJobTypeToString(agentJob),
+ qemuDomainAsyncJobTypeToString(priv->job.asyncJob),
+ obj, obj->def->name);
+
+ qemuDomainObjResetJob(priv);
+ qemuDomainObjResetAgentJob(priv);
+ if (qemuDomainTrackJob(job))
+ qemuDomainObjSaveJob(driver, obj);
+ virCondBroadcast(&priv->job.cond);
}
void
@@ -6800,8 +6914,9 @@ qemuDomainObjEnterMonitorAsync(virQEMUDriverPtr driver,
* obj must be locked before calling
*
* To be called immediately before any QEMU agent API call.
- * Must have already called qemuDomainObjBeginJob() and checked
- * that the VM is still active.
+ * Must have already called qemuDomainObjBeginAgentJob() or
+ * qemuDomainObjBeginJobWithAgent() and checked that the VM is
+ * still active.
The documentation of qemuDomainObjEnterMonitorInternal would benefit
from similar treatment.
*
* To be followed with qemuDomainObjExitAgent() once complete
*/
...
With the small issues fixed
Reviewed-by: Jiri Denemark <jdenemar(a)redhat.com>