Re: [libvirt] [PATCHv3 01/16] blockjob: add API for async virDomainBlockJobAbort

2012-04-09 Thread Eric Blake
On 04/09/2012 02:45 PM, Laine Stump wrote:
> On 04/06/2012 02:29 PM, Eric Blake wrote:
>> From: Adam Litke 
>>
>> Qemu has changed the semantics of the "block_job_cancel" API.  The original
>> qed implementation (pretty much only backported to RHEL 6.2 qemu) was
>> synchronous (ie. upon command completion, the operation was guaranteed to
>> be completely stopped).  With the new semantics going into qemu 1.1 for
>> qcow2, a "block_job_cancel" merely requests that the operation be cancelled
>> and an event is triggered once the cancellation request has been honored.
>>
>>
>> @@ -3617,6 +3626,7 @@ typedef void 
>> (*virConnectDomainEventGraphicsCallback)(virConnectPtr conn,
>>  typedef enum {
>>  VIR_DOMAIN_BLOCK_JOB_COMPLETED = 0,
>>  VIR_DOMAIN_BLOCK_JOB_FAILED = 1,
>> +VIR_DOMAIN_BLOCK_JOB_CANCELED = 2,
> 
> 
> You know, this is one case where I think the Queen's English has it
> right - something just seems *wrong* about spelling CANCELED with one "L"...
> 

I think the consensus was to add aliases so that either spelling would
work, but that will have to be another patch for another day.

>>   *
>> + * By default, this function performs a synchronous operation and the caller
>> + * may assume that the operation has completed when 0 is returned.  However,
>> + * BlockJob operations may take a long time to complete, and during this 
>> time
>> + * further domain interactions may be unresponsive.  To avoid this problem,
>> + * pass VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC in the @flags argument to enable
>> + * asynchronous behavior.  Either way, when the job has been cancelled, a
>> + * BlockJob event will be emitted, with status 
>> VIR_DOMAIN_BLOCK_JOB_CANCELLED.

But it would help if I can spell it consistently within a single patch :)

Also, I'm going to update this to mention that the ASYNC flag is not
recognized if qemu is too old, and that the event is not guaranteed
(along with the polling loop that the user can rely on if they don't
want to trust the event).


>> @@ -785,11 +789,19 @@ static void 
>> qemuMonitorJSONHandleBlockJob(qemuMonitorPtr mon, virJSONValuePtr da
>>  if (STREQ(type_str, "stream"))
>>  type = VIR_DOMAIN_BLOCK_JOB_TYPE_PULL;
>>
>> -if (offset != 0 && offset == len)
>> -status = VIR_DOMAIN_BLOCK_JOB_COMPLETED;
>> +switch (event) {
>> +case VIR_DOMAIN_BLOCK_JOB_COMPLETED:
>> +/* Make sure the whole device has been processed */
>> +if (offset != len)
>> +event = VIR_DOMAIN_BLOCK_JOB_FAILED;
>> +break;
> 
> 
> Okay, so if you're talking to an "old" qemu, it will return a COMPLETED
> event with offset < length to indicate failure, which is handled here...

Actually, the old qemu would never return an event on cancellation
(except in the inherent race where you request a cancel, but the job
completes normally before qemu can act on your request).  Rather, both
the old qemu and new qemu return the 'BLOCK_JOB_COMPLETE' event on
completion, and you must then look at offset/len to decide if it was a
successful completion or a failure.  It is only the new qemu that adds
the new 'BLOCK_JOB_CANCELLED' event (this time, we have the qemu
spelling, and it is UK spelling).

> 
> 
>> +case VIR_DOMAIN_BLOCK_JOB_FAILED:
> 
> 
> and if it's a new qemu, it will return FAILED explicitly, which is
> handled here. What happens when it's an old libvirt and new qemu? (not
> that there's anything libvirt could do about it, I'm just curious)

Actually, new qemu only emits two types of events (COMPLETE and
CANCELLED), and this helper function converts those two types into three
user-visible types (COMPLETED, FAILED, CANCELED).  I think what I should
do is mark the unreachable case statement accordingly.

> 
> 
>> +case VIR_DOMAIN_BLOCK_JOB_CANCELED:
>> +break;
>> +}
>>
>>  out:
>> -qemuMonitorEmitBlockJob(mon, device, type, status);
>> +qemuMonitorEmitBlockJob(mon, device, type, event);
>>  }
>>
>>  static void
>> @@ -832,6 +844,22 @@ qemuMonitorJSONHandlePMSuspend(qemuMonitorPtr mon,
>>  qemuMonitorEmitPMSuspend(mon);
>>  }
>>
>> +static void
>> +qemuMonitorJSONHandleBlockJobCompleted(qemuMonitorPtr mon,
>> +   virJSONValuePtr data)
>> +{
>> +qemuMonitorJSONHandleBlockJobImpl(mon, data,
>> +  VIR_DOMAIN_BLOCK_JOB_COMPLETED);
>> +}
>> +
>> +static void
>> +qemuMonitorJSONHandleBlockJobCanceled(qemuMonitorPtr mon,
>> +   virJSONValuePtr data)
>> +{
>> +qemuMonitorJSONHandleBlockJobImpl(mon, data,
>> +  VIR_DOMAIN_BLOCK_JOB_CANCELED);
>> +}

Here's our two event handlers that both call into the helper function
that translates the 2 qemu events into 3 libvirt events.

>> @@ -7613,19 +7621,23 @@ cmdBlockJob(vshControl *ctl, const vshCmd *cmd)
>>  virDomainBlockJobInfo info;
>>  const char *type;
>>  int ret;
>> +

