Block layer patches

-----BEGIN PGP SIGNATURE-----
 
 iQIcBAABAgAGBQJZr/vJAAoJEH8JsnLIjy/WrNMP/RMlpIfzjPTIKl1qwdxEbtEe
 kdsQulnSILVAWnXldB6xiQ8/epO2oTP+8sE9VCAoblQfJjD6RgffF1YCC7h1ZyBX
 182ZnhapIwprH5RLKz/kgjfkx5/bCYjqpQ3JzznKJHNXJOAexznrYJMcbA2agfII
 5qijA06dDoMIQTz49J2vvFAHrRUq/JqK85Ao8Zk41GDHDan5OfvQwsgt+Wa0V3vz
 mV6G1UsWCe4pmrv7v7/buhkVypy/BYz7vu6N20+2o3GDLwHmsgfKogUiSAC1N3iR
 olkeKtXdplY17iO6VgVrmFdkvaja0XCxYJjXnL54x/f1lQQQc01wUFNrh6WoIQLO
 Bl+XZ0oEQpFKJeBlu9mbDvgit0AGYE/yaLkCnfRFOU15lW5rjwqpF8husU0ntUcI
 TzGWt21kG0EXisejLMGEzEkMwkdhTwX6U+U7x5pF+x+pwSdcREDekeFcVhsb42Y/
 brTgZCXdf32eJ8gOSzFoBJ5KfFaCqKgA6lWAv/kLsVs8DN+MnAv3SJGRBr22854W
 yJC5e3yLh36RVemjBqbqsU9VMD/P8fB3nJQwZRMyQh5A3RNxrK1y6e4XIqRwGqcC
 aj4cT2GbLWFH+EJUVdSRELmrLJPLyj5a1lU28Dq6b2Q34f9Hvg8GjSBOFf+4Vx6C
 N/z6+8O1mDtXdGHuCbmI
 =qJjo
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/kevin/tags/for-upstream' into staging

Block layer patches

# gpg: Signature made Wed 06 Sep 2017 14:44:41 BST
# gpg:                using RSA key 0x7F09B272C88F2FD6
# gpg: Good signature from "Kevin Wolf <kwolf@redhat.com>"
# Primary key fingerprint: DC3D EB15 9A9A F95D 3D74  56FE 7F09 B272 C88F 2FD6

* remotes/kevin/tags/for-upstream:
  qcow2: move qcow2_store_persistent_dirty_bitmaps() before cache flushing
  qemu-iotests: add 184 for throttle filter driver
  block: add throttle block filter driver
  block: convert ThrottleGroup to object with QOM
  block: tidy ThrottleGroupMember initializations
  block: add aio_context field in ThrottleGroupMember
  block: move ThrottleGroup membership to ThrottleGroupMember
  block: document semantics of bdrv_co_preadv|pwritev
  qcow: Check failure of bdrv_getlength() and bdrv_truncate()
  qcow: Change signature of get_cluster_offset()
  block: add default implementations for bdrv_co_get_block_status()
  block: remove bdrv_truncate callback in blkdebug
  block: remove unused bdrv_media_changed
  block: pass bdrv_* methods to bs->file by default in block filters

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2017-09-07 10:45:18 +01:00
commit 8ee5f9b3ec
26 changed files with 1885 additions and 452 deletions

35
block.c
View File

@ -496,6 +496,8 @@ int bdrv_probe_blocksizes(BlockDriverState *bs, BlockSizes *bsz)
if (drv && drv->bdrv_probe_blocksizes) { if (drv && drv->bdrv_probe_blocksizes) {
return drv->bdrv_probe_blocksizes(bs, bsz); return drv->bdrv_probe_blocksizes(bs, bsz);
} else if (drv && drv->is_filter && bs->file) {
return bdrv_probe_blocksizes(bs->file->bs, bsz);
} }
return -ENOTSUP; return -ENOTSUP;
@ -513,6 +515,8 @@ int bdrv_probe_geometry(BlockDriverState *bs, HDGeometry *geo)
if (drv && drv->bdrv_probe_geometry) { if (drv && drv->bdrv_probe_geometry) {
return drv->bdrv_probe_geometry(bs, geo); return drv->bdrv_probe_geometry(bs, geo);
} else if (drv && drv->is_filter && bs->file) {
return bdrv_probe_geometry(bs->file->bs, geo);
} }
return -ENOTSUP; return -ENOTSUP;
@ -3426,11 +3430,15 @@ int bdrv_truncate(BdrvChild *child, int64_t offset, PreallocMode prealloc,
assert(child->perm & BLK_PERM_RESIZE); assert(child->perm & BLK_PERM_RESIZE);
/* if bs->drv == NULL, bs is closed, so there's nothing to do here */
if (!drv) { if (!drv) {
error_setg(errp, "No medium inserted"); error_setg(errp, "No medium inserted");
return -ENOMEDIUM; return -ENOMEDIUM;
} }
if (!drv->bdrv_truncate) { if (!drv->bdrv_truncate) {
if (bs->file && drv->is_filter) {
return bdrv_truncate(bs->file, offset, prealloc, errp);
}
error_setg(errp, "Image format driver does not support resize"); error_setg(errp, "Image format driver does not support resize");
return -ENOTSUP; return -ENOTSUP;
} }
@ -3767,6 +3775,9 @@ int bdrv_has_zero_init(BlockDriverState *bs)
if (bs->drv->bdrv_has_zero_init) { if (bs->drv->bdrv_has_zero_init) {
return bs->drv->bdrv_has_zero_init(bs); return bs->drv->bdrv_has_zero_init(bs);
} }
if (bs->file && bs->drv->is_filter) {
return bdrv_has_zero_init(bs->file->bs);
}
/* safe default */ /* safe default */
return 0; return 0;
@ -3821,10 +3832,16 @@ void bdrv_get_backing_filename(BlockDriverState *bs,
int bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) int bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
{ {
BlockDriver *drv = bs->drv; BlockDriver *drv = bs->drv;
if (!drv) /* if bs->drv == NULL, bs is closed, so there's nothing to do here */
if (!drv) {
return -ENOMEDIUM; return -ENOMEDIUM;
if (!drv->bdrv_get_info) }
if (!drv->bdrv_get_info) {
if (bs->file && drv->is_filter) {
return bdrv_get_info(bs->file->bs, bdi);
}
return -ENOTSUP; return -ENOTSUP;
}
memset(bdi, 0, sizeof(*bdi)); memset(bdi, 0, sizeof(*bdi));
return drv->bdrv_get_info(bs, bdi); return drv->bdrv_get_info(bs, bdi);
} }
@ -4174,20 +4191,6 @@ bool bdrv_is_inserted(BlockDriverState *bs)
return true; return true;
} }
/**
* Return whether the media changed since the last call to this
* function, or -ENOTSUP if we don't know. Most drivers don't know.
*/
int bdrv_media_changed(BlockDriverState *bs)
{
BlockDriver *drv = bs->drv;
if (drv && drv->bdrv_media_changed) {
return drv->bdrv_media_changed(bs);
}
return -ENOTSUP;
}
/** /**
* If eject_flag is TRUE, eject the media. Otherwise, close the tray * If eject_flag is TRUE, eject the media. Otherwise, close the tray
*/ */

View File

@ -25,6 +25,7 @@ block-obj-y += accounting.o dirty-bitmap.o
block-obj-y += write-threshold.o block-obj-y += write-threshold.o
block-obj-y += backup.o block-obj-y += backup.o
block-obj-$(CONFIG_REPLICATION) += replication.o block-obj-$(CONFIG_REPLICATION) += replication.o
block-obj-y += throttle.o
block-obj-y += crypto.o block-obj-y += crypto.o

View File

