block: Framework for reopening files safely
This is based 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.
Block drivers are passed a reopen state structure, that contains:
    * BDS to reopen
    * flags for the reopen
    * opaque pointer for any driver-specific data that needs to be
      persistent from _prepare to _commit/_abort
    * reopen queue pointer, if the driver needs to queue additional
      BDS for a reopen
Signed-off-by: Jeff Cody <jcody@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
			
			
This commit is contained in:
		
							parent
							
								
									55b110f24e
								
							
						
					
					
						commit
						e971aa1273
					
				
							
								
								
									
										232
									
								
								block.c
									
									
									
									
									
								
							
							
						
						
									
										232
									
								
								block.c
									
									
									
									
									
								
							@ -863,6 +863,238 @@ unlink_and_fail:
 | 
				
			|||||||
    return ret;
 | 
					    return ret;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef struct BlockReopenQueueEntry {
 | 
				
			||||||
 | 
					     bool prepared;
 | 
				
			||||||
 | 
					     BDRVReopenState state;
 | 
				
			||||||
 | 
					     QSIMPLEQ_ENTRY(BlockReopenQueueEntry) entry;
 | 
				
			||||||
 | 
					} BlockReopenQueueEntry;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Adds a BlockDriverState to a simple queue for an atomic, transactional
 | 
				
			||||||
 | 
					 * reopen of multiple devices.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * bs_queue can either be an existing BlockReopenQueue that has had QSIMPLE_INIT
 | 
				
			||||||
 | 
					 * already performed, or alternatively may be NULL a new BlockReopenQueue will
 | 
				
			||||||
 | 
					 * be created and initialized. This newly created BlockReopenQueue should be
 | 
				
			||||||
 | 
					 * passed back in for subsequent calls that are intended to be of the same
 | 
				
			||||||
 | 
					 * atomic 'set'.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * bs is the BlockDriverState to add to the reopen queue.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * flags contains the open flags for the associated bs
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * returns a pointer to bs_queue, which is either the newly allocated
 | 
				
			||||||
 | 
					 * bs_queue, or the existing bs_queue being used.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue,
 | 
				
			||||||
 | 
					                                    BlockDriverState *bs, int flags)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    assert(bs != NULL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    BlockReopenQueueEntry *bs_entry;
 | 
				
			||||||
 | 
					    if (bs_queue == NULL) {
 | 
				
			||||||
 | 
					        bs_queue = g_new0(BlockReopenQueue, 1);
 | 
				
			||||||
 | 
					        QSIMPLEQ_INIT(bs_queue);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (bs->file) {
 | 
				
			||||||
 | 
					        bdrv_reopen_queue(bs_queue, bs->file, flags);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bs_entry = g_new0(BlockReopenQueueEntry, 1);
 | 
				
			||||||
 | 
					    QSIMPLEQ_INSERT_TAIL(bs_queue, bs_entry, entry);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bs_entry->state.bs = bs;
 | 
				
			||||||
 | 
					    bs_entry->state.flags = flags;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return bs_queue;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * 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, *next;
 | 
				
			||||||
 | 
					    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, bs_queue, &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_SAFE(bs_entry, bs_queue, entry, next) {
 | 
				
			||||||
 | 
					        if (ret && bs_entry->prepared) {
 | 
				
			||||||
 | 
					            bdrv_reopen_abort(&bs_entry->state);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        g_free(bs_entry);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    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
 | 
				
			||||||
 | 
					 * 'opaque' field of the BDRVReopenState, which is used and allocated by
 | 
				
			||||||
 | 
					 * the block driver layer .bdrv_reopen_prepare()
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * bs is the BlockDriverState to reopen
 | 
				
			||||||
 | 
					 * flags are the new open flags
 | 
				
			||||||
 | 
					 * queue is the reopen queue
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * 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, BlockReopenQueue *queue,
 | 
				
			||||||
 | 
					                        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->open_flags & BDRV_O_ALLOW_RDWR) &&
 | 
				
			||||||
 | 
					        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, ERROR_CLASS_GENERIC_ERROR, "Error (%s) flushing drive",
 | 
				
			||||||
 | 
					                  strerror(-ret));
 | 
				
			||||||
 | 
					        goto error;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (drv->bdrv_reopen_prepare) {
 | 
				
			||||||
 | 
					        ret = drv->bdrv_reopen_prepare(reopen_state, queue, &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;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ret = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					error:
 | 
				
			||||||
 | 
					    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);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * 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)
 | 
					void bdrv_close(BlockDriverState *bs)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    bdrv_flush(bs);
 | 
					    bdrv_flush(bs);
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										17
									
								
								block.h
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								block.h
									
									
									
									
									
								
							@ -97,6 +97,15 @@ typedef enum {
 | 
				
			|||||||
    BDRV_ACTION_REPORT, BDRV_ACTION_IGNORE, BDRV_ACTION_STOP
 | 
					    BDRV_ACTION_REPORT, BDRV_ACTION_IGNORE, BDRV_ACTION_STOP
 | 
				
			||||||
} BlockQMPEventAction;
 | 
					} BlockQMPEventAction;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef QSIMPLEQ_HEAD(BlockReopenQueue, BlockReopenQueueEntry) BlockReopenQueue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef struct BDRVReopenState {
 | 
				
			||||||
 | 
					    BlockDriverState *bs;
 | 
				
			||||||
 | 
					    int flags;
 | 
				
			||||||
 | 
					    void *opaque;
 | 
				
			||||||
 | 
					} BDRVReopenState;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void bdrv_iostatus_enable(BlockDriverState *bs);
 | 
					void bdrv_iostatus_enable(BlockDriverState *bs);
 | 
				
			||||||
void bdrv_iostatus_reset(BlockDriverState *bs);
 | 
					void bdrv_iostatus_reset(BlockDriverState *bs);
 | 
				
			||||||
void bdrv_iostatus_disable(BlockDriverState *bs);
 | 
					void bdrv_iostatus_disable(BlockDriverState *bs);
 | 
				
			||||||
@ -131,6 +140,14 @@ int bdrv_parse_cache_flags(const char *mode, int *flags);
 | 
				
			|||||||
int bdrv_file_open(BlockDriverState **pbs, const char *filename, int flags);
 | 
					int bdrv_file_open(BlockDriverState **pbs, const char *filename, int flags);
 | 
				
			||||||
int bdrv_open(BlockDriverState *bs, const char *filename, int flags,
 | 
					int bdrv_open(BlockDriverState *bs, const char *filename, int flags,
 | 
				
			||||||
              BlockDriver *drv);
 | 
					              BlockDriver *drv);
 | 
				
			||||||
 | 
					BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue,
 | 
				
			||||||
 | 
					                                    BlockDriverState *bs, int flags);
 | 
				
			||||||
 | 
					int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp);
 | 
				
			||||||
 | 
					int bdrv_reopen(BlockDriverState *bs, int bdrv_flags, Error **errp);
 | 
				
			||||||
 | 
					int bdrv_reopen_prepare(BDRVReopenState *reopen_state,
 | 
				
			||||||
 | 
					                        BlockReopenQueue *queue, Error **errp);
 | 
				
			||||||
 | 
					void bdrv_reopen_commit(BDRVReopenState *reopen_state);
 | 
				
			||||||
 | 
					void bdrv_reopen_abort(BDRVReopenState *reopen_state);
 | 
				
			||||||
