From: Max Reitz <mreitz@redhat.com>
To: Eric Blake <eblake@redhat.com>, qemu-devel@nongnu.org
Cc: Kevin Wolf <kwolf@redhat.com>, Peter Lieven <pl@kamp.de>,
Stefan Hajnoczi <stefanha@redhat.com>
Subject: Re: [Qemu-devel] [PATCH 17/21] qcow2: Use intermediate helper CB for amend
Date: Wed, 12 Nov 2014 10:10:11 +0100 [thread overview]
Message-ID: <546323F3.8030209@redhat.com> (raw)
In-Reply-To: <546279FD.80507@redhat.com>
On 2014-11-11 at 22:05, Eric Blake wrote:
> On 11/10/2014 06:45 AM, Max Reitz wrote:
>> If there is more than one time-consuming operation to be performed for
>> qcow2_amend_options(), we need an intermediate CB which coordinates the
>> progress of the individual operations and passes the result to the
>> original status callback.
>>
>> Signed-off-by: Max Reitz <mreitz@redhat.com>
>> ---
>> block/qcow2.c | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
>> 1 file changed, 75 insertions(+), 1 deletion(-)
> Getting trickier to review.
Yes, and 18 is the worst.
>> diff --git a/block/qcow2.c b/block/qcow2.c
>> index eaef251..e6b93d1 100644
>> --- a/block/qcow2.c
>> +++ b/block/qcow2.c
>> @@ -2655,6 +2655,71 @@ static int qcow2_downgrade(BlockDriverState *bs, int target_version,
>> return 0;
>> }
>>
>> +typedef enum Qcow2AmendOperation {
>> + /* This is the value Qcow2AmendHelperCBInfo::last_operation will be
>> + * statically initialized to so that the helper CB can discern the first
>> + * invocation from an operation change */
>> + QCOW2_NO_OPERATION = 0,
>> +
>> + QCOW2_DOWNGRADING,
>> +} Qcow2AmendOperation;
> So for this patch, you still have just one operation, but later in the
> series, you add a second (and the goal of THIS patch is that it will
> work even if there are 3 or more operations, even though this series
> doesn't add that many).
Right.
>> +static void qcow2_amend_helper_cb(BlockDriverState *bs, int64_t offset,
>> + int64_t total_work_size, void *opaque)
> indentation looks off
Right, will fix.
>> +{
>> + Qcow2AmendHelperCBInfo *info = opaque;
>> + int64_t current_work_size;
>> + int64_t projected_work_size;
> Worth asserting that info->total_operations is non-zero? Or is there
> ever a valid case for calling the callback even when there are no
> sub-operations, and therefore we are automatically complete (offset ==
> total_work_size)?
No, the driver does not have to call the status CB; qemu-img amend will
mind that case by first printing 0 %, then invoking the amend operation,
and then printing 100 %. Will add an assertion.
>> +
>> + if (info->current_operation != info->last_operation) {
>> + if (info->last_operation != QCOW2_NO_OPERATION) {
>> + info->offset_completed += info->last_work_size;
>> + info->operations_completed++;
>> + }
> Would it be any easier to guarantee that we come to 100% completion by
> requiring the coordinator to pass a final completion callback? [1]
> info->current_operation = QCOW2_NO_OPERATION;
> cb(bs, 0, 0, info)
No, because the amend CB does not have to be called on completion;
img_amend() in qemu-img.c takes care of that case.
>> +
>> + info->last_operation = info->current_operation;
>> + }
>> +
>> + info->last_work_size = total_work_size;
> Took me a while to realize that total_work_size is the incoming
> (estimated) total size for the current sub-operation, and not the total
> over the combination of all sub-operations...
Ah, right, I'll change the variable name, maybe to operation_work_size
or something like that.
>> +
>> + current_work_size = info->offset_completed + total_work_size;
>> +
>> + /* current_work_size is the total work size for (operations_completed + 1)
> but this comment helped.
>
>> + * operations (which includes this one), so multiply it by the number of
>> + * operations not covered and divide it by the number of operations
>> + * covered to get a projection for the operations not covered */
>> + projected_work_size = current_work_size * (info->total_operations -
>> + info->operations_completed - 1)
>> + / (info->operations_completed + 1);
> So, when there is just one sub-operation (which is the case until later
> patches add a second), this results in the following calculation for ALL
> calls during the intermediate steps of the sub-operation:
>
> projected_work_size = total_work_size * (1 - 0 - 1) / (0 + 1)
>
> that is, we are projecting 0 additional work because we have zero
> additional stages to complete. Am I correct that we will never enter
> the callback in a state where
> info->operations_completed==info->total_operations?
Yes, we won't.
> (because if we do,
> you'd have a computation of final_size * (1 - 1 - 1) / (1 + 1) which
> looks weird). Worth an assert()?
assert()s are always worth it.
> Then again, my proposal above [1] to
> guarantee a 100% completion by use of a final cleanup callback would
> indeed reach the point where operations_completed==total_operations.
>
>> +
>> + info->original_status_cb(bs, info->offset_completed + offset,
>> + current_work_size + projected_work_size,
>> + info->original_cb_opaque);
> So, as long as we don't add a second phase, this is strictly equivalent
> to calling the original callback with the original offset (since
> info->offset_completed remains 0) and original work size (since
> projected_work_size remains 0). That part works fine.
>
> Let's see what happens if we had three phases. To make it more
> interesting, let's pick some numbers - how about the first phase
> progresses from 0-10, the second from 0-100, and the third from 0-10,
> and where none of the sub-operations change predicted total_work_size.
> The caller would first set info->current_operation to 1, then call the
> callback a few times; how about twice with 5/10 and 10/10. For both
> calls, current_work_size is 0+10, then projected_work_size is
> 10*(3-0-1)/(0+1) == 20, and we call the original callback with
> (0+5)/(10+20) and (0+10)/(10+20). Pretty good (5/30 and 10/30 are right
> on if the first sub-command is exactly one-third of the time of the
> overall command; and even if it is not, it still shows reasonable progress).
>
> Then we move on to the second sub-command where the coordinator updates
> info->current_operation to 2 before triggering several callbacks; let's
> suppose it reports at 0/100, 30/100, 60/100, and 100/100. The first
> call updates info to track that we've detected a change in sub-command
> (offset_completed is now 10, operations_completed is now 1). Then for
> all four calls, current_work_size is 10+100, and projected_work_size is
> 110*(3-1-1)/(1+1) == 55. So we call the original callback with
> (10+0)/(110+55), (10+30)/(110+55), (10+60)/(110+55), (10+100)/(110+55).
> The first report of 10/165 looks like we jumped backwards (much smaller
> progress than our previous report of 10/30), but that's merely a
> representation that this phase is estimating a larger total_work count,
> and we have no way of correlating whether 1 unit of work count in each
> phase is equivalent to an equal amount of time. But by the end, we
> report 110/165, which is spot on for being two-thirds complete.
>
> Another assignment to info->current_operation, and a couple more
> callbacks; let's again use 5/10 and 10/10. The first callback updates
> info (offset_completed is now 110, operations_completed is now 2). For
> each call, current_work_size is 110+10, and projected_work_size is
> 120*(3-2-1/(2+1) == 0. We call the original callback with
> (120+5)/(120+10) and (120+10)/(120+10). We've done a very rapid jump
> from 2/3 to 125/130, but end the overall operation with the two values
> equal. So the function is not very smooth, but at least it is as good
> an estimate as possible along each stage of the operation, and we never
> violate the premise of reporting equal values until all sub-commands are
> complete.
Yes, it's not pretty but it's the best we can do without either
hard-coding some estimations or trying some kind of dry-run to determine
each operation's work size beforehand.
>> +}
>> +
>> static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
>> BlockDriverAmendStatusCB *status_cb,
>> void *cb_opaque)
>> @@ -2669,6 +2734,7 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
>> bool encrypt;
>> int ret;
>> QemuOptDesc *desc = opts->list->desc;
>> + Qcow2AmendHelperCBInfo helper_cb_info;
>>
>> while (desc && desc->name) {
>> if (!qemu_opt_find(opts, desc->name)) {
>> @@ -2726,6 +2792,12 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
>> desc++;
>> }
>>
>> + helper_cb_info = (Qcow2AmendHelperCBInfo){
>> + .original_status_cb = status_cb,
>> + .original_cb_opaque = cb_opaque,
>> + .total_operations = (new_version < old_version)
>> + };
> Slick.
>
>> +
>> /* Upgrade first (some features may require compat=1.1) */
>> if (new_version > old_version) {
>> s->qcow_version = new_version;
>> @@ -2784,7 +2856,9 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
>>
>> /* Downgrade last (so unsupported features can be removed before) */
>> if (new_version < old_version) {
>> - ret = qcow2_downgrade(bs, new_version, status_cb, cb_opaque);
>> + helper_cb_info.current_operation = QCOW2_DOWNGRADING;
>> + ret = qcow2_downgrade(bs, new_version, &qcow2_amend_helper_cb,
>> + &helper_cb_info);
> Looks correct to me. Other than the indentation issue and possible
> addition of some asserts, this is good to go.
>
> Reviewed-by: Eric Blake <eblake@redhat.com>
Thanks!
Max
next prev parent reply other threads:[~2014-11-12 9:10 UTC|newest]
Thread overview: 75+ messages / expand[flat|nested] mbox.gz Atom feed top
2014-11-10 13:45 [Qemu-devel] [PATCH 00/21] qcow2: Support refcount orders != 4 Max Reitz
2014-11-10 13:45 ` [Qemu-devel] [PATCH 01/21] qcow2: Add two new fields to BDRVQcowState Max Reitz
2014-11-10 19:00 ` Eric Blake
2014-11-10 13:45 ` [Qemu-devel] [PATCH 02/21] qcow2: Add refcount_width to format-specific info Max Reitz
2014-11-10 19:06 ` Eric Blake
2014-11-11 8:11 ` Max Reitz
2014-11-11 15:49 ` Eric Blake
2014-11-10 13:45 ` [Qemu-devel] [PATCH 03/21] qcow2: Use 64 bits for refcount values Max Reitz
2014-11-10 20:59 ` Eric Blake
2014-11-11 8:12 ` Max Reitz
2014-11-11 9:22 ` Kevin Wolf
2014-11-11 9:25 ` Max Reitz
2014-11-11 9:26 ` Max Reitz
2014-11-11 9:36 ` Kevin Wolf
2014-11-10 13:45 ` [Qemu-devel] [PATCH 04/21] qcow2: Respect error in qcow2_alloc_bytes() Max Reitz
2014-11-10 21:05 ` Eric Blake
2014-11-10 13:45 ` [Qemu-devel] [PATCH 05/21] qcow2: Refcount overflow and qcow2_alloc_bytes() Max Reitz
2014-11-10 21:12 ` Eric Blake
2014-11-11 8:22 ` Max Reitz
2014-11-11 16:13 ` Eric Blake
2014-11-11 16:18 ` Max Reitz
2014-11-11 19:49 ` Eric Blake
2014-11-12 8:52 ` Max Reitz
2014-11-10 13:45 ` [Qemu-devel] [PATCH 06/21] qcow2: Helper function for refcount modification Max Reitz
2014-11-10 22:30 ` Eric Blake
2014-11-11 8:35 ` Max Reitz
2014-11-11 9:43 ` Max Reitz
2014-11-11 10:56 ` Max Reitz
2014-11-10 13:45 ` [Qemu-devel] [PATCH 07/21] qcow2: Helper for refcount array size calculation Max Reitz
2014-11-10 22:49 ` Eric Blake
2014-11-11 8:37 ` Max Reitz
2014-11-11 10:08 ` Max Reitz
2014-11-10 13:45 ` [Qemu-devel] [PATCH 08/21] qcow2: More helpers for refcount modification Max Reitz
2014-11-11 0:29 ` Eric Blake
2014-11-11 8:42 ` Max Reitz
2014-11-10 13:45 ` [Qemu-devel] [PATCH 09/21] qcow2: Open images with refcount order != 4 Max Reitz
2014-11-10 17:03 ` Eric Blake
2014-11-10 17:06 ` Max Reitz
2014-11-10 13:45 ` [Qemu-devel] [PATCH 10/21] qcow2: refcount_order parameter for qcow2_create2 Max Reitz
2014-11-11 5:40 ` Eric Blake
2014-11-11 8:48 ` Max Reitz
2014-11-10 13:45 ` [Qemu-devel] [PATCH 11/21] iotests: Prepare for refcount_width option Max Reitz
2014-11-11 17:57 ` Eric Blake
2014-11-12 8:41 ` Max Reitz
2014-11-10 13:45 ` [Qemu-devel] [PATCH 12/21] qcow2: Allow creation with refcount order != 4 Max Reitz
2014-11-11 18:05 ` Eric Blake
2014-11-12 8:47 ` Max Reitz
2014-11-10 13:45 ` [Qemu-devel] [PATCH 13/21] block: Add opaque value to the amend CB Max Reitz
2014-11-11 18:08 ` Eric Blake
2014-11-10 13:45 ` [Qemu-devel] [PATCH 14/21] qcow2: Use error_report() in qcow2_amend_options() Max Reitz
2014-11-11 18:11 ` Eric Blake
2014-11-12 8:47 ` Max Reitz
2014-11-10 13:45 ` [Qemu-devel] [PATCH 15/21] qcow2: Use abort() instead of assert(false) Max Reitz
2014-11-11 18:12 ` Eric Blake
2014-11-12 8:48 ` Max Reitz
2014-11-10 13:45 ` [Qemu-devel] [PATCH 16/21] qcow2: Split upgrade/downgrade paths for amend Max Reitz
2014-11-11 18:14 ` Eric Blake
2014-11-10 13:45 ` [Qemu-devel] [PATCH 17/21] qcow2: Use intermediate helper CB " Max Reitz
2014-11-11 21:05 ` Eric Blake
2014-11-12 9:10 ` Max Reitz [this message]
2014-11-10 13:45 ` [Qemu-devel] [PATCH 18/21] qcow2: Add function for refcount order amendment Max Reitz
2014-11-12 4:15 ` Eric Blake
2014-11-12 9:55 ` Max Reitz
2014-11-12 13:50 ` Eric Blake
2014-11-12 14:19 ` Eric Blake
2014-11-12 14:21 ` Max Reitz
2014-11-12 17:45 ` Max Reitz
2014-11-12 20:21 ` Eric Blake
2014-11-10 13:45 ` [Qemu-devel] [PATCH 19/21] qcow2: Invoke refcount order amendment function Max Reitz
2014-11-12 4:36 ` Eric Blake
2014-11-10 13:45 ` [Qemu-devel] [PATCH 20/21] qcow2: Point to amend function in check Max Reitz
2014-11-12 4:38 ` Eric Blake
2014-11-10 13:45 ` [Qemu-devel] [PATCH 21/21] iotests: Add test for different refcount widths Max Reitz
2014-11-11 19:53 ` Eric Blake
2014-11-12 8:58 ` Max Reitz
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=546323F3.8030209@redhat.com \
--to=mreitz@redhat.com \
--cc=eblake@redhat.com \
--cc=kwolf@redhat.com \
--cc=pl@kamp.de \
--cc=qemu-devel@nongnu.org \
--cc=stefanha@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.