|
|
a3bc25 |
From 6f532f2e3615bb573a186cfcfddf8fcc1eec7efd Mon Sep 17 00:00:00 2001
|
|
|
a3bc25 |
Message-Id: <6f532f2e3615bb573a186cfcfddf8fcc1eec7efd@dist-git>
|
|
|
a3bc25 |
From: Jiri Denemark <jdenemar@redhat.com>
|
|
|
a3bc25 |
Date: Tue, 11 Jul 2017 15:53:58 +0200
|
|
|
a3bc25 |
Subject: [PATCH] qemu: Move qemuProcessReconnect to the end of qemu_process.c
|
|
|
a3bc25 |
|
|
|
a3bc25 |
qemuProcessReconnect will need to call additional functions which were
|
|
|
a3bc25 |
originally defined further in qemu_process.c.
|
|
|
a3bc25 |
|
|
|
a3bc25 |
Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
|
|
|
a3bc25 |
Reviewed-by: Pavel Hrdina <phrdina@redhat.com>
|
|
|
a3bc25 |
(cherry picked from commit aad362f93b4451e2f3c98923e5e44c4fe6d26d75)
|
|
|
a3bc25 |
|
|
|
a3bc25 |
Conflicts:
|
|
|
a3bc25 |
src/qemu/qemu_process.c
|
|
|
a3bc25 |
|
|
|
a3bc25 |
https://bugzilla.redhat.com/show_bug.cgi?id=1470582
|
|
|
a3bc25 |
|
|
|
a3bc25 |
Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
|
|
|
a3bc25 |
Reviewed-by: Pavel Hrdina <phrdina@redhat.com>
|
|
|
a3bc25 |
---
|
|
|
a3bc25 |
src/qemu/qemu_process.c | 645 ++++++++++++++++++++++++------------------------
|
|
|
a3bc25 |
1 file changed, 323 insertions(+), 322 deletions(-)
|
|
|
a3bc25 |
|
|
|
a3bc25 |
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c
|
|
|
a3bc25 |
index 9df463094e..e6f56dc484 100644
|
|
|
a3bc25 |
--- a/src/qemu/qemu_process.c
|
|
|
a3bc25 |
+++ b/src/qemu/qemu_process.c
|
|
|
a3bc25 |
@@ -3331,328 +3331,6 @@ qemuProcessBuildDestroyHugepagesPath(virQEMUDriverPtr driver,
|
|
|
a3bc25 |
}
|
|
|
a3bc25 |
|
|
|
a3bc25 |
|
|
|
a3bc25 |
-struct qemuProcessReconnectData {
|
|
|
a3bc25 |
- virConnectPtr conn;
|
|
|
a3bc25 |
- virQEMUDriverPtr driver;
|
|
|
a3bc25 |
- virDomainObjPtr obj;
|
|
|
a3bc25 |
-};
|
|
|
a3bc25 |
-/*
|
|
|
a3bc25 |
- * Open an existing VM's monitor, re-detect VCPU threads
|
|
|
a3bc25 |
- * and re-reserve the security labels in use
|
|
|
a3bc25 |
- *
|
|
|
a3bc25 |
- * We own the virConnectPtr we are passed here - whoever started
|
|
|
a3bc25 |
- * this thread function has increased the reference counter to it
|
|
|
a3bc25 |
- * so that we now have to close it.
|
|
|
a3bc25 |
- *
|
|
|
a3bc25 |
- * This function also inherits a locked and ref'd domain object.
|
|
|
a3bc25 |
- *
|
|
|
a3bc25 |
- * This function needs to:
|
|
|
a3bc25 |
- * 1. Enter job
|
|
|
a3bc25 |
- * 1. just before monitor reconnect do lightweight MonitorEnter
|
|
|
a3bc25 |
- * (increase VM refcount and unlock VM)
|
|
|
a3bc25 |
- * 2. reconnect to monitor
|
|
|
a3bc25 |
- * 3. do lightweight MonitorExit (lock VM)
|
|
|
a3bc25 |
- * 4. continue reconnect process
|
|
|
a3bc25 |
- * 5. EndJob
|
|
|
a3bc25 |
- *
|
|
|
a3bc25 |
- * We can't do normal MonitorEnter & MonitorExit because these two lock the
|
|
|
a3bc25 |
- * monitor lock, which does not exists in this early phase.
|
|
|
a3bc25 |
- */
|
|
|
a3bc25 |
-static void
|
|
|
a3bc25 |
-qemuProcessReconnect(void *opaque)
|
|
|
a3bc25 |
-{
|
|
|
a3bc25 |
- struct qemuProcessReconnectData *data = opaque;
|
|
|
a3bc25 |
- virQEMUDriverPtr driver = data->driver;
|
|
|
a3bc25 |
- virDomainObjPtr obj = data->obj;
|
|
|
a3bc25 |
- qemuDomainObjPrivatePtr priv;
|
|
|
a3bc25 |
- virConnectPtr conn = data->conn;
|
|
|
a3bc25 |
- struct qemuDomainJobObj oldjob;
|
|
|
a3bc25 |
- int state;
|
|
|
a3bc25 |
- int reason;
|
|
|
a3bc25 |
- virQEMUDriverConfigPtr cfg;
|
|
|
a3bc25 |
- size_t i;
|
|
|
a3bc25 |
- unsigned int stopFlags = 0;
|
|
|
a3bc25 |
- bool jobStarted = false;
|
|
|
a3bc25 |
- virCapsPtr caps = NULL;
|
|
|
a3bc25 |
-
|
|
|
a3bc25 |
- VIR_FREE(data);
|
|
|
a3bc25 |
-
|
|
|
a3bc25 |
- qemuDomainObjRestoreJob(obj, &oldjob);
|
|
|
a3bc25 |
- if (oldjob.asyncJob == QEMU_ASYNC_JOB_MIGRATION_IN)
|
|
|
a3bc25 |
- stopFlags |= VIR_QEMU_PROCESS_STOP_MIGRATED;
|
|
|
a3bc25 |
-
|
|
|
a3bc25 |
- cfg = virQEMUDriverGetConfig(driver);
|
|
|
a3bc25 |
- priv = obj->privateData;
|
|
|
a3bc25 |
-
|
|
|
a3bc25 |
- if (!(caps = virQEMUDriverGetCapabilities(driver, false)))
|
|
|
a3bc25 |
- goto error;
|
|
|
a3bc25 |
-
|
|
|
a3bc25 |
- if (qemuDomainObjBeginJob(driver, obj, QEMU_JOB_MODIFY) < 0)
|
|
|
a3bc25 |
- goto error;
|
|
|
a3bc25 |
- jobStarted = true;
|
|
|
a3bc25 |
-
|
|
|
a3bc25 |
- /* XXX If we ever gonna change pid file pattern, come up with
|
|
|
a3bc25 |
- * some intelligence here to deal with old paths. */
|
|
|
a3bc25 |
- if (!(priv->pidfile = virPidFileBuildPath(cfg->stateDir, obj->def->name)))
|
|
|
a3bc25 |
- goto error;
|
|
|
a3bc25 |
-
|
|
|
a3bc25 |
- /* Restore the masterKey */
|
|
|
a3bc25 |
- if (qemuDomainMasterKeyReadFile(priv) < 0)
|
|
|
a3bc25 |
- goto error;
|
|
|
a3bc25 |
-
|
|
|
a3bc25 |
- virNWFilterReadLockFilterUpdates();
|
|
|
a3bc25 |
-
|
|
|
a3bc25 |
- VIR_DEBUG("Reconnect monitor to %p '%s'", obj, obj->def->name);
|
|
|
a3bc25 |
-
|
|
|
a3bc25 |
- /* XXX check PID liveliness & EXE path */
|
|
|
a3bc25 |
- if (qemuConnectMonitor(driver, obj, QEMU_ASYNC_JOB_NONE, NULL) < 0)
|
|
|
a3bc25 |
- goto error;
|
|
|
a3bc25 |
-
|
|
|
a3bc25 |
- if (qemuHostdevUpdateActiveDomainDevices(driver, obj->def) < 0)
|
|
|
a3bc25 |
- goto error;
|
|
|
a3bc25 |
-
|
|
|
a3bc25 |
- if (qemuConnectCgroup(driver, obj) < 0)
|
|
|
a3bc25 |
- goto error;
|
|
|
a3bc25 |
-
|
|
|
a3bc25 |
- if (qemuDomainPerfRestart(obj) < 0)
|
|
|
a3bc25 |
- goto error;
|
|
|
a3bc25 |
-
|
|
|
a3bc25 |
- /* XXX: Need to change as long as lock is introduced for
|
|
|
a3bc25 |
- * qemu_driver->sharedDevices.
|
|
|
a3bc25 |
- */
|
|
|
a3bc25 |
- for (i = 0; i < obj->def->ndisks; i++) {
|
|
|
a3bc25 |
- virDomainDeviceDef dev;
|
|
|
a3bc25 |
-
|
|
|
a3bc25 |
- if (virStorageTranslateDiskSourcePool(conn, obj->def->disks[i]) < 0)
|
|
|
a3bc25 |
- goto error;
|
|
|
a3bc25 |
-
|
|
|
a3bc25 |
- /* XXX we should be able to restore all data from XML in the future.
|
|
|
a3bc25 |
- * This should be the only place that calls qemuDomainDetermineDiskChain
|
|
|
a3bc25 |
- * with @report_broken == false to guarantee best-effort domain
|
|
|
a3bc25 |
- * reconnect */
|
|
|
a3bc25 |
- if (qemuDomainDetermineDiskChain(driver, obj, obj->def->disks[i],
|
|
|
a3bc25 |
- true, false) < 0)
|
|
|
a3bc25 |
- goto error;
|
|
|
a3bc25 |
-
|
|
|
a3bc25 |
- dev.type = VIR_DOMAIN_DEVICE_DISK;
|
|
|
a3bc25 |
- dev.data.disk = obj->def->disks[i];
|
|
|
a3bc25 |
- if (qemuAddSharedDevice(driver, &dev, obj->def->name) < 0)
|
|
|
a3bc25 |
- goto error;
|
|
|
a3bc25 |
- }
|
|
|
a3bc25 |
-
|
|
|
a3bc25 |
- if (qemuProcessUpdateState(driver, obj) < 0)
|
|
|
a3bc25 |
- goto error;
|
|
|
a3bc25 |
-
|
|
|
a3bc25 |
- state = virDomainObjGetState(obj, &reason);
|
|
|
a3bc25 |
- if (state == VIR_DOMAIN_SHUTOFF ||
|
|
|
a3bc25 |
- (state == VIR_DOMAIN_PAUSED &&
|
|
|
a3bc25 |
- reason == VIR_DOMAIN_PAUSED_STARTING_UP)) {
|
|
|
a3bc25 |
- VIR_DEBUG("Domain '%s' wasn't fully started yet, killing it",
|
|
|
a3bc25 |
- obj->def->name);
|
|
|
a3bc25 |
- goto error;
|
|
|
a3bc25 |
- }
|
|
|
a3bc25 |
-
|
|
|
a3bc25 |
- /* If upgrading from old libvirtd we won't have found any
|
|
|
a3bc25 |
- * caps in the domain status, so re-query them
|
|
|
a3bc25 |
- */
|
|
|
a3bc25 |
- if (!priv->qemuCaps &&
|
|
|
a3bc25 |
- !(priv->qemuCaps = virQEMUCapsCacheLookupCopy(caps,
|
|
|
a3bc25 |
- driver->qemuCapsCache,
|
|
|
a3bc25 |
- obj->def->emulator,
|
|
|
a3bc25 |
- obj->def->os.machine)))
|
|
|
a3bc25 |
- goto error;
|
|
|
a3bc25 |
-
|
|
|
a3bc25 |
- /* In case the domain shutdown while we were not running,
|
|
|
a3bc25 |
- * we need to finish the shutdown process. And we need to do it after
|
|
|
a3bc25 |
- * we have virQEMUCaps filled in.
|
|
|
a3bc25 |
- */
|
|
|
a3bc25 |
- if (state == VIR_DOMAIN_SHUTDOWN ||
|
|
|
a3bc25 |
- (state == VIR_DOMAIN_PAUSED &&
|
|
|
a3bc25 |
- reason == VIR_DOMAIN_PAUSED_SHUTTING_DOWN)) {
|
|
|
a3bc25 |
- VIR_DEBUG("Finishing shutdown sequence for domain %s",
|
|
|
a3bc25 |
- obj->def->name);
|
|
|
a3bc25 |
- qemuProcessShutdownOrReboot(driver, obj);
|
|
|
a3bc25 |
- goto cleanup;
|
|
|
a3bc25 |
- }
|
|
|
a3bc25 |
-
|
|
|
a3bc25 |
- if (qemuProcessBuildDestroyHugepagesPath(driver, obj, true) < 0)
|
|
|
a3bc25 |
- goto error;
|
|
|
a3bc25 |
-
|
|
|
a3bc25 |
- if ((qemuDomainAssignAddresses(obj->def, priv->qemuCaps,
|
|
|
a3bc25 |
- driver, obj, false)) < 0) {
|
|
|
a3bc25 |
- goto error;
|
|
|
a3bc25 |
- }
|
|
|
a3bc25 |
-
|
|
|
a3bc25 |
- /* if domain requests security driver we haven't loaded, report error, but
|
|
|
a3bc25 |
- * do not kill the domain
|
|
|
a3bc25 |
- */
|
|
|
a3bc25 |
- ignore_value(qemuSecurityCheckAllLabel(driver->securityManager,
|
|
|
a3bc25 |
- obj->def));
|
|
|
a3bc25 |
-
|
|
|
a3bc25 |
- if (qemuDomainRefreshVcpuInfo(driver, obj, QEMU_ASYNC_JOB_NONE, true) < 0)
|
|
|
a3bc25 |
- goto error;
|
|
|
a3bc25 |
-
|
|
|
a3bc25 |
- qemuDomainVcpuPersistOrder(obj->def);
|
|
|
a3bc25 |
-
|
|
|
a3bc25 |
- if (qemuSecurityReserveLabel(driver->securityManager, obj->def, obj->pid) < 0)
|
|
|
a3bc25 |
- goto error;
|
|
|
a3bc25 |
-
|
|
|
a3bc25 |
- qemuProcessNotifyNets(obj->def);
|
|
|
a3bc25 |
-
|
|
|
a3bc25 |
- if (qemuProcessFiltersInstantiate(obj->def))
|
|
|
a3bc25 |
- goto error;
|
|
|
a3bc25 |
-
|
|
|
a3bc25 |
- if (qemuProcessRefreshDisks(driver, obj, QEMU_ASYNC_JOB_NONE) < 0)
|
|
|
a3bc25 |
- goto error;
|
|
|
a3bc25 |
-
|
|
|
a3bc25 |
- if (qemuBlockNodeNamesDetect(driver, obj) < 0)
|
|
|
a3bc25 |
- goto error;
|
|
|
a3bc25 |
-
|
|
|
a3bc25 |
- if (qemuRefreshVirtioChannelState(driver, obj, QEMU_ASYNC_JOB_NONE) < 0)
|
|
|
a3bc25 |
- goto error;
|
|
|
a3bc25 |
-
|
|
|
a3bc25 |
- /* If querying of guest's RTC failed, report error, but do not kill the domain. */
|
|
|
a3bc25 |
- qemuRefreshRTC(driver, obj);
|
|
|
a3bc25 |
-
|
|
|
a3bc25 |
- if (qemuProcessRefreshBalloonState(driver, obj, QEMU_ASYNC_JOB_NONE) < 0)
|
|
|
a3bc25 |
- goto error;
|
|
|
a3bc25 |
-
|
|
|
a3bc25 |
- if (qemuProcessRecoverJob(driver, obj, conn, &oldjob, &stopFlags) < 0)
|
|
|
a3bc25 |
- goto error;
|
|
|
a3bc25 |
-
|
|
|
a3bc25 |
- if (qemuProcessUpdateDevices(driver, obj) < 0)
|
|
|
a3bc25 |
- goto error;
|
|
|
a3bc25 |
-
|
|
|
a3bc25 |
- qemuProcessReconnectCheckMemAliasOrderMismatch(obj);
|
|
|
a3bc25 |
-
|
|
|
a3bc25 |
- if (qemuConnectAgent(driver, obj) < 0)
|
|
|
a3bc25 |
- goto error;
|
|
|
a3bc25 |
-
|
|
|
a3bc25 |
- /* update domain state XML with possibly updated state in virDomainObj */
|
|
|
a3bc25 |
- if (virDomainSaveStatus(driver->xmlopt, cfg->stateDir, obj, driver->caps) < 0)
|
|
|
a3bc25 |
- goto error;
|
|
|
a3bc25 |
-
|
|
|
a3bc25 |
- /* Run an hook to allow admins to do some magic */
|
|
|
a3bc25 |
- if (virHookPresent(VIR_HOOK_DRIVER_QEMU)) {
|
|
|
a3bc25 |
- char *xml = qemuDomainDefFormatXML(driver, obj->def, 0);
|
|
|
a3bc25 |
- int hookret;
|
|
|
a3bc25 |
-
|
|
|
a3bc25 |
- hookret = virHookCall(VIR_HOOK_DRIVER_QEMU, obj->def->name,
|
|
|
a3bc25 |
- VIR_HOOK_QEMU_OP_RECONNECT, VIR_HOOK_SUBOP_BEGIN,
|
|
|
a3bc25 |
- NULL, xml, NULL);
|
|
|
a3bc25 |
- VIR_FREE(xml);
|
|
|
a3bc25 |
-
|
|
|
a3bc25 |
- /*
|
|
|
a3bc25 |
- * If the script raised an error abort the launch
|
|
|
a3bc25 |
- */
|
|
|
a3bc25 |
- if (hookret < 0)
|
|
|
a3bc25 |
- goto error;
|
|
|
a3bc25 |
- }
|
|
|
a3bc25 |
-
|
|
|
a3bc25 |
- if (virAtomicIntInc(&driver->nactive) == 1 && driver->inhibitCallback)
|
|
|
a3bc25 |
- driver->inhibitCallback(true, driver->inhibitOpaque);
|
|
|
a3bc25 |
-
|
|
|
a3bc25 |
- cleanup:
|
|
|
a3bc25 |
- if (jobStarted)
|
|
|
a3bc25 |
- qemuDomainObjEndJob(driver, obj);
|
|
|
a3bc25 |
- if (!virDomainObjIsActive(obj))
|
|
|
a3bc25 |
- qemuDomainRemoveInactive(driver, obj);
|
|
|
a3bc25 |
- virDomainObjEndAPI(&obj);
|
|
|
a3bc25 |
- virObjectUnref(conn);
|
|
|
a3bc25 |
- virObjectUnref(cfg);
|
|
|
a3bc25 |
- virObjectUnref(caps);
|
|
|
a3bc25 |
- virNWFilterUnlockFilterUpdates();
|
|
|
a3bc25 |
- return;
|
|
|
a3bc25 |
-
|
|
|
a3bc25 |
- error:
|
|
|
a3bc25 |
- if (virDomainObjIsActive(obj)) {
|
|
|
a3bc25 |
- /* We can't get the monitor back, so must kill the VM
|
|
|
a3bc25 |
- * to remove danger of it ending up running twice if
|
|
|
a3bc25 |
- * user tries to start it again later
|
|
|
a3bc25 |
- */
|
|
|
a3bc25 |
- if (virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_NO_SHUTDOWN)) {
|
|
|
a3bc25 |
- /* If we couldn't get the monitor and qemu supports
|
|
|
a3bc25 |
- * no-shutdown, we can safely say that the domain
|
|
|
a3bc25 |
- * crashed ... */
|
|
|
a3bc25 |
- state = VIR_DOMAIN_SHUTOFF_CRASHED;
|
|
|
a3bc25 |
- } else {
|
|
|
a3bc25 |
- /* ... but if it doesn't we can't say what the state
|
|
|
a3bc25 |
- * really is and FAILED means "failed to start" */
|
|
|
a3bc25 |
- state = VIR_DOMAIN_SHUTOFF_UNKNOWN;
|
|
|
a3bc25 |
- }
|
|
|
a3bc25 |
- /* If BeginJob failed, we jumped here without a job, let's hope another
|
|
|
a3bc25 |
- * thread didn't have a chance to start playing with the domain yet
|
|
|
a3bc25 |
- * (it's all we can do anyway).
|
|
|
a3bc25 |
- */
|
|
|
a3bc25 |
- qemuProcessStop(driver, obj, state, QEMU_ASYNC_JOB_NONE, stopFlags);
|
|
|
a3bc25 |
- }
|
|
|
a3bc25 |
- goto cleanup;
|
|
|
a3bc25 |
-}
|
|
|
a3bc25 |
-
|
|
|
a3bc25 |
-static int
|
|
|
a3bc25 |
-qemuProcessReconnectHelper(virDomainObjPtr obj,
|
|
|
a3bc25 |
- void *opaque)
|
|
|
a3bc25 |
-{
|
|
|
a3bc25 |
- virThread thread;
|
|
|
a3bc25 |
- struct qemuProcessReconnectData *src = opaque;
|
|
|
a3bc25 |
- struct qemuProcessReconnectData *data;
|
|
|
a3bc25 |
-
|
|
|
a3bc25 |
- /* If the VM was inactive, we don't need to reconnect */
|
|
|
a3bc25 |
- if (!obj->pid)
|
|
|
a3bc25 |
- return 0;
|
|
|
a3bc25 |
-
|
|
|
a3bc25 |
- if (VIR_ALLOC(data) < 0)
|
|
|
a3bc25 |
- return -1;
|
|
|
a3bc25 |
-
|
|
|
a3bc25 |
- memcpy(data, src, sizeof(*data));
|
|
|
a3bc25 |
- data->obj = obj;
|
|
|
a3bc25 |
-
|
|
|
a3bc25 |
- /* this lock and reference will be eventually transferred to the thread
|
|
|
a3bc25 |
- * that handles the reconnect */
|
|
|
a3bc25 |
- virObjectLock(obj);
|
|
|
a3bc25 |
- virObjectRef(obj);
|
|
|
a3bc25 |
-
|
|
|
a3bc25 |
- /* Since we close the connection later on, we have to make sure that the
|
|
|
a3bc25 |
- * threads we start see a valid connection throughout their lifetime. We
|
|
|
a3bc25 |
- * simply increase the reference counter here.
|
|
|
a3bc25 |
- */
|
|
|
a3bc25 |
- virObjectRef(data->conn);
|
|
|
a3bc25 |
-
|
|
|
a3bc25 |
- if (virThreadCreate(&thread, false, qemuProcessReconnect, data) < 0) {
|
|
|
a3bc25 |
- virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
a3bc25 |
- _("Could not create thread. QEMU initialization "
|
|
|
a3bc25 |
- "might be incomplete"));
|
|
|
a3bc25 |
- /* We can't spawn a thread and thus connect to monitor. Kill qemu.
|
|
|
a3bc25 |
- * It's safe to call qemuProcessStop without a job here since there
|
|
|
a3bc25 |
- * is no thread that could be doing anything else with the same domain
|
|
|
a3bc25 |
- * object.
|
|
|
a3bc25 |
- */
|
|
|
a3bc25 |
- qemuProcessStop(src->driver, obj, VIR_DOMAIN_SHUTOFF_FAILED,
|
|
|
a3bc25 |
- QEMU_ASYNC_JOB_NONE, 0);
|
|
|
a3bc25 |
- qemuDomainRemoveInactive(src->driver, obj);
|
|
|
a3bc25 |
-
|
|
|
a3bc25 |
- virDomainObjEndAPI(&obj);
|
|
|
a3bc25 |
- virObjectUnref(data->conn);
|
|
|
a3bc25 |
- VIR_FREE(data);
|
|
|
a3bc25 |
- return -1;
|
|
|
a3bc25 |
- }
|
|
|
a3bc25 |
-
|
|
|
a3bc25 |
- return 0;
|
|
|
a3bc25 |
-}
|
|
|
a3bc25 |
-
|
|
|
a3bc25 |
-/**
|
|
|
a3bc25 |
- * qemuProcessReconnectAll
|
|
|
a3bc25 |
- *
|
|
|
a3bc25 |
- * Try to re-open the resources for live VMs that we care
|
|
|
a3bc25 |
- * about.
|
|
|
a3bc25 |
- */
|
|
|
a3bc25 |
-void
|
|
|
a3bc25 |
-qemuProcessReconnectAll(virConnectPtr conn, virQEMUDriverPtr driver)
|
|
|
a3bc25 |
-{
|
|
|
a3bc25 |
- struct qemuProcessReconnectData data = {.conn = conn, .driver = driver};
|
|
|
a3bc25 |
- virDomainObjListForEach(driver->domains, qemuProcessReconnectHelper, &data);
|
|
|
a3bc25 |
-}
|
|
|
a3bc25 |
-
|
|
|
a3bc25 |
static int
|
|
|
a3bc25 |
qemuProcessVNCAllocatePorts(virQEMUDriverPtr driver,
|
|
|
a3bc25 |
virDomainGraphicsDefPtr graphics,
|
|
|
a3bc25 |
@@ -6958,3 +6636,326 @@ qemuProcessRefreshDisks(virQEMUDriverPtr driver,
|
|
|
a3bc25 |
virHashFree(table);
|
|
|
a3bc25 |
return ret;
|
|
|
a3bc25 |
}
|
|
|
a3bc25 |
+
|
|
|
a3bc25 |
+
|
|
|
a3bc25 |
+struct qemuProcessReconnectData {
|
|
|
a3bc25 |
+ virConnectPtr conn;
|
|
|
a3bc25 |
+ virQEMUDriverPtr driver;
|
|
|
a3bc25 |
+ virDomainObjPtr obj;
|
|
|
a3bc25 |
+};
|
|
|
a3bc25 |
+/*
|
|
|
a3bc25 |
+ * Open an existing VM's monitor, re-detect VCPU threads
|
|
|
a3bc25 |
+ * and re-reserve the security labels in use
|
|
|
a3bc25 |
+ *
|
|
|
a3bc25 |
+ * We own the virConnectPtr we are passed here - whoever started
|
|
|
a3bc25 |
+ * this thread function has increased the reference counter to it
|
|
|
a3bc25 |
+ * so that we now have to close it.
|
|
|
a3bc25 |
+ *
|
|
|
a3bc25 |
+ * This function also inherits a locked and ref'd domain object.
|
|
|
a3bc25 |
+ *
|
|
|
a3bc25 |
+ * This function needs to:
|
|
|
a3bc25 |
+ * 1. Enter job
|
|
|
a3bc25 |
+ * 1. just before monitor reconnect do lightweight MonitorEnter
|
|
|
a3bc25 |
+ * (increase VM refcount and unlock VM)
|
|
|
a3bc25 |
+ * 2. reconnect to monitor
|
|
|
a3bc25 |
+ * 3. do lightweight MonitorExit (lock VM)
|
|
|
a3bc25 |
+ * 4. continue reconnect process
|
|
|
a3bc25 |
+ * 5. EndJob
|
|
|
a3bc25 |
+ *
|
|
|
a3bc25 |
+ * We can't do normal MonitorEnter & MonitorExit because these two lock the
|
|
|
a3bc25 |
+ * monitor lock, which does not exists in this early phase.
|
|
|
a3bc25 |
+ */
|
|
|
a3bc25 |
+static void
|
|
|
a3bc25 |
+qemuProcessReconnect(void *opaque)
|
|
|
a3bc25 |
+{
|
|
|
a3bc25 |
+ struct qemuProcessReconnectData *data = opaque;
|
|
|
a3bc25 |
+ virQEMUDriverPtr driver = data->driver;
|
|
|
a3bc25 |
+ virDomainObjPtr obj = data->obj;
|
|
|
a3bc25 |
+ qemuDomainObjPrivatePtr priv;
|
|
|
a3bc25 |
+ virConnectPtr conn = data->conn;
|
|
|
a3bc25 |
+ struct qemuDomainJobObj oldjob;
|
|
|
a3bc25 |
+ int state;
|
|
|
a3bc25 |
+ int reason;
|
|
|
a3bc25 |
+ virQEMUDriverConfigPtr cfg;
|
|
|
a3bc25 |
+ size_t i;
|
|
|
a3bc25 |
+ unsigned int stopFlags = 0;
|
|
|
a3bc25 |
+ bool jobStarted = false;
|
|
|
a3bc25 |
+ virCapsPtr caps = NULL;
|
|
|
a3bc25 |
+
|
|
|
a3bc25 |
+ VIR_FREE(data);
|
|
|
a3bc25 |
+
|
|
|
a3bc25 |
+ qemuDomainObjRestoreJob(obj, &oldjob);
|
|
|
a3bc25 |
+ if (oldjob.asyncJob == QEMU_ASYNC_JOB_MIGRATION_IN)
|
|
|
a3bc25 |
+ stopFlags |= VIR_QEMU_PROCESS_STOP_MIGRATED;
|
|
|
a3bc25 |
+
|
|
|
a3bc25 |
+ cfg = virQEMUDriverGetConfig(driver);
|
|
|
a3bc25 |
+ priv = obj->privateData;
|
|
|
a3bc25 |
+
|
|
|
a3bc25 |
+ if (!(caps = virQEMUDriverGetCapabilities(driver, false)))
|
|
|
a3bc25 |
+ goto error;
|
|
|
a3bc25 |
+
|
|
|
a3bc25 |
+ if (qemuDomainObjBeginJob(driver, obj, QEMU_JOB_MODIFY) < 0)
|
|
|
a3bc25 |
+ goto error;
|
|
|
a3bc25 |
+ jobStarted = true;
|
|
|
a3bc25 |
+
|
|
|
a3bc25 |
+ /* XXX If we ever gonna change pid file pattern, come up with
|
|
|
a3bc25 |
+ * some intelligence here to deal with old paths. */
|
|
|
a3bc25 |
+ if (!(priv->pidfile = virPidFileBuildPath(cfg->stateDir, obj->def->name)))
|
|
|
a3bc25 |
+ goto error;
|
|
|
a3bc25 |
+
|
|
|
a3bc25 |
+ /* Restore the masterKey */
|
|
|
a3bc25 |
+ if (qemuDomainMasterKeyReadFile(priv) < 0)
|
|
|
a3bc25 |
+ goto error;
|
|
|
a3bc25 |
+
|
|
|
a3bc25 |
+ virNWFilterReadLockFilterUpdates();
|
|
|
a3bc25 |
+
|
|
|
a3bc25 |
+ VIR_DEBUG("Reconnect monitor to %p '%s'", obj, obj->def->name);
|
|
|
a3bc25 |
+
|
|
|
a3bc25 |
+ /* XXX check PID liveliness & EXE path */
|
|
|
a3bc25 |
+ if (qemuConnectMonitor(driver, obj, QEMU_ASYNC_JOB_NONE, NULL) < 0)
|
|
|
a3bc25 |
+ goto error;
|
|
|
a3bc25 |
+
|
|
|
a3bc25 |
+ if (qemuHostdevUpdateActiveDomainDevices(driver, obj->def) < 0)
|
|
|
a3bc25 |
+ goto error;
|
|
|
a3bc25 |
+
|
|
|
a3bc25 |
+ if (qemuConnectCgroup(driver, obj) < 0)
|
|
|
a3bc25 |
+ goto error;
|
|
|
a3bc25 |
+
|
|
|
a3bc25 |
+ if (qemuDomainPerfRestart(obj) < 0)
|
|
|
a3bc25 |
+ goto error;
|
|
|
a3bc25 |
+
|
|
|
a3bc25 |
+ /* XXX: Need to change as long as lock is introduced for
|
|
|
a3bc25 |
+ * qemu_driver->sharedDevices.
|
|
|
a3bc25 |
+ */
|
|
|
a3bc25 |
+ for (i = 0; i < obj->def->ndisks; i++) {
|
|
|
a3bc25 |
+ virDomainDeviceDef dev;
|
|
|
a3bc25 |
+
|
|
|
a3bc25 |
+ if (virStorageTranslateDiskSourcePool(conn, obj->def->disks[i]) < 0)
|
|
|
a3bc25 |
+ goto error;
|
|
|
a3bc25 |
+
|
|
|
a3bc25 |
+ /* XXX we should be able to restore all data from XML in the future.
|
|
|
a3bc25 |
+ * This should be the only place that calls qemuDomainDetermineDiskChain
|
|
|
a3bc25 |
+ * with @report_broken == false to guarantee best-effort domain
|
|
|
a3bc25 |
+ * reconnect */
|
|
|
a3bc25 |
+ if (qemuDomainDetermineDiskChain(driver, obj, obj->def->disks[i],
|
|
|
a3bc25 |
+ true, false) < 0)
|
|
|
a3bc25 |
+ goto error;
|
|
|
a3bc25 |
+
|
|
|
a3bc25 |
+ dev.type = VIR_DOMAIN_DEVICE_DISK;
|
|
|
a3bc25 |
+ dev.data.disk = obj->def->disks[i];
|
|
|
a3bc25 |
+ if (qemuAddSharedDevice(driver, &dev, obj->def->name) < 0)
|
|
|
a3bc25 |
+ goto error;
|
|
|
a3bc25 |
+ }
|
|
|
a3bc25 |
+
|
|
|
a3bc25 |
+ if (qemuProcessUpdateState(driver, obj) < 0)
|
|
|
a3bc25 |
+ goto error;
|
|
|
a3bc25 |
+
|
|
|
a3bc25 |
+ state = virDomainObjGetState(obj, &reason);
|
|
|
a3bc25 |
+ if (state == VIR_DOMAIN_SHUTOFF ||
|
|
|
a3bc25 |
+ (state == VIR_DOMAIN_PAUSED &&
|
|
|
a3bc25 |
+ reason == VIR_DOMAIN_PAUSED_STARTING_UP)) {
|
|
|
a3bc25 |
+ VIR_DEBUG("Domain '%s' wasn't fully started yet, killing it",
|
|
|
a3bc25 |
+ obj->def->name);
|
|
|
a3bc25 |
+ goto error;
|
|
|
a3bc25 |
+ }
|
|
|
a3bc25 |
+
|
|
|
a3bc25 |
+ /* If upgrading from old libvirtd we won't have found any
|
|
|
a3bc25 |
+ * caps in the domain status, so re-query them
|
|
|
a3bc25 |
+ */
|
|
|
a3bc25 |
+ if (!priv->qemuCaps &&
|
|
|
a3bc25 |
+ !(priv->qemuCaps = virQEMUCapsCacheLookupCopy(caps,
|
|
|
a3bc25 |
+ driver->qemuCapsCache,
|
|
|
a3bc25 |
+ obj->def->emulator,
|
|
|
a3bc25 |
+ obj->def->os.machine)))
|
|
|
a3bc25 |
+ goto error;
|
|
|
a3bc25 |
+
|
|
|
a3bc25 |
+ /* In case the domain shutdown while we were not running,
|
|
|
a3bc25 |
+ * we need to finish the shutdown process. And we need to do it after
|
|
|
a3bc25 |
+ * we have virQEMUCaps filled in.
|
|
|
a3bc25 |
+ */
|
|
|
a3bc25 |
+ if (state == VIR_DOMAIN_SHUTDOWN ||
|
|
|
a3bc25 |
+ (state == VIR_DOMAIN_PAUSED &&
|
|
|
a3bc25 |
+ reason == VIR_DOMAIN_PAUSED_SHUTTING_DOWN)) {
|
|
|
a3bc25 |
+ VIR_DEBUG("Finishing shutdown sequence for domain %s",
|
|
|
a3bc25 |
+ obj->def->name);
|
|
|
a3bc25 |
+ qemuProcessShutdownOrReboot(driver, obj);
|
|
|
a3bc25 |
+ goto cleanup;
|
|
|
a3bc25 |
+ }
|
|
|
a3bc25 |
+
|
|
|
a3bc25 |
+ if (qemuProcessBuildDestroyHugepagesPath(driver, obj, true) < 0)
|
|
|
a3bc25 |
+ goto error;
|
|
|
a3bc25 |
+
|
|
|
a3bc25 |
+ if ((qemuDomainAssignAddresses(obj->def, priv->qemuCaps,
|
|
|
a3bc25 |
+ driver, obj, false)) < 0) {
|
|
|
a3bc25 |
+ goto error;
|
|
|
a3bc25 |
+ }
|
|
|
a3bc25 |
+
|
|
|
a3bc25 |
+ /* if domain requests security driver we haven't loaded, report error, but
|
|
|
a3bc25 |
+ * do not kill the domain
|
|
|
a3bc25 |
+ */
|
|
|
a3bc25 |
+ ignore_value(qemuSecurityCheckAllLabel(driver->securityManager,
|
|
|
a3bc25 |
+ obj->def));
|
|
|
a3bc25 |
+
|
|
|
a3bc25 |
+ if (qemuDomainRefreshVcpuInfo(driver, obj, QEMU_ASYNC_JOB_NONE, true) < 0)
|
|
|
a3bc25 |
+ goto error;
|
|
|
a3bc25 |
+
|
|
|
a3bc25 |
+ qemuDomainVcpuPersistOrder(obj->def);
|
|
|
a3bc25 |
+
|
|
|
a3bc25 |
+ if (qemuSecurityReserveLabel(driver->securityManager, obj->def, obj->pid) < 0)
|
|
|
a3bc25 |
+ goto error;
|
|
|
a3bc25 |
+
|
|
|
a3bc25 |
+ qemuProcessNotifyNets(obj->def);
|
|
|
a3bc25 |
+
|
|
|
a3bc25 |
+ if (qemuProcessFiltersInstantiate(obj->def))
|
|
|
a3bc25 |
+ goto error;
|
|
|
a3bc25 |
+
|
|
|
a3bc25 |
+ if (qemuProcessRefreshDisks(driver, obj, QEMU_ASYNC_JOB_NONE) < 0)
|
|
|
a3bc25 |
+ goto error;
|
|
|
a3bc25 |
+
|
|
|
a3bc25 |
+ if (qemuBlockNodeNamesDetect(driver, obj) < 0)
|
|
|
a3bc25 |
+ goto error;
|
|
|
a3bc25 |
+
|
|
|
a3bc25 |
+ if (qemuRefreshVirtioChannelState(driver, obj, QEMU_ASYNC_JOB_NONE) < 0)
|
|
|
a3bc25 |
+ goto error;
|
|
|
a3bc25 |
+
|
|
|
a3bc25 |
+ /* If querying of guest's RTC failed, report error, but do not kill the domain. */
|
|
|
a3bc25 |
+ qemuRefreshRTC(driver, obj);
|
|
|
a3bc25 |
+
|
|
|
a3bc25 |
+ if (qemuProcessRefreshBalloonState(driver, obj, QEMU_ASYNC_JOB_NONE) < 0)
|
|
|
a3bc25 |
+ goto error;
|
|
|
a3bc25 |
+
|
|
|
a3bc25 |
+ if (qemuProcessRecoverJob(driver, obj, conn, &oldjob, &stopFlags) < 0)
|
|
|
a3bc25 |
+ goto error;
|
|
|
a3bc25 |
+
|
|
|
a3bc25 |
+ if (qemuProcessUpdateDevices(driver, obj) < 0)
|
|
|
a3bc25 |
+ goto error;
|
|
|
a3bc25 |
+
|
|
|
a3bc25 |
+ qemuProcessReconnectCheckMemAliasOrderMismatch(obj);
|
|
|
a3bc25 |
+
|
|
|
a3bc25 |
+ if (qemuConnectAgent(driver, obj) < 0)
|
|
|
a3bc25 |
+ goto error;
|
|
|
a3bc25 |
+
|
|
|
a3bc25 |
+ /* update domain state XML with possibly updated state in virDomainObj */
|
|
|
a3bc25 |
+ if (virDomainSaveStatus(driver->xmlopt, cfg->stateDir, obj, driver->caps) < 0)
|
|
|
a3bc25 |
+ goto error;
|
|
|
a3bc25 |
+
|
|
|
a3bc25 |
+ /* Run an hook to allow admins to do some magic */
|
|
|
a3bc25 |
+ if (virHookPresent(VIR_HOOK_DRIVER_QEMU)) {
|
|
|
a3bc25 |
+ char *xml = qemuDomainDefFormatXML(driver, obj->def, 0);
|
|
|
a3bc25 |
+ int hookret;
|
|
|
a3bc25 |
+
|
|
|
a3bc25 |
+ hookret = virHookCall(VIR_HOOK_DRIVER_QEMU, obj->def->name,
|
|
|
a3bc25 |
+ VIR_HOOK_QEMU_OP_RECONNECT, VIR_HOOK_SUBOP_BEGIN,
|
|
|
a3bc25 |
+ NULL, xml, NULL);
|
|
|
a3bc25 |
+ VIR_FREE(xml);
|
|
|
a3bc25 |
+
|
|
|
a3bc25 |
+ /*
|
|
|
a3bc25 |
+ * If the script raised an error abort the launch
|
|
|
a3bc25 |
+ */
|
|
|
a3bc25 |
+ if (hookret < 0)
|
|
|
a3bc25 |
+ goto error;
|
|
|
a3bc25 |
+ }
|
|
|
a3bc25 |
+
|
|
|
a3bc25 |
+ if (virAtomicIntInc(&driver->nactive) == 1 && driver->inhibitCallback)
|
|
|
a3bc25 |
+ driver->inhibitCallback(true, driver->inhibitOpaque);
|
|
|
a3bc25 |
+
|
|
|
a3bc25 |
+ cleanup:
|
|
|
a3bc25 |
+ if (jobStarted)
|
|
|
a3bc25 |
+ qemuDomainObjEndJob(driver, obj);
|
|
|
a3bc25 |
+ if (!virDomainObjIsActive(obj))
|
|
|
a3bc25 |
+ qemuDomainRemoveInactive(driver, obj);
|
|
|
a3bc25 |
+ virDomainObjEndAPI(&obj);
|
|
|
a3bc25 |
+ virObjectUnref(conn);
|
|
|
a3bc25 |
+ virObjectUnref(cfg);
|
|
|
a3bc25 |
+ virObjectUnref(caps);
|
|
|
a3bc25 |
+ virNWFilterUnlockFilterUpdates();
|
|
|
a3bc25 |
+ return;
|
|
|
a3bc25 |
+
|
|
|
a3bc25 |
+ error:
|
|
|
a3bc25 |
+ if (virDomainObjIsActive(obj)) {
|
|
|
a3bc25 |
+ /* We can't get the monitor back, so must kill the VM
|
|
|
a3bc25 |
+ * to remove danger of it ending up running twice if
|
|
|
a3bc25 |
+ * user tries to start it again later
|
|
|
a3bc25 |
+ */
|
|
|
a3bc25 |
+ if (virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_NO_SHUTDOWN)) {
|
|
|
a3bc25 |
+ /* If we couldn't get the monitor and qemu supports
|
|
|
a3bc25 |
+ * no-shutdown, we can safely say that the domain
|
|
|
a3bc25 |
+ * crashed ... */
|
|
|
a3bc25 |
+ state = VIR_DOMAIN_SHUTOFF_CRASHED;
|
|
|
a3bc25 |
+ } else {
|
|
|
a3bc25 |
+ /* ... but if it doesn't we can't say what the state
|
|
|
a3bc25 |
+ * really is and FAILED means "failed to start" */
|
|
|
a3bc25 |
+ state = VIR_DOMAIN_SHUTOFF_UNKNOWN;
|
|
|
a3bc25 |
+ }
|
|
|
a3bc25 |
+ /* If BeginJob failed, we jumped here without a job, let's hope another
|
|
|
a3bc25 |
+ * thread didn't have a chance to start playing with the domain yet
|
|
|
a3bc25 |
+ * (it's all we can do anyway).
|
|
|
a3bc25 |
+ */
|
|
|
a3bc25 |
+ qemuProcessStop(driver, obj, state, QEMU_ASYNC_JOB_NONE, stopFlags);
|
|
|
a3bc25 |
+ }
|
|
|
a3bc25 |
+ goto cleanup;
|
|
|
a3bc25 |
+}
|
|
|
a3bc25 |
+
|
|
|
a3bc25 |
+static int
|
|
|
a3bc25 |
+qemuProcessReconnectHelper(virDomainObjPtr obj,
|
|
|
a3bc25 |
+ void *opaque)
|
|
|
a3bc25 |
+{
|
|
|
a3bc25 |
+ virThread thread;
|
|
|
a3bc25 |
+ struct qemuProcessReconnectData *src = opaque;
|
|
|
a3bc25 |
+ struct qemuProcessReconnectData *data;
|
|
|
a3bc25 |
+
|
|
|
a3bc25 |
+ /* If the VM was inactive, we don't need to reconnect */
|
|
|
a3bc25 |
+ if (!obj->pid)
|
|
|
a3bc25 |
+ return 0;
|
|
|
a3bc25 |
+
|
|
|
a3bc25 |
+ if (VIR_ALLOC(data) < 0)
|
|
|
a3bc25 |
+ return -1;
|
|
|
a3bc25 |
+
|
|
|
a3bc25 |
+ memcpy(data, src, sizeof(*data));
|
|
|
a3bc25 |
+ data->obj = obj;
|
|
|
a3bc25 |
+
|
|
|
a3bc25 |
+ /* this lock and reference will be eventually transferred to the thread
|
|
|
a3bc25 |
+ * that handles the reconnect */
|
|
|
a3bc25 |
+ virObjectLock(obj);
|
|
|
a3bc25 |
+ virObjectRef(obj);
|
|
|
a3bc25 |
+
|
|
|
a3bc25 |
+ /* Since we close the connection later on, we have to make sure that the
|
|
|
a3bc25 |
+ * threads we start see a valid connection throughout their lifetime. We
|
|
|
a3bc25 |
+ * simply increase the reference counter here.
|
|
|
a3bc25 |
+ */
|
|
|
a3bc25 |
+ virObjectRef(data->conn);
|
|
|
a3bc25 |
+
|
|
|
a3bc25 |
+ if (virThreadCreate(&thread, false, qemuProcessReconnect, data) < 0) {
|
|
|
a3bc25 |
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
a3bc25 |
+ _("Could not create thread. QEMU initialization "
|
|
|
a3bc25 |
+ "might be incomplete"));
|
|
|
a3bc25 |
+ /* We can't spawn a thread and thus connect to monitor. Kill qemu.
|
|
|
a3bc25 |
+ * It's safe to call qemuProcessStop without a job here since there
|
|
|
a3bc25 |
+ * is no thread that could be doing anything else with the same domain
|
|
|
a3bc25 |
+ * object.
|
|
|
a3bc25 |
+ */
|
|
|
a3bc25 |
+ qemuProcessStop(src->driver, obj, VIR_DOMAIN_SHUTOFF_FAILED,
|
|
|
a3bc25 |
+ QEMU_ASYNC_JOB_NONE, 0);
|
|
|
a3bc25 |
+ qemuDomainRemoveInactive(src->driver, obj);
|
|
|
a3bc25 |
+
|
|
|
a3bc25 |
+ virDomainObjEndAPI(&obj);
|
|
|
a3bc25 |
+ virObjectUnref(data->conn);
|
|
|
a3bc25 |
+ VIR_FREE(data);
|
|
|
a3bc25 |
+ return -1;
|
|
|
a3bc25 |
+ }
|
|
|
a3bc25 |
+
|
|
|
a3bc25 |
+ return 0;
|
|
|
a3bc25 |
+}
|
|
|
a3bc25 |
+
|
|
|
a3bc25 |
+/**
|
|
|
a3bc25 |
+ * qemuProcessReconnectAll
|
|
|
a3bc25 |
+ *
|
|
|
a3bc25 |
+ * Try to re-open the resources for live VMs that we care
|
|
|
a3bc25 |
+ * about.
|
|
|
a3bc25 |
+ */
|
|
|
a3bc25 |
+void
|
|
|
a3bc25 |
+qemuProcessReconnectAll(virConnectPtr conn, virQEMUDriverPtr driver)
|
|
|
a3bc25 |
+{
|
|
|
a3bc25 |
+ struct qemuProcessReconnectData data = {.conn = conn, .driver = driver};
|
|
|
a3bc25 |
+ virDomainObjListForEach(driver->domains, qemuProcessReconnectHelper, &data);
|
|
|
a3bc25 |
+}
|
|
|
a3bc25 |
--
|
|
|
a3bc25 |
2.13.2
|
|
|
a3bc25 |
|