void bdrv_close(BlockDriverState *bs);
 | 
					void bdrv_close(BlockDriverState *bs);
 | 
				
			||||||
int bdrv_attach_dev(BlockDriverState *bs, void *dev);
 | 
					int bdrv_attach_dev(BlockDriverState *bs, void *dev);
 | 
				
			||||||
void bdrv_attach_dev_nofail(BlockDriverState *bs, void *dev);
 | 
					void bdrv_attach_dev_nofail(BlockDriverState *bs, void *dev);
 | 
				
			||||||
 | 
				
			|||||||
@ -139,6 +139,13 @@ struct BlockDriver {
 | 
				
			|||||||
    int instance_size;
 | 
					    int instance_size;
 | 
				
			||||||
    int (*bdrv_probe)(const uint8_t *buf, int buf_size, const char *filename);
 | 
					    int (*bdrv_probe)(const uint8_t *buf, int buf_size, const char *filename);
 | 
				
			||||||
    int (*bdrv_probe_device)(const char *filename);
 | 
					    int (*bdrv_probe_device)(const char *filename);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* For handling image reopen for split or non-split files */
 | 
				
			||||||
 | 
					    int (*bdrv_reopen_prepare)(BDRVReopenState *reopen_state,
 | 
				
			||||||
 | 
					                               BlockReopenQueue *queue, Error **errp);
 | 
				
			||||||
 | 
					    void (*bdrv_reopen_commit)(BDRVReopenState *reopen_state);
 | 
				
			||||||
 | 
					    void (*bdrv_reopen_abort)(BDRVReopenState *reopen_state);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    int (*bdrv_open)(BlockDriverState *bs, int flags);
 | 
					    int (*bdrv_open)(BlockDriverState *bs, int flags);
 | 
				
			||||||
    int (*bdrv_file_open)(BlockDriverState *bs, const char *filename, int flags);
 | 
					    int (*bdrv_file_open)(BlockDriverState *bs, const char *filename, int flags);
 | 
				
			||||||
    int (*bdrv_read)(BlockDriverState *bs, int64_t sector_num,
 | 
					    int (*bdrv_read)(BlockDriverState *bs, int64_t sector_num,
 | 
				
			||||||
@ -336,6 +343,7 @@ struct BlockDriverState {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    /* long-running background operation */
 | 
					    /* long-running background operation */
 | 
				
			||||||
    BlockJob *job;
 | 
					    BlockJob *job;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int get_tmp_filename(char *filename, int size);
 | 
					int get_tmp_filename(char *filename, int size);
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user