[libvirt] [PATCH v4 3/3] qemuDomainPMSuspendForDuration: check for wake-up support

Daniel Henrique Barboza danielhb413 at gmail.com
Thu Apr 25 11:31:03 UTC 2019



On 4/25/19 6:46 AM, Michal Privoznik wrote:
> On 4/24/19 11:16 PM, Daniel Henrique Barboza wrote:
>> If the current QEMU guest can't wake up from suspend properly,
>> and we are able to determine that, avoid suspending the guest
>> at all. To be able to determine this support, QEMU needs to
>> implement the 'query-current-machine' QMP call. This is reflected
>> by the QEMU_CAPS_QUERY_CURRENT_MACHINE cap.
>>
>> If the cap is enabled, a new function qemuDomainProbeQMPCurrentMachine
>> is called. This is wrapper for qemuMonitorGetCurrentMachineInfo,
>> where the 'wakeup-suspend-support' flag is retrieved from
>> 'query-current-machine'. If wakeupSuspendSupport is true,
>> proceed with the regular flow of qemuDomainPMSuspendForDuration.
>>
>> The absence of QEMU_CAPS_QUERY_CURRENT_MACHINE indicates that
>> we're dealing with a QEMU version older than 4.0 (which implements
>> the required QMP API). In this case, proceed as usual with the
>> suspend logic of qemuDomainPMSuspendForDuration, since we can't
>> assume whether the guest has support or not.
>>
>> Fixes: https://bugs.launchpad.net/ubuntu/+source/qemu/+bug/1759509
>> Reported-by: Balamuruhan S <bala24 at linux.vnet.ibm.com>
>> Signed-off-by: Daniel Henrique Barboza <danielhb413 at gmail.com>
>> ---
>>   src/qemu/qemu_driver.c | 54 ++++++++++++++++++++++++++++++++++++++++++
>>   1 file changed, 54 insertions(+)
>>
>> diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
>> index 31d8647eee..d584f6ae66 100644
>> --- a/src/qemu/qemu_driver.c
>> +++ b/src/qemu/qemu_driver.c
>> @@ -19145,6 +19145,35 @@ qemuDomainGetCPUStats(virDomainPtr domain,
>>       return ret;
>>   }
>>   +static int
>> +qemuDomainProbeQMPCurrentMachine(virQEMUDriverPtr driver,
>> +                                 virDomainObjPtr vm,
>> +                                 bool *enabled)
>> +{
>> +    qemuMonitorCurrentMachineInfo info = { 0 };
>> +    qemuDomainObjPrivatePtr priv;
>> +    int ret = -1;
>> +
>> +    *enabled = false;
>> +    priv = vm->privateData;
>> +
>> +    qemuDomainObjEnterMonitor(driver, vm);
>> +
>> +    if (qemuMonitorGetCurrentMachineInfo(priv->mon, &info) < 0)
>> +        goto cleanup;
>> +
>> +    ret = 0;
>> +
>> +    if (info.wakeupSuspendSupport)
>> +        *enabled = true;
>> +
>> + cleanup:
>> +    if (qemuDomainObjExitMonitor(driver, vm) < 0)
>> +        ret = -1;
>> +
>> +    return ret;
>
> This can be simplified.
>
>> +}
>> +
>>   static int
>>   qemuDomainPMSuspendForDuration(virDomainPtr dom,
>>                                  unsigned int target,
>> @@ -19152,6 +19181,7 @@ qemuDomainPMSuspendForDuration(virDomainPtr dom,
>>                                  unsigned int flags)
>>   {
>>       virQEMUDriverPtr driver = dom->conn->privateData;
>> +    qemuDomainObjPrivatePtr priv;
>>       virDomainObjPtr vm;
>>       qemuAgentPtr agent;
>>       int ret = -1;
>> @@ -19185,6 +19215,30 @@ qemuDomainPMSuspendForDuration(virDomainPtr 
>> dom,
>>       if (virDomainObjCheckActive(vm) < 0)
>>           goto endjob;
>>   +    /*
>> +     * The case we want to handle here is when QEMU has the API (i.e.
>> +     * QEMU_CAPS_QUERY_CURRENT_MACHINE is set). Otherwise, do not 
>> interfere
>> +     * with the suspend process. This means that existing running 
>> domains,
>> +     * that don't know about this cap, will keep their old behavior of
>> +     * suspending 'in the dark'.
>> +     */
>> +    priv = vm->privateData;
>> +    if (virQEMUCapsGet(priv->qemuCaps, 
>> QEMU_CAPS_QUERY_CURRENT_MACHINE)) {
>> +        bool enabled;
>
> s/enabled/wakeupSupported/ to make it a bit more obvious what it is 
> we're querying for here.
>
>> +
>> +        if (qemuDomainProbeQMPCurrentMachine(driver, vm, &enabled) < 
>> 0) {
>
> This is not sufficient. A few lines above (not visible in the patch 
> context) we grab agent job but this enters the monitor then. This is 
> dangerous because it means we're talking on monitor without locking it 
> and thus might interfere with some other thread. Either we grab the 
> correct job depending whether we are going to talk on monitor or not, 
> or we can just grab both jobs even if qemu doesn't have the capability.

Thanks for the explanation and fixing before pushing. I was under the 
impression
that the now extinct line:


"if (qemuDomainObjBeginAgentJob(driver, vm, QEMU_AGENT_JOB_MODIFY) < 0)"


Was enough to hold the lock context for all the VM resources, not only 
the QEMU
agent. Mostly because I saw the usage of:

     if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MODIFY) < 0)

Right below, in qemuDomainPMWakeup(), that access the monitor in the same
function.

But now that I'm putting it all together I noticed that the functions
are all different .... so qemuDomainObjBeginJob() locks the monitor,
qemuDomainObjBeginAgentJob() locks the agent, and
qemuDomainObjBeginJobWithAgent() can lock both, depending if
job isn't QEMU_JOB_NONE. Is this somewhat close to the actual usage?


Thanks,

DHB



>
> The cleaner solution would be the former.
>
>> + virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
>> +                           _("Error executing query-current-machine 
>> call"));
>
> No need to overwrite an error here. I mean, 
> qemuDomainProbeQMPCurrentMachine() will report error on failure (not 
> explicitly, but the functions it's calling do report error.
>
>> +            goto endjob;
>> +        }
>> +
>> +        if (!enabled) {
>> +            virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
>> +                          _("Domain does not have suspend support"));
>> +            goto endjob;
>> +        }
>> +    }
>> +
>>       if (vm->def->pm.s3 || vm->def->pm.s4) {
>>           if (vm->def->pm.s3 == VIR_TRISTATE_BOOL_NO &&
>>               (target == VIR_NODE_SUSPEND_TARGET_MEM ||
>>
>
> I'll fix the issues before pushing.
>
> Michal




More information about the libvir-list mailing list