@ -628,16 +628,6 @@ static int coroutine_fn blkdebug_co_pdiscard(BlockDriverState *bs,
return bdrv_co_pdiscard(bs->file->bs, offset, bytes); return bdrv_co_pdiscard(bs->file->bs, offset, bytes);
} }
static int64_t coroutine_fn blkdebug_co_get_block_status(
BlockDriverState *bs, int64_t sector_num, int nb_sectors, int *pnum,
BlockDriverState **file)
{
*pnum = nb_sectors;
*file = bs->file->bs;
return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID |
(sector_num << BDRV_SECTOR_BITS);
}
static void blkdebug_close(BlockDriverState *bs) static void blkdebug_close(BlockDriverState *bs)
{ {
BDRVBlkdebugState *s = bs->opaque; BDRVBlkdebugState *s = bs->opaque;
@ -808,12 +798,6 @@ static int64_t blkdebug_getlength(BlockDriverState *bs)
return bdrv_getlength(bs->file->bs); return bdrv_getlength(bs->file->bs);
} }
static int blkdebug_truncate(BlockDriverState *bs, int64_t offset,
PreallocMode prealloc, Error **errp)
{
return bdrv_truncate(bs->file, offset, prealloc, errp);
}
static void blkdebug_refresh_filename(BlockDriverState *bs, QDict *options) static void blkdebug_refresh_filename(BlockDriverState *bs, QDict *options)
{ {
BDRVBlkdebugState *s = bs->opaque; BDRVBlkdebugState *s = bs->opaque;
@ -896,6 +880,7 @@ static BlockDriver bdrv_blkdebug = {
.format_name = "blkdebug", .format_name = "blkdebug",
.protocol_name = "blkdebug", .protocol_name = "blkdebug",
.instance_size = sizeof(BDRVBlkdebugState), .instance_size = sizeof(BDRVBlkdebugState),
.is_filter = true,
.bdrv_parse_filename = blkdebug_parse_filename, .bdrv_parse_filename = blkdebug_parse_filename,
.bdrv_file_open = blkdebug_open, .bdrv_file_open = blkdebug_open,
@ -904,7 +889,6 @@ static BlockDriver bdrv_blkdebug = {
.bdrv_child_perm = bdrv_filter_default_perms, .bdrv_child_perm = bdrv_filter_default_perms,
.bdrv_getlength = blkdebug_getlength, .bdrv_getlength = blkdebug_getlength,
.bdrv_truncate = blkdebug_truncate,
.bdrv_refresh_filename = blkdebug_refresh_filename, .bdrv_refresh_filename = blkdebug_refresh_filename,
.bdrv_refresh_limits = blkdebug_refresh_limits, .bdrv_refresh_limits = blkdebug_refresh_limits,
@ -913,7 +897,7 @@ static BlockDriver bdrv_blkdebug = {
.bdrv_co_flush_to_disk = blkdebug_co_flush, .bdrv_co_flush_to_disk = blkdebug_co_flush,
.bdrv_co_pwrite_zeroes = blkdebug_co_pwrite_zeroes, .bdrv_co_pwrite_zeroes = blkdebug_co_pwrite_zeroes,
.bdrv_co_pdiscard = blkdebug_co_pdiscard, .bdrv_co_pdiscard = blkdebug_co_pdiscard,
.bdrv_co_get_block_status = blkdebug_co_get_block_status, .bdrv_co_get_block_status = bdrv_co_get_block_status_from_file,
.bdrv_debug_event = blkdebug_debug_event, .bdrv_debug_event = blkdebug_debug_event,
.bdrv_debug_breakpoint = blkdebug_debug_breakpoint, .bdrv_debug_breakpoint = blkdebug_debug_breakpoint,

View File

@ -273,9 +273,6 @@ BlockBackend *blk_new(uint64_t perm, uint64_t shared_perm)
blk->shared_perm = shared_perm; blk->shared_perm = shared_perm;
blk_set_enable_write_cache(blk, true); blk_set_enable_write_cache(blk, true);
qemu_co_mutex_init(&blk->public.throttled_reqs_lock);
qemu_co_queue_init(&blk->public.throttled_reqs[0]);
qemu_co_queue_init(&blk->public.throttled_reqs[1]);
block_acct_init(&blk->stats); block_acct_init(&blk->stats);
notifier_list_init(&blk->remove_bs_notifiers); notifier_list_init(&blk->remove_bs_notifiers);
@ -343,7 +340,7 @@ static void blk_delete(BlockBackend *blk)
assert(!blk->refcnt); assert(!blk->refcnt);
assert(!blk->name); assert(!blk->name);
assert(!blk->dev); assert(!blk->dev);
if (blk->public.throttle_state) { if (blk->public.throttle_group_member.throttle_state) {
blk_io_limits_disable(blk); blk_io_limits_disable(blk);
} }
if (blk->root) { if (blk->root) {
@ -658,9 +655,12 @@ BlockBackend *blk_by_public(BlockBackendPublic *public)
*/ */
void blk_remove_bs(BlockBackend *blk) void blk_remove_bs(BlockBackend *blk)
{ {
ThrottleTimers *tt;
notifier_list_notify(&blk->remove_bs_notifiers, blk); notifier_list_notify(&blk->remove_bs_notifiers, blk);
if (blk->public.throttle_state) { if (blk->public.throttle_group_member.throttle_state) {
throttle_timers_detach_aio_context(&blk->public.throttle_timers); tt = &blk->public.throttle_group_member.throttle_timers;
throttle_timers_detach_aio_context(tt);
} }
blk_update_root_state(blk); blk_update_root_state(blk);
@ -682,9 +682,10 @@ int blk_insert_bs(BlockBackend *blk, BlockDriverState *bs, Error **errp)
bdrv_ref(bs); bdrv_ref(bs);
notifier_list_notify(&blk->insert_bs_notifiers, blk); notifier_list_notify(&blk->insert_bs_notifiers, blk);
if (blk->public.throttle_state) { if (blk->public.throttle_group_member.throttle_state) {
throttle_timers_attach_aio_context( throttle_timers_attach_aio_context(
&blk->public.throttle_timers, bdrv_get_aio_context(bs)); &blk->public.throttle_group_member.throttle_timers,
bdrv_get_aio_context(bs));
} }
return 0; return 0;
@ -1046,8 +1047,9 @@ int coroutine_fn blk_co_preadv(BlockBackend *blk, int64_t offset,
bdrv_inc_in_flight(bs); bdrv_inc_in_flight(bs);
/* throttling disk I/O */ /* throttling disk I/O */
if (blk->public.throttle_state) { if (blk->public.throttle_group_member.throttle_state) {
throttle_group_co_io_limits_intercept(blk, bytes, false); throttle_group_co_io_limits_intercept(&blk->public.throttle_group_member,
bytes, false);
} }
ret = bdrv_co_preadv(blk->root, offset, bytes, qiov, flags); ret = bdrv_co_preadv(blk->root, offset, bytes, qiov, flags);
@ -1070,10 +1072,10 @@ int coroutine_fn blk_co_pwritev(BlockBackend *blk, int64_t offset,
} }
bdrv_inc_in_flight(bs); bdrv_inc_in_flight(bs);
/* throttling disk I/O */ /* throttling disk I/O */
if (blk->public.throttle_state) { if (blk->public.throttle_group_member.throttle_state) {
throttle_group_co_io_limits_intercept(blk, bytes, true); throttle_group_co_io_limits_intercept(&blk->public.throttle_group_member,
bytes, true);
} }
if (!blk->enable_write_cache) { if (!blk->enable_write_cache) {
@ -1742,16 +1744,14 @@ static AioContext *blk_aiocb_get_aio_context(BlockAIOCB *acb)
void blk_set_aio_context(BlockBackend *blk, AioContext *new_context) void blk_set_aio_context(BlockBackend *blk, AioContext *new_context)
{ {
BlockDriverState *bs = blk_bs(blk); BlockDriverState *bs = blk_bs(blk);
ThrottleGroupMember *tgm = &blk->public.throttle_group_member;
if (bs) { if (bs) {
if (blk->public.throttle_state) { if (tgm->throttle_state) {
throttle_timers_detach_aio_context(&blk->public.throttle_timers); throttle_group_detach_aio_context(tgm);
throttle_group_attach_aio_context(tgm, new_context);
} }
bdrv_set_aio_context(bs, new_context); bdrv_set_aio_context(bs, new_context);
if (blk->public.throttle_state) {
throttle_timers_attach_aio_context(&blk->public.throttle_timers,
new_context);
}
} }
} }
@ -1969,33 +1969,35 @@ int blk_commit_all(void)
/* throttling disk I/O limits */ /* throttling disk I/O limits */
void blk_set_io_limits(BlockBackend *blk, ThrottleConfig *cfg) void blk_set_io_limits(BlockBackend *blk, ThrottleConfig *cfg)
{ {
throttle_group_config(blk, cfg); throttle_group_config(&blk->public.throttle_group_member, cfg);
} }
void blk_io_limits_disable(BlockBackend *blk) void blk_io_limits_disable(BlockBackend *blk)
{ {
assert(blk->public.throttle_state); assert(blk->public.throttle_group_member.throttle_state);
bdrv_drained_begin(blk_bs(blk)); bdrv_drained_begin(blk_bs(blk));
throttle_group_unregister_blk(blk); throttle_group_unregister_tgm(&blk->public.throttle_group_member);
bdrv_drained_end(blk_bs(blk)); bdrv_drained_end(blk_bs(blk));
} }
/* should be called before blk_set_io_limits if a limit is set */ /* should be called before blk_set_io_limits if a limit is set */
void blk_io_limits_enable(BlockBackend *blk, const char *group) void blk_io_limits_enable(BlockBackend *blk, const char *group)
{ {
assert(!blk->public.throttle_state); assert(!blk->public.throttle_group_member.throttle_state);
throttle_group_register_blk(blk, group); throttle_group_register_tgm(&blk->public.throttle_group_member,
group, blk_get_aio_context(blk));
} }
void blk_io_limits_update_group(BlockBackend *blk, const char *group) void blk_io_limits_update_group(BlockBackend *blk, const char *group)
{ {
/* this BB is not part of any group */ /* this BB is not part of any group */
if (!blk->public.throttle_state) { if (!blk->public.throttle_group_member.throttle_state) {
return; return;
} }
/* this BB is a part of the same group than the one we want */ /* this BB is a part of the same group than the one we want */
if (!g_strcmp0(throttle_group_get_name(blk), group)) { if (!g_strcmp0(throttle_group_get_name(&blk->public.throttle_group_member),
group)) {
return; return;
} }
@ -2017,8 +2019,8 @@ static void blk_root_drained_begin(BdrvChild *child)
/* Note that blk->root may not be accessible here yet if we are just /* Note that blk->root may not be accessible here yet if we are just
* attaching to a BlockDriverState that is drained. Use child instead. */ * attaching to a BlockDriverState that is drained. Use child instead. */
if (atomic_fetch_inc(&blk->public.io_limits_disabled) == 0) { if (atomic_fetch_inc(&blk->public.throttle_group_member.io_limits_disabled) == 0) {
throttle_group_restart_blk(blk); throttle_group_restart_tgm(&blk->public.throttle_group_member);
} }
} }
@ -2027,8 +2029,8 @@ static void blk_root_drained_end(BdrvChild *child)
BlockBackend *blk = child->opaque; BlockBackend *blk = child->opaque;
assert(blk->quiesce_counter); assert(blk->quiesce_counter);
assert(blk->public.io_limits_disabled); assert(blk->public.throttle_group_member.io_limits_disabled);
atomic_dec(&blk->public.io_limits_disabled); atomic_dec(&blk->public.throttle_group_member.io_limits_disabled);
if (--blk->quiesce_counter == 0) { if (--blk->quiesce_counter == 0) {
if (blk->dev_ops && blk->dev_ops->drained_end) { if (blk->dev_ops && blk->dev_ops->drained_end) {

View File

@ -244,16 +244,6 @@ static int coroutine_fn bdrv_commit_top_preadv(BlockDriverState *bs,
return bdrv_co_preadv(bs->backing, offset, bytes, qiov, flags); return bdrv_co_preadv(bs->backing, offset, bytes, qiov, flags);
} }
static int64_t coroutine_fn bdrv_commit_top_get_block_status(
BlockDriverState *bs, int64_t sector_num, int nb_sectors, int *pnum,
BlockDriverState **file)
{
*pnum = nb_sectors;
*file = bs->backing->bs;
return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID |
(sector_num << BDRV_SECTOR_BITS);
}
static void bdrv_commit_top_refresh_filename(BlockDriverState *bs, QDict *opts) static void bdrv_commit_top_refresh_filename(BlockDriverState *bs, QDict *opts)
{ {
bdrv_refresh_filename(bs->backing->bs); bdrv_refresh_filename(bs->backing->bs);
@ -279,7 +269,7 @@ static void bdrv_commit_top_child_perm(BlockDriverState *bs, BdrvChild *c,
static BlockDriver bdrv_commit_top = { static BlockDriver bdrv_commit_top = {
.format_name = "commit_top", .format_name = "commit_top",
.bdrv_co_preadv = bdrv_commit_top_preadv, .bdrv_co_preadv = bdrv_commit_top_preadv,
.bdrv_co_get_block_status = bdrv_commit_top_get_block_status, .bdrv_co_get_block_status = bdrv_co_get_block_status_from_backing,
.bdrv_refresh_filename = bdrv_commit_top_refresh_filename, .bdrv_refresh_filename = bdrv_commit_top_refresh_filename,
.bdrv_close = bdrv_commit_top_close, .bdrv_close = bdrv_commit_top_close,
.bdrv_child_perm = bdrv_commit_top_child_perm, .bdrv_child_perm = bdrv_commit_top_child_perm,

View File

@ -1714,6 +1714,32 @@ typedef struct BdrvCoGetBlockStatusData {
bool done; bool done;
} BdrvCoGetBlockStatusData; } BdrvCoGetBlockStatusData;
int64_t coroutine_fn bdrv_co_get_block_status_from_file(BlockDriverState *bs,
int64_t sector_num,
int nb_sectors,
int *pnum,
BlockDriverState **file)
{
assert(bs->file && bs->file->bs);
*pnum = nb_sectors;
*file = bs->file->bs;
return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID |
(sector_num << BDRV_SECTOR_BITS);
}
int64_t coroutine_fn bdrv_co_get_block_status_from_backing(BlockDriverState *bs,
int64_t sector_num,
int nb_sectors,
int *pnum,
BlockDriverState **file)
{
assert(bs->backing && bs->backing->bs);
*pnum = nb_sectors;
*file = bs->backing->bs;
return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID |
(sector_num << BDRV_SECTOR_BITS);
}
/* /*
* Returns the allocation status of the specified sectors. * Returns the allocation status of the specified sectors.
* Drivers not implementing the functionality are assumed to not support * Drivers not implementing the functionality are assumed to not support

View File

@ -1059,16 +1059,6 @@ static int coroutine_fn bdrv_mirror_top_flush(BlockDriverState *bs)
return bdrv_co_flush(bs->backing->bs); return bdrv_co_flush(bs->backing->bs);
} }
static int64_t coroutine_fn bdrv_mirror_top_get_block_status(
BlockDriverState *bs, int64_t sector_num, int nb_sectors, int *pnum,
BlockDriverState **file)
{
*pnum = nb_sectors;
*file = bs->backing->bs;
return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID |
(sector_num << BDRV_SECTOR_BITS);
}
static int coroutine_fn bdrv_mirror_top_pwrite_zeroes(BlockDriverState *bs, static int coroutine_fn bdrv_mirror_top_pwrite_zeroes(BlockDriverState *bs,
int64_t offset, int bytes, BdrvRequestFlags flags) int64_t offset, int bytes, BdrvRequestFlags flags)
{ {
@ -1115,7 +1105,7 @@ static BlockDriver bdrv_mirror_top = {
.bdrv_co_pwrite_zeroes = bdrv_mirror_top_pwrite_zeroes, .bdrv_co_pwrite_zeroes = bdrv_mirror_top_pwrite_zeroes,
.bdrv_co_pdiscard = bdrv_mirror_top_pdiscard, .bdrv_co_pdiscard = bdrv_mirror_top_pdiscard,
.bdrv_co_flush = bdrv_mirror_top_flush, .bdrv_co_flush = bdrv_mirror_top_flush,
.bdrv_co_get_block_status = bdrv_mirror_top_get_block_status, .bdrv_co_get_block_status = bdrv_co_get_block_status_from_backing,
.bdrv_refresh_filename = bdrv_mirror_top_refresh_filename, .bdrv_refresh_filename = bdrv_mirror_top_refresh_filename,
.bdrv_close = bdrv_mirror_top_close, .bdrv_close = bdrv_mirror_top_close,
.bdrv_child_perm = bdrv_mirror_top_child_perm, .bdrv_child_perm = bdrv_mirror_top_child_perm,

View File

@ -66,10 +66,11 @@ BlockDeviceInfo *bdrv_block_device_info(BlockBackend *blk,
info->detect_zeroes = bs->detect_zeroes; info->detect_zeroes = bs->detect_zeroes;
if (blk && blk_get_public(blk)->throttle_state) { if (blk && blk_get_public(blk)->throttle_group_member.throttle_state) {
ThrottleConfig cfg; ThrottleConfig cfg;
BlockBackendPublic *blkp = blk_get_public(blk);
throttle_group_get_config(blk, &cfg); throttle_group_get_config(&blkp->throttle_group_member, &cfg);
info->bps = cfg.buckets[THROTTLE_BPS_TOTAL].avg; info->bps = cfg.buckets[THROTTLE_BPS_TOTAL].avg;
info->bps_rd = cfg.buckets[THROTTLE_BPS_READ].avg; info->bps_rd = cfg.buckets[THROTTLE_BPS_READ].avg;
@ -117,7 +118,8 @@ BlockDeviceInfo *bdrv_block_device_info(BlockBackend *blk,
info->iops_size = cfg.op_size; info->iops_size = cfg.op_size;
info->has_group = true; info->has_group = true;
info->group = g_strdup(throttle_group_get_name(blk)); info->group =
g_strdup(throttle_group_get_name(&blkp->throttle_group_member));
} }
info->write_threshold = bdrv_write_threshold_get(bs); info->write_threshold = bdrv_write_threshold_get(bs);

View File

@ -347,19 +347,22 @@ static int qcow_reopen_prepare(BDRVReopenState *state,
* 'compressed_size'. 'compressed_size' must be > 0 and < * 'compressed_size'. 'compressed_size' must be > 0 and <
* cluster_size * cluster_size
* *
* return 0 if not allocated. * return 0 if not allocated, 1 if *result is assigned, and negative
* errno on failure.
*/ */
static uint64_t get_cluster_offset(BlockDriverState *bs, static int get_cluster_offset(BlockDriverState *bs,
uint64_t offset, int allocate, uint64_t offset, int allocate,
int compressed_size, int compressed_size,
int n_start, int n_end) int n_start, int n_end, uint64_t *result)
{ {
BDRVQcowState *s = bs->opaque; BDRVQcowState *s = bs->opaque;
int min_index, i, j, l1_index, l2_index; int min_index, i, j, l1_index, l2_index, ret;
uint64_t l2_offset, *l2_table, cluster_offset, tmp; int64_t l2_offset;
uint64_t *l2_table, cluster_offset, tmp;
uint32_t min_count; uint32_t min_count;
int new_l2_table; int new_l2_table;
*result = 0;
l1_index = offset >> (s->l2_bits + s->cluster_bits); l1_index = offset >> (s->l2_bits + s->cluster_bits);
l2_offset = s->l1_table[l1_index]; l2_offset = s->l1_table[l1_index];
new_l2_table = 0; new_l2_table = 0;
@ -368,15 +371,20 @@ static uint64_t get_cluster_offset(BlockDriverState *bs,
return 0; return 0;
/* allocate a new l2 entry */ /* allocate a new l2 entry */
l2_offset = bdrv_getlength(bs->file->bs); l2_offset = bdrv_getlength(bs->file->bs);
if (l2_offset < 0) {
return l2_offset;
}
/* round to cluster size */ /* round to cluster size */
l2_offset = (l2_offset + s->cluster_size - 1) & ~(s->cluster_size - 1); l2_offset = QEMU_ALIGN_UP(l2_offset, s->cluster_size);
/* update the L1 entry */ /* update the L1 entry */
s->l1_table[l1_index] = l2_offset; s->l1_table[l1_index] = l2_offset;
tmp = cpu_to_be64(l2_offset); tmp = cpu_to_be64(l2_offset);
if (bdrv_pwrite_sync(bs->file, ret = bdrv_pwrite_sync(bs->file,
s->l1_table_offset + l1_index * sizeof(tmp), s->l1_table_offset + l1_index * sizeof(tmp),
&tmp, sizeof(tmp)) < 0) &tmp, sizeof(tmp));
return 0; if (ret < 0) {
return ret;
}
new_l2_table = 1; new_l2_table = 1;
} }
for(i = 0; i < L2_CACHE_SIZE; i++) { for(i = 0; i < L2_CACHE_SIZE; i++) {
@ -403,14 +411,17 @@ static uint64_t get_cluster_offset(BlockDriverState *bs,
l2_table = s->l2_cache + (min_index << s->l2_bits); l2_table = s->l2_cache + (min_index << s->l2_bits);
if (new_l2_table) { if (new_l2_table) {
memset(l2_table, 0, s->l2_size * sizeof(uint64_t)); memset(l2_table, 0, s->l2_size * sizeof(uint64_t));
if (bdrv_pwrite_sync(bs->file, l2_offset, l2_table, ret = bdrv_pwrite_sync(bs->file, l2_offset, l2_table,
s->l2_size * sizeof(uint64_t)) < 0) s->l2_size * sizeof(uint64_t));
return 0; if (ret < 0) {
return ret;
}
} else { } else {
if (bdrv_pread(bs->file, l2_offset, l2_table, ret = bdrv_pread(bs->file, l2_offset, l2_table,
s->l2_size * sizeof(uint64_t)) != s->l2_size * sizeof(uint64_t));
s->l2_size * sizeof(uint64_t)) if (ret < 0) {
return 0; return ret;
}
} }
s->l2_cache_offsets[min_index] = l2_offset; s->l2_cache_offsets[min_index] = l2_offset;
s->l2_cache_counts[min_index] = 1; s->l2_cache_counts[min_index] = 1;
@ -427,24 +438,36 @@ static uint64_t get_cluster_offset(BlockDriverState *bs,
/* if the cluster is already compressed, we must /* if the cluster is already compressed, we must
decompress it in the case it is not completely decompress it in the case it is not completely
overwritten */ overwritten */
if (decompress_cluster(bs, cluster_offset) < 0) if (decompress_cluster(bs, cluster_offset) < 0) {
return 0; return -EIO;
}
cluster_offset = bdrv_getlength(bs->file->bs); cluster_offset = bdrv_getlength(bs->file->bs);
cluster_offset = (cluster_offset + s->cluster_size - 1) & if ((int64_t) cluster_offset < 0) {
~(s->cluster_size - 1); return cluster_offset;
}
cluster_offset = QEMU_ALIGN_UP(cluster_offset, s->cluster_size);
/* write the cluster content */ /* write the cluster content */
if (bdrv_pwrite(bs->file, cluster_offset, s->cluster_cache, ret = bdrv_pwrite(bs->file, cluster_offset, s->cluster_cache,
s->cluster_size) != s->cluster_size);
s->cluster_size) if (ret < 0) {
return -1; return ret;
}
} else { } else {
cluster_offset = bdrv_getlength(bs->file->bs); cluster_offset = bdrv_getlength(bs->file->bs);
if ((int64_t) cluster_offset < 0) {
return cluster_offset;
}
if (allocate == 1) { if (allocate == 1) {
/* round to cluster size */ /* round to cluster size */
cluster_offset = (cluster_offset + s->cluster_size - 1) & cluster_offset = QEMU_ALIGN_UP(cluster_offset, s->cluster_size);
~(s->cluster_size - 1); if (cluster_offset + s->cluster_size > INT64_MAX) {
bdrv_truncate(bs->file, cluster_offset + s->cluster_size, return -E2BIG;
PREALLOC_MODE_OFF, NULL); }
ret = bdrv_truncate(bs->file, cluster_offset + s->cluster_size,
PREALLOC_MODE_OFF, NULL);
if (ret < 0) {
return ret;
}
/* if encrypted, we must initialize the cluster /* if encrypted, we must initialize the cluster
content which won't be written */ content which won't be written */
if (bs->encrypted && if (bs->encrypted &&
@ -459,13 +482,14 @@ static uint64_t get_cluster_offset(BlockDriverState *bs,
s->cluster_data, s->cluster_data,
BDRV_SECTOR_SIZE, BDRV_SECTOR_SIZE,
NULL) < 0) { NULL) < 0) {
errno = EIO; return -EIO;
return -1; }
ret = bdrv_pwrite(bs->file,
cluster_offset + i * 512,
s->cluster_data, 512);
if (ret < 0) {
return ret;
} }
if (bdrv_pwrite(bs->file,
cluster_offset + i * 512,
s->cluster_data, 512) != 512)
return -1;
} }
} }
} }
@ -477,23 +501,29 @@ static uint64_t get_cluster_offset(BlockDriverState *bs,
/* update L2 table */ /* update L2 table */
tmp = cpu_to_be64(cluster_offset); tmp = cpu_to_be64(cluster_offset);
l2_table[l2_index] = tmp; l2_table[l2_index] = tmp;
if (bdrv_pwrite_sync(bs->file, l2_offset + l2_index * sizeof(tmp), ret = bdrv_pwrite_sync(bs->file, l2_offset + l2_index * sizeof(tmp),
&tmp, sizeof(tmp)) < 0) &tmp, sizeof(tmp));
return 0; if (ret < 0) {
return ret;
}
} }
return cluster_offset; *result = cluster_offset;
return 1;
} }
static int64_t coroutine_fn qcow_co_get_block_status(BlockDriverState *bs, static int64_t coroutine_fn qcow_co_get_block_status(BlockDriverState *bs,
int64_t sector_num, int nb_sectors, int *pnum, BlockDriverState **file) int64_t sector_num, int nb_sectors, int *pnum, BlockDriverState **file)
{ {
BDRVQcowState *s = bs->opaque; BDRVQcowState *s = bs->opaque;
int index_in_cluster, n; int index_in_cluster, n, ret;
uint64_t cluster_offset; uint64_t cluster_offset;
qemu_co_mutex_lock(&s->lock); qemu_co_mutex_lock(&s->lock);
cluster_offset = get_cluster_offset(bs, sector_num << 9, 0, 0, 0, 0); ret = get_cluster_offset(bs, sector_num << 9, 0, 0, 0, 0, &cluster_offset);
qemu_co_mutex_unlock(&s->lock); qemu_co_mutex_unlock(&s->lock);
if (ret < 0) {
return ret;
}
index_in_cluster = sector_num & (s->cluster_sectors - 1); index_in_cluster = sector_num & (s->cluster_sectors - 1);
n = s->cluster_sectors - index_in_cluster; n = s->cluster_sectors - index_in_cluster;
if (n > nb_sectors) if (n > nb_sectors)
@ -585,8 +615,11 @@ static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num,
while (nb_sectors != 0) { while (nb_sectors != 0) {
/* prepare next request */ /* prepare next request */
cluster_offset = get_cluster_offset(bs, sector_num << 9, ret = get_cluster_offset(bs, sector_num << 9,
0, 0, 0, 0); 0, 0, 0, 0, &cluster_offset);
if (ret < 0) {
break;
}
index_in_cluster = sector_num & (s->cluster_sectors - 1); index_in_cluster = sector_num & (s->cluster_sectors - 1);
n = s->cluster_sectors - index_in_cluster; n = s->cluster_sectors - index_in_cluster;
if (n > nb_sectors) { if (n > nb_sectors) {
@ -603,7 +636,7 @@ static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num,
ret = bdrv_co_readv(bs->backing, sector_num, n, &hd_qiov); ret = bdrv_co_readv(bs->backing, sector_num, n, &hd_qiov);
qemu_co_mutex_lock(&s->lock); qemu_co_mutex_lock(&s->lock);
if (ret < 0) { if (ret < 0) {
goto fail; break;
} }
} else { } else {
/* Note: in this case, no need to wait */ /* Note: in this case, no need to wait */
@ -612,13 +645,15 @@ static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num,
} else if (cluster_offset & QCOW_OFLAG_COMPRESSED) { } else if (cluster_offset & QCOW_OFLAG_COMPRESSED) {
/* add AIO support for compressed blocks ? */ /* add AIO support for compressed blocks ? */
if (decompress_cluster(bs, cluster_offset) < 0) { if (decompress_cluster(bs, cluster_offset) < 0) {
goto fail; ret = -EIO;
break;
} }
memcpy(buf, memcpy(buf,
s->cluster_cache + index_in_cluster * 512, 512 * n); s->cluster_cache + index_in_cluster * 512, 512 * n);
} else { } else {
if ((cluster_offset & 511) != 0) { if ((cluster_offset & 511) != 0) {
goto fail; ret = -EIO;
break;
} }
hd_iov.iov_base = (void *)buf; hd_iov.iov_base = (void *)buf;
hd_iov.iov_len = n * 512; hd_iov.iov_len = n * 512;
@ -635,7 +670,8 @@ static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num,
assert(s->crypto); assert(s->crypto);
if (qcrypto_block_decrypt(s->crypto, sector_num, buf, if (qcrypto_block_decrypt(s->crypto, sector_num, buf,
n * BDRV_SECTOR_SIZE, NULL) < 0) { n * BDRV_SECTOR_SIZE, NULL) < 0) {
goto fail; ret = -EIO;
break;
} }
} }
} }
@ -646,7 +682,6 @@ static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num,
buf += n * 512; buf += n * 512;
} }
done:
qemu_co_mutex_unlock(&s->lock); qemu_co_mutex_unlock(&s->lock);
if (qiov->niov > 1) { if (qiov->niov > 1) {
@ -655,10 +690,6 @@ done:
} }
return ret; return ret;
fail:
ret = -EIO;
goto done;
} }
static coroutine_fn int qcow_co_writev(BlockDriverState *bs, int64_t sector_num, static coroutine_fn int qcow_co_writev(BlockDriverState *bs, int64_t sector_num,
@ -697,9 +728,12 @@ static coroutine_fn int qcow_co_writev(BlockDriverState *bs, int64_t sector_num,
if (n > nb_sectors) { if (n > nb_sectors) {
n = nb_sectors; n = nb_sectors;
} }
cluster_offset = get_cluster_offset(bs, sector_num << 9, 1, 0, ret = get_cluster_offset(bs, sector_num << 9, 1, 0,
index_in_cluster, index_in_cluster,
index_in_cluster + n); index_in_cluster + n, &cluster_offset);
if (ret < 0) {
break;
}
if (!cluster_offset || (cluster_offset & 511) != 0) { if (!cluster_offset || (cluster_offset & 511) != 0) {
ret = -EIO; ret = -EIO;
break; break;
@ -995,8 +1029,11 @@ qcow_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset,
goto success; goto success;
} }
qemu_co_mutex_lock(&s->lock); qemu_co_mutex_lock(&s->lock);
cluster_offset = get_cluster_offset(bs, offset, 2, out_len, 0, 0); ret = get_cluster_offset(bs, offset, 2, out_len, 0, 0, &cluster_offset);
qemu_co_mutex_unlock(&s->lock); qemu_co_mutex_unlock(&s->lock);
if (ret < 0) {
goto fail;
}
if (cluster_offset == 0) { if (cluster_offset == 0) {
ret = -EIO; ret = -EIO;
goto fail; goto fail;

View File

@ -2036,6 +2036,14 @@ static int qcow2_inactivate(BlockDriverState *bs)
int ret, result = 0; int ret, result = 0;
Error *local_err = NULL; Error *local_err = NULL;
qcow2_store_persistent_dirty_bitmaps(bs, &local_err);
if (local_err != NULL) {
result = -EINVAL;
error_report_err(local_err);
error_report("Persistent bitmaps are lost for node '%s'",
bdrv_get_device_or_node_name(bs));
}
ret = qcow2_cache_flush(bs, s->l2_table_cache); ret = qcow2_cache_flush(bs, s->l2_table_cache);
if (ret) { if (ret) {
result = ret; result = ret;
@ -2050,14 +2058,6 @@ static int qcow2_inactivate(BlockDriverState *bs)
strerror(-ret)); strerror(-ret));
} }
qcow2_store_persistent_dirty_bitmaps(bs, &local_err);
if (local_err != NULL) {
result = -EINVAL;
error_report_err(local_err);
error_report("Persistent bitmaps are lost for node '%s'",
bdrv_get_device_or_node_name(bs));
}
if (result == 0) { if (result == 0) {
qcow2_mark_clean(bs); qcow2_mark_clean(bs);
} }

View File

@ -372,11 +372,6 @@ static int raw_truncate(BlockDriverState *bs, int64_t offset,
return bdrv_truncate(bs->file, offset, prealloc, errp); return bdrv_truncate(bs->file, offset, prealloc, errp);
} }
static int raw_media_changed(BlockDriverState *bs)
{
return bdrv_media_changed(bs->file->bs);
}
static void raw_eject(BlockDriverState *bs, bool eject_flag) static void raw_eject(BlockDriverState *bs, bool eject_flag)
{ {
bdrv_eject(bs->file->bs, eject_flag); bdrv_eject(bs->file->bs, eject_flag);
@ -510,7 +505,6 @@ BlockDriver bdrv_raw = {
.bdrv_refresh_limits = &raw_refresh_limits, .bdrv_refresh_limits = &raw_refresh_limits,
.bdrv_probe_blocksizes = &raw_probe_blocksizes, .bdrv_probe_blocksizes = &raw_probe_blocksizes,
.bdrv_probe_geometry = &raw_probe_geometry, .bdrv_probe_geometry = &raw_probe_geometry,
.bdrv_media_changed = &raw_media_changed,
.bdrv_eject = &raw_eject, .bdrv_eject = &raw_eject,
.bdrv_lock_medium = &raw_lock_medium, .bdrv_lock_medium = &raw_lock_medium,
.bdrv_co_ioctl = &raw_co_ioctl, .bdrv_co_ioctl = &raw_co_ioctl,

File diff suppressed because it is too large Load Diff

237
block/throttle.c Normal file
View File

@ -0,0 +1,237 @@
/*
* QEMU block throttling filter driver infrastructure
*
* Copyright (c) 2017 Manos Pitsidianakis
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 or
* (at your option) version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include "qemu/osdep.h"
#include "block/throttle-groups.h"
#include "qemu/throttle-options.h"
#include "qapi/error.h"
static QemuOptsList throttle_opts = {
.name = "throttle",
.head = QTAILQ_HEAD_INITIALIZER(throttle_opts.head),
.desc = {
{
.name = QEMU_OPT_THROTTLE_GROUP_NAME,
.type = QEMU_OPT_STRING,
.help = "Name of the throttle group",
},
{ /* end of list */ }
},
};
static int throttle_configure_tgm(BlockDriverState *bs,
ThrottleGroupMember *tgm,
QDict *options, Error **errp)
{
int ret;
const char *group_name;
Error *local_err = NULL;
QemuOpts *opts = qemu_opts_create(&throttle_opts, NULL, 0, &error_abort);
qemu_opts_absorb_qdict(opts, options, &local_err);
if (local_err) {
error_propagate(errp, local_err);
ret = -EINVAL;
goto fin;
}
group_name = qemu_opt_get(opts, QEMU_OPT_THROTTLE_GROUP_NAME);
if (!group_name) {
error_setg(errp, "Please specify a throttle group");
ret = -EINVAL;
goto fin;
} else if (!throttle_group_exists(group_name)) {
error_setg(errp, "Throttle group '%s' does not exist", group_name);
ret = -EINVAL;
goto fin;
}
/* Register membership to group with name group_name */
throttle_group_register_tgm(tgm, group_name, bdrv_get_aio_context(bs));
ret = 0;
fin:
qemu_opts_del(opts);
return ret;
}
static int throttle_open(BlockDriverState *bs, QDict *options,
int flags, Error **errp)
{
ThrottleGroupMember *tgm = bs->opaque;
bs->file = bdrv_open_child(NULL, options, "file", bs,
&child_file, false, errp);
if (!bs->file) {
return -EINVAL;
}
bs->supported_write_flags = bs->file->bs->supported_write_flags;
bs->supported_zero_flags = bs->file->bs->supported_zero_flags;
return throttle_configure_tgm(bs, tgm, options, errp);
}
static void throttle_close(BlockDriverState *bs)
{
ThrottleGroupMember *tgm = bs->opaque;
throttle_group_unregister_tgm(tgm);
}
static int64_t throttle_getlength(BlockDriverState *bs)
{
return bdrv_getlength(bs->file->bs);
}
static int coroutine_fn throttle_co_preadv(BlockDriverState *bs,
uint64_t offset, uint64_t bytes,
QEMUIOVector *qiov, int flags)
{
ThrottleGroupMember *tgm = bs->opaque;
throttle_group_co_io_limits_intercept(tgm, bytes, false);
return bdrv_co_preadv(bs->file, offset, bytes, qiov, flags);
}
static int coroutine_fn throttle_co_pwritev(BlockDriverState *bs,
uint64_t offset, uint64_t bytes,
QEMUIOVector *qiov, int flags)
{
ThrottleGroupMember *tgm = bs->opaque;
throttle_group_co_io_limits_intercept(tgm, bytes, true);
return bdrv_co_pwritev(bs->file, offset, bytes, qiov, flags);
}
static int coroutine_fn throttle_co_pwrite_zeroes(BlockDriverState *bs,
int64_t offset, int bytes,
BdrvRequestFlags flags)
{
ThrottleGroupMember *tgm = bs->opaque;
throttle_group_co_io_limits_intercept(tgm, bytes, true);
return bdrv_co_pwrite_zeroes(bs->file, offset, bytes, flags);
}
static int coroutine_fn throttle_co_pdiscard(BlockDriverState *bs,
int64_t offset, int bytes)
{
ThrottleGroupMember *tgm = bs->opaque;
throttle_group_co_io_limits_intercept(tgm, bytes, true);
return bdrv_co_pdiscard(bs->file->bs, offset, bytes);
}
static int throttle_co_flush(BlockDriverState *bs)
{
return bdrv_co_flush(bs->file->bs);
}
static void throttle_detach_aio_context(BlockDriverState *bs)
{
ThrottleGroupMember *tgm = bs->opaque;
throttle_group_detach_aio_context(tgm);
}
static void throttle_attach_aio_context(BlockDriverState *bs,
AioContext *new_context)
{
ThrottleGroupMember *tgm = bs->opaque;
throttle_group_attach_aio_context(tgm, new_context);
}
static int throttle_reopen_prepare(BDRVReopenState *reopen_state,
BlockReopenQueue *queue, Error **errp)
{
ThrottleGroupMember *tgm;
assert(reopen_state != NULL);
assert(reopen_state->bs != NULL);
reopen_state->opaque = g_new0(ThrottleGroupMember, 1);
tgm = reopen_state->opaque;
return throttle_configure_tgm(reopen_state->bs, tgm, reopen_state->options,
errp);
}
static void throttle_reopen_commit(BDRVReopenState *reopen_state)
{
ThrottleGroupMember *old_tgm = reopen_state->bs->opaque;
ThrottleGroupMember *new_tgm = reopen_state->opaque;
throttle_group_unregister_tgm(old_tgm);
g_free(old_tgm);
reopen_state->bs->opaque = new_tgm;
reopen_state->opaque = NULL;
}
static void throttle_reopen_abort(BDRVReopenState *reopen_state)
{
ThrottleGroupMember *tgm = reopen_state->opaque;
throttle_group_unregister_tgm(tgm);
g_free(tgm);
reopen_state->opaque = NULL;
}
static bool throttle_recurse_is_first_non_filter(BlockDriverState *bs,
BlockDriverState *candidate)
{
return bdrv_recurse_is_first_non_filter(bs->file->bs, candidate);
}
static BlockDriver bdrv_throttle = {
.format_name = "throttle",
.protocol_name = "throttle",
.instance_size = sizeof(ThrottleGroupMember),
.bdrv_file_open = throttle_open,
.bdrv_close = throttle_close,
.bdrv_co_flush = throttle_co_flush,
.bdrv_child_perm = bdrv_filter_default_perms,
.bdrv_getlength = throttle_getlength,
.bdrv_co_preadv = throttle_co_preadv,
.bdrv_co_pwritev = throttle_co_pwritev,
.bdrv_co_pwrite_zeroes = throttle_co_pwrite_zeroes,
.bdrv_co_pdiscard = throttle_co_pdiscard,
.bdrv_recurse_is_first_non_filter = throttle_recurse_is_first_non_filter,
.bdrv_attach_aio_context = throttle_attach_aio_context,
.bdrv_detach_aio_context = throttle_detach_aio_context,
.bdrv_reopen_prepare = throttle_reopen_prepare,
.bdrv_reopen_commit = throttle_reopen_commit,
.bdrv_reopen_abort = throttle_reopen_abort,
.bdrv_co_get_block_status = bdrv_co_get_block_status_from_file,
.is_filter = true,
};
static void bdrv_throttle_init(void)
{
bdrv_register(&bdrv_throttle);
}
block_init(bdrv_throttle_init);

View File

@ -2686,7 +2686,7 @@ void qmp_block_set_io_throttle(BlockIOThrottle *arg, Error **errp)
if (throttle_enabled(&cfg)) { if (throttle_enabled(&cfg)) {
/* Enable I/O limits if they're not enabled yet, otherwise /* Enable I/O limits if they're not enabled yet, otherwise
* just update the throttling group. */ * just update the throttling group. */
if (!blk_get_public(blk)->throttle_state) { if (!blk_get_public(blk)->throttle_group_member.throttle_state) {
blk_io_limits_enable(blk, blk_io_limits_enable(blk,
arg->has_group ? arg->group : arg->has_group ? arg->group :
arg->has_device ? arg->device : arg->has_device ? arg->device :
@ -2696,7 +2696,7 @@ void qmp_block_set_io_throttle(BlockIOThrottle *arg, Error **errp)
} }
/* Set the new throttling configuration */ /* Set the new throttling configuration */
blk_set_io_limits(blk, &cfg); blk_set_io_limits(blk, &cfg);
} else if (blk_get_public(blk)->throttle_state) { } else if (blk_get_public(blk)->throttle_group_member.throttle_state) {
/* If all throttling settings are set to 0, disable I/O limits */ /* If all throttling settings are set to 0, disable I/O limits */
blk_io_limits_disable(blk); blk_io_limits_disable(blk);
} }

View File

@ -441,7 +441,6 @@ int bdrv_can_set_read_only(BlockDriverState *bs, bool read_only,
int bdrv_set_read_only(BlockDriverState *bs, bool read_only, Error **errp); int bdrv_set_read_only(BlockDriverState *bs, bool read_only, Error **errp);
bool bdrv_is_sg(BlockDriverState *bs); bool bdrv_is_sg(BlockDriverState *bs);
bool bdrv_is_inserted(BlockDriverState *bs); bool bdrv_is_inserted(BlockDriverState *bs);
int bdrv_media_changed(BlockDriverState *bs);
void bdrv_lock_medium(BlockDriverState *bs, bool locked); void bdrv_lock_medium(BlockDriverState *bs, bool locked);
void bdrv_eject(BlockDriverState *bs, bool eject_flag); void bdrv_eject(BlockDriverState *bs, bool eject_flag);
const char *bdrv_get_format_name(BlockDriverState *bs); const char *bdrv_get_format_name(BlockDriverState *bs);

View File

@ -87,7 +87,11 @@ struct BlockDriver {
const char *format_name; const char *format_name;
int instance_size; int instance_size;
/* set to true if the BlockDriver is a block filter */ /* set to true if the BlockDriver is a block filter. Block filters pass
* certain callbacks that refer to data (see block.c) to their bs->file if
* the driver doesn't implement them. Drivers that do not wish to forward
* must implement them and return -ENOTSUP.
*/
bool is_filter; bool is_filter;
/* for snapshots block filter like Quorum can implement the /* for snapshots block filter like Quorum can implement the
* following recursive callback. * following recursive callback.
@ -275,7 +279,6 @@ struct BlockDriver {
/* removable device specific */ /* removable device specific */
bool (*bdrv_is_inserted)(BlockDriverState *bs); bool (*bdrv_is_inserted)(BlockDriverState *bs);
int (*bdrv_media_changed)(BlockDriverState *bs);
void (*bdrv_eject)(BlockDriverState *bs, bool eject_flag); void (*bdrv_eject)(BlockDriverState *bs, bool eject_flag);
void (*bdrv_lock_medium)(BlockDriverState *bs, bool locked); void (*bdrv_lock_medium)(BlockDriverState *bs, bool locked);
@ -992,6 +995,24 @@ void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c,
uint64_t perm, uint64_t shared, uint64_t perm, uint64_t shared,
uint64_t *nperm, uint64_t *nshared); uint64_t *nperm, uint64_t *nshared);
/*
* Default implementation for drivers to pass bdrv_co_get_block_status() to
* their file.
*/
int64_t coroutine_fn bdrv_co_get_block_status_from_file(BlockDriverState *bs,
int64_t sector_num,
int nb_sectors,
int *pnum,
BlockDriverState **file);
/*
* Default implementation for drivers to pass bdrv_co_get_block_status() to
* their backing file.
*/
int64_t coroutine_fn bdrv_co_get_block_status_from_backing(BlockDriverState *bs,
int64_t sector_num,
int nb_sectors,
int *pnum,
BlockDriverState **file);
const char *bdrv_get_parent_name(const BlockDriverState *bs); const char *bdrv_get_parent_name(const BlockDriverState *bs);
void blk_dev_change_media_cb(BlockBackend *blk, bool load, Error **errp); void blk_dev_change_media_cb(BlockBackend *blk, bool load, Error **errp);
bool blk_dev_has_removable_media(BlockBackend *blk); bool blk_dev_has_removable_media(BlockBackend *blk);

View File

@ -28,20 +28,58 @@
#include "qemu/throttle.h" #include "qemu/throttle.h"
#include "block/block_int.h" #include "block/block_int.h"
const char *throttle_group_get_name(BlockBackend *blk); /* The ThrottleGroupMember structure indicates membership in a ThrottleGroup
* and holds related data.
*/
typedef struct ThrottleGroupMember {
AioContext *aio_context;
/* throttled_reqs_lock protects the CoQueues for throttled requests. */
CoMutex throttled_reqs_lock;
CoQueue throttled_reqs[2];
/* Nonzero if the I/O limits are currently being ignored; generally
* it is zero. Accessed with atomic operations.
*/
unsigned int io_limits_disabled;
/* The following fields are protected by the ThrottleGroup lock.
* See the ThrottleGroup documentation for details.
* throttle_state tells us if I/O limits are configured. */
ThrottleState *throttle_state;
ThrottleTimers throttle_timers;
unsigned pending_reqs[2];
QLIST_ENTRY(ThrottleGroupMember) round_robin;
} ThrottleGroupMember;
#define TYPE_THROTTLE_GROUP "throttle-group"
#define THROTTLE_GROUP(obj) OBJECT_CHECK(ThrottleGroup, (obj), TYPE_THROTTLE_GROUP)
const char *throttle_group_get_name(ThrottleGroupMember *tgm);
ThrottleState *throttle_group_incref(const char *name); ThrottleState *throttle_group_incref(const char *name);
void throttle_group_unref(ThrottleState *ts); void throttle_group_unref(ThrottleState *ts);
void throttle_group_config(BlockBackend *blk, ThrottleConfig *cfg); void throttle_group_config(ThrottleGroupMember *tgm, ThrottleConfig *cfg);
void throttle_group_get_config(BlockBackend *blk, ThrottleConfig *cfg); void throttle_group_get_config(ThrottleGroupMember *tgm, ThrottleConfig *cfg);
void throttle_group_register_blk(BlockBackend *blk, const char *groupname); void throttle_group_register_tgm(ThrottleGroupMember *tgm,
void throttle_group_unregister_blk(BlockBackend *blk); const char *groupname,
void throttle_group_restart_blk(BlockBackend *blk); AioContext *ctx);
void throttle_group_unregister_tgm(ThrottleGroupMember *tgm);
void throttle_group_restart_tgm(ThrottleGroupMember *tgm);
void coroutine_fn throttle_group_co_io_limits_intercept(BlockBackend *blk, void coroutine_fn throttle_group_co_io_limits_intercept(ThrottleGroupMember *tgm,
unsigned int bytes, unsigned int bytes,
bool is_write); bool is_write);
void throttle_group_attach_aio_context(ThrottleGroupMember *tgm,
AioContext *new_context);
void throttle_group_detach_aio_context(ThrottleGroupMember *tgm);
/*
* throttle_group_exists() must be called under the global
* mutex.
*/
bool throttle_group_exists(const char *name);
#endif #endif

View File

@ -10,81 +10,103 @@
#ifndef THROTTLE_OPTIONS_H #ifndef THROTTLE_OPTIONS_H
#define THROTTLE_OPTIONS_H #define THROTTLE_OPTIONS_H
#define QEMU_OPT_IOPS_TOTAL "iops-total"
#define QEMU_OPT_IOPS_TOTAL_MAX "iops-total-max"
#define QEMU_OPT_IOPS_TOTAL_MAX_LENGTH "iops-total-max-length"
#define QEMU_OPT_IOPS_READ "iops-read"
#define QEMU_OPT_IOPS_READ_MAX "iops-read-max"
#define QEMU_OPT_IOPS_READ_MAX_LENGTH "iops-read-max-length"
#define QEMU_OPT_IOPS_WRITE "iops-write"
#define QEMU_OPT_IOPS_WRITE_MAX "iops-write-max"
#define QEMU_OPT_IOPS_WRITE_MAX_LENGTH "iops-write-max-length"
#define QEMU_OPT_BPS_TOTAL "bps-total"
#define QEMU_OPT_BPS_TOTAL_MAX "bps-total-max"
#define QEMU_OPT_BPS_TOTAL_MAX_LENGTH "bps-total-max-length"
#define QEMU_OPT_BPS_READ "bps-read"
#define QEMU_OPT_BPS_READ_MAX "bps-read-max"
#define QEMU_OPT_BPS_READ_MAX_LENGTH "bps-read-max-length"
#define QEMU_OPT_BPS_WRITE "bps-write"
#define QEMU_OPT_BPS_WRITE_MAX "bps-write-max"
#define QEMU_OPT_BPS_WRITE_MAX_LENGTH "bps-write-max-length"
#define QEMU_OPT_IOPS_SIZE "iops-size"
#define QEMU_OPT_THROTTLE_GROUP_NAME "throttle-group"
#define THROTTLE_OPT_PREFIX "throttling."
#define THROTTLE_OPTS \ #define THROTTLE_OPTS \
{ \ { \
.name = "throttling.iops-total",\ .name = THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_TOTAL,\
.type = QEMU_OPT_NUMBER,\ .type = QEMU_OPT_NUMBER,\
.help = "limit total I/O operations per second",\ .help = "limit total I/O operations per second",\
},{ \ },{ \
.name = "throttling.iops-read",\ .name = THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_READ,\
.type = QEMU_OPT_NUMBER,\ .type = QEMU_OPT_NUMBER,\
.help = "limit read operations per second",\ .help = "limit read operations per second",\
},{ \ },{ \
.name = "throttling.iops-write",\ .name = THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_WRITE,\
.type = QEMU_OPT_NUMBER,\ .type = QEMU_OPT_NUMBER,\
.help = "limit write operations per second",\ .help = "limit write operations per second",\
},{ \ },{ \
.name = "throttling.bps-total",\ .name = THROTTLE_OPT_PREFIX QEMU_OPT_BPS_TOTAL,\
.type = QEMU_OPT_NUMBER,\ .type = QEMU_OPT_NUMBER,\
.help = "limit total bytes per second",\ .help = "limit total bytes per second",\
},{ \ },{ \
.name = "throttling.bps-read",\ .name = THROTTLE_OPT_PREFIX QEMU_OPT_BPS_READ,\
.type = QEMU_OPT_NUMBER,\ .type = QEMU_OPT_NUMBER,\
.help = "limit read bytes per second",\ .help = "limit read bytes per second",\
},{ \ },{ \
.name = "throttling.bps-write",\ .name = THROTTLE_OPT_PREFIX QEMU_OPT_BPS_WRITE,\
.type = QEMU_OPT_NUMBER,\ .type = QEMU_OPT_NUMBER,\
.help = "limit write bytes per second",\ .help = "limit write bytes per second",\
},{ \ },{ \
.name = "throttling.iops-total-max",\ .name = THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_TOTAL_MAX,\
.type = QEMU_OPT_NUMBER,\ .type = QEMU_OPT_NUMBER,\
.help = "I/O operations burst",\ .help = "I/O operations burst",\
},{ \ },{ \
.name = "throttling.iops-read-max",\ .name = THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_READ_MAX,\
.type = QEMU_OPT_NUMBER,\ .type = QEMU_OPT_NUMBER,\
.help = "I/O operations read burst",\ .help = "I/O operations read burst",\
},{ \ },{ \
.name = "throttling.iops-write-max",\ .name = THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_WRITE_MAX,\
.type = QEMU_OPT_NUMBER,\ .type = QEMU_OPT_NUMBER,\
.help = "I/O operations write burst",\ .help = "I/O operations write burst",\
},{ \ },{ \
.name = "throttling.bps-total-max",\ .name = THROTTLE_OPT_PREFIX QEMU_OPT_BPS_TOTAL_MAX,\
.type = QEMU_OPT_NUMBER,\ .type = QEMU_OPT_NUMBER,\
.help = "total bytes burst",\ .help = "total bytes burst",\
},{ \ },{ \
.name = "throttling.bps-read-max",\ .name = THROTTLE_OPT_PREFIX QEMU_OPT_BPS_READ_MAX,\
.type = QEMU_OPT_NUMBER,\ .type = QEMU_OPT_NUMBER,\
.help = "total bytes read burst",\ .help = "total bytes read burst",\
},{ \ },{ \
.name = "throttling.bps-write-max",\ .name = THROTTLE_OPT_PREFIX QEMU_OPT_BPS_WRITE_MAX,\
.type = QEMU_OPT_NUMBER,\ .type = QEMU_OPT_NUMBER,\
.help = "total bytes write burst",\ .help = "total bytes write burst",\
},{ \ },{ \
.name = "throttling.iops-total-max-length",\ .name = THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_TOTAL_MAX_LENGTH,\
.type = QEMU_OPT_NUMBER,\ .type = QEMU_OPT_NUMBER,\
.help = "length of the iops-total-max burst period, in seconds",\ .help = "length of the iops-total-max burst period, in seconds",\
},{ \ },{ \
.name = "throttling.iops-read-max-length",\ .name = THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_READ_MAX_LENGTH,\
.type = QEMU_OPT_NUMBER,\ .type = QEMU_OPT_NUMBER,\
.help = "length of the iops-read-max burst period, in seconds",\ .help = "length of the iops-read-max burst period, in seconds",\
},{ \ },{ \
.name = "throttling.iops-write-max-length",\ .name = THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_WRITE_MAX_LENGTH,\
.type = QEMU_OPT_NUMBER,\ .type = QEMU_OPT_NUMBER,\
.help = "length of the iops-write-max burst period, in seconds",\ .help = "length of the iops-write-max burst period, in seconds",\
},{ \ },{ \
.name = "throttling.bps-total-max-length",\ .name = THROTTLE_OPT_PREFIX QEMU_OPT_BPS_TOTAL_MAX_LENGTH,\
.type = QEMU_OPT_NUMBER,\ .type = QEMU_OPT_NUMBER,\
.help = "length of the bps-total-max burst period, in seconds",\ .help = "length of the bps-total-max burst period, in seconds",\
},{ \ },{ \
.name = "throttling.bps-read-max-length",\ .name = THROTTLE_OPT_PREFIX QEMU_OPT_BPS_READ_MAX_LENGTH,\
.type = QEMU_OPT_NUMBER,\ .type = QEMU_OPT_NUMBER,\
.help = "length of the bps-read-max burst period, in seconds",\ .help = "length of the bps-read-max burst period, in seconds",\
},{ \ },{ \
.name = "throttling.bps-write-max-length",\ .name = THROTTLE_OPT_PREFIX QEMU_OPT_BPS_WRITE_MAX_LENGTH,\
.type = QEMU_OPT_NUMBER,\ .type = QEMU_OPT_NUMBER,\
.help = "length of the bps-write-max burst period, in seconds",\ .help = "length of the bps-write-max burst period, in seconds",\
},{ \ },{ \
.name = "throttling.iops-size",\ .name = THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_SIZE,\
.type = QEMU_OPT_NUMBER,\ .type = QEMU_OPT_NUMBER,\
.help = "when limiting by iops max size of an I/O in bytes",\ .help = "when limiting by iops max size of an I/O in bytes",\
} }

View File

@ -152,5 +152,8 @@ bool throttle_schedule_timer(ThrottleState *ts,
bool is_write); bool is_write);
void throttle_account(ThrottleState *ts, bool is_write, uint64_t size); void throttle_account(ThrottleState *ts, bool is_write, uint64_t size);
void throttle_limits_to_config(ThrottleLimits *arg, ThrottleConfig *cfg,
Error **errp);
void throttle_config_to_limits(ThrottleConfig *cfg, ThrottleLimits *var);
#endif #endif

View File

@ -70,24 +70,10 @@ typedef struct BlockDevOps {
/* This struct is embedded in (the private) BlockBackend struct and contains /* This struct is embedded in (the private) BlockBackend struct and contains
* fields that must be public. This is in particular for QLIST_ENTRY() and * fields that must be public. This is in particular for QLIST_ENTRY() and
* friends so that BlockBackends can be kept in lists outside block-backend.c */ * friends so that BlockBackends can be kept in lists outside block-backend.c
* */
typedef struct BlockBackendPublic { typedef struct BlockBackendPublic {
/* throttled_reqs_lock protects the CoQueues for throttled requests. */ ThrottleGroupMember throttle_group_member;
CoMutex throttled_reqs_lock;
CoQueue throttled_reqs[2];
/* Nonzero if the I/O limits are currently being ignored; generally
* it is zero. Accessed with atomic operations.
*/
unsigned int io_limits_disabled;
/* The following fields are protected by the ThrottleGroup lock.
* See the ThrottleGroup documentation for details.
* throttle_state tells us if I/O limits are configured. */
ThrottleState *throttle_state;
ThrottleTimers throttle_timers;
unsigned pending_reqs[2];
QLIST_ENTRY(BlockBackendPublic) round_robin;
} BlockBackendPublic; } BlockBackendPublic;
BlockBackend *blk_new(uint64_t perm, uint64_t shared_perm); BlockBackend *blk_new(uint64_t perm, uint64_t shared_perm);

View File

@ -1905,6 +1905,54 @@
'*iops_rd_max_length': 'int', '*iops_wr_max_length': 'int', '*iops_rd_max_length': 'int', '*iops_wr_max_length': 'int',
'*iops_size': 'int', '*group': 'str' } } '*iops_size': 'int', '*group': 'str' } }
##
# @ThrottleLimits:
#
# Limit parameters for throttling.
# Since some limit combinations are illegal, limits should always be set in one
# transaction. All fields are optional. When setting limits, if a field is
# missing the current value is not changed.
#
# @iops-total: limit total I/O operations per second
# @iops-total-max: I/O operations burst
# @iops-total-max-length: length of the iops-total-max burst period, in seconds
# It must only be set if @iops-total-max is set as well.
# @iops-read: limit read operations per second
# @iops-read-max: I/O operations read burst
# @iops-read-max-length: length of the iops-read-max burst period, in seconds
# It must only be set if @iops-read-max is set as well.
# @iops-write: limit write operations per second
# @iops-write-max: I/O operations write burst
# @iops-write-max-length: length of the iops-write-max burst period, in seconds
# It must only be set if @iops-write-max is set as well.
# @bps-total: limit total bytes per second
# @bps-total-max: total bytes burst
# @bps-total-max-length: length of the bps-total-max burst period, in seconds.
# It must only be set if @bps-total-max is set as well.
# @bps-read: limit read bytes per second
# @bps-read-max: total bytes read burst
# @bps-read-max-length: length of the bps-read-max burst period, in seconds
# It must only be set if @bps-read-max is set as well.
# @bps-write: limit write bytes per second
# @bps-write-max: total bytes write burst
# @bps-write-max-length: length of the bps-write-max burst period, in seconds
# It must only be set if @bps-write-max is set as well.
# @iops-size: when limiting by iops max size of an I/O in bytes
#
# Since: 2.11
##
{ 'struct': 'ThrottleLimits',
'data': { '*iops-total' : 'int', '*iops-total-max' : 'int',
'*iops-total-max-length' : 'int', '*iops-read' : 'int',
'*iops-read-max' : 'int', '*iops-read-max-length' : 'int',
'*iops-write' : 'int', '*iops-write-max' : 'int',
'*iops-write-max-length' : 'int', '*bps-total' : 'int',
'*bps-total-max' : 'int', '*bps-total-max-length' : 'int',
'*bps-read' : 'int', '*bps-read-max' : 'int',
'*bps-read-max-length' : 'int', '*bps-write' : 'int',
'*bps-write-max' : 'int', '*bps-write-max-length' : 'int',
'*iops-size' : 'int' } }
## ##
# @block-stream: # @block-stream:
# #
@ -2175,6 +2223,7 @@
# Drivers that are supported in block device operations. # Drivers that are supported in block device operations.
# #
# @vxhs: Since 2.10 # @vxhs: Since 2.10
# @throttle: Since 2.11
# #
# Since: 2.9 # Since: 2.9
## ##
@ -2184,7 +2233,7 @@
'host_device', 'http', 'https', 'iscsi', 'luks', 'nbd', 'nfs', 'host_device', 'http', 'https', 'iscsi', 'luks', 'nbd', 'nfs',
'null-aio', 'null-co', 'parallels', 'qcow', 'qcow2', 'qed', 'null-aio', 'null-co', 'parallels', 'qcow', 'qcow2', 'qed',
'quorum', 'raw', 'rbd', 'replication', 'sheepdog', 'ssh', 'quorum', 'raw', 'rbd', 'replication', 'sheepdog', 'ssh',
'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat', 'vxhs' ] } 'throttle', 'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat', 'vxhs' ] }
## ##
# @BlockdevOptionsFile: # @BlockdevOptionsFile:
@ -3048,6 +3097,20 @@
'*tls-creds': 'str' } } '*tls-creds': 'str' } }
## ##
# @BlockdevOptionsThrottle:
#
# Driver specific block device options for the throttle driver
#
# @throttle-group: the name of the throttle-group object to use. It
# must already exist.
# @file: reference to or definition of the data source block device
# Since: 2.11
##
{ 'struct': 'BlockdevOptionsThrottle',
'data': { 'throttle-group': 'str',
'file' : 'BlockdevRef'
} }
##
# @BlockdevOptions: # @BlockdevOptions:
# #
# Options for creating a block device. Many options are available for all # Options for creating a block device. Many options are available for all
@ -3108,6 +3171,7 @@
'replication':'BlockdevOptionsReplication', 'replication':'BlockdevOptionsReplication',
'sheepdog': 'BlockdevOptionsSheepdog', 'sheepdog': 'BlockdevOptionsSheepdog',
'ssh': 'BlockdevOptionsSsh', 'ssh': 'BlockdevOptionsSsh',
'throttle': 'BlockdevOptionsThrottle',
'vdi': 'BlockdevOptionsGenericFormat', 'vdi': 'BlockdevOptionsGenericFormat',
'vhdx': 'BlockdevOptionsGenericFormat', 'vhdx': 'BlockdevOptionsGenericFormat',
'vmdk': 'BlockdevOptionsGenericCOWFormat', 'vmdk': 'BlockdevOptionsGenericCOWFormat',

205
tests/qemu-iotests/184 Executable file
View File

@ -0,0 +1,205 @@
#!/bin/bash
#
# Test I/O throttle block filter driver interface
#
# Copyright (C) 2017 Manos Pitsidianakis
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# creator
owner="Manos Pitsidianakis"
seq=`basename $0`
echo "QA output created by $seq"
here=`pwd`
status=1 # failure is the default!
_cleanup()
{
_cleanup_test_img
}
trap "_cleanup; exit \$status" 0 1 2 3 15
# get standard environment, filters and checks
. ./common.rc
. ./common.filter
_supported_fmt qcow2
_supported_proto file
_supported_os Linux
function do_run_qemu()
{
echo Testing: "$@" | _filter_imgfmt
$QEMU -nographic -qmp-pretty stdio -serial none "$@"
echo
}
function run_qemu()
{
do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qemu | _filter_qmp\
| _filter_qemu_io | _filter_generated_node_ids
}
_make_test_img 64M
test_throttle=$($QEMU_IMG --help|grep throttle)
[ "$test_throttle" = "" ] && _supported_fmt throttle
echo
echo "== checking interface =="
run_qemu <<EOF
{ "execute": "qmp_capabilities" }
{ "execute": "blockdev-add",
"arguments": {
"driver": "$IMGFMT",
"node-name": "disk0",
"file": {
"driver": "file",
"filename": "$TEST_IMG"
}
}
}
{ "execute": "object-add",
"arguments": {
"qom-type": "throttle-group",
"id": "group0",
"props": {
"limits" : {
"iops-total": 1000
}
}
}
}
{ "execute": "blockdev-add",
"arguments": {
"driver": "throttle",
"node-name": "throttle0",
"throttle-group": "group0",
"file": "disk0"
}
}
{ "execute": "query-named-block-nodes" }
{ "execute": "query-block" }
{ "execute": "quit" }
EOF
echo
echo "== property changes in ThrottleGroup =="
run_qemu <<EOF
{ "execute": "qmp_capabilities" }
{ "execute": "object-add",
"arguments": {
"qom-type": "throttle-group",
"id": "group0",
"props" : {
"limits": {
"iops-total": 1000
}
}
}
}
{ "execute" : "qom-get",
"arguments" : {
"path" : "group0",
"property" : "limits"
}
}
{ "execute" : "qom-set",
"arguments" : {
"path" : "group0",
"property" : "limits",
"value" : {
"iops-total" : 0
}
}
}
{ "execute" : "qom-get",
"arguments" : {
"path" : "group0",
"property" : "limits"
}
}
{ "execute": "quit" }
EOF
echo
echo "== object creation/set errors =="
run_qemu <<EOF
{ "execute": "qmp_capabilities" }
{ "execute": "object-add",
"arguments": {
"qom-type": "throttle-group",
"id": "group0",
"props" : {
"limits": {
"iops-total": 1000
}
}
}
}
{ "execute" : "qom-set",
"arguments" : {
"path" : "group0",
"property" : "x-iops-total",
"value" : 0
}
}
{ "execute" : "qom-set",
"arguments" : {
"path" : "group0",
"property" : "limits",
"value" : {
"iops-total" : 10,
"iops-read" : 10
}
}
}
{ "execute": "quit" }
EOF
echo
echo "== don't specify group =="
run_qemu <<EOF
{ "execute": "qmp_capabilities" }
{ "execute": "blockdev-add",
"arguments": {
"driver": "$IMGFMT",
"node-name": "disk0",
"file": {
"driver": "file",
"filename": "$TEST_IMG"
}
}
}
{ "execute": "blockdev-add",
"arguments": {
"driver": "throttle",
"node-name": "throttle0",
"file": "disk0"
}
}
{ "execute": "quit" }
EOF
echo
# success, all done
echo "*** done"
rm -f $seq.full
status=0

302
tests/qemu-iotests/184.out Normal file
View File

@ -0,0 +1,302 @@
QA output created by 184
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
== checking interface ==
Testing:
{
QMP_VERSION
}
{
"return": {
}
}
{
"return": {
}
}
{
"return": {
}
}
{
"return": {
}
}
{
"return": [
{
"iops_rd": 0,
"detect_zeroes": "off",
"image": {
"virtual-size": 67108864,
"filename": "json:{\"throttle-group\": \"group0\", \"driver\": \"throttle\", \"file\": {\"driver\": \"qcow2\", \"file\": {\"driver\": \"file\", \"filename\": \"TEST_DIR/t.qcow2\"}}}",
"cluster-size": 65536,
"format": "throttle",
"actual-size": 200704,
"dirty-flag": false
},
"iops_wr": 0,
"ro": false,
"node-name": "throttle0",
"backing_file_depth": 0,
"drv": "throttle",
"iops": 0,
"bps_wr": 0,
"write_threshold": 0,
"encrypted": false,
"bps": 0,
"bps_rd": 0,
"cache": {
"no-flush": false,
"direct": false,
"writeback": true
},
"file": "json:{\"throttle-group\": \"group0\", \"driver\": \"throttle\", \"file\": {\"driver\": \"qcow2\", \"file\": {\"driver\": \"file\", \"filename\": \"TEST_DIR/t.qcow2\"}}}",
"encryption_key_missing": false
},
{
"iops_rd": 0,
"detect_zeroes": "off",
"image": {
"virtual-size": 67108864,
"filename": "TEST_DIR/t.qcow2",
"cluster-size": 65536,
"format": "qcow2",
"actual-size": 200704,
"format-specific": {
"type": "qcow2",
"data": {
"compat": "1.1",
"lazy-refcounts": false,
"refcount-bits": 16,
"corrupt": false
}
},
"dirty-flag": false
},
"iops_wr": 0,
"ro": false,
"node-name": "disk0",
"backing_file_depth": 0,
"drv": "qcow2",
"iops": 0,
"bps_wr": 0,
"write_threshold": 0,
"encrypted": false,
"bps": 0,
"bps_rd": 0,
"cache": {
"no-flush": false,
"direct": false,
"writeback": true
},
"file": "TEST_DIR/t.qcow2",
"encryption_key_missing": false
},
{
"iops_rd": 0,
"detect_zeroes": "off",
"image": {
"virtual-size": 197120,
"filename": "TEST_DIR/t.qcow2",
"format": "file",
"actual-size": 200704,
"dirty-flag": false
},
"iops_wr": 0,
"ro": false,
"node-name": "NODE_NAME",
"backing_file_depth": 0,
"drv": "file",
"iops": 0,
"bps_wr": 0,
"write_threshold": 0,
"encrypted": false,
"bps": 0,
"bps_rd": 0,
"cache": {
"no-flush": false,
"direct": false,
"writeback": true
},
"file": "TEST_DIR/t.qcow2",
"encryption_key_missing": false
}
]
}
{
"return": [
]
}
{
"return": {
}
}
{
"timestamp": {
"seconds": TIMESTAMP,
"microseconds": TIMESTAMP
},
"event": "SHUTDOWN",
"data": {
"guest": false
}
}
== property changes in ThrottleGroup ==
Testing:
{
QMP_VERSION
}
{
"return": {
}
}
{
"return": {
}
}
{
"return": {
"bps-read-max-length": 1,
"iops-read-max-length": 1,
"bps-read-max": 0,
"bps-total": 0,
"iops-total-max-length": 1,
"iops-total": 1000,
"iops-write-max": 0,
"bps-write": 0,
"bps-total-max": 0,
"bps-write-max": 0,
"iops-size": 0,
"iops-read": 0,
"iops-write-max-length": 1,
"iops-write": 0,
"bps-total-max-length": 1,
"iops-read-max": 0,
"bps-read": 0,
"bps-write-max-length": 1,
"iops-total-max": 0
}
}
{
"return": {
}
}
{
"return": {
"bps-read-max-length": 1,
"iops-read-max-length": 1,
"bps-read-max": 0,
"bps-total": 0,
"iops-total-max-length": 1,
"iops-total": 0,
"iops-write-max": 0,
"bps-write": 0,
"bps-total-max": 0,
"bps-write-max": 0,
"iops-size": 0,
"iops-read": 0,
"iops-write-max-length": 1,
"iops-write": 0,
"bps-total-max-length": 1,
"iops-read-max": 0,
"bps-read": 0,
"bps-write-max-length": 1,
"iops-total-max": 0
}
}
{
"return": {
}
}
{
"timestamp": {
"seconds": TIMESTAMP,
"microseconds": TIMESTAMP
},
"event": "SHUTDOWN",
"data": {
"guest": false
}
}
== object creation/set errors ==
Testing:
{
QMP_VERSION
}
{
"return": {
}
}
{
"return": {
}
}
{
"error": {
"class": "GenericError",
"desc": "Property cannot be set after initialization"
}
}
{
"error": {
"class": "GenericError",
"desc": "bps/iops/max total values and read/write values cannot be used at the same time"
}
}
{
"return": {
}
}
{
"timestamp": {
"seconds": TIMESTAMP,
"microseconds": TIMESTAMP
},
"event": "SHUTDOWN",
"data": {
"guest": false
}
}
== don't specify group ==
Testing:
{
QMP_VERSION
}
{
"return": {
}
}
{
"return": {
}
}
{
"error": {
"class": "GenericError",
"desc": "Parameter 'throttle-group' is missing"
}
}
{
"return": {
}
}
{
"timestamp": {
"seconds": TIMESTAMP,
"microseconds": TIMESTAMP
},
"event": "SHUTDOWN",
"data": {
"guest": false
}
}
*** done

View File

@ -180,6 +180,7 @@
181 rw auto migration 181 rw auto migration
182 rw auto quick 182 rw auto quick
183 rw auto migration 183 rw auto migration
184 rw auto quick
185 rw auto 185 rw auto
186 rw auto 186 rw auto
187 rw auto 187 rw auto

View File

@ -24,8 +24,9 @@
static AioContext *ctx; static AioContext *ctx;
static LeakyBucket bkt; static LeakyBucket bkt;
static ThrottleConfig cfg; static ThrottleConfig cfg;
static ThrottleGroupMember tgm;
static ThrottleState ts; static ThrottleState ts;
static ThrottleTimers tt; static ThrottleTimers *tt;
/* useful function */ /* useful function */
static bool double_cmp(double x, double y) static bool double_cmp(double x, double y)
@ -153,19 +154,21 @@ static void test_init(void)
{ {
int i; int i;
tt = &tgm.throttle_timers;
/* fill the structures with crap */ /* fill the structures with crap */
memset(&ts, 1, sizeof(ts)); memset(&ts, 1, sizeof(ts));
memset(&tt, 1, sizeof(tt)); memset(tt, 1, sizeof(*tt));
/* init structures */ /* init structures */
throttle_init(&ts); throttle_init(&ts);
throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL, throttle_timers_init(tt, ctx, QEMU_CLOCK_VIRTUAL,
read_timer_cb, write_timer_cb, &ts); read_timer_cb, write_timer_cb, &ts);
/* check initialized fields */ /* check initialized fields */
g_assert(tt.clock_type == QEMU_CLOCK_VIRTUAL); g_assert(tt->clock_type == QEMU_CLOCK_VIRTUAL);
g_assert(tt.timers[0]); g_assert(tt->timers[0]);
g_assert(tt.timers[1]); g_assert(tt->timers[1]);
/* check other fields where cleared */ /* check other fields where cleared */
g_assert(!ts.previous_leak); g_assert(!ts.previous_leak);
@ -176,18 +179,18 @@ static void test_init(void)
g_assert(!ts.cfg.buckets[i].level); g_assert(!ts.cfg.buckets[i].level);
} }
throttle_timers_destroy(&tt); throttle_timers_destroy(tt);
} }
static void test_destroy(void) static void test_destroy(void)
{ {
int i; int i;
throttle_init(&ts); throttle_init(&ts);
throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL, throttle_timers_init(tt, ctx, QEMU_CLOCK_VIRTUAL,
read_timer_cb, write_timer_cb, &ts); read_timer_cb, write_timer_cb, &ts);
throttle_timers_destroy(&tt); throttle_timers_destroy(tt);
for (i = 0; i < 2; i++) { for (i = 0; i < 2; i++) {
g_assert(!tt.timers[i]); g_assert(!tt->timers[i]);
} }
} }
@ -224,7 +227,7 @@ static void test_config_functions(void)
orig_cfg.op_size = 1; orig_cfg.op_size = 1;
throttle_init(&ts); throttle_init(&ts);
throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL, throttle_timers_init(tt, ctx, QEMU_CLOCK_VIRTUAL,
read_timer_cb, write_timer_cb, &ts); read_timer_cb, write_timer_cb, &ts);
/* structure reset by throttle_init previous_leak should be null */ /* structure reset by throttle_init previous_leak should be null */
g_assert(!ts.previous_leak); g_assert(!ts.previous_leak);
@ -236,7 +239,7 @@ static void test_config_functions(void)
/* get back the fixed configuration */ /* get back the fixed configuration */
throttle_get_config(&ts, &final_cfg); throttle_get_config(&ts, &final_cfg);
throttle_timers_destroy(&tt); throttle_timers_destroy(tt);
g_assert(final_cfg.buckets[THROTTLE_BPS_TOTAL].avg == 153); g_assert(final_cfg.buckets[THROTTLE_BPS_TOTAL].avg == 153);
g_assert(final_cfg.buckets[THROTTLE_BPS_READ].avg == 56); g_assert(final_cfg.buckets[THROTTLE_BPS_READ].avg == 56);
@ -494,45 +497,45 @@ static void test_have_timer(void)
{ {
/* zero structures */ /* zero structures */
memset(&ts, 0, sizeof(ts)); memset(&ts, 0, sizeof(ts));
memset(&tt, 0, sizeof(tt)); memset(tt, 0, sizeof(*tt));
/* no timer set should return false */ /* no timer set should return false */
g_assert(!throttle_timers_are_initialized(&tt)); g_assert(!throttle_timers_are_initialized(tt));
/* init structures */ /* init structures */
throttle_init(&ts); throttle_init(&ts);
throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL, throttle_timers_init(tt, ctx, QEMU_CLOCK_VIRTUAL,
read_timer_cb, write_timer_cb, &ts); read_timer_cb, write_timer_cb, &ts);
/* timer set by init should return true */ /* timer set by init should return true */
g_assert(throttle_timers_are_initialized(&tt)); g_assert(throttle_timers_are_initialized(tt));
throttle_timers_destroy(&tt); throttle_timers_destroy(tt);
} }
static void test_detach_attach(void) static void test_detach_attach(void)
{ {
/* zero structures */ /* zero structures */
memset(&ts, 0, sizeof(ts)); memset(&ts, 0, sizeof(ts));
memset(&tt, 0, sizeof(tt)); memset(tt, 0, sizeof(*tt));
/* init the structure */ /* init the structure */
throttle_init(&ts); throttle_init(&ts);
throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL, throttle_timers_init(tt, ctx, QEMU_CLOCK_VIRTUAL,
read_timer_cb, write_timer_cb, &ts); read_timer_cb, write_timer_cb, &ts);
/* timer set by init should return true */ /* timer set by init should return true */
g_assert(throttle_timers_are_initialized(&tt)); g_assert(throttle_timers_are_initialized(tt));
/* timer should no longer exist after detaching */ /* timer should no longer exist after detaching */
throttle_timers_detach_aio_context(&tt); throttle_timers_detach_aio_context(tt);
g_assert(!throttle_timers_are_initialized(&tt)); g_assert(!throttle_timers_are_initialized(tt));
/* timer should exist again after attaching */ /* timer should exist again after attaching */
throttle_timers_attach_aio_context(&tt, ctx); throttle_timers_attach_aio_context(tt, ctx);
g_assert(throttle_timers_are_initialized(&tt)); g_assert(throttle_timers_are_initialized(tt));
throttle_timers_destroy(&tt); throttle_timers_destroy(tt);
} }
static bool do_test_accounting(bool is_ops, /* are we testing bps or ops */ static bool do_test_accounting(bool is_ops, /* are we testing bps or ops */
@ -561,7 +564,7 @@ static bool do_test_accounting(bool is_ops, /* are we testing bps or ops */
cfg.op_size = op_size; cfg.op_size = op_size;
throttle_init(&ts); throttle_init(&ts);
throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL, throttle_timers_init(tt, ctx, QEMU_CLOCK_VIRTUAL,
read_timer_cb, write_timer_cb, &ts); read_timer_cb, write_timer_cb, &ts);
throttle_config(&ts, QEMU_CLOCK_VIRTUAL, &cfg); throttle_config(&ts, QEMU_CLOCK_VIRTUAL, &cfg);
@ -588,7 +591,7 @@ static bool do_test_accounting(bool is_ops, /* are we testing bps or ops */
return false; return false;
} }
throttle_timers_destroy(&tt); throttle_timers_destroy(tt);
return true; return true;
} }
@ -669,6 +672,7 @@ static void test_groups(void)
ThrottleConfig cfg1, cfg2; ThrottleConfig cfg1, cfg2;
BlockBackend *blk1, *blk2, *blk3; BlockBackend *blk1, *blk2, *blk3;
BlockBackendPublic *blkp1, *blkp2, *blkp3; BlockBackendPublic *blkp1, *blkp2, *blkp3;
ThrottleGroupMember *tgm1, *tgm2, *tgm3;
/* No actual I/O is performed on these devices */ /* No actual I/O is performed on these devices */
blk1 = blk_new(0, BLK_PERM_ALL); blk1 = blk_new(0, BLK_PERM_ALL);
@ -679,21 +683,25 @@ static void test_groups(void)
blkp2 = blk_get_public(blk2); blkp2 = blk_get_public(blk2);
blkp3 = blk_get_public(blk3); blkp3 = blk_get_public(blk3);
g_assert(blkp1->throttle_state == NULL); tgm1 = &blkp1->throttle_group_member;
g_assert(blkp2->throttle_state == NULL); tgm2 = &blkp2->throttle_group_member;
g_assert(blkp3->throttle_state == NULL); tgm3 = &blkp3->throttle_group_member;
throttle_group_register_blk(blk1, "bar"); g_assert(tgm1->throttle_state == NULL);
throttle_group_register_blk(blk2, "foo"); g_assert(tgm2->throttle_state == NULL);
throttle_group_register_blk(blk3, "bar"); g_assert(tgm3->throttle_state == NULL);
g_assert(blkp1->throttle_state != NULL); throttle_group_register_tgm(tgm1, "bar", blk_get_aio_context(blk1));
g_assert(blkp2->throttle_state != NULL); throttle_group_register_tgm(tgm2, "foo", blk_get_aio_context(blk2));
g_assert(blkp3->throttle_state != NULL); throttle_group_register_tgm(tgm3, "bar", blk_get_aio_context(blk3));
g_assert(!strcmp(throttle_group_get_name(blk1), "bar")); g_assert(tgm1->throttle_state != NULL);
g_assert(!strcmp(throttle_group_get_name(blk2), "foo")); g_assert(tgm2->throttle_state != NULL);
g_assert(blkp1->throttle_state == blkp3->throttle_state); g_assert(tgm3->throttle_state != NULL);
g_assert(!strcmp(throttle_group_get_name(tgm1), "bar"));
g_assert(!strcmp(throttle_group_get_name(tgm2), "foo"));
g_assert(tgm1->throttle_state == tgm3->throttle_state);
/* Setting the config of a group member affects the whole group */ /* Setting the config of a group member affects the whole group */
throttle_config_init(&cfg1); throttle_config_init(&cfg1);
@ -701,29 +709,29 @@ static void test_groups(void)
cfg1.buckets[THROTTLE_BPS_WRITE].avg = 285000; cfg1.buckets[THROTTLE_BPS_WRITE].avg = 285000;
cfg1.buckets[THROTTLE_OPS_READ].avg = 20000; cfg1.buckets[THROTTLE_OPS_READ].avg = 20000;
cfg1.buckets[THROTTLE_OPS_WRITE].avg = 12000; cfg1.buckets[THROTTLE_OPS_WRITE].avg = 12000;
throttle_group_config(blk1, &cfg1); throttle_group_config(tgm1, &cfg1);
throttle_group_get_config(blk1, &cfg1); throttle_group_get_config(tgm1, &cfg1);
throttle_group_get_config(blk3, &cfg2); throttle_group_get_config(tgm3, &cfg2);
g_assert(!memcmp(&cfg1, &cfg2, sizeof(cfg1))); g_assert(!memcmp(&cfg1, &cfg2, sizeof(cfg1)));
cfg2.buckets[THROTTLE_BPS_READ].avg = 4547; cfg2.buckets[THROTTLE_BPS_READ].avg = 4547;
cfg2.buckets[THROTTLE_BPS_WRITE].avg = 1349; cfg2.buckets[THROTTLE_BPS_WRITE].avg = 1349;
cfg2.buckets[THROTTLE_OPS_READ].avg = 123; cfg2.buckets[THROTTLE_OPS_READ].avg = 123;
cfg2.buckets[THROTTLE_OPS_WRITE].avg = 86; cfg2.buckets[THROTTLE_OPS_WRITE].avg = 86;
throttle_group_config(blk3, &cfg1); throttle_group_config(tgm3, &cfg1);
throttle_group_get_config(blk1, &cfg1); throttle_group_get_config(tgm1, &cfg1);
throttle_group_get_config(blk3, &cfg2); throttle_group_get_config(tgm3, &cfg2);
g_assert(!memcmp(&cfg1, &cfg2, sizeof(cfg1))); g_assert(!memcmp(&cfg1, &cfg2, sizeof(cfg1)));
throttle_group_unregister_blk(blk1); throttle_group_unregister_tgm(tgm1);
throttle_group_unregister_blk(blk2); throttle_group_unregister_tgm(tgm2);
throttle_group_unregister_blk(blk3); throttle_group_unregister_tgm(tgm3);
g_assert(blkp1->throttle_state == NULL); g_assert(tgm1->throttle_state == NULL);
g_assert(blkp2->throttle_state == NULL); g_assert(tgm2->throttle_state == NULL);
g_assert(blkp3->throttle_state == NULL); g_assert(tgm3->throttle_state == NULL);
} }
int main(int argc, char **argv) int main(int argc, char **argv)
@ -731,6 +739,7 @@ int main(int argc, char **argv)
qemu_init_main_loop(&error_fatal); qemu_init_main_loop(&error_fatal);
ctx = qemu_get_aio_context(); ctx = qemu_get_aio_context();
bdrv_init(); bdrv_init();
module_call_init(MODULE_INIT_QOM);
do {} while (g_main_context_iteration(NULL, false)); do {} while (g_main_context_iteration(NULL, false));

