qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Jeff Cody <jcody@redhat.com>
To: Kevin Wolf <kwolf@redhat.com>
Cc: supriyak@linux.vnet.ibm.com, pbonzini@redhat.com,
	eblake@redhat.com, qemu-devel@nongnu.org, stefanha@gmail.com
Subject: Re: [Qemu-devel] [PATCH 2/7] block: Framework for reopening files safely
Date: Wed, 05 Sep 2012 12:38:49 -0400	[thread overview]
Message-ID: <50478019.60405@redhat.com> (raw)
In-Reply-To: <50476B1D.7070406@redhat.com>

On 09/05/2012 11:09 AM, Kevin Wolf wrote:
> Am 30.08.2012 20:47, schrieb Jeff Cody:
>> This is based heavily on Supriya Kannery's bdrv_reopen()
>> patch series.
>>
>> This provides a transactional method to reopen multiple
>> images files safely.
>>
>> Image files are queue for reopen via bdrv_reopen_queue(), and the
>> reopen occurs when bdrv_reopen_multiple() is called.  Changes are
>> staged in bdrv_reopen_prepare() and in the equivalent driver level
>> functions.  If any of the staged images fails a prepare, then all
>> of the images left untouched, and the staged changes for each image
>> abandoned.
>>
>> Signed-off-by: Jeff Cody <jcody@redhat.com>
> 
>> +/*
>> + * Reopen multiple BlockDriverStates atomically & transactionally.
>> + *
>> + * The queue passed in (bs_queue) must have been built up previous
>> + * via bdrv_reopen_queue().
>> + *
>> + * Reopens all BDS specified in the queue, with the appropriate
>> + * flags.  All devices are prepared for reopen, and failure of any
>> + * device will cause all device changes to be abandonded, and intermediate
>> + * data cleaned up.
>> + *
>> + * If all devices prepare successfully, then the changes are committed
>> + * to all devices.
>> + *
>> + */
>> +int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp)
>> +{
>> +    int ret = -1;
>> +    BlockReopenQueueEntry *bs_entry;
>> +    Error *local_err = NULL;
>> +
>> +    assert(bs_queue != NULL);
>> +
>> +    bdrv_drain_all();
>> +
>> +    QSIMPLEQ_FOREACH(bs_entry, bs_queue, entry) {
>> +        if (bdrv_reopen_prepare(bs_entry->state, &local_err)) {
>> +            error_propagate(errp, local_err);
>> +            goto cleanup;
>> +        }
>> +        bs_entry->prepared = true;
>> +    }
>> +
>> +    /* If we reach this point, we have success and just need to apply the
>> +     * changes
>> +     */
>> +    QSIMPLEQ_FOREACH(bs_entry, bs_queue, entry) {
>> +        bdrv_reopen_commit(bs_entry->state);
>> +    }
>> +
>> +    ret = 0;
>> +
>> +cleanup:
>> +    QSIMPLEQ_FOREACH(bs_entry, bs_queue, entry) {
>> +        if (ret && bs_entry->prepared) {
>> +            bdrv_reopen_abort(bs_entry->state);
>> +        }
>> +        g_free(bs_entry->state);
>> +        g_free(bs_entry);
>> +    }
> 
> Without QSIMPLEQ_FOREACH_SAFE, isn't this a use after free?
> 

Yes - that needs to be a QSIMPLEQ_FOREACH_SAFE.


>> +    g_free(bs_queue);
>> +    return ret;
>> +}
>> +
>> +
>> +/* Reopen a single BlockDriverState with the specified flags. */
>> +int bdrv_reopen(BlockDriverState *bs, int bdrv_flags, Error **errp)
>> +{
>> +    int ret = -1;
>> +    Error *local_err = NULL;
>> +    BlockReopenQueue *queue = bdrv_reopen_queue(NULL, bs, bdrv_flags);
>> +
>> +    ret = bdrv_reopen_multiple(queue, &local_err);
>> +    if (local_err != NULL) {
>> +        error_propagate(errp, local_err);
>> +    }
>> +    return ret;
>> +}
>> +
>> +
>> +/*
>> + * Prepares a BlockDriverState for reopen. All changes are staged in the
>> + * 'reopen_state' field of the BlockDriverState, which must be NULL when
>> + * entering (all previous reopens must have completed for the BDS).
>> + *
>> + * bs is the BlockDriverState to reopen
>> + * flags are the new open flags
>> + *
>> + * Returns 0 on success, non-zero on error.  On error errp will be set
>> + * as well.
>> + *
>> + * On failure, bdrv_reopen_abort() will be called to clean up any data.
>> + * It is the responsibility of the caller to then call the abort() or
>> + * commit() for any other BDS that have been left in a prepare() state
>> + *
>> + */
>> +int bdrv_reopen_prepare(BDRVReopenState *reopen_state, Error **errp)
>> +{
>> +    int ret = -1;
>> +    Error *local_err = NULL;
>> +    BlockDriver *drv;
>> +
>> +    assert(reopen_state != NULL);
>> +    assert(reopen_state->bs->drv != NULL);
>> +    drv = reopen_state->bs->drv;
>> +
>> +    /* if we are to stay read-only, do not allow permission change
>> +     * to r/w */
>> +    if (reopen_state->bs->keep_read_only &&
> 
> Just for completeness, we decided to use the flag here instead of
> keep_read_only.
> 
>> +        reopen_state->flags & BDRV_O_RDWR) {
>> +        error_set(errp, QERR_DEVICE_IS_READ_ONLY,
>> +                  reopen_state->bs->device_name);
>> +        goto error;
>> +    }
>> +
>> +
>> +    ret = bdrv_flush(reopen_state->bs);
>> +    if (ret) {
>> +        error_set(errp, QERR_IO_ERROR);
>> +        goto error;
>> +    }
> 
> This throws the error code away. Bad. We should probably change
> QERR_IO_ERROR so that you can include strerror(-ret).
> 

Or, I could use the new error_setg() here, and pass along all relevant
information.

>> +
>> +    if (drv->bdrv_reopen_prepare) {
>> +        ret = drv->bdrv_reopen_prepare(reopen_state, &local_err);
>> +        if (ret) {
>> +            if (local_err != NULL) {
>> +                error_propagate(errp, local_err);
>> +            } else {
>> +                error_set(errp, QERR_OPEN_FILE_FAILED,
>> +                          reopen_state->bs->filename);
>> +            }
>> +            goto error;
>> +        }
>> +    } else {
>> +        /* It is currently mandatory to have a bdrv_reopen_prepare()
>> +         * handler for each supported drv. */
>> +        error_set(errp, QERR_BLOCK_FORMAT_FEATURE_NOT_SUPPORTED,
>> +                  drv->format_name, reopen_state->bs->device_name,
>> +                 "reopening of file");
>> +        ret = -1;
>> +        goto error;
>> +    }
>> +
>> +    return 0;
>> +
>> +error:
>> +    bdrv_reopen_abort(reopen_state);
> 
> This is unexpected for me. Shouldn't .bdrv_reopen_prepare() clean up
> before returning an error, like any other function does? (Which could
> actually be a call to bdrv_reopen_abort() where it makes sense.)
> 
> If you use .bdrv_reopen_abort() for it, block drivers must take care to
> write this function in way that doesn't assume that
> .bdrv_reopen_prepare() has completed. Sounds rather nasty to me.
> 

Hmm.  Yes, I can see that - and there is no cleanup this function needs
to do itself, so it can just assume that .bdrv_reopen_prepare() will
clean up after itself (which, as you mentioned, will likely be the
driver calling its own .bdrv_reopen_abort()).

Although, I think it should always be good form for the block drivers to
not make such assumptions, if possible.


>> +    return ret;
>> +}
>> +
>> +/*
>> + * Takes the staged changes for the reopen from bdrv_reopen_prepare(), and
>> + * makes them final by swapping the staging BlockDriverState contents into
>> + * the active BlockDriverState contents.
>> + */
>> +void bdrv_reopen_commit(BDRVReopenState *reopen_state)
>> +{
>> +    BlockDriver *drv;
>> +
>> +    assert(reopen_state != NULL);
>> +    drv = reopen_state->bs->drv;
>> +    assert(drv != NULL);
>> +
>> +    /* If there are any driver level actions to take */
>> +    if (drv->bdrv_reopen_commit) {
>> +        drv->bdrv_reopen_commit(reopen_state);
>> +    }
>> +
>> +    /* set BDS specific flags now */
>> +    reopen_state->bs->open_flags         = reopen_state->flags;
>> +    reopen_state->bs->enable_write_cache = !!(reopen_state->flags &
>> +                                              BDRV_O_CACHE_WB);
>> +    reopen_state->bs->read_only = !(reopen_state->flags & BDRV_O_RDWR);
> 
> Hm, I wonder if these three lines can somehow be shared with the normal
> bdrv_open so that they stay in sync.

Sure, especially setting enable_write_cache.

> 
>> +}
>> +
>> +/*
>> + * Abort the reopen, and delete and free the staged changes in
>> + * reopen_state
>> + */
>> +void bdrv_reopen_abort(BDRVReopenState *reopen_state)
>> +{
>> +    BlockDriver *drv;
>> +
>> +    assert(reopen_state != NULL);
>> +    drv = reopen_state->bs->drv;
>> +    assert(drv != NULL);
>> +
>> +    if (drv->bdrv_reopen_abort) {
>> +        drv->bdrv_reopen_abort(reopen_state);
>> +    }
>> +}
>> +
>> +
>>  void bdrv_close(BlockDriverState *bs)
>>  {
>>      bdrv_flush(bs);
>> diff --git a/block.h b/block.h
>> index 4d919c2..db812b1 100644
>> --- a/block.h
>> +++ b/block.h
>> @@ -97,6 +97,14 @@ typedef enum {
>>      BDRV_ACTION_REPORT, BDRV_ACTION_IGNORE, BDRV_ACTION_STOP
>>  } BlockQMPEventAction;
>>  
>> +typedef struct BlockReopenQueueEntry {
>> +     bool prepared;
>> +     BDRVReopenState *state;
> 
> As discussed on IRC, this can be directly embedded instead of using a
> pointer.
> 
>> +     QSIMPLEQ_ENTRY(BlockReopenQueueEntry) entry;
>> +} BlockReopenQueueEntry;
> 
> 
> Kevin
> 

  reply	other threads:[~2012-09-05 16:38 UTC|newest]

Thread overview: 27+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2012-08-30 18:47 [Qemu-devel] [PATCH 0/7] block: bdrv_reopen() patches Jeff Cody
2012-08-30 18:47 ` [Qemu-devel] [PATCH 1/7] block: correctly set the keep_read_only flag Jeff Cody
2012-09-05 12:47   ` Kevin Wolf
2012-09-05 13:08     ` Jeff Cody
2012-09-05 13:12       ` Kevin Wolf
2012-08-30 18:47 ` [Qemu-devel] [PATCH 2/7] block: Framework for reopening files safely Jeff Cody
2012-09-05 15:09   ` Kevin Wolf
2012-09-05 16:38     ` Jeff Cody [this message]
2012-09-11 14:57   ` Jeff Cody
2012-09-11 15:14     ` Kevin Wolf
2012-09-11 15:36       ` Jeff Cody
2012-09-11 15:41         ` Kevin Wolf
2012-08-30 18:47 ` [Qemu-devel] [PATCH 3/7] block: raw-posix image file reopen Jeff Cody
2012-08-30 22:15   ` Eric Blake
2012-08-31 14:42     ` Jeff Cody
2012-08-31 14:49       ` Kevin Wolf
2012-08-31 15:10         ` Jeff Cody
2012-09-05 15:30   ` Kevin Wolf
2012-09-05 16:43     ` Jeff Cody
2012-09-06  9:23       ` Kevin Wolf
2012-09-06 15:34         ` Corey Bryant
2012-09-07 10:40           ` Kevin Wolf
2012-09-07 14:29             ` Corey Bryant
2012-08-30 18:47 ` [Qemu-devel] [PATCH 4/7] block: raw " Jeff Cody
2012-08-30 18:47 ` [Qemu-devel] [PATCH 5/7] block: qed " Jeff Cody
2012-08-30 18:47 ` [Qemu-devel] [PATCH 6/7] block: qcow2 " Jeff Cody
2012-08-30 18:47 ` [Qemu-devel] [PATCH 7/7] block: qcow " Jeff Cody

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=50478019.60405@redhat.com \
    --to=jcody@redhat.com \
    --cc=eblake@redhat.com \
    --cc=kwolf@redhat.com \
    --cc=pbonzini@redhat.com \
    --cc=qemu-devel@nongnu.org \
    --cc=stefanha@gmail.com \
    --cc=supriyak@linux.vnet.ibm.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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).