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) {
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;
@ -513,6 +515,8 @@ int bdrv_probe_geometry(BlockDriverState *bs, HDGeometry *geo)
if (drv && drv->bdrv_probe_geometry) {
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;
@ -3426,11 +3430,15 @@ int bdrv_truncate(BdrvChild *child, int64_t offset, PreallocMode prealloc,
assert(child->perm & BLK_PERM_RESIZE);
/* if bs->drv == NULL, bs is closed, so there's nothing to do here */
if (!drv) {
error_setg(errp, "No medium inserted");
return -ENOMEDIUM;
}
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");
return -ENOTSUP;
}
@ -3767,6 +3775,9 @@ int bdrv_has_zero_init(BlockDriverState *bs)
if (bs->drv->bdrv_has_zero_init) {
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 */
return 0;
@ -3821,10 +3832,16 @@ void bdrv_get_backing_filename(BlockDriverState *bs,
int bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
{
BlockDriver *drv = bs->drv;
if (!drv)
/* if bs->drv == NULL, bs is closed, so there's nothing to do here */
if (!drv) {
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;
}
memset(bdi, 0, sizeof(*bdi));
return drv->bdrv_get_info(bs, bdi);
}
@ -4174,20 +4191,6 @@ bool bdrv_is_inserted(BlockDriverState *bs)
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
*/

View File

@ -25,6 +25,7 @@ block-obj-y += accounting.o dirty-bitmap.o
block-obj-y += write-threshold.o
block-obj-y += backup.o
block-obj-$(CONFIG_REPLICATION) += replication.o
block-obj-y += throttle.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);
}
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)
{
BDRVBlkdebugState *s = bs->opaque;
@ -808,12 +798,6 @@ static int64_t blkdebug_getlength(BlockDriverState *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)
{
BDRVBlkdebugState *s = bs->opaque;
@ -896,6 +880,7 @@ static BlockDriver bdrv_blkdebug = {
.format_name = "blkdebug",
.protocol_name = "blkdebug",
.instance_size = sizeof(BDRVBlkdebugState),
.is_filter = true,
.bdrv_parse_filename = blkdebug_parse_filename,
.bdrv_file_open = blkdebug_open,
@ -904,7 +889,6 @@ static BlockDriver bdrv_blkdebug = {
.bdrv_child_perm = bdrv_filter_default_perms,
.bdrv_getlength = blkdebug_getlength,
.bdrv_truncate = blkdebug_truncate,
.bdrv_refresh_filename = blkdebug_refresh_filename,
.bdrv_refresh_limits = blkdebug_refresh_limits,
@ -913,7 +897,7 @@ static BlockDriver bdrv_blkdebug = {
.bdrv_co_flush_to_disk = blkdebug_co_flush,
.bdrv_co_pwrite_zeroes = blkdebug_co_pwrite_zeroes,
.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_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_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);
notifier_list_init(&blk->remove_bs_notifiers);
@ -343,7 +340,7 @@ static void blk_delete(BlockBackend *blk)
assert(!blk->refcnt);
assert(!blk->name);
assert(!blk->dev);
if (blk->public.throttle_state) {
if (blk->public.throttle_group_member.throttle_state) {
blk_io_limits_disable(blk);
}
if (blk->root) {
@ -658,9 +655,12 @@ BlockBackend *blk_by_public(BlockBackendPublic *public)
*/
void blk_remove_bs(BlockBackend *blk)
{
ThrottleTimers *tt;
notifier_list_notify(&blk->remove_bs_notifiers, blk);
if (blk->public.throttle_state) {
throttle_timers_detach_aio_context(&blk->public.throttle_timers);
if (blk->public.throttle_group_member.throttle_state) {
tt = &blk->public.throttle_group_member.throttle_timers;
throttle_timers_detach_aio_context(tt);
}
blk_update_root_state(blk);
@ -682,9 +682,10 @@ int blk_insert_bs(BlockBackend *blk, BlockDriverState *bs, Error **errp)
bdrv_ref(bs);
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(
&blk->public.throttle_timers, bdrv_get_aio_context(bs));
&blk->public.throttle_group_member.throttle_timers,
bdrv_get_aio_context(bs));
}
return 0;
@ -1046,8 +1047,9 @@ int coroutine_fn blk_co_preadv(BlockBackend *blk, int64_t offset,
bdrv_inc_in_flight(bs);
/* throttling disk I/O */
if (blk->public.throttle_state) {
throttle_group_co_io_limits_intercept(blk, bytes, false);
if (blk->public.throttle_group_member.throttle_state) {
throttle_group_co_io_limits_intercept(&blk->public.throttle_group_member,
bytes, false);
}
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);
/* throttling disk I/O */
if (blk->public.throttle_state) {
throttle_group_co_io_limits_intercept(blk, bytes, true);
if (blk->public.throttle_group_member.throttle_state) {
throttle_group_co_io_limits_intercept(&blk->public.throttle_group_member,
bytes, true);
}
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)
{
BlockDriverState *bs = blk_bs(blk);
ThrottleGroupMember *tgm = &blk->public.throttle_group_member;
if (bs) {
if (blk->public.throttle_state) {
throttle_timers_detach_aio_context(&blk->public.throttle_timers);
if (tgm->throttle_state) {
throttle_group_detach_aio_context(tgm);
throttle_group_attach_aio_context(tgm, 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 */
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)
{
assert(blk->public.throttle_state);
assert(blk->public.throttle_group_member.throttle_state);
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));
}
/* should be called before blk_set_io_limits if a limit is set */
void blk_io_limits_enable(BlockBackend *blk, const char *group)
{
assert(!blk->public.throttle_state);
throttle_group_register_blk(blk, group);
assert(!blk->public.throttle_group_member.throttle_state);
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)
{
/* this BB is not part of any group */
if (!blk->public.throttle_state) {
if (!blk->public.throttle_group_member.throttle_state) {
return;
}
/* 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;
}
@ -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
* attaching to a BlockDriverState that is drained. Use child instead. */
if (atomic_fetch_inc(&blk->public.io_limits_disabled) == 0) {
throttle_group_restart_blk(blk);
if (atomic_fetch_inc(&blk->public.throttle_group_member.io_limits_disabled) == 0) {
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;
assert(blk->quiesce_counter);
assert(blk->public.io_limits_disabled);
atomic_dec(&blk->public.io_limits_disabled);
assert(blk->public.throttle_group_member.io_limits_disabled);
atomic_dec(&blk->public.throttle_group_member.io_limits_disabled);
if (--blk->quiesce_counter == 0) {
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);
}
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)
{
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 = {
.format_name = "commit_top",
.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_close = bdrv_commit_top_close,
.bdrv_child_perm = bdrv_commit_top_child_perm,

View File

@ -1714,6 +1714,32 @@ typedef struct BdrvCoGetBlockStatusData {
bool done;
} 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.
* 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);
}
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,
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_pdiscard = bdrv_mirror_top_pdiscard,
.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_close = bdrv_mirror_top_close,
.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;
if (blk && blk_get_public(blk)->throttle_state) {
if (blk && blk_get_public(blk)->throttle_group_member.throttle_state) {
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_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->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);

View File

@ -347,19 +347,22 @@ static int qcow_reopen_prepare(BDRVReopenState *state,
* 'compressed_size'. 'compressed_size' must be > 0 and <
* 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,
uint64_t offset, int allocate,
int compressed_size,
int n_start, int n_end)
static int get_cluster_offset(BlockDriverState *bs,
uint64_t offset, int allocate,
int compressed_size,
int n_start, int n_end, uint64_t *result)
{
BDRVQcowState *s = bs->opaque;
int min_index, i, j, l1_index, l2_index;
uint64_t l2_offset, *l2_table, cluster_offset, tmp;
int min_index, i, j, l1_index, l2_index, ret;
int64_t l2_offset;
uint64_t *l2_table, cluster_offset, tmp;
uint32_t min_count;
int new_l2_table;
*result = 0;
l1_index = offset >> (s->l2_bits + s->cluster_bits);
l2_offset = s->l1_table[l1_index];
new_l2_table = 0;
@ -368,15 +371,20 @@ static uint64_t get_cluster_offset(BlockDriverState *bs,
return 0;
/* allocate a new l2 entry */
l2_offset = bdrv_getlength(bs->file->bs);
if (l2_offset < 0) {
return l2_offset;
}
/* 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 */
s->l1_table[l1_index] = l2_offset;
tmp = cpu_to_be64(l2_offset);
if (bdrv_pwrite_sync(bs->file,
s->l1_table_offset + l1_index * sizeof(tmp),
&tmp, sizeof(tmp)) < 0)
return 0;
ret = bdrv_pwrite_sync(bs->file,
s->l1_table_offset + l1_index * sizeof(tmp),
&tmp, sizeof(tmp));
if (ret < 0) {
return ret;
}
new_l2_table = 1;
}
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);
if (new_l2_table) {
memset(l2_table, 0, s->l2_size * sizeof(uint64_t));
if (bdrv_pwrite_sync(bs->file, l2_offset, l2_table,
s->l2_size * sizeof(uint64_t)) < 0)
return 0;
ret = bdrv_pwrite_sync(bs->file, l2_offset, l2_table,
s->l2_size * sizeof(uint64_t));
if (ret < 0) {
return ret;
}
} else {
if (bdrv_pread(bs->file, l2_offset, l2_table,
s->l2_size * sizeof(uint64_t)) !=
s->l2_size * sizeof(uint64_t))
return 0;
ret = bdrv_pread(bs->file, l2_offset, l2_table,
s->l2_size * sizeof(uint64_t));
if (ret < 0) {
return ret;
}
}
s->l2_cache_offsets[min_index] = l2_offset;
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
decompress it in the case it is not completely
overwritten */
if (decompress_cluster(bs, cluster_offset) < 0)
return 0;
if (decompress_cluster(bs, cluster_offset) < 0) {
return -EIO;
}
cluster_offset = bdrv_getlength(bs->file->bs);
cluster_offset = (cluster_offset + s->cluster_size - 1) &
~(s->cluster_size - 1);
if ((int64_t) cluster_offset < 0) {
return cluster_offset;
}
cluster_offset = QEMU_ALIGN_UP(cluster_offset, s->cluster_size);
/* write the cluster content */
if (bdrv_pwrite(bs->file, cluster_offset, s->cluster_cache,
s->cluster_size) !=
s->cluster_size)
return -1;
ret = bdrv_pwrite(bs->file, cluster_offset, s->cluster_cache,
s->cluster_size);
if (ret < 0) {
return ret;
}
} else {
cluster_offset = bdrv_getlength(bs->file->bs);
if ((int64_t) cluster_offset < 0) {
return cluster_offset;
}
if (allocate == 1) {
/* round to cluster size */
cluster_offset = (cluster_offset + s->cluster_size - 1) &
~(s->cluster_size - 1);
bdrv_truncate(bs->file, cluster_offset + s->cluster_size,
PREALLOC_MODE_OFF, NULL);
cluster_offset = QEMU_ALIGN_UP(cluster_offset, s->cluster_size);
if (cluster_offset + s->cluster_size > INT64_MAX) {
return -E2BIG;
}
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
content which won't be written */
if (bs->encrypted &&
@ -459,13 +482,14 @@ static uint64_t get_cluster_offset(BlockDriverState *bs,
s->cluster_data,
BDRV_SECTOR_SIZE,
NULL) < 0) {
errno = EIO;
return -1;
return -EIO;
}
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 */
tmp = cpu_to_be64(cluster_offset);
l2_table[l2_index] = tmp;
if (bdrv_pwrite_sync(bs->file, l2_offset + l2_index * sizeof(tmp),
&tmp, sizeof(tmp)) < 0)
return 0;
ret = bdrv_pwrite_sync(bs->file, l2_offset + l2_index * sizeof(tmp),
&tmp, sizeof(tmp));
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,
int64_t sector_num, int nb_sectors, int *pnum, BlockDriverState **file)
{
BDRVQcowState *s = bs->opaque;
int index_in_cluster, n;
int index_in_cluster, n, ret;
uint64_t cluster_offset;
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);
if (ret < 0) {
return ret;
}
index_in_cluster = sector_num & (s->cluster_sectors - 1);
n = s->cluster_sectors - index_in_cluster;
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) {
/* prepare next request */
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);
if (ret < 0) {
break;
}
index_in_cluster = sector_num & (s->cluster_sectors - 1);
n = s->cluster_sectors - index_in_cluster;
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);
qemu_co_mutex_lock(&s->lock);
if (ret < 0) {
goto fail;
break;
}
} else {
/* 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) {
/* add AIO support for compressed blocks ? */
if (decompress_cluster(bs, cluster_offset) < 0) {
goto fail;
ret = -EIO;
break;
}
memcpy(buf,
s->cluster_cache + index_in_cluster * 512, 512 * n);
} else {
if ((cluster_offset & 511) != 0) {
goto fail;
ret = -EIO;
break;
}
hd_iov.iov_base = (void *)buf;
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);
if (qcrypto_block_decrypt(s->crypto, sector_num, buf,
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;
}
done:
qemu_co_mutex_unlock(&s->lock);
if (qiov->niov > 1) {
@ -655,10 +690,6 @@ done:
}
return ret;
fail:
ret = -EIO;
goto done;
}
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) {
n = nb_sectors;
}
cluster_offset = get_cluster_offset(bs, sector_num << 9, 1, 0,
index_in_cluster,
index_in_cluster + n);
ret = get_cluster_offset(bs, sector_num << 9, 1, 0,
index_in_cluster,
index_in_cluster + n, &cluster_offset);
if (ret < 0) {
break;
}
if (!cluster_offset || (cluster_offset & 511) != 0) {
ret = -EIO;
break;
@ -995,8 +1029,11 @@ qcow_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset,
goto success;
}
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);
if (ret < 0) {
goto fail;
}
if (cluster_offset == 0) {
ret = -EIO;
goto fail;

View File

@ -2036,6 +2036,14 @@ static int qcow2_inactivate(BlockDriverState *bs)
int ret, result = 0;
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);
if (ret) {
result = ret;
@ -2050,14 +2058,6 @@ static int qcow2_inactivate(BlockDriverState *bs)
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) {
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);
}
static int raw_media_changed(BlockDriverState *bs)
{
return bdrv_media_changed(bs->file->bs);
}
static void raw_eject(BlockDriverState *bs, bool eject_flag)
{
bdrv_eject(bs->file->bs, eject_flag);
@ -510,7 +505,6 @@ BlockDriver bdrv_raw = {
.bdrv_refresh_limits = &raw_refresh_limits,
.bdrv_probe_blocksizes = &raw_probe_blocksizes,
.bdrv_probe_geometry = &raw_probe_geometry,
.bdrv_media_changed = &raw_media_changed,
.bdrv_eject = &raw_eject,
.bdrv_lock_medium = &raw_lock_medium,
.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)) {
/* Enable I/O limits if they're not enabled yet, otherwise
* 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,
arg->has_group ? arg->group :
arg->has_device ? arg->device :
@ -2696,7 +2696,7 @@ void qmp_block_set_io_throttle(BlockIOThrottle *arg, Error **errp)
}
/* Set the new throttling configuration */
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 */
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);
bool bdrv_is_sg(BlockDriverState *bs);
bool bdrv_is_inserted(BlockDriverState *bs);
int bdrv_media_changed(BlockDriverState *bs);
void bdrv_lock_medium(BlockDriverState *bs, bool locked);
void bdrv_eject(BlockDriverState *bs, bool eject_flag);
const char *bdrv_get_format_name(BlockDriverState *bs);

View File

@ -87,7 +87,11 @@ struct BlockDriver {
const char *format_name;
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;
/* for snapshots block filter like Quorum can implement the
* following recursive callback.
@ -275,7 +279,6 @@ struct BlockDriver {
/* removable device specific */
bool (*bdrv_is_inserted)(BlockDriverState *bs);
int (*bdrv_media_changed)(BlockDriverState *bs);
void (*bdrv_eject)(BlockDriverState *bs, bool eject_flag);
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 *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);
void blk_dev_change_media_cb(BlockBackend *blk, bool load, Error **errp);
bool blk_dev_has_removable_media(BlockBackend *blk);

View File

@ -28,20 +28,58 @@
#include "qemu/throttle.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);
void throttle_group_unref(ThrottleState *ts);
void throttle_group_config(BlockBackend *blk, ThrottleConfig *cfg);
void throttle_group_get_config(BlockBackend *blk, ThrottleConfig *cfg);
void throttle_group_config(ThrottleGroupMember *tgm, ThrottleConfig *cfg);
void throttle_group_get_config(ThrottleGroupMember *tgm, ThrottleConfig *cfg);
void throttle_group_register_blk(BlockBackend *blk, const char *groupname);
void throttle_group_unregister_blk(BlockBackend *blk);
void throttle_group_restart_blk(BlockBackend *blk);
void throttle_group_register_tgm(ThrottleGroupMember *tgm,
const char *groupname,
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,
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

View File

@ -10,81 +10,103 @@
#ifndef 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 \
{ \
.name = "throttling.iops-total",\
.name = THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_TOTAL,\
.type = QEMU_OPT_NUMBER,\
.help = "limit total I/O operations per second",\
},{ \
.name = "throttling.iops-read",\
.name = THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_READ,\
.type = QEMU_OPT_NUMBER,\
.help = "limit read operations per second",\
},{ \
.name = "throttling.iops-write",\
.name = THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_WRITE,\
.type = QEMU_OPT_NUMBER,\
.help = "limit write operations per second",\
},{ \
.name = "throttling.bps-total",\
.name = THROTTLE_OPT_PREFIX QEMU_OPT_BPS_TOTAL,\
.type = QEMU_OPT_NUMBER,\
.help = "limit total bytes per second",\
},{ \
.name = "throttling.bps-read",\
.name = THROTTLE_OPT_PREFIX QEMU_OPT_BPS_READ,\
.type = QEMU_OPT_NUMBER,\
.help = "limit read bytes per second",\
},{ \
.name = "throttling.bps-write",\
.name = THROTTLE_OPT_PREFIX QEMU_OPT_BPS_WRITE,\
.type = QEMU_OPT_NUMBER,\
.help = "limit write bytes per second",\
},{ \
.name = "throttling.iops-total-max",\
.name = THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_TOTAL_MAX,\
.type = QEMU_OPT_NUMBER,\
.help = "I/O operations burst",\
},{ \
.name = "throttling.iops-read-max",\
.name = THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_READ_MAX,\
.type = QEMU_OPT_NUMBER,\
.help = "I/O operations read burst",\
},{ \
.name = "throttling.iops-write-max",\
.name = THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_WRITE_MAX,\
.type = QEMU_OPT_NUMBER,\
.help = "I/O operations write burst",\
},{ \
.name = "throttling.bps-total-max",\
.name = THROTTLE_OPT_PREFIX QEMU_OPT_BPS_TOTAL_MAX,\
.type = QEMU_OPT_NUMBER,\
.help = "total bytes burst",\
},{ \
.name = "throttling.bps-read-max",\
.name = THROTTLE_OPT_PREFIX QEMU_OPT_BPS_READ_MAX,\
.type = QEMU_OPT_NUMBER,\
.help = "total bytes read burst",\
},{ \
.name = "throttling.bps-write-max",\
.name = THROTTLE_OPT_PREFIX QEMU_OPT_BPS_WRITE_MAX,\
.type = QEMU_OPT_NUMBER,\
.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,\
.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,\
.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,\
.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,\
.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,\
.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,\
.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,\
.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);
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

View File

@ -70,24 +70,10 @@ typedef struct BlockDevOps {
/* This struct is embedded in (the private) BlockBackend struct and contains
* 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 {
/* 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(BlockBackendPublic) round_robin;
ThrottleGroupMember throttle_group_member;
} BlockBackendPublic;
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_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:
#
@ -2175,6 +2223,7 @@
# Drivers that are supported in block device operations.
#
# @vxhs: Since 2.10
# @throttle: Since 2.11
#
# Since: 2.9
##
@ -2184,7 +2233,7 @@
'host_device', 'http', 'https', 'iscsi', 'luks', 'nbd', 'nfs',
'null-aio', 'null-co', 'parallels', 'qcow', 'qcow2', 'qed',
'quorum', 'raw', 'rbd', 'replication', 'sheepdog', 'ssh',
'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat', 'vxhs' ] }
'throttle', 'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat', 'vxhs' ] }
##
# @BlockdevOptionsFile:
@ -3048,6 +3097,20 @@
'*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:
#
# Options for creating a block device. Many options are available for all
@ -3108,6 +3171,7 @@
'replication':'BlockdevOptionsReplication',
'sheepdog': 'BlockdevOptionsSheepdog',
'ssh': 'BlockdevOptionsSsh',
'throttle': 'BlockdevOptionsThrottle',
'vdi': 'BlockdevOptionsGenericFormat',
'vhdx': 'BlockdevOptionsGenericFormat',
'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
182 rw auto quick
183 rw auto migration
184 rw auto quick
185 rw auto
186 rw auto
187 rw auto

View File

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