[libvirt] [PATCH] qemu_hotplug: try harder to eject media
John Ferlan
jferlan at redhat.com
Thu Jun 25 18:21:30 UTC 2015
On 06/25/2015 01:52 PM, Pavel Hrdina wrote:
> On Thu, Jun 25, 2015 at 12:06:49PM -0400, John Ferlan wrote:
>>
>>
>> On 06/23/2015 07:13 AM, Pavel Hrdina wrote:
>>> Some guests lock the tray and QEMU eject command will simply fail to
>>> eject the media. But the guest OS can handle this attempt to eject the
>>> media and can unlock the tray and open it. In this case, we should try
>>> again to actually eject the media.
>>>
>>> If the first attempt fails we will wait for tray to open and try again
>>> to eject the media, but if the second attempt fails, returns with error.
>>>
>>> Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1147471
>>>
>>> Signed-off-by: Pavel Hrdina <phrdina at redhat.com>
>>> ---
>>> src/qemu/qemu_hotplug.c | 33 ++++++++++++++++++++++++++++-----
>>> 1 file changed, 28 insertions(+), 5 deletions(-)
>>>
>>> diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c
>>> index 0628964..0cebaad 100644
>>> --- a/src/qemu/qemu_hotplug.c
>>> +++ b/src/qemu/qemu_hotplug.c
>>> @@ -201,13 +201,17 @@ qemuDomainChangeEjectableMedia(virQEMUDriverPtr driver,
>>> }
>>>
>>> if (ret < 0)
>>> - goto error;
>>> + VIR_DEBUG("tray is probably locked, wait for the guest to unlock "
>>> + "the tray and open it");
>>
>> There are more reasons than waiting for the unlock that would cause a
>> negative value to be returned, so this seems "dangerous"..
>
> Yes, I know that. That's why the code will wait for the event DEVICE_TRAY_MOVED
> and only if that event where emitted from QEMU, then we will tray again.
> Otherwise the original error from the first attempt will be used.
>
I didn't see DEVICE_TRAY_MOVED in this patch... The wait loop I see
below was VIR_DOMAIN_DISK_TRAY_OPEN.
>>
>> !mon
>> if json
>> !cmd
>> qemuMonitorJSONCommand failure
>> qemuMonitorJSONCheckError failure
>> else
>> virAsprintf Failure
>> qemuMonitorHMPCommand failure
>> c_strcasestr failure
>>
>>
>> Is there no way to determine that the failure was due to a locked tray
>> from the error message? The bz notes a DEVICE_TRAY_MOVED event, but I
>> don't see that here.
>
> Yes, there is a way, that we can parse the error message and wait only, if it
> contains the phrase "is locked". For that QEMU event we have a handler
> registered and this handler updates the disk->tray_status.
>
> IOW, checking the disk->tray_status is the same as waiting for DEVICE_TRAY_MOVED
> event.
>
Right and I didn't see that so it wasn't clear to me how this patch
fixed the problem....
>>
>> I would think that if this is "handle-able" from qemu/json only, then
>> the qemuMonitorJSONEjectMedia would be the best place to at least check
>> for this path of retry logic.
>>
>> You'll have the json error returned (per the link in bz comment 3):
>>
>> {"error": {"class": "GenericError", "desc": "Device 'cd' is locked"}}
>>
>> That should be parse-able by this code and either handled back in the
>> caller or in this code.
>
> Also HMP returns the same error, so it's definitely parse-able for both
> monitors. At first I didn't want to go through parsing the error message, but
> it makes sense to retry only in case, that the error message contains "is
> locked" message.
>
>>
>> Since qemuMonitorJSONEjectMedia doesn't list return values, perhaps use
>> "-1" to mean total failure, 0 to mean pseudo-success as it does today,
>> and 1 to indicate tray was locked and that the caller can/should retry.
>>
>
>
>
>> FWIW: By pseudo-success... I see that if 0 is currently returned, the
>> while loop will look for the "tray_status == VIR_DOMAIN_DISK_TRAY_OPEN"
>> and fail if not eventually seen
>>
>> Consider the following...
>>
>> bool retry_lock_tray = false;
>>
>> do {
>> qemuDomainObjEnterMonitor(driver, vm);
>> ret = qemuMonitorEjectMedia(priv->mon, driveAlias, force);
>> if (qemuDomainObjExitMonitor(driver, vm) < 0) {
>> ret = -1;
>> goto cleanup;
>> }
>> /* We already tried, but got the locked error again */
>> if (ret == 1 && retry_lock_tray) {
>> some sort of failure since you retried
>> ret = -1;
>> goto error;
>> }
>> /* We were locked, but haven't retried */
>> if (ret == 1) {
>> Wait some period of time for the DEVICE_TRAY_MOVED event
>> if event found
>> retry_lock_tray = true;
>> }
>> } while (retry_lock_tray);
>>
>> if (ret < 0)
>> goto error;
>>
>> if (ret == 1) {
>> ERROR tray locked by guest, never got DEVICE_TRAY_MOVED
>> ret = -1;
>> got error;
>> }
>>
>
> I'll post a v2.
>
>>
>>
>>>
>>> virObjectRef(vm);
>>> /* we don't want to report errors from media tray_open polling */
>>> while (retries) {
>>> - if (disk->tray_status == VIR_DOMAIN_DISK_TRAY_OPEN)
>>> + if (disk->tray_status == VIR_DOMAIN_DISK_TRAY_OPEN) {
>>> + ret = 0;
>>> + virResetLastError();
>>> break;
>>> + }
>>>
>>> retries--;
>>> virObjectUnlock(vm);
>>
>> If it's locked how would this loop ever succeed? That is how would the
>> tray open "magically" if it's locked and we haven't sent another eject
>> request?
>
> I don't understand this question?? The vm object is locked before we enter this
> function and this loop only unlock the vm object for the time it is waiting for
> the event to occur.
>
So given the patch that I read, I saw nothing that polled for
DEVICE_TRAY_MOVED, thus how would tray_status become TRAY_OPEN? I think
the answer is - it won't under this scenario, hence the code beyond it
which does the eject again under the assumption that the reason we had a
-1 return was because it was locked. That second eject doesn't go
through this wait for TRAY_OPEN event either, so hence my suggestion for
a do...while loop.
Or are you indicating that TRAY_OPEN setting occurs for a TRAY_MOVED.
It just wasn't clear from what I read in the patch
John
>>
>>> @@ -218,10 +222,29 @@ qemuDomainChangeEjectableMedia(virQEMUDriverPtr driver,
>>> virObjectUnref(vm);
>>>
>>> if (retries <= 0) {
>>> - virReportError(VIR_ERR_OPERATION_FAILED, "%s",
>>> - _("Unable to eject media"));
>>> - ret = -1;
>>> + /* Report a new error only if the first attempt don't fail and we don't
>>> + * receive VIR_DOMAIN_DISK_TRAY_OPEN event, otherwise report the error
>>> + * from first attempt. */
>>> + if (ret == 0) {
>>> + virReportError(VIR_ERR_OPERATION_FAILED, "%s",
>>> + _("Unable to eject media"));
>>> + ret = -1;
>>> + }
>>> goto error;
>>> + } else {
>>> + /* QEMU will send eject request to guest, but if the tray is locked,
>>> + * always returns with error. However the guest can handle the eject
>>> + * request, unlock the tray and open it. In case this happens, we
>>> + * should try to eject the media once more. */
>>> + qemuDomainObjEnterMonitor(driver, vm);
>>> + ret = qemuMonitorEjectMedia(priv->mon, driveAlias, force);
>>> + if (qemuDomainObjExitMonitor(driver, vm) < 0) {
>>> + ret = -1;
>>> + goto cleanup;
>>> + }
>>> +
>>> + if (ret < 0)
>>> + goto error;
>>> }
>>>
>>> if (!virStorageSourceIsEmpty(newsrc)) {
>>>
More information about the libvir-list
mailing list