View File

@ -484,3 +484,154 @@ void throttle_account(ThrottleState *ts, bool is_write, uint64_t size)
} }
} }
/* return a ThrottleConfig based on the options in a ThrottleLimits
*
* @arg: the ThrottleLimits object to read from
* @cfg: the ThrottleConfig to edit
* @errp: error object
*/
void throttle_limits_to_config(ThrottleLimits *arg, ThrottleConfig *cfg,
Error **errp)
{
if (arg->has_bps_total) {
cfg->buckets[THROTTLE_BPS_TOTAL].avg = arg->bps_total;
}
if (arg->has_bps_read) {
cfg->buckets[THROTTLE_BPS_READ].avg = arg->bps_read;
}
if (arg->has_bps_write) {
cfg->buckets[THROTTLE_BPS_WRITE].avg = arg->bps_write;
}
if (arg->has_iops_total) {
cfg->buckets[THROTTLE_OPS_TOTAL].avg = arg->iops_total;
}
if (arg->has_iops_read) {
cfg->buckets[THROTTLE_OPS_READ].avg = arg->iops_read;
}
if (arg->has_iops_write) {
cfg->buckets[THROTTLE_OPS_WRITE].avg = arg->iops_write;
}
if (arg->has_bps_total_max) {
cfg->buckets[THROTTLE_BPS_TOTAL].max = arg->bps_total_max;
}
if (arg->has_bps_read_max) {
cfg->buckets[THROTTLE_BPS_READ].max = arg->bps_read_max;
}
if (arg->has_bps_write_max) {
cfg->buckets[THROTTLE_BPS_WRITE].max = arg->bps_write_max;
}
if (arg->has_iops_total_max) {
cfg->buckets[THROTTLE_OPS_TOTAL].max = arg->iops_total_max;
}
if (arg->has_iops_read_max) {
cfg->buckets[THROTTLE_OPS_READ].max = arg->iops_read_max;
}
if (arg->has_iops_write_max) {
cfg->buckets[THROTTLE_OPS_WRITE].max = arg->iops_write_max;
}
if (arg->has_bps_total_max_length) {
if (arg->bps_total_max_length > UINT_MAX) {
error_setg(errp, "bps-total-max-length value must be in"
" the range [0, %u]", UINT_MAX);
return;
}
cfg->buckets[THROTTLE_BPS_TOTAL].burst_length = arg->bps_total_max_length;
}
if (arg->has_bps_read_max_length) {
if (arg->bps_read_max_length > UINT_MAX) {
error_setg(errp, "bps-read-max-length value must be in"
" the range [0, %u]", UINT_MAX);
return;
}
cfg->buckets[THROTTLE_BPS_READ].burst_length = arg->bps_read_max_length;
}
if (arg->has_bps_write_max_length) {
if (arg->bps_write_max_length > UINT_MAX) {
error_setg(errp, "bps-write-max-length value must be in"
" the range [0, %u]", UINT_MAX);
return;
}
cfg->buckets[THROTTLE_BPS_WRITE].burst_length = arg->bps_write_max_length;
}
if (arg->has_iops_total_max_length) {
if (arg->iops_total_max_length > UINT_MAX) {
error_setg(errp, "iops-total-max-length value must be in"
" the range [0, %u]", UINT_MAX);
return;
}
cfg->buckets[THROTTLE_OPS_TOTAL].burst_length = arg->iops_total_max_length;
}
if (arg->has_iops_read_max_length) {
if (arg->iops_read_max_length > UINT_MAX) {
error_setg(errp, "iops-read-max-length value must be in"
" the range [0, %u]", UINT_MAX);
return;
}
cfg->buckets[THROTTLE_OPS_READ].burst_length = arg->iops_read_max_length;
}
if (arg->has_iops_write_max_length) {
if (arg->iops_write_max_length > UINT_MAX) {
error_setg(errp, "iops-write-max-length value must be in"
" the range [0, %u]", UINT_MAX);
return;
}
cfg->buckets[THROTTLE_OPS_WRITE].burst_length = arg->iops_write_max_length;
}
if (arg->has_iops_size) {
cfg->op_size = arg->iops_size;
}
throttle_is_valid(cfg, errp);
}
/* write the options of a ThrottleConfig to a ThrottleLimits
*
* @cfg: the ThrottleConfig to read from
* @var: the ThrottleLimits to write to
*/
void throttle_config_to_limits(ThrottleConfig *cfg, ThrottleLimits *var)
{
var->bps_total = cfg->buckets[THROTTLE_BPS_TOTAL].avg;
var->bps_read = cfg->buckets[THROTTLE_BPS_READ].avg;
var->bps_write = cfg->buckets[THROTTLE_BPS_WRITE].avg;
var->iops_total = cfg->buckets[THROTTLE_OPS_TOTAL].avg;
var->iops_read = cfg->buckets[THROTTLE_OPS_READ].avg;
var->iops_write = cfg->buckets[THROTTLE_OPS_WRITE].avg;
var->bps_total_max = cfg->buckets[THROTTLE_BPS_TOTAL].max;
var->bps_read_max = cfg->buckets[THROTTLE_BPS_READ].max;
var->bps_write_max = cfg->buckets[THROTTLE_BPS_WRITE].max;
var->iops_total_max = cfg->buckets[THROTTLE_OPS_TOTAL].max;
var->iops_read_max = cfg->buckets[THROTTLE_OPS_READ].max;
var->iops_write_max = cfg->buckets[THROTTLE_OPS_WRITE].max;
var->bps_total_max_length = cfg->buckets[THROTTLE_BPS_TOTAL].burst_length;
var->bps_read_max_length = cfg->buckets[THROTTLE_BPS_READ].burst_length;
var->bps_write_max_length = cfg->buckets[THROTTLE_BPS_WRITE].burst_length;
var->iops_total_max_length = cfg->buckets[THROTTLE_OPS_TOTAL].burst_length;
var->iops_read_max_length = cfg->buckets[THROTTLE_OPS_READ].burst_length;
var->iops_write_max_length = cfg->buckets[THROTTLE_OPS_WRITE].burst_length;
var->iops_size = cfg->op_size;
var->has_bps_total = true;
var->has_bps_read = true;
var->has_bps_write = true;
var->has_iops_total = true;
var->has_iops_read = true;
var->has_iops_write = true;
var->has_bps_total_max = true;
var->has_bps_read_max = true;
var->has_bps_write_max = true;
var->has_iops_total_max = true;
var->has_iops_read_max = true;
var->has_iops_write_max = true;
var->has_bps_read_max_length = true;
var->has_bps_total_max_length = true;
var->has_bps_write_max_length = true;
var->has_iops_total_max_length = true;
var->has_iops_read_max_length = true;
var->has_iops_write_max_length = true;
var->has_iops_size = true;
}