Comparing branches: left: git-publish.image-fleecing.v4 right: HEAD Author: Fam Zheng Author: Fam Zheng Date: Tue Nov 19 17:20:48 2013 +0800 Date: Tue Nov 19 17:20:48 2013 +0800 qapi: Add BlockOperationType enum qapi: Add BlockOperationType enum This adds the enum of all the operations that can be take This adds the enum of all the operations that can be take device. device. Signed-off-by: Fam Zheng Signed-off-by: Fam Zheng diff --git a/qapi-schema.json b/qapi-schema.json diff --git a/qapi-schema.json b/qapi-schema.json index XXXXXXX..XXXXXXX XXXXXX index XXXXXXX..XXXXXXX XXXXXX --- a/qapi-schema.json --- a/qapi-schema.json +++ b/qapi-schema.json +++ b/qapi-schema.json @@ -XXX,X +XXX,X @@ @@ -XXX,X +XXX,X @@ 'data': ['commit', 'stream', 'mirror', 'backup'] } 'data': ['commit', 'stream', 'mirror', 'backup'] } ## ## +# @BlockOperationType +# @BlockOperationType +# Type of a block operation. | +# Type of a block operation. (since 1.8) > +# > +# @backup-source: As a backup source. See the 'drive-backup' > +# > +# @backup-target: As a backup target. See the 'drive-backup' > +# > +# @change: See the 'change' command. > +# > +# @commit: See the 'block-commi' command. > +# > +# @dataplane: The virtio-blk dataplane feature. > +# > +# @drive-del: See the 'drive_del' HMP command. > +# > +# @eject: See the 'eject' command. > +# > +# @external-snapshot: See the 'blockdev-snapshot-sync' comma > +# > +# @internal-snapshot: See the 'blockdev-snapshot-internal-sy > +# > +# @internal-snapshot-delete: See the 'blockdev-snapshot-dele > +# > +# @mirror: See the 'drive-mirror' command. > +# > +# @resize: See the 'block-resize' command. > +# > +# @stream: See the 'block-stream' command. +# +# +# Since: 1.8 +# Since: 1.8 +## +## +{ 'enum': 'BlockOpType', +{ 'enum': 'BlockOpType', + 'data': [ + 'data': [ + 'backup', | + 'backup-source', > + 'backup-target', + 'change', + 'change', + 'commit', + 'commit', + 'dataplane', + 'dataplane', + 'drive-del', + 'drive-del', + 'eject', + 'eject', + 'external-snapshot', + 'external-snapshot', + 'internal-snapshot', + 'internal-snapshot', + 'internal-snapshot-delete', + 'internal-snapshot-delete', + 'mirror', + 'mirror', + 'nbd-server-add', < + 'passwd', < + 'resize', + 'resize', + 'set-io-throttle', < + 'stream' + 'stream' +] } +] } + + +## +## # @BlockJobInfo: # @BlockJobInfo: # # # Information about a long-running block device operation. # Information about a long-running block device operation. commit XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX commit XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Author: Fam Zheng Author: Fam Zheng Date: Tue Nov 19 17:55:06 2013 +0800 Date: Tue Nov 19 17:55:06 2013 +0800 block: Introduce op_blockers to BlockDriverState block: Introduce op_blockers to BlockDriverState BlockDriverState.op_blockers is an array of list with BLO | BlockDriverState.op_blockers is an array of lists with BL elements. Each list is a list of blockers of an operation elements. Each list is a list of blockers of an operation (BlockOpType), that marks this BDS is currently blocked f | (BlockOpType), that marks this BDS as currently blocked f of operation with reason errors stored in the list. The r | type of operation with reason errors stored in the list. is: | usage is: * BDS user who wants to take an operation should check i * BDS user who wants to take an operation should check i blocker of the type with bdrv_op_is_blocked(). blocker of the type with bdrv_op_is_blocked(). * BDS user who wants to block certain types of operation * BDS user who wants to block certain types of operation bdrv_op_block (or bdrv_op_block_all to block all types bdrv_op_block (or bdrv_op_block_all to block all types which is similar to bdrv_set_in_use of now). which is similar to bdrv_set_in_use of now). * A blocker is only referenced by op_blockers, so the li * A blocker is only referenced by op_blockers, so the li managed by caller, and shouldn't be lost until unblock managed by caller, and shouldn't be lost until unblock a caller does these: a caller does these: - Allocate a blocker with error_setg or similar, call - Allocate a blocker with error_setg or similar, call to block some operations. to block some operations. - Hold the blocker, do his job. - Hold the blocker, do his job. - Unblock operations that it blocked, with the same re - Unblock operations that it blocked, with the same re passed to bdrv_op_unblock(). passed to bdrv_op_unblock(). - Release the blocker with error_free(). - Release the blocker with error_free(). diff --git a/block.c b/block.c diff --git a/block.c b/block.c index XXXXXXX..XXXXXXX XXXXXX index XXXXXXX..XXXXXXX XXXXXX --- a/block.c --- a/block.c +++ b/block.c +++ b/block.c @@ -XXX,X +XXX,X @@ void bdrv_unref(BlockDriverState *bs) @@ -XXX,X +XXX,X @@ void bdrv_unref(BlockDriverState *bs) } } } } +struct BdrvOpBlocker { +struct BdrvOpBlocker { + Error *reason; + Error *reason; + QLIST_ENTRY(BdrvOpBlocker) list; + QLIST_ENTRY(BdrvOpBlocker) list; +}; +}; + + +bool bdrv_op_is_blocked(BlockDriverState *bs, BlockOpType op +bool bdrv_op_is_blocked(BlockDriverState *bs, BlockOpType op +{ +{ + BdrvOpBlocker *blocker; + BdrvOpBlocker *blocker; + assert(op >=0 && op < BLOCK_OP_TYPE_MAX); | + assert((int) op >= 0 && op < BLOCK_OP_TYPE_MAX); + if (!QLIST_EMPTY(&bs->op_blockers[op])) { + if (!QLIST_EMPTY(&bs->op_blockers[op])) { + blocker = QLIST_FIRST(&bs->op_blockers[op]); + blocker = QLIST_FIRST(&bs->op_blockers[op]); + *errp = error_copy(blocker->reason); | + if (errp) { > + *errp = error_copy(blocker->reason); > + } + return true; + return true; + } + } + return false; + return false; +} +} + + +void bdrv_op_block(BlockDriverState *bs, BlockOpType op, Err +void bdrv_op_block(BlockDriverState *bs, BlockOpType op, Err +{ +{ + BdrvOpBlocker *blocker; + BdrvOpBlocker *blocker; + assert(op >=0 && op < BLOCK_OP_TYPE_MAX); | + assert((int) op >= 0 && op < BLOCK_OP_TYPE_MAX); + + + blocker = g_malloc0(sizeof(BdrvOpBlocker)); + blocker = g_malloc0(sizeof(BdrvOpBlocker)); + blocker->reason = reason; + blocker->reason = reason; + QLIST_INSERT_HEAD(&bs->op_blockers[op], blocker, list); + QLIST_INSERT_HEAD(&bs->op_blockers[op], blocker, list); +} +} + + +void bdrv_op_unblock(BlockDriverState *bs, BlockOpType op, E +void bdrv_op_unblock(BlockDriverState *bs, BlockOpType op, E +{ +{ + BdrvOpBlocker *blocker, *next; + BdrvOpBlocker *blocker, *next; + assert(op >=0 && op < BLOCK_OP_TYPE_MAX); | + assert((int) op >= 0 && op < BLOCK_OP_TYPE_MAX); + QLIST_FOREACH_SAFE(blocker, &bs->op_blockers[op], list, + QLIST_FOREACH_SAFE(blocker, &bs->op_blockers[op], list, + if (blocker->reason == reason) { + if (blocker->reason == reason) { + QLIST_REMOVE(blocker, list); + QLIST_REMOVE(blocker, list); + g_free(blocker); + g_free(blocker); + } + } + } + } +} +} + + +void bdrv_op_block_all(BlockDriverState *bs, Error *reason) +void bdrv_op_block_all(BlockDriverState *bs, Error *reason) +{ +{ + int i; + int i; + for (i = 0; i < BLOCK_OP_TYPE_MAX; i++) { + for (i = 0; i < BLOCK_OP_TYPE_MAX; i++) { + bdrv_op_block(bs, i, reason); + bdrv_op_block(bs, i, reason); + } + } +} +} + + +void bdrv_op_unblock_all(BlockDriverState *bs, Error *reason +void bdrv_op_unblock_all(BlockDriverState *bs, Error *reason +{ +{ + int i; + int i; + for (i = 0; i < BLOCK_OP_TYPE_MAX; i++) { + for (i = 0; i < BLOCK_OP_TYPE_MAX; i++) { + bdrv_op_unblock(bs, i, reason); + bdrv_op_unblock(bs, i, reason); + } + } +} +} + + void bdrv_set_in_use(BlockDriverState *bs, int in_use) void bdrv_set_in_use(BlockDriverState *bs, int in_use) { { assert(bs->in_use != in_use); assert(bs->in_use != in_use); diff --git a/include/block/block.h b/include/block/block.h diff --git a/include/block/block.h b/include/block/block.h index XXXXXXX..XXXXXXX XXXXXX index XXXXXXX..XXXXXXX XXXXXX --- a/include/block/block.h --- a/include/block/block.h +++ b/include/block/block.h +++ b/include/block/block.h @@ -XXX,X +XXX,X @@ void bdrv_unref(BlockDriverState *bs); @@ -XXX,X +XXX,X @@ void bdrv_unref(BlockDriverState *bs); void bdrv_set_in_use(BlockDriverState *bs, int in_use); void bdrv_set_in_use(BlockDriverState *bs, int in_use); int bdrv_in_use(BlockDriverState *bs); int bdrv_in_use(BlockDriverState *bs); +bool bdrv_op_is_blocked(BlockDriverState *bs, BlockOpType op +bool bdrv_op_is_blocked(BlockDriverState *bs, BlockOpType op +void bdrv_op_block(BlockDriverState *bs, BlockOpType op, Err +void bdrv_op_block(BlockDriverState *bs, BlockOpType op, Err +void bdrv_op_unblock(BlockDriverState *bs, BlockOpType op, E +void bdrv_op_unblock(BlockDriverState *bs, BlockOpType op, E +void bdrv_op_block_all(BlockDriverState *bs, Error *reason); +void bdrv_op_block_all(BlockDriverState *bs, Error *reason); +void bdrv_op_unblock_all(BlockDriverState *bs, Error *reason +void bdrv_op_unblock_all(BlockDriverState *bs, Error *reason + + #ifdef CONFIG_LINUX_AIO #ifdef CONFIG_LINUX_AIO int raw_get_aio_fd(BlockDriverState *bs); int raw_get_aio_fd(BlockDriverState *bs); #else #else diff --git a/include/block/block_int.h b/include/block/block_ diff --git a/include/block/block_int.h b/include/block/block_ index XXXXXXX..XXXXXXX XXXXXX index XXXXXXX..XXXXXXX XXXXXX --- a/include/block/block_int.h --- a/include/block/block_int.h +++ b/include/block/block_int.h +++ b/include/block/block_int.h @@ -XXX,X +XXX,X @@ struct BlockDriver { @@ -XXX,X +XXX,X @@ struct BlockDriver { QLIST_ENTRY(BlockDriver) list; QLIST_ENTRY(BlockDriver) list; }; }; +typedef struct BdrvOpBlocker BdrvOpBlocker; +typedef struct BdrvOpBlocker BdrvOpBlocker; + + /* /* * Note: the function bdrv_append() copies and swaps content * Note: the function bdrv_append() copies and swaps content * BlockDriverStates, so if you add new fields to this struc * BlockDriverStates, so if you add new fields to this struc @@ -XXX,X +XXX,X @@ struct BlockDriverState { @@ -XXX,X +XXX,X @@ struct BlockDriverState { QLIST_HEAD(, BdrvTrackedRequest) tracked_requests; QLIST_HEAD(, BdrvTrackedRequest) tracked_requests; + /* operation blockers */ + /* operation blockers */ + QLIST_HEAD(, BdrvOpBlocker) op_blockers[BLOCK_OP_TYPE_MA + QLIST_HEAD(, BdrvOpBlocker) op_blockers[BLOCK_OP_TYPE_MA + + /* long-running background operation */ /* long-running background operation */ BlockJob *job; BlockJob *job; commit XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX commit XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Author: Fam Zheng Author: Fam Zheng Date: Fri Nov 22 12:07:03 2013 +0800 Date: Fri Nov 22 12:07:03 2013 +0800 block: Replace in_use with operation blocker block: Replace in_use with operation blocker This drops BlockDriverState.in_use with op_blockers: This drops BlockDriverState.in_use with op_blockers: - Call bdrv_op_block_all in place of bdrv_set_in_use(bs - Call bdrv_op_block_all in place of bdrv_set_in_use(bs - Call bdrv_op_unblock_all in place of bdrv_set_in_use( - Call bdrv_op_unblock_all in place of bdrv_set_in_use( - Check bdrv_op_is_blocked() in place of bdrv_in_use(bs - Check bdrv_op_is_blocked() in place of bdrv_in_use(bs The specific types are used, e.g. in place of startin The specific types are used, e.g. in place of startin bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_BACKUP, ...). bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_BACKUP, ...). - Check bdrv_op_blocker_is_empty() in place of assert(! - Check bdrv_op_blocker_is_empty() in place of assert(! Note: there is no block for only specific types for now, Note: there is no block for only specific types for now, blocks all or none. So although the checks are type speci blocks all or none. So although the checks are type speci changes can still be seen as identical in logic. changes can still be seen as identical in logic. Signed-off-by: Fam Zheng Signed-off-by: Fam Zheng diff --git a/block-migration.c b/block-migration.c diff --git a/block-migration.c b/block-migration.c index XXXXXXX..XXXXXXX XXXXXX index XXXXXXX..XXXXXXX XXXXXX --- a/block-migration.c --- a/block-migration.c +++ b/block-migration.c +++ b/block-migration.c @@ -XXX,X +XXX,X @@ typedef struct BlkMigDevState { @@ -XXX,X +XXX,X @@ typedef struct BlkMigDevState { /* Protected by block migration lock. */ /* Protected by block migration lock. */ unsigned long *aio_bitmap; unsigned long *aio_bitmap; int64_t completed_sectors; int64_t completed_sectors; + + + Error *blocker; + Error *blocker; } BlkMigDevState; } BlkMigDevState; typedef struct BlkMigBlock { typedef struct BlkMigBlock { @@ -XXX,X +XXX,X @@ static void init_blk_migration_it(void *o @@ -XXX,X +XXX,X @@ static void init_blk_migration_it(void *o bmds->completed_sectors = 0; bmds->completed_sectors = 0; bmds->shared_base = block_mig_state.shared_base; bmds->shared_base = block_mig_state.shared_base; alloc_aio_bitmap(bmds); alloc_aio_bitmap(bmds); - bdrv_set_in_use(bs, 1); - bdrv_set_in_use(bs, 1); + error_setg(&bmds->blocker, "block device is in use b + error_setg(&bmds->blocker, "block device is in use b + bdrv_op_block_all(bs, bmds->blocker); + bdrv_op_block_all(bs, bmds->blocker); bdrv_ref(bs); bdrv_ref(bs); block_mig_state.total_sector_sum += sectors; block_mig_state.total_sector_sum += sectors; @@ -XXX,X +XXX,X @@ static void blk_mig_cleanup(void) @@ -XXX,X +XXX,X @@ static void blk_mig_cleanup(void) blk_mig_lock(); blk_mig_lock(); while ((bmds = QSIMPLEQ_FIRST(&block_mig_state.bmds_list while ((bmds = QSIMPLEQ_FIRST(&block_mig_state.bmds_list QSIMPLEQ_REMOVE_HEAD(&block_mig_state.bmds_list, ent QSIMPLEQ_REMOVE_HEAD(&block_mig_state.bmds_list, ent - bdrv_set_in_use(bmds->bs, 0); - bdrv_set_in_use(bmds->bs, 0); + bdrv_op_unblock_all(bmds->bs, bmds->blocker); + bdrv_op_unblock_all(bmds->bs, bmds->blocker); + error_free(bmds->blocker); + error_free(bmds->blocker); bdrv_unref(bmds->bs); bdrv_unref(bmds->bs); g_free(bmds->aio_bitmap); g_free(bmds->aio_bitmap); g_free(bmds); g_free(bmds); diff --git a/block.c b/block.c diff --git a/block.c b/block.c index XXXXXXX..XXXXXXX XXXXXX index XXXXXXX..XXXXXXX XXXXXX --- a/block.c --- a/block.c +++ b/block.c +++ b/block.c @@ -XXX,X +XXX,X @@ static void bdrv_move_feature_fields(Bloc @@ -XXX,X +XXX,X @@ static void bdrv_move_feature_fields(Bloc bs_dest->refcnt = bs_src->refcnt; bs_dest->refcnt = bs_src->refcnt; /* job */ /* job */ - bs_dest->in_use = bs_src->in_use; - bs_dest->in_use = bs_src->in_use; bs_dest->job = bs_src->job; bs_dest->job = bs_src->job; /* keep the same entry in bdrv_states */ /* keep the same entry in bdrv_states */ pstrcpy(bs_dest->device_name, sizeof(bs_dest->device_nam pstrcpy(bs_dest->device_name, sizeof(bs_dest->device_nam bs_src->device_name); bs_src->device_name); + memcpy(bs_dest->op_blockers, bs_src->op_blockers, + memcpy(bs_dest->op_blockers, bs_src->op_blockers, + sizeof(bs_dest->op_blockers)); + sizeof(bs_dest->op_blockers)); bs_dest->list = bs_src->list; bs_dest->list = bs_src->list; } } +static bool bdrv_op_blocker_is_empty(BlockDriverState *bs); +static bool bdrv_op_blocker_is_empty(BlockDriverState *bs); /* /* * Swap bs contents for two image chains while they are live * Swap bs contents for two image chains while they are live * while keeping required fields on the BlockDriverState tha * while keeping required fields on the BlockDriverState tha @@ -XXX,X +XXX,X @@ void bdrv_swap(BlockDriverState *bs_new, @@ -XXX,X +XXX,X @@ void bdrv_swap(BlockDriverState *bs_new, assert(bs_new->dirty_bitmap == NULL); assert(bs_new->dirty_bitmap == NULL); assert(bs_new->job == NULL); assert(bs_new->job == NULL); assert(bs_new->dev == NULL); assert(bs_new->dev == NULL); - assert(bs_new->in_use == 0); - assert(bs_new->in_use == 0); + assert(bdrv_op_blocker_is_empty(bs_new)); + assert(bdrv_op_blocker_is_empty(bs_new)); assert(bs_new->io_limits_enabled == false); assert(bs_new->io_limits_enabled == false); assert(!throttle_have_timer(&bs_new->throttle_state)); assert(!throttle_have_timer(&bs_new->throttle_state)); @@ -XXX,X +XXX,X @@ void bdrv_swap(BlockDriverState *bs_new, @@ -XXX,X +XXX,X @@ void bdrv_swap(BlockDriverState *bs_new, /* Check a few fields that should remain attached to the /* Check a few fields that should remain attached to the assert(bs_new->dev == NULL); assert(bs_new->dev == NULL); assert(bs_new->job == NULL); assert(bs_new->job == NULL); - assert(bs_new->in_use == 0); - assert(bs_new->in_use == 0); + assert(bdrv_op_blocker_is_empty(bs_new)); + assert(bdrv_op_blocker_is_empty(bs_new)); assert(bs_new->io_limits_enabled == false); assert(bs_new->io_limits_enabled == false); assert(!throttle_have_timer(&bs_new->throttle_state)); assert(!throttle_have_timer(&bs_new->throttle_state)); @@ -XXX,X +XXX,X @@ static void bdrv_delete(BlockDriverState @@ -XXX,X +XXX,X @@ static void bdrv_delete(BlockDriverState { { assert(!bs->dev); assert(!bs->dev); assert(!bs->job); assert(!bs->job); - assert(!bs->in_use); - assert(!bs->in_use); + assert(bdrv_op_blocker_is_empty(bs)); + assert(bdrv_op_blocker_is_empty(bs)); assert(!bs->refcnt); assert(!bs->refcnt); bdrv_close(bs); bdrv_close(bs); @@ -XXX,X +XXX,X @@ int bdrv_commit(BlockDriverState *bs) @@ -XXX,X +XXX,X @@ int bdrv_commit(BlockDriverState *bs) int ret = 0; int ret = 0; uint8_t *buf; uint8_t *buf; char filename[PATH_MAX]; char filename[PATH_MAX]; + Error *local_err; + Error *local_err; if (!drv) if (!drv) return -ENOMEDIUM; return -ENOMEDIUM; @@ -XXX,X +XXX,X @@ int bdrv_commit(BlockDriverState *bs) @@ -XXX,X +XXX,X @@ int bdrv_commit(BlockDriverState *bs) return -ENOTSUP; return -ENOTSUP; } } - if (bdrv_in_use(bs) || bdrv_in_use(bs->backing_hd)) { - if (bdrv_in_use(bs) || bdrv_in_use(bs->backing_hd)) { + if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_COMMIT, &local_ + if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_COMMIT, &local_ + bdrv_op_is_blocked(bs->backing_hd, BLOCK_OP_TYPE_COM + bdrv_op_is_blocked(bs->backing_hd, BLOCK_OP_TYPE_COM + error_free(local_err); + error_free(local_err); return -EBUSY; return -EBUSY; } } @@ -XXX,X +XXX,X @@ int coroutine_fn bdrv_co_write_zeroes(Blo @@ -XXX,X +XXX,X @@ int coroutine_fn bdrv_co_write_zeroes(Blo int bdrv_truncate(BlockDriverState *bs, int64_t offset) int bdrv_truncate(BlockDriverState *bs, int64_t offset) { { BlockDriver *drv = bs->drv; BlockDriver *drv = bs->drv; + Error *local_err; + Error *local_err; int ret; int ret; if (!drv) if (!drv) return -ENOMEDIUM; return -ENOMEDIUM; @@ -XXX,X +XXX,X @@ int bdrv_truncate(BlockDriverState *bs, i @@ -XXX,X +XXX,X @@ int bdrv_truncate(BlockDriverState *bs, i return -ENOTSUP; return -ENOTSUP; if (bs->read_only) if (bs->read_only) return -EACCES; return -EACCES; - if (bdrv_in_use(bs)) - if (bdrv_in_use(bs)) + if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_RESIZE, &local_ + if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_RESIZE, &local_ + error_free(local_err); + error_free(local_err); return -EBUSY; return -EBUSY; + } + } ret = drv->bdrv_truncate(bs, offset); ret = drv->bdrv_truncate(bs, offset); if (ret == 0) { if (ret == 0) { ret = refresh_total_sectors(bs, offset >> BDRV_SECTO ret = refresh_total_sectors(bs, offset >> BDRV_SECTO @@ -XXX,X +XXX,X @@ void bdrv_op_unblock_all(BlockDriverState @@ -XXX,X +XXX,X @@ void bdrv_op_unblock_all(BlockDriverState } } } } -void bdrv_set_in_use(BlockDriverState *bs, int in_use) -void bdrv_set_in_use(BlockDriverState *bs, int in_use) +static bool bdrv_op_blocker_is_empty(BlockDriverState *bs) +static bool bdrv_op_blocker_is_empty(BlockDriverState *bs) { { - assert(bs->in_use != in_use); - assert(bs->in_use != in_use); - bs->in_use = in_use; - bs->in_use = in_use; -} -} + bool ret = true; + bool ret = true; + int i; + int i; -int bdrv_in_use(BlockDriverState *bs) -int bdrv_in_use(BlockDriverState *bs) -{ -{ - return bs->in_use; - return bs->in_use; + for (i = 0; i < BLOCK_OP_TYPE_MAX; i++) { + for (i = 0; i < BLOCK_OP_TYPE_MAX; i++) { + ret = ret && QLIST_EMPTY(&bs->op_blockers[i]); + ret = ret && QLIST_EMPTY(&bs->op_blockers[i]); + } + } + return ret; + return ret; } } void bdrv_iostatus_enable(BlockDriverState *bs) void bdrv_iostatus_enable(BlockDriverState *bs) diff --git a/blockdev.c b/blockdev.c diff --git a/blockdev.c b/blockdev.c index XXXXXXX..XXXXXXX XXXXXX index XXXXXXX..XXXXXXX XXXXXX --- a/blockdev.c --- a/blockdev.c +++ b/blockdev.c +++ b/blockdev.c @@ -XXX,X +XXX,X @@ static void external_snapshot_prepare(Blk @@ -XXX,X +XXX,X @@ static void external_snapshot_prepare(Blk return; return; } } - if (bdrv_in_use(state->old_bs)) { - if (bdrv_in_use(state->old_bs)) { - error_set(errp, QERR_DEVICE_IN_USE, device); - error_set(errp, QERR_DEVICE_IN_USE, device); + if (bdrv_op_is_blocked(state->old_bs, + if (bdrv_op_is_blocked(state->old_bs, + BLOCK_OP_TYPE_EXTERNAL_SNAPSHOT, + BLOCK_OP_TYPE_EXTERNAL_SNAPSHOT, return; return; } } @@ -XXX,X +XXX,X @@ exit: @@ -XXX,X +XXX,X @@ exit: static void eject_device(BlockDriverState *bs, int force, Er static void eject_device(BlockDriverState *bs, int force, Er { { - if (bdrv_in_use(bs)) { - if (bdrv_in_use(bs)) { - error_set(errp, QERR_DEVICE_IN_USE, bdrv_get_device_ - error_set(errp, QERR_DEVICE_IN_USE, bdrv_get_device_ + if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_EJECT, errp)) { + if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_EJECT, errp)) { return; return; } } + + if (!bdrv_dev_has_removable_media(bs)) { if (!bdrv_dev_has_removable_media(bs)) { error_set(errp, QERR_DEVICE_NOT_REMOVABLE, bdrv_get_ error_set(errp, QERR_DEVICE_NOT_REMOVABLE, bdrv_get_ return; return; @@ -XXX,X +XXX,X @@ int do_drive_del(Monitor *mon, const QDic @@ -XXX,X +XXX,X @@ int do_drive_del(Monitor *mon, const QDic { { const char *id = qdict_get_str(qdict, "id"); const char *id = qdict_get_str(qdict, "id"); BlockDriverState *bs; BlockDriverState *bs; + Error *local_err; + Error *local_err; bs = bdrv_find(id); bs = bdrv_find(id); if (!bs) { if (!bs) { qerror_report(QERR_DEVICE_NOT_FOUND, id); qerror_report(QERR_DEVICE_NOT_FOUND, id); return -1; return -1; } } - if (bdrv_in_use(bs)) { - if (bdrv_in_use(bs)) { - qerror_report(QERR_DEVICE_IN_USE, id); - qerror_report(QERR_DEVICE_IN_USE, id); + if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_DRIVE_DEL, &loc + if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_DRIVE_DEL, &loc + error_report("%s", error_get_pretty(local_err)); + error_report("%s", error_get_pretty(local_err)); + error_free(local_err); + error_free(local_err); return -1; return -1; } } @@ -XXX,X +XXX,X @@ void qmp_block_stream(const char *device, @@ -XXX,X +XXX,X @@ void qmp_block_stream(const char *device, return; return; } } + if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_STREAM, errp)) + if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_STREAM, errp)) + return; + return; + } + } + + if (base) { if (base) { base_bs = bdrv_find_backing_image(bs, base); base_bs = bdrv_find_backing_image(bs, base); if (base_bs == NULL) { if (base_bs == NULL) { @@ -XXX,X +XXX,X @@ void qmp_block_commit(const char *device, @@ -XXX,X +XXX,X @@ void qmp_block_commit(const char *device, return; return; } } + if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_COMMIT, errp)) + if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_COMMIT, errp)) + return; + return; + } + } + + /* default top_bs is the active layer */ /* default top_bs is the active layer */ top_bs = bs; top_bs = bs; @@ -XXX,X +XXX,X @@ void qmp_drive_backup(const char *device, @@ -XXX,X +XXX,X @@ void qmp_drive_backup(const char *device, } } } } - if (bdrv_in_use(bs)) { - if (bdrv_in_use(bs)) { - error_set(errp, QERR_DEVICE_IN_USE, device); - error_set(errp, QERR_DEVICE_IN_USE, device); + if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_BACKUP, errp)) | + if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_BACKUP_SOURCE, return; return; } } @@ -XXX,X +XXX,X @@ void qmp_drive_mirror(const char *device, @@ -XXX,X +XXX,X @@ void qmp_drive_mirror(const char *device, } } } } - if (bdrv_in_use(bs)) { - if (bdrv_in_use(bs)) { - error_set(errp, QERR_DEVICE_IN_USE, device); - error_set(errp, QERR_DEVICE_IN_USE, device); + if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_MIRROR, errp)) + if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_MIRROR, errp)) return; return; } } + + flags = bs->open_flags | BDRV_O_RDWR; flags = bs->open_flags | BDRV_O_RDWR; source = bs->backing_hd; source = bs->backing_hd; if (!source && sync == MIRROR_SYNC_MODE_TOP) { if (!source && sync == MIRROR_SYNC_MODE_TOP) { diff --git a/blockjob.c b/blockjob.c diff --git a/blockjob.c b/blockjob.c index XXXXXXX..XXXXXXX XXXXXX index XXXXXXX..XXXXXXX XXXXXX --- a/blockjob.c --- a/blockjob.c +++ b/blockjob.c +++ b/blockjob.c @@ -XXX,X +XXX,X @@ void *block_job_create(const BlockJobDriv @@ -XXX,X +XXX,X @@ void *block_job_create(const BlockJobDriv { { BlockJob *job; BlockJob *job; - if (bs->job || bdrv_in_use(bs)) { - if (bs->job || bdrv_in_use(bs)) { + if (bs->job) { + if (bs->job) { error_set(errp, QERR_DEVICE_IN_USE, bdrv_get_device_ error_set(errp, QERR_DEVICE_IN_USE, bdrv_get_device_ return NULL; return NULL; } } bdrv_ref(bs); bdrv_ref(bs); - bdrv_set_in_use(bs, 1); - bdrv_set_in_use(bs, 1); - - job = g_malloc0(driver->instance_size); job = g_malloc0(driver->instance_size); job->driver = driver; job->driver = driver; job->bs = bs; job->bs = bs; @@ -XXX,X +XXX,X @@ void *block_job_create(const BlockJobDriv @@ -XXX,X +XXX,X @@ void *block_job_create(const BlockJobDriv if (error_is_set(&local_err)) { if (error_is_set(&local_err)) { bs->job = NULL; bs->job = NULL; g_free(job); g_free(job); - bdrv_set_in_use(bs, 0); - bdrv_set_in_use(bs, 0); error_propagate(errp, local_err); error_propagate(errp, local_err); return NULL; return NULL; } } } } + error_setg(&job->blocker, "block device is in use by blo + error_setg(&job->blocker, "block device is in use by blo + BlockJobType_lookup[driver->job_type]); + BlockJobType_lookup[driver->job_type]); + bdrv_op_block_all(bs, job->blocker); + bdrv_op_block_all(bs, job->blocker); + + return job; return job; } } @@ -XXX,X +XXX,X @@ void block_job_completed(BlockJob *job, i @@ -XXX,X +XXX,X @@ void block_job_completed(BlockJob *job, i assert(bs->job == job); assert(bs->job == job); job->cb(job->opaque, ret); job->cb(job->opaque, ret); bs->job = NULL; bs->job = NULL; + bdrv_op_unblock_all(bs, job->blocker); + bdrv_op_unblock_all(bs, job->blocker); + error_free(job->blocker); + error_free(job->blocker); g_free(job); g_free(job); - bdrv_set_in_use(bs, 0); - bdrv_set_in_use(bs, 0); } } void block_job_set_speed(BlockJob *job, int64_t speed, Error void block_job_set_speed(BlockJob *job, int64_t speed, Error diff --git a/hw/block/dataplane/virtio-blk.c b/hw/block/datap diff --git a/hw/block/dataplane/virtio-blk.c b/hw/block/datap index XXXXXXX..XXXXXXX XXXXXX index XXXXXXX..XXXXXXX XXXXXX --- a/hw/block/dataplane/virtio-blk.c --- a/hw/block/dataplane/virtio-blk.c +++ b/hw/block/dataplane/virtio-blk.c +++ b/hw/block/dataplane/virtio-blk.c @@ -XXX,X +XXX,X @@ struct VirtIOBlockDataPlane { @@ -XXX,X +XXX,X @@ struct VirtIOBlockDataPlane { queue */ queue */ unsigned int num_reqs; unsigned int num_reqs; + + + /* Operation blocker on BDS */ + /* Operation blocker on BDS */ + Error *blocker; + Error *blocker; }; }; /* Raise an interrupt to signal guest, if necessary */ /* Raise an interrupt to signal guest, if necessary */ @@ -XXX,X +XXX,X @@ bool virtio_blk_data_plane_create(VirtIOD @@ -XXX,X +XXX,X @@ bool virtio_blk_data_plane_create(VirtIOD { { VirtIOBlockDataPlane *s; VirtIOBlockDataPlane *s; int fd; int fd; + Error *local_err = NULL; + Error *local_err = NULL; *dataplane = NULL; *dataplane = NULL; @@ -XXX,X +XXX,X @@ bool virtio_blk_data_plane_create(VirtIOD @@ -XXX,X +XXX,X @@ bool virtio_blk_data_plane_create(VirtIOD /* If dataplane is (re-)enabled while the guest is runni /* If dataplane is (re-)enabled while the guest is runni * block jobs that can conflict. * block jobs that can conflict. */ */ - if (bdrv_in_use(blk->conf.bs)) { - if (bdrv_in_use(blk->conf.bs)) { - error_report("cannot start dataplane thread while de - error_report("cannot start dataplane thread while de + if (bdrv_op_is_blocked(blk->conf.bs, BLOCK_OP_TYPE_DATAP + if (bdrv_op_is_blocked(blk->conf.bs, BLOCK_OP_TYPE_DATAP + error_report("%s", error_get_pretty(local_err)); + error_report("%s", error_get_pretty(local_err)); + error_free(local_err); + error_free(local_err); return false; return false; } } @@ -XXX,X +XXX,X @@ bool virtio_blk_data_plane_create(VirtIOD @@ -XXX,X +XXX,X @@ bool virtio_blk_data_plane_create(VirtIOD s->vdev = vdev; s->vdev = vdev; s->fd = fd; s->fd = fd; s->blk = blk; s->blk = blk; - - - /* Prevent block operations that conflict with data plan - /* Prevent block operations that conflict with data plan - bdrv_set_in_use(blk->conf.bs, 1); - bdrv_set_in_use(blk->conf.bs, 1); + error_setg(&s->blocker, "block device is in use by data + error_setg(&s->blocker, "block device is in use by data + bdrv_op_block_all(blk->conf.bs, s->blocker); + bdrv_op_block_all(blk->conf.bs, s->blocker); *dataplane = s; *dataplane = s; return true; return true; @@ -XXX,X +XXX,X @@ void virtio_blk_data_plane_destroy(VirtIO @@ -XXX,X +XXX,X @@ void virtio_blk_data_plane_destroy(VirtIO } } virtio_blk_data_plane_stop(s); virtio_blk_data_plane_stop(s); - bdrv_set_in_use(s->blk->conf.bs, 0); - bdrv_set_in_use(s->blk->conf.bs, 0); + bdrv_op_unblock_all(s->blk->conf.bs, s->blocker); + bdrv_op_unblock_all(s->blk->conf.bs, s->blocker); g_free(s); g_free(s); } } diff --git a/include/block/block.h b/include/block/block.h diff --git a/include/block/block.h b/include/block/block.h index XXXXXXX..XXXXXXX XXXXXX index XXXXXXX..XXXXXXX XXXXXX --- a/include/block/block.h --- a/include/block/block.h +++ b/include/block/block.h +++ b/include/block/block.h @@ -XXX,X +XXX,X @@ void bdrv_disable_copy_on_read(BlockDrive @@ -XXX,X +XXX,X @@ void bdrv_disable_copy_on_read(BlockDrive void bdrv_ref(BlockDriverState *bs); void bdrv_ref(BlockDriverState *bs); void bdrv_unref(BlockDriverState *bs); void bdrv_unref(BlockDriverState *bs); -void bdrv_set_in_use(BlockDriverState *bs, int in_use); -void bdrv_set_in_use(BlockDriverState *bs, int in_use); -int bdrv_in_use(BlockDriverState *bs); -int bdrv_in_use(BlockDriverState *bs); bool bdrv_op_is_blocked(BlockDriverState *bs, BlockOpType op bool bdrv_op_is_blocked(BlockDriverState *bs, BlockOpType op void bdrv_op_block(BlockDriverState *bs, BlockOpType op, Err void bdrv_op_block(BlockDriverState *bs, BlockOpType op, Err diff --git a/include/block/block_int.h b/include/block/block_ diff --git a/include/block/block_int.h b/include/block/block_ index XXXXXXX..XXXXXXX XXXXXX index XXXXXXX..XXXXXXX XXXXXX --- a/include/block/block_int.h --- a/include/block/block_int.h +++ b/include/block/block_int.h +++ b/include/block/block_int.h @@ -XXX,X +XXX,X @@ struct BlockDriverState { @@ -XXX,X +XXX,X @@ struct BlockDriverState { char device_name[32]; char device_name[32]; HBitmap *dirty_bitmap; HBitmap *dirty_bitmap; int refcnt; int refcnt; - int in_use; /* users other than guest access, eg. block - int in_use; /* users other than guest access, eg. block QTAILQ_ENTRY(BlockDriverState) list; QTAILQ_ENTRY(BlockDriverState) list; QLIST_HEAD(, BdrvTrackedRequest) tracked_requests; QLIST_HEAD(, BdrvTrackedRequest) tracked_requests; diff --git a/include/block/blockjob.h b/include/block/blockjo diff --git a/include/block/blockjob.h b/include/block/blockjo index XXXXXXX..XXXXXXX XXXXXX index XXXXXXX..XXXXXXX XXXXXX --- a/include/block/blockjob.h --- a/include/block/blockjob.h +++ b/include/block/blockjob.h +++ b/include/block/blockjob.h @@ -XXX,X +XXX,X @@ struct BlockJob { @@ -XXX,X +XXX,X @@ struct BlockJob { /** The completion function that will be called when the /** The completion function that will be called when the BlockDriverCompletionFunc *cb; BlockDriverCompletionFunc *cb; + /** Block other operations when block job is running */ + /** Block other operations when block job is running */ + Error *blocker; + Error *blocker; + + /** The opaque value that is passed to the completion fu /** The opaque value that is passed to the completion fu void *opaque; void *opaque; }; }; commit XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX commit XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Author: Fam Zheng Author: Fam Zheng Date: Fri Nov 22 12:10:38 2013 +0800 Date: Fri Nov 22 12:10:38 2013 +0800 block: Add checks of blocker in block operations block: Add checks of blocker in block operations Before operate on a BlockDriverState, respective types ar Before operate on a BlockDriverState, respective types ar against bs->op_blockers and it will error out if there's against bs->op_blockers and it will error out if there's Signed-off-by: Fam Zheng Signed-off-by: Fam Zheng diff --git a/blockdev-nbd.c b/blockdev-nbd.c < index XXXXXXX..XXXXXXX XXXXXX < --- a/blockdev-nbd.c < +++ b/blockdev-nbd.c < @@ -XXX,X +XXX,X @@ void qmp_nbd_server_add(const char *devic < return; < } < < + if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_NBD_SERVER_ADD, < + return; < + } < + < if (!has_writable) { < writable = false; < } < diff --git a/blockdev.c b/blockdev.c diff --git a/blockdev.c b/blockdev.c index XXXXXXX..XXXXXXX XXXXXX index XXXXXXX..XXXXXXX XXXXXX --- a/blockdev.c --- a/blockdev.c +++ b/blockdev.c +++ b/blockdev.c @@ -XXX,X +XXX,X @@ SnapshotInfo *qmp_blockdev_snapshot_delet @@ -XXX,X +XXX,X @@ SnapshotInfo *qmp_blockdev_snapshot_delet return NULL; return NULL; } } + if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_INTERNAL_SNAPSH + if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_INTERNAL_SNAPSH + errp)) { + errp)) { + return NULL; + return NULL; + } + } + + ret = bdrv_snapshot_find_by_id_and_name(bs, id, name, &s ret = bdrv_snapshot_find_by_id_and_name(bs, id, name, &s if (error_is_set(&local_err)) { if (error_is_set(&local_err)) { error_propagate(errp, local_err); error_propagate(errp, local_err); @@ -XXX,X +XXX,X @@ static void internal_snapshot_prepare(Blk @@ -XXX,X +XXX,X @@ static void internal_snapshot_prepare(Blk return; return; } } + if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_INTERNAL_SNAPSH + if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_INTERNAL_SNAPSH + return; + return; + } + } + + if (!bdrv_is_inserted(bs)) { if (!bdrv_is_inserted(bs)) { error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, device); error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, device); return; return; @@ -XXX,X +XXX,X @@ void qmp_block_passwd(const char *device, < return; < } < < + if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_PASSWD, errp)) < + return; < + } < + < err = bdrv_set_key(bs, password); < if (err == -EINVAL) { < error_set(errp, QERR_DEVICE_NOT_ENCRYPTED, bdrv_get_ < @@ -XXX,X +XXX,X @@ void qmp_change_blockdev(const char *devi @@ -XXX,X +XXX,X @@ void qmp_change_blockdev(const char *devi return; return; } } + if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_CHANGE, errp)) + if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_CHANGE, errp)) + return; + return; + } + } + + if (format) { if (format) { drv = bdrv_find_whitelisted_format(format, bs->read_ drv = bdrv_find_whitelisted_format(format, bs->read_ if (!drv) { if (!drv) { @@ -XXX,X +XXX,X @@ void qmp_block_set_io_throttle(const char < return; < } < < + if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_SET_IO_THROTTLE < + return; < + } < + < memset(&cfg, 0, sizeof(cfg)); < cfg.buckets[THROTTLE_BPS_TOTAL].avg = bps; < cfg.buckets[THROTTLE_BPS_READ].avg = bps_rd; < @@ -XXX,X +XXX,X @@ void qmp_block_resize(const char *device, @@ -XXX,X +XXX,X @@ void qmp_block_resize(const char *device, return; return; } } + if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_RESIZE, errp)) + if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_RESIZE, errp)) + return; + return; + } + } + + if (size < 0) { if (size < 0) { error_set(errp, QERR_INVALID_PARAMETER_VALUE, "size" error_set(errp, QERR_INVALID_PARAMETER_VALUE, "size" return; return; commit XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX commit XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Author: Fam Zheng Author: Fam Zheng Date: Tue Sep 24 14:58:30 2013 +0800 Date: Tue Sep 24 14:58:30 2013 +0800 block: Parse "backing" option to reference existing BDS block: Parse "backing" option to reference existing BDS Signed-off-by: Fam Zheng Signed-off-by: Fam Zheng diff --git a/block.c b/block.c diff --git a/block.c b/block.c index XXXXXXX..XXXXXXX XXXXXX index XXXXXXX..XXXXXXX XXXXXX --- a/block.c --- a/block.c +++ b/block.c +++ b/block.c > @@ -XXX,X +XXX,X @@ fail: > /* > * Opens the backing file for a BlockDriverState if not yet > * > + * If backing_bs is not NULL or empty, find the BDS by name > + * the backing_hd, in this case options is ignored. > + * > * options is a QDict of options to pass to the block driver > * empty set of options. The reference to the QDict is trans > * function (even on failure), so if the caller intends to r > * it needs to use QINCREF() before calling bdrv_file_open. > */ > -int bdrv_open_backing_file(BlockDriverState *bs, QDict *opti > +int bdrv_open_backing_file(BlockDriverState *bs, const char > + QDict *options, Error **errp) > { > char backing_filename[PATH_MAX]; > int back_flags, ret; > BlockDriver *back_drv = NULL; > Error *local_err = NULL; > > + if (backing_bs && *backing_bs != '\0') { > + bs->backing_hd = bdrv_find(backing_bs); > + if (!bs->backing_hd) { > + error_setg(errp, "Backing device not found: %s", > + return -ENOENT; > + } > + bdrv_ref(bs->backing_hd); > + assert(!bs->backing_blocker); > + error_setg(&bs->backing_blocker, > + "device is used as backing hd of '%s'", > + bs->device_name); > + bdrv_op_block_all(bs->backing_hd, bs->backing_blocke > + pstrcpy(bs->backing_file, sizeof(bs->backing_file), > + bs->backing_hd->filename); > + pstrcpy(bs->backing_format, sizeof(bs->backing_forma > + bs->backing_hd->drv->format_name); > + } > if (bs->backing_hd != NULL) { > QDECREF(options); > return 0; @@ -XXX,X +XXX,X @@ int bdrv_open(BlockDriverState *bs, const @@ -XXX,X +XXX,X @@ int bdrv_open(BlockDriverState *bs, const /* If there is a backing file, use it */ /* If there is a backing file, use it */ if ((flags & BDRV_O_NO_BACKING) == 0) { if ((flags & BDRV_O_NO_BACKING) == 0) { QDict *backing_options; QDict *backing_options; - - - qdict_extract_subqdict(options, &backing_options, "b | qdict_extract_subqdict(options, &backing_options, "b - ret = bdrv_open_backing_file(bs, backing_options, &l - ret = bdrv_open_backing_file(bs, backing_options, &l - if (ret < 0) { | + ret = bdrv_open_backing_file(bs, qdict_get_try_str(o - goto close_and_fail; | + backing_options, &local + const char *backing_bs; | + + | + qdict_del(options, "backing"); + backing_bs = qdict_get_try_str(options, "backing"); | if (ret < 0) { + if (backing_bs) { | goto close_and_fail; + bs->backing_hd = bdrv_find(backing_bs); < + qdict_del(options, "backing"); < + if (bs->backing_hd) { < + bdrv_ref(bs->backing_hd); < + assert(!bs->backing_blocker); < + error_setg(&bs->backing_blocker, < + "device is used as backing hd of < + bs->device_name); < + bdrv_op_block_all(bs->backing_hd, bs->backin < + pstrcpy(bs->backing_file, sizeof(bs->backing < + bs->backing_hd->filename); < + pstrcpy(bs->backing_format, sizeof(bs->backi < + bs->backing_hd->drv->format_name); < + } else { < + error_setg(errp, "Backing device not found: < + goto close_and_fail; < + } < + } else { < + qdict_extract_subqdict(options, &backing_options < + ret = bdrv_open_backing_file(bs, backing_options < + if (ret < 0) { < + goto close_and_fail; < + } < } } } < < @@ -XXX,X +XXX,X @@ void bdrv_close(BlockDriverState *bs) @@ -XXX,X +XXX,X @@ void bdrv_close(BlockDriverState *bs) if (bs->drv) { if (bs->drv) { if (bs->backing_hd) { if (bs->backing_hd) { + if (error_is_set(&bs->backing_blocker)) { | + bdrv_op_unblock_all(bs->backing_hd, bs->backing_ + bdrv_op_unblock_all(bs->backing_hd, | + error_free(bs->backing_blocker); + bs->backing_blocker); < + error_free(bs->backing_blocker); < + } < bdrv_unref(bs->backing_hd); bdrv_unref(bs->backing_hd); bs->backing_hd = NULL; bs->backing_hd = NULL; } } > diff --git a/block/mirror.c b/block/mirror.c > index XXXXXXX..XXXXXXX XXXXXX > --- a/block/mirror.c > +++ b/block/mirror.c > @@ -XXX,X +XXX,X @@ static void mirror_complete(BlockJob *job > Error *local_err = NULL; > int ret; > > - ret = bdrv_open_backing_file(s->target, NULL, &local_err > + ret = bdrv_open_backing_file(s->target, NULL, NULL, &loc > if (ret < 0) { > char backing_filename[PATH_MAX]; > bdrv_get_full_backing_filename(s->target, backing_fi > diff --git a/include/block/block.h b/include/block/block.h > index XXXXXXX..XXXXXXX XXXXXX > --- a/include/block/block.h > +++ b/include/block/block.h > @@ -XXX,X +XXX,X @@ int bdrv_parse_cache_flags(const char *mo > int bdrv_parse_discard_flags(const char *mode, int *flags); > int bdrv_file_open(BlockDriverState **pbs, const char *filen > QDict *options, int flags, Error **errp); > -int bdrv_open_backing_file(BlockDriverState *bs, QDict *opti > +int bdrv_open_backing_file(BlockDriverState *bs, const char > + QDict *options, Error **errp); > int bdrv_open(BlockDriverState *bs, const char *filename, QD > int flags, BlockDriver *drv, Error **errp); > BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_que diff --git a/include/block/block_int.h b/include/block/block_ diff --git a/include/block/block_int.h b/include/block/block_ index XXXXXXX..XXXXXXX XXXXXX index XXXXXXX..XXXXXXX XXXXXX --- a/include/block/block_int.h --- a/include/block/block_int.h +++ b/include/block/block_int.h +++ b/include/block/block_int.h @@ -XXX,X +XXX,X @@ struct BlockDriverState { @@ -XXX,X +XXX,X @@ struct BlockDriverState { BlockJob *job; BlockJob *job; QDict *options; QDict *options; + + + /* For backing reference, block the operations of named + /* For backing reference, block the operations of named + Error *backing_blocker; + Error *backing_blocker; }; }; int get_tmp_filename(char *filename, int size); int get_tmp_filename(char *filename, int size); commit XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX commit XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Author: Fam Zheng Author: Fam Zheng Date: Wed Sep 25 10:19:42 2013 +0800 Date: Wed Sep 25 10:19:42 2013 +0800 qmp: add command 'blockdev-backup' qmp: add command 'blockdev-backup' Similar to drive-backup, but this command uses a device i Similar to drive-backup, but this command uses a device i instead of creating/opening an image file. instead of creating/opening an image file. Also add blocker on target bs, since the target is also a Also add blocker on target bs, since the target is also a now. now. Signed-off-by: Fam Zheng Signed-off-by: Fam Zheng diff --git a/block/backup.c b/block/backup.c diff --git a/block/backup.c b/block/backup.c index XXXXXXX..XXXXXXX XXXXXX index XXXXXXX..XXXXXXX XXXXXX --- a/block/backup.c --- a/block/backup.c +++ b/block/backup.c +++ b/block/backup.c @@ -XXX,X +XXX,X @@ static void coroutine_fn backup_run(void @@ -XXX,X +XXX,X @@ static void coroutine_fn backup_run(void hbitmap_free(job->bitmap); hbitmap_free(job->bitmap); bdrv_iostatus_disable(target); bdrv_iostatus_disable(target); + bdrv_op_unblock_all(target, job->common.blocker); + bdrv_op_unblock_all(target, job->common.blocker); bdrv_unref(target); bdrv_unref(target); block_job_completed(&job->common, ret); block_job_completed(&job->common, ret); @@ -XXX,X +XXX,X @@ void backup_start(BlockDriverState *bs, B @@ -XXX,X +XXX,X @@ void backup_start(BlockDriverState *bs, B return; return; } } + if (!bdrv_is_inserted(bs)) { + if (!bdrv_is_inserted(bs)) { + error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, bs->devic + error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, bs->devic + return; + return; + } + } + + + if (!bdrv_is_inserted(target)) { + if (!bdrv_is_inserted(target)) { + error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, target->d + error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, target->d + return; + return; + } + } + + + if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_BACKUP, errp)) | + if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_BACKUP_SOURCE, + return; + return; + } + } + + + if (bdrv_op_is_blocked(target, BLOCK_OP_TYPE_BACKUP, err | + if (bdrv_op_is_blocked(target, BLOCK_OP_TYPE_BACKUP_TARG + return; + return; + } + } + + len = bdrv_getlength(bs); len = bdrv_getlength(bs); if (len < 0) { if (len < 0) { error_setg_errno(errp, -len, "unable to get length f error_setg_errno(errp, -len, "unable to get length f @@ -XXX,X +XXX,X @@ void backup_start(BlockDriverState *bs, B @@ -XXX,X +XXX,X @@ void backup_start(BlockDriverState *bs, B return; return; } } + bdrv_op_block_all(target, job->common.blocker); + bdrv_op_block_all(target, job->common.blocker); + bdrv_op_unblock(target, BLOCK_OP_TYPE_NBD_SERVER_ADD, jo < + + job->on_source_error = on_source_error; job->on_source_error = on_source_error; job->on_target_error = on_target_error; job->on_target_error = on_target_error; job->target = target; job->target = target; diff --git a/blockdev.c b/blockdev.c diff --git a/blockdev.c b/blockdev.c index XXXXXXX..XXXXXXX XXXXXX index XXXXXXX..XXXXXXX XXXXXX --- a/blockdev.c --- a/blockdev.c +++ b/blockdev.c +++ b/blockdev.c @@ -XXX,X +XXX,X @@ void qmp_drive_backup(const char *device, @@ -XXX,X +XXX,X @@ void qmp_drive_backup(const char *device, return; return; } } + /* Although backup_run has this check too, we need to us + /* Although backup_run has this check too, we need to us + * do an early check redundantly. */ + * do an early check redundantly. */ if (!bdrv_is_inserted(bs)) { if (!bdrv_is_inserted(bs)) { error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, device); error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, device); return; return; @@ -XXX,X +XXX,X @@ void qmp_drive_backup(const char *device, @@ -XXX,X +XXX,X @@ void qmp_drive_backup(const char *device, > } > } > > + /* Early check to avoid creating target */ > if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_BACKUP_SOURCE, > return; > } > @@ -XXX,X +XXX,X @@ void qmp_drive_backup(const char *device, } } } } +void qmp_blockdev_backup(const char *device, const char *tar +void qmp_blockdev_backup(const char *device, const char *tar + enum MirrorSyncMode sync, + enum MirrorSyncMode sync, + bool has_speed, int64_t speed, + bool has_speed, int64_t speed, + bool has_on_source_error, + bool has_on_source_error, + BlockdevOnError on_source_error, + BlockdevOnError on_source_error, + bool has_on_target_error, + bool has_on_target_error, + BlockdevOnError on_target_error, + BlockdevOnError on_target_error, + Error **errp) + Error **errp) +{ +{ + BlockDriverState *bs; + BlockDriverState *bs; + BlockDriverState *target_bs; + BlockDriverState *target_bs; + Error *local_err = NULL; + Error *local_err = NULL; + + + if (!has_speed) { + if (!has_speed) { + speed = 0; + speed = 0; + } + } + if (!has_on_source_error) { + if (!has_on_source_error) { + on_source_error = BLOCKDEV_ON_ERROR_REPORT; + on_source_error = BLOCKDEV_ON_ERROR_REPORT; + } + } + if (!has_on_target_error) { + if (!has_on_target_error) { + on_target_error = BLOCKDEV_ON_ERROR_REPORT; + on_target_error = BLOCKDEV_ON_ERROR_REPORT; + } + } + + + bs = bdrv_find(device); + bs = bdrv_find(device); + if (!bs) { + if (!bs) { + error_set(errp, QERR_DEVICE_NOT_FOUND, device); + error_set(errp, QERR_DEVICE_NOT_FOUND, device); + return; + return; + } + } + + + target_bs = bdrv_find(target); + target_bs = bdrv_find(target); + if (!target_bs) { + if (!target_bs) { + error_set(errp, QERR_DEVICE_NOT_FOUND, target); + error_set(errp, QERR_DEVICE_NOT_FOUND, target); + return; + return; + } + } + + + bdrv_ref(target_bs); + bdrv_ref(target_bs); + backup_start(bs, target_bs, speed, sync, on_source_error + backup_start(bs, target_bs, speed, sync, on_source_error + block_job_cb, bs, &local_err); + block_job_cb, bs, &local_err); + if (local_err != NULL) { + if (local_err != NULL) { + bdrv_unref(target_bs); + bdrv_unref(target_bs); + error_propagate(errp, local_err); + error_propagate(errp, local_err); + } + } +} +} + + #define DEFAULT_MIRROR_BUF_SIZE (10 << 20) #define DEFAULT_MIRROR_BUF_SIZE (10 << 20) void qmp_drive_mirror(const char *device, const char *target void qmp_drive_mirror(const char *device, const char *target diff --git a/qapi-schema.json b/qapi-schema.json diff --git a/qapi-schema.json b/qapi-schema.json index XXXXXXX..XXXXXXX XXXXXX index XXXXXXX..XXXXXXX XXXXXX --- a/qapi-schema.json --- a/qapi-schema.json +++ b/qapi-schema.json +++ b/qapi-schema.json @@ -XXX,X +XXX,X @@ @@ -XXX,X +XXX,X @@ '*on-target-error': 'BlockdevOnError' } } '*on-target-error': 'BlockdevOnError' } } ## ## +# @BlockdevBackup +# @BlockdevBackup +# +# +# @device: the name of the device which should be copied. +# @device: the name of the device which should be copied. +# +# +# @target: the name of the backup target device | +# @target: the name of the backup target device. +# +# +# @sync: what parts of the disk image should be copied to th +# @sync: what parts of the disk image should be copied to th +# (all the disk, only the sectors allocated in the to +# (all the disk, only the sectors allocated in the to +# only new I/O). +# only new I/O). +# +# +# @speed: #optional the maximum speed, in bytes per second | +# @speed: #optional the maximum speed, in bytes per second. +# +# +# @on-source-error: #optional the action to take on an error +# @on-source-error: #optional the action to take on an error +# default 'report'. 'stop' and 'enospc' c +# default 'report'. 'stop' and 'enospc' c +# if the block device supports io-status ( +# if the block device supports io-status ( +# +# +# @on-target-error: #optional the action to take on an error +# @on-target-error: #optional the action to take on an error +# default 'report' (no limitations, since +# default 'report' (no limitations, since +# a different block device than @device). +# a different block device than @device). +# +# +# Note that @on-source-error and @on-target-error only affec +# Note that @on-source-error and @on-target-error only affec +# If an error occurs during a guest write request, the devic +# If an error occurs during a guest write request, the devic +# actions will be used. +# actions will be used. +# +# +# Since: 1.7 | +# Since: 1.8 +## +## +{ 'type': 'BlockdevBackup', +{ 'type': 'BlockdevBackup', + 'data': { 'device': 'str', 'target': 'str', + 'data': { 'device': 'str', 'target': 'str', + 'sync': 'MirrorSyncMode', + 'sync': 'MirrorSyncMode', + '*speed': 'int', + '*speed': 'int', + '*on-source-error': 'BlockdevOnError', + '*on-source-error': 'BlockdevOnError', + '*on-target-error': 'BlockdevOnError' } } + '*on-target-error': 'BlockdevOnError' } } + + +## +## # @Abort # @Abort # # # This action can be used to test transaction failure. # This action can be used to test transaction failure. @@ -XXX,X +XXX,X @@ @@ -XXX,X +XXX,X @@ { 'command': 'drive-backup', 'data': 'DriveBackup' } { 'command': 'drive-backup', 'data': 'DriveBackup' } ## ## +# @blockdev-backup +# @blockdev-backup +# +# +# Block device version for drive-backup command. Use existin +# Block device version for drive-backup command. Use existin +# of backup. +# of backup. +# +# +# For the arguments, see the documentation of BlockdevBackup +# For the arguments, see the documentation of BlockdevBackup +# +# +# Returns: nothing on success | +# Returns: Nothing on success. +# If @device or @target is not a valid block device | +# If @device or @target is not a valid block device +# +# +# Since 1.7 | +# Since 1.8 +## +## +{ 'command': 'blockdev-backup', 'data': 'BlockdevBackup' } +{ 'command': 'blockdev-backup', 'data': 'BlockdevBackup' } + + +## +## # @drive-mirror # @drive-mirror # # # Start mirroring a block device's writes to a new destinati # Start mirroring a block device's writes to a new destinati diff --git a/qmp-commands.hx b/qmp-commands.hx diff --git a/qmp-commands.hx b/qmp-commands.hx index XXXXXXX..XXXXXXX XXXXXX index XXXXXXX..XXXXXXX XXXXXX --- a/qmp-commands.hx --- a/qmp-commands.hx +++ b/qmp-commands.hx +++ b/qmp-commands.hx @@ -XXX,X +XXX,X @@ Example: @@ -XXX,X +XXX,X @@ Example: "sync": "full "sync": "full "target": "ba "target": "ba <- { "return": {} } <- { "return": {} } + + +EQMP +EQMP + + + { + { + .name = "blockdev-backup", + .name = "blockdev-backup", + .args_type = "sync:s,device:B,target:B,speed:i?," + .args_type = "sync:s,device:B,target:B,speed:i?," + "on-source-error:s?,on-target-error:s? + "on-source-error:s?,on-target-error:s? + .mhandler.cmd_new = qmp_marshal_input_blockdev_backu + .mhandler.cmd_new = qmp_marshal_input_blockdev_backu + }, + }, + + +SQMP +SQMP +blockdev-backup +blockdev-backup +------------ +------------ + + +The device version of drive-backup: this command takes a exi +The device version of drive-backup: this command takes a exi +as backup target. +as backup target. + + +Arguments: +Arguments: + + +- "device": the name of the device which should be copied. +- "device": the name of the device which should be copied. + (json-string) + (json-string) +- "target": the target of the new image. If the file exists, +- "target": the target of the new image. If the file exists, + device, the existing file/device will be used as + device, the existing file/device will be used as + destination. If it does not exist, a new file w + destination. If it does not exist, a new file w + (json-string) + (json-string) +- "sync": what parts of the disk image should be copied to t +- "sync": what parts of the disk image should be copied to t + possibilities include "full" for all the disk, "top" for o | + possibilities include "full" for all the disk, "to + allocated in the topmost image, or "none" to only replicat | + sectors allocated in the topmost image, or "none" + (MirrorSyncMode). | + new I/O (MirrorSyncMode). +- "mode": whether and how QEMU should create a new image < + (NewImageMode, optional, default 'absolute-paths') < +- "speed": the maximum speed, in bytes per second (json-int, +- "speed": the maximum speed, in bytes per second (json-int, +- "on-source-error": the action to take on an error on the s +- "on-source-error": the action to take on an error on the s + 'report'. 'stop' and 'enospc' can only + 'report'. 'stop' and 'enospc' can only + if the block device supports io-status. + if the block device supports io-status. + (BlockdevOnError, optional) + (BlockdevOnError, optional) +- "on-target-error": the action to take on an error on the t +- "on-target-error": the action to take on an error on the t + 'report' (no limitations, since this ap + 'report' (no limitations, since this ap + a different block device than device). + a different block device than device). + (BlockdevOnError, optional) + (BlockdevOnError, optional) + + +Example: +Example: +-> { "execute": "blockdev-backup", "arguments": { "device": +-> { "execute": "blockdev-backup", "arguments": { "device": + "target": + "target": +<- { "return": {} } +<- { "return": {} } + + EQMP EQMP { { commit XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX commit XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Author: Fam Zheng Author: Fam Zheng Date: Fri Nov 22 10:58:43 2013 +0800 Date: Fri Nov 22 10:58:43 2013 +0800 block: Allow backup on referenced named BlockDriverState block: Allow backup on referenced named BlockDriverState Drive backup is a read only operation on source bs. We wa Drive backup is a read only operation on source bs. We wa this specific case to enable image-fleecing. Note that wh this specific case to enable image-fleecing. Note that wh image-fleecing job starts, the job still add its blocker image-fleecing job starts, the job still add its blocker and any other operation on it will be blocked by that. and any other operation on it will be blocked by that. Signed-off-by: Fam Zheng Signed-off-by: Fam Zheng diff --git a/block.c b/block.c diff --git a/block.c b/block.c index XXXXXXX..XXXXXXX XXXXXX index XXXXXXX..XXXXXXX XXXXXX --- a/block.c --- a/block.c +++ b/block.c +++ b/block.c @@ -XXX,X +XXX,X @@ int bdrv_open(BlockDriverState *bs, const | @@ -XXX,X +XXX,X @@ int bdrv_open_backing_file(BlockDriverSta "device is used as backing hd of | "device is used as backing hd of '%s'", bs->device_name); | bs->device_name); bdrv_op_block_all(bs->backing_hd, bs->backin | bdrv_op_block_all(bs->backing_hd, bs->backing_blocke + bdrv_op_unblock(bs->backing_hd, BLOCK_OP_TYP | + bdrv_op_unblock(bs->backing_hd, BLOCK_OP_TYPE_BACKUP + bs->backing_blocker); | + bs->backing_blocker); pstrcpy(bs->backing_file, sizeof(bs->backing | pstrcpy(bs->backing_file, sizeof(bs->backing_file), bs->backing_hd->filename); | bs->backing_hd->filename); pstrcpy(bs->backing_format, sizeof(bs->backi | pstrcpy(bs->backing_format, sizeof(bs->backing_forma