Re: [libvirt] [PATCHv3 01/16] blockjob: add API for async virDomainBlockJobAbort

2012-04-09 Thread Laine Stump
On 04/06/2012 02:29 PM, Eric Blake wrote:
> From: Adam Litke 
>
> Qemu has changed the semantics of the "block_job_cancel" API.  The original
> qed implementation (pretty much only backported to RHEL 6.2 qemu) was
> synchronous (ie. upon command completion, the operation was guaranteed to
> be completely stopped).  With the new semantics going into qemu 1.1 for
> qcow2, a "block_job_cancel" merely requests that the operation be cancelled
> and an event is triggered once the cancellation request has been honored.
>
> To adopt the new semantics while preserving compatibility the following
> updates are made to the virDomainBlockJob API:
>
> A new block job event type VIR_DOMAIN_BLOCK_JOB_CANCELLED is recognized by
> libvirt.  Regardless of the flags used with virDomainBlockJobAbort, this event
> will be raised whenever it is received from qemu.  This event indicates that a
> block job has been successfully cancelled.  For now, libvirt does not try
> to synthesize this event if using an older qemu that did not generate it.
>
> A new extension flag VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC is added to the
> virDomainBlockJobAbort API.  When enabled, this function will operate
> asynchronously (ie, it can return before the job has actually been cancelled).
> When the API is used in this mode, it is the responsibility of the caller to
> wait for a VIR_DOMAIN_BLOCK_JOB_CANCELLED event or poll via the
> virDomainGetBlockJobInfo API to check the cancellation status; this flag
> is an error if it is not known if the hypervisor supports asynchronous cancel.
>
> This patch also exposes the new flag through virsh.
>
> Signed-off-by: Adam Litke 
> Cc: Stefan Hajnoczi 
> Signed-off-by: Eric Blake 
> ---
>  include/libvirt/libvirt.h.in |   10 +
>  src/libvirt.c|   10 -
>  src/qemu/qemu_monitor_json.c |   42 +++--
>  tools/virsh.c|   46 ++---
>  tools/virsh.pod  |9 +--
>  5 files changed, 89 insertions(+), 28 deletions(-)
>
> diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in
> index 499dcd4..97ad99d 100644
> --- a/include/libvirt/libvirt.h.in
> +++ b/include/libvirt/libvirt.h.in
> @@ -1946,6 +1946,15 @@ typedef enum {
>  #endif
>  } virDomainBlockJobType;
>
> +/**
> + * virDomainBlockJobAbortFlags:
> + *
> + * VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC: Request only, do not wait for completion
> + */
> +typedef enum {
> +VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC = 1 << 0,
> +} virDomainBlockJobAbortFlags;
> +
>  /* An iterator for monitoring block job operations */
>  typedef unsigned long long virDomainBlockJobCursor;
>
> @@ -3617,6 +3626,7 @@ typedef void 
> (*virConnectDomainEventGraphicsCallback)(virConnectPtr conn,
>  typedef enum {
>  VIR_DOMAIN_BLOCK_JOB_COMPLETED = 0,
>  VIR_DOMAIN_BLOCK_JOB_FAILED = 1,
> +VIR_DOMAIN_BLOCK_JOB_CANCELED = 2,


You know, this is one case where I think the Queen's English has it
right - something just seems *wrong* about spelling CANCELED with one "L"...


>
>  #ifdef VIR_ENUM_SENTINELS
>  VIR_DOMAIN_BLOCK_JOB_LAST
> diff --git a/src/libvirt.c b/src/libvirt.c
> index 16d1fd5..af22232 100644
> --- a/src/libvirt.c
> +++ b/src/libvirt.c
> @@ -17902,7 +17902,7 @@ error:
>   * virDomainBlockJobAbort:
>   * @dom: pointer to domain object
>   * @disk: path to the block device, or device shorthand
> - * @flags: extra flags; not used yet, so callers should always pass 0
> + * @flags: bitwise-OR of virDomainBlockJobAbortFlags
>   *
>   * Cancel the active block job on the given disk.
>   *
> @@ -17913,6 +17913,14 @@ error:
>   * can be found by calling virDomainGetXMLDesc() and inspecting
>   * elements within //domain/devices/disk.
>   *
> + * By default, this function performs a synchronous operation and the caller
> + * may assume that the operation has completed when 0 is returned.  However,
> + * BlockJob operations may take a long time to complete, and during this time
> + * further domain interactions may be unresponsive.  To avoid this problem,
> + * pass VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC in the @flags argument to enable
> + * asynchronous behavior.  Either way, when the job has been cancelled, a
> + * BlockJob event will be emitted, with status 
> VIR_DOMAIN_BLOCK_JOB_CANCELLED.
> + *
>   * Returns -1 in case of failure, 0 when successful.
>   */
>  int virDomainBlockJobAbort(virDomainPtr dom, const char *disk,
> diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c
> index d09d779..c9f0f0c 100644
> --- a/src/qemu/qemu_monitor_json.c
> +++ b/src/qemu/qemu_monitor_json.c
> @@ -58,13 +58,14 @@ static void qemuMonitorJSONHandleIOError(qemuMonitorPtr 
> mon, virJSONValuePtr dat
>  static void qemuMonitorJSONHandleVNCConnect(qemuMonitorPtr mon, 
> virJSONValuePtr data);
>  static void qemuMonitorJSONHandleVNCInitialize(qemuMonitorPtr mon, 
> virJSONValuePtr data);
>  static void qemuMonitorJSONHandleVNCDisconnect(qemuMonitor