[PATCH 3/3] qemu: Update paused reason on unexpected 'STOP' event

Michal Privoznik mprivozn at redhat.com
Fri Mar 19 13:01:12 UTC 2021


On 3/18/21 6:40 PM, Peter Krempa wrote:
> The qemu STOP event doesn't really report why the VM was stopped. In
> certain cases we do expect this by storing the expectation in the
> private data.
> 
> In cases we've encountered an unexpected STOP event we can offload to
> the event thread a job to refresh the state using 'query-status'.
> 
> For all other paused reasons which are expected we keep the original
> logic.
> 
> Signed-off-by: Peter Krempa <pkrempa at redhat.com>
> ---
>   src/qemu/qemu_domain.c  |  1 +
>   src/qemu/qemu_domain.h  |  1 +
>   src/qemu/qemu_driver.c  | 37 +++++++++++++++++++++++++++++++++++++
>   src/qemu/qemu_process.c | 22 +++++++++++++++++++---
>   4 files changed, 58 insertions(+), 3 deletions(-)
> 
> diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c
> index dde9ba92c6..106a292ef3 100644
> --- a/src/qemu/qemu_domain.c
> +++ b/src/qemu/qemu_domain.c
> @@ -11049,6 +11049,7 @@ qemuProcessEventFree(struct qemuProcessEvent *event)
>           virObjectUnref(event->data);
>           break;
>       case QEMU_PROCESS_EVENT_PR_DISCONNECT:
> +    case QEMU_PROCESS_EVENT_STOP:
>       case QEMU_PROCESS_EVENT_LAST:
>           break;
>       }
> diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h
> index 949307229b..b057e94df0 100644
> --- a/src/qemu/qemu_domain.h
> +++ b/src/qemu/qemu_domain.h
> @@ -441,6 +441,7 @@ typedef enum {
>       QEMU_PROCESS_EVENT_PR_DISCONNECT,
>       QEMU_PROCESS_EVENT_RDMA_GID_STATUS_CHANGED,
>       QEMU_PROCESS_EVENT_GUEST_CRASHLOADED,
> +    QEMU_PROCESS_EVENT_STOP,
> 
>       QEMU_PROCESS_EVENT_LAST
>   } qemuProcessEventType;
> diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
> index f3f8caab44..53be363338 100644
> --- a/src/qemu/qemu_driver.c
> +++ b/src/qemu/qemu_driver.c
> @@ -4316,6 +4316,40 @@ processGuestCrashloadedEvent(virQEMUDriverPtr driver,
>   }
> 
> 
> +static void
> +processGuestStopEvent(virDomainObjPtr vm)
> +{
> +    qemuDomainObjPrivatePtr priv = vm->privateData;
> +    virObjectEventPtr event = NULL;
> +    virDomainPausedReason reason = VIR_DOMAIN_PAUSED_UNKNOWN;
> +
> +    if (qemuDomainObjBeginJob(priv->driver, vm, QEMU_JOB_MODIFY) == 0) {
> +        bool running;
> +        int rc;

While @vm is locked when this function is called it was unlocked for a 
brief moment. Therefore, another thread might have changed its state. 
Hence, virDomainObjIsActive() is a must here.

> +
> +        qemuDomainObjEnterMonitor(priv->driver, vm);
> +
> +        rc = qemuMonitorGetStatus(priv->mon, &running, &reason);
> +
> +        /* update reason only on successful state update */
> +        if (qemuDomainObjExitMonitor(priv->driver, vm) < 0)
> +            rc = -1;
> +
> +        qemuDomainObjEndJob(priv->driver, vm);
> +
> +        if (rc < 0)
> +            return;
> +    }
> +
> +    virDomainObjSetState(vm, VIR_DOMAIN_PAUSED, reason);
> +

Again, another thread might have just resumed vCPUS. Setting this here 
might lead to wrong state. What if we called SetState() iff !running && 
reason? I mean, SetState() was called from the in-loop handler already 
and this is here only to report reason (if any).

> +    event = virDomainEventLifecycleNewFromObj(vm, VIR_DOMAIN_EVENT_SUSPENDED,
> +                                              qemuDomainPausedReasonToSuspendedEvent(reason));
> +
> +    virObjectEventStateQueue(priv->driver->domainEventState, event);
> +}
> +
> +
>   static void qemuProcessEventHandler(void *data, void *opaque)
>   {
>       struct qemuProcessEvent *processEvent = data;
> @@ -4365,6 +4399,9 @@ static void qemuProcessEventHandler(void *data, void *opaque)
>       case QEMU_PROCESS_EVENT_GUEST_CRASHLOADED:
>           processGuestCrashloadedEvent(driver, vm);
>           break;
> +    case QEMU_PROCESS_EVENT_STOP:
> +        processGuestStopEvent(vm);
> +        break;
>       case QEMU_PROCESS_EVENT_LAST:
>           break;
>       }
> diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c
> index 5f31260221..7003c92046 100644
> --- a/src/qemu/qemu_process.c
> +++ b/src/qemu/qemu_process.c
> @@ -689,10 +689,26 @@ qemuProcessHandleStop(qemuMonitorPtr mon G_GNUC_UNUSED,
>           if (priv->signalStop)
>               virDomainObjBroadcast(vm);
> 
> +        /* In case of VIR_DOMAIN_PAUSED_UNKNOWN we'll update it later, but we
> +         * must set the state to PAUSED right away. We delay the event until
> +         * the update. */
>           virDomainObjSetState(vm, VIR_DOMAIN_PAUSED, reason);
> -        event = virDomainEventLifecycleNewFromObj(vm,
> -                                                  VIR_DOMAIN_EVENT_SUSPENDED,
> -                                                  detail);
> +        if (reason == VIR_DOMAIN_PAUSED_UNKNOWN) {
> +            /* offload unknown state to thread updating state */
> +            struct qemuProcessEvent *processEvent = g_new0(struct qemuProcessEvent, 1);
> +
> +            processEvent->eventType = QEMU_PROCESS_EVENT_STOP;
> +            processEvent->vm = virObjectRef(vm);
> +
> +            if (virThreadPoolSendJob(driver->workerPool, 0, processEvent) < 0) {
> +                virObjectUnref(vm);
> +                qemuProcessEventFree(processEvent);
> +            }
> +        } else {
> +            event = virDomainEventLifecycleNewFromObj(vm,
> +                                                      VIR_DOMAIN_EVENT_SUSPENDED,
> +                                                      detail);
> +        }
> 
>           VIR_FREE(priv->lockState);
>           if (virDomainLockProcessPause(driver->lockManager, vm, &priv->lockState) < 0)
> 

Michal




More information about the libvir-list mailing list