* [Qemu-devel] [PULL 00/14] Block layer patches
@ 2017-01-09 13:44 Kevin Wolf
2017-01-09 15:30 ` Peter Maydell
0 siblings, 1 reply; 24+ messages in thread
From: Kevin Wolf @ 2017-01-09 13:44 UTC (permalink / raw)
To: qemu-block; +Cc: kwolf, qemu-devel
The following changes since commit ffe22bf51065dd33022cf91f77a821d1f11c250d:
Merge remote-tracking branch 'remotes/gonglei/tags/cryptodev-next-20161224' into staging (2017-01-06 15:18:09 +0000)
are available in the git repository at:
git://repo.or.cz/qemu/kevin.git tags/for-upstream
for you to fetch changes up to c1bb86cd8ae67c14f79422b6e544d1e2bf40eeb2:
block: Rename raw-{posix,win32} to file-*.c (2017-01-09 13:30:53 +0100)
----------------------------------------------------------------
Block layer patches
----------------------------------------------------------------
Eric Blake (2):
block: Rename raw_bsd to raw-format.c
block: Rename raw-{posix,win32} to file-*.c
Kevin Wolf (11):
coroutine: Introduce qemu_coroutine_enter_if_inactive()
quorum: Remove s from quorum_aio_get() arguments
quorum: Implement .bdrv_co_readv/writev
quorum: Do cleanup in caller coroutine
quorum: Inline quorum_aio_cb()
quorum: Avoid bdrv_aio_writev() for rewrites
quorum: Implement .bdrv_co_preadv/pwritev()
quorum: Inline quorum_fifo_aio_cb()
quorum: Clean up quorum_aio_get()
blkdebug: Implement bdrv_co_preadv/pwritev/flush
blkverify: Implement bdrv_co_preadv/pwritev/flush
Paolo Bonzini (1):
qemu-img: fix in-flight count for qemu-img bench
MAINTAINERS | 6 +-
block/Makefile.objs | 6 +-
block/blkdebug.c | 86 ++++----
block/blkverify.c | 201 +++++++++---------
block/{raw-posix.c => file-posix.c} | 0
block/{raw-win32.c => file-win32.c} | 0
block/gluster.c | 4 +-
block/quorum.c | 410 +++++++++++++++++++-----------------
block/{raw_bsd.c => raw-format.c} | 2 +-
block/trace-events | 4 +-
configure | 2 +-
include/block/block_int.h | 2 +-
include/qemu/coroutine.h | 6 +
qemu-img.c | 17 +-
tests/qemu-iotests/071.out | 8 +-
util/qemu-coroutine.c | 7 +
16 files changed, 392 insertions(+), 369 deletions(-)
rename block/{raw-posix.c => file-posix.c} (100%)
rename block/{raw-win32.c => file-win32.c} (100%)
rename block/{raw_bsd.c => raw-format.c} (99%)
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [Qemu-devel] [PULL 00/14] Block layer patches
2017-01-09 13:44 Kevin Wolf
@ 2017-01-09 15:30 ` Peter Maydell
0 siblings, 0 replies; 24+ messages in thread
From: Peter Maydell @ 2017-01-09 15:30 UTC (permalink / raw)
To: Kevin Wolf; +Cc: Qemu-block, QEMU Developers
On 9 January 2017 at 13:44, Kevin Wolf <kwolf@redhat.com> wrote:
> The following changes since commit ffe22bf51065dd33022cf91f77a821d1f11c250d:
>
> Merge remote-tracking branch 'remotes/gonglei/tags/cryptodev-next-20161224' into staging (2017-01-06 15:18:09 +0000)
>
> are available in the git repository at:
>
>
> git://repo.or.cz/qemu/kevin.git tags/for-upstream
>
> for you to fetch changes up to c1bb86cd8ae67c14f79422b6e544d1e2bf40eeb2:
>
> block: Rename raw-{posix,win32} to file-*.c (2017-01-09 13:30:53 +0100)
>
> ----------------------------------------------------------------
> Block layer patches
>
> ----------------------------------------------------------------
Applied, thanks.
-- PMM
^ permalink raw reply [flat|nested] 24+ messages in thread
* [Qemu-devel] [PULL 00/14] Block layer patches
@ 2017-09-06 14:02 Kevin Wolf
2017-09-06 14:02 ` [Qemu-devel] [PULL 01/14] block: pass bdrv_* methods to bs->file by default in block filters Kevin Wolf
` (14 more replies)
0 siblings, 15 replies; 24+ messages in thread
From: Kevin Wolf @ 2017-09-06 14:02 UTC (permalink / raw)
To: qemu-block; +Cc: kwolf, qemu-devel
The following changes since commit 98bfaac788be0ca63d7d010c8d4ba100ff1d8278:
Merge remote-tracking branch 'remotes/armbru/tags/pull-qapi-2017-09-01-v3' into staging (2017-09-04 13:28:09 +0100)
are available in the git repository at:
git://repo.or.cz/qemu/kevin.git tags/for-upstream
for you to fetch changes up to 83a8c775a8bf134eb18a719322939b74a818d750:
qcow2: move qcow2_store_persistent_dirty_bitmaps() before cache flushing (2017-09-06 14:40:18 +0200)
----------------------------------------------------------------
Block layer patches
----------------------------------------------------------------
Daniel P. Berrange (1):
block: document semantics of bdrv_co_preadv|pwritev
Eric Blake (2):
qcow: Change signature of get_cluster_offset()
qcow: Check failure of bdrv_getlength() and bdrv_truncate()
Manos Pitsidianakis (10):
block: pass bdrv_* methods to bs->file by default in block filters
block: remove unused bdrv_media_changed
block: remove bdrv_truncate callback in blkdebug
block: add default implementations for bdrv_co_get_block_status()
block: move ThrottleGroup membership to ThrottleGroupMember
block: add aio_context field in ThrottleGroupMember
block: tidy ThrottleGroupMember initializations
block: convert ThrottleGroup to object with QOM
block: add throttle block filter driver
qemu-iotests: add 184 for throttle filter driver
Pavel Butsykin (1):
qcow2: move qcow2_store_persistent_dirty_bitmaps() before cache flushing
qapi/block-core.json | 66 +++-
include/block/block.h | 1 -
include/block/block_int.h | 56 ++-
include/block/throttle-groups.h | 52 ++-
include/qemu/throttle-options.h | 60 +++-
include/qemu/throttle.h | 3 +
include/sysemu/block-backend.h | 20 +-
block.c | 35 +-
block/blkdebug.c | 20 +-
block/block-backend.c | 62 ++--
block/commit.c | 12 +-
block/io.c | 26 ++
block/mirror.c | 12 +-
block/qapi.c | 8 +-
block/qcow.c | 153 ++++----
block/qcow2.c | 16 +-
block/raw-format.c | 6 -
block/throttle-groups.c | 750 ++++++++++++++++++++++++++++++----------
block/throttle.c | 237 +++++++++++++
blockdev.c | 4 +-
tests/test-throttle.c | 111 +++---
util/throttle.c | 151 ++++++++
block/Makefile.objs | 1 +
tests/qemu-iotests/184 | 205 +++++++++++
tests/qemu-iotests/184.out | 302 ++++++++++++++++
tests/qemu-iotests/group | 1 +
26 files changed, 1917 insertions(+), 453 deletions(-)
create mode 100644 block/throttle.c
create mode 100755 tests/qemu-iotests/184
create mode 100644 tests/qemu-iotests/184.out
^ permalink raw reply [flat|nested] 24+ messages in thread
* [Qemu-devel] [PULL 01/14] block: pass bdrv_* methods to bs->file by default in block filters
2017-09-06 14:02 [Qemu-devel] [PULL 00/14] Block layer patches Kevin Wolf
@ 2017-09-06 14:02 ` Kevin Wolf
2017-09-06 14:02 ` [Qemu-devel] [PULL 02/14] block: remove unused bdrv_media_changed Kevin Wolf
` (13 subsequent siblings)
14 siblings, 0 replies; 24+ messages in thread
From: Kevin Wolf @ 2017-09-06 14:02 UTC (permalink / raw)
To: qemu-block; +Cc: kwolf, qemu-devel
From: Manos Pitsidianakis <el13635@mail.ntua.gr>
The following functions fail if bs->drv is a filter and does not
implement them:
bdrv_probe_blocksizes
bdrv_probe_geometry
bdrv_truncate
bdrv_has_zero_init
bdrv_get_info
Instead, the call should be passed to bs->file if it exists, to allow
filter drivers to support those methods without implementing them. This
commit makes `drv->is_filter = true` imply that these callbacks will be
forwarded to bs->file by default, so disabling support for these
functions must be done explicitly.
Reviewed-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
include/block/block_int.h | 6 +++++-
block.c | 21 +++++++++++++++++++--
2 files changed, 24 insertions(+), 3 deletions(-)
diff --git a/include/block/block_int.h b/include/block/block_int.h
index 7571c0aaaf..71183cc76e 100644
--- a/include/block/block_int.h
+++ b/include/block/block_int.h
@@ -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.
diff --git a/block.c b/block.c
index b749bd6404..175e8d35ff 100644
--- a/block.c
+++ b/block.c
@@ -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);
}
--
2.13.5
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [Qemu-devel] [PULL 02/14] block: remove unused bdrv_media_changed
2017-09-06 14:02 [Qemu-devel] [PULL 00/14] Block layer patches Kevin Wolf
2017-09-06 14:02 ` [Qemu-devel] [PULL 01/14] block: pass bdrv_* methods to bs->file by default in block filters Kevin Wolf
@ 2017-09-06 14:02 ` Kevin Wolf
2017-09-06 14:02 ` [Qemu-devel] [PULL 03/14] block: remove bdrv_truncate callback in blkdebug Kevin Wolf
` (12 subsequent siblings)
14 siblings, 0 replies; 24+ messages in thread
From: Kevin Wolf @ 2017-09-06 14:02 UTC (permalink / raw)
To: qemu-block; +Cc: kwolf, qemu-devel
From: Manos Pitsidianakis <el13635@mail.ntua.gr>
This function is not used anywhere, so remove it.
Markus Armbruster adds:
The i82078 floppy device model used to call bdrv_media_changed() to
implement its media change bit when backed by a host floppy. This
went away in 21fcf36 "fdc: simplify media change handling".
Probably broke host floppy media change. Host floppy pass-through
was dropped in commit f709623. bdrv_media_changed() has never been
used for anything else. Remove it.
(Source is Message-ID: <87y3ruaypm.fsf@dusky.pond.sub.org>)
Reviewed-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
include/block/block.h | 1 -
include/block/block_int.h | 1 -
block.c | 14 --------------
block/raw-format.c | 6 ------
4 files changed, 22 deletions(-)
diff --git a/include/block/block.h b/include/block/block.h
index ab80195378..2ad18775af 100644
--- a/include/block/block.h
+++ b/include/block/block.h
@@ -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);
diff --git a/include/block/block_int.h b/include/block/block_int.h
index 71183cc76e..ca4e7e5629 100644
--- a/include/block/block_int.h
+++ b/include/block/block_int.h
@@ -248,7 +248,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);
diff --git a/block.c b/block.c
index 175e8d35ff..6dd47e414e 100644
--- a/block.c
+++ b/block.c
@@ -4192,20 +4192,6 @@ bool bdrv_is_inserted(BlockDriverState *bs)
}
/**
- * 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
*/
void bdrv_eject(BlockDriverState *bs, bool eject_flag)
diff --git a/block/raw-format.c b/block/raw-format.c
index 142649ed56..ab552c0954 100644
--- a/block/raw-format.c
+++ b/block/raw-format.c
@@ -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,
--
2.13.5
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [Qemu-devel] [PULL 03/14] block: remove bdrv_truncate callback in blkdebug
2017-09-06 14:02 [Qemu-devel] [PULL 00/14] Block layer patches Kevin Wolf
2017-09-06 14:02 ` [Qemu-devel] [PULL 01/14] block: pass bdrv_* methods to bs->file by default in block filters Kevin Wolf
2017-09-06 14:02 ` [Qemu-devel] [PULL 02/14] block: remove unused bdrv_media_changed Kevin Wolf
@ 2017-09-06 14:02 ` Kevin Wolf
2017-09-06 14:02 ` [Qemu-devel] [PULL 04/14] block: add default implementations for bdrv_co_get_block_status() Kevin Wolf
` (11 subsequent siblings)
14 siblings, 0 replies; 24+ messages in thread
From: Kevin Wolf @ 2017-09-06 14:02 UTC (permalink / raw)
To: qemu-block; +Cc: kwolf, qemu-devel
From: Manos Pitsidianakis <el13635@mail.ntua.gr>
Now that bdrv_truncate is passed to bs->file by default, remove the
callback from block/blkdebug.c and set is_filter to true. is_filter also gives
access to other callbacks that are forwarded automatically to bs->file for
filters.
Reviewed-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
block/blkdebug.c | 8 +-------
1 file changed, 1 insertion(+), 7 deletions(-)
diff --git a/block/blkdebug.c b/block/blkdebug.c
index 8e385acf54..f10e3e5638 100644
--- a/block/blkdebug.c
+++ b/block/blkdebug.c
@@ -808,12 +808,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 +890,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 +899,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,
--
2.13.5
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [Qemu-devel] [PULL 04/14] block: add default implementations for bdrv_co_get_block_status()
2017-09-06 14:02 [Qemu-devel] [PULL 00/14] Block layer patches Kevin Wolf
` (2 preceding siblings ...)
2017-09-06 14:02 ` [Qemu-devel] [PULL 03/14] block: remove bdrv_truncate callback in blkdebug Kevin Wolf
@ 2017-09-06 14:02 ` Kevin Wolf
2017-09-06 14:02 ` [Qemu-devel] [PULL 05/14] qcow: Change signature of get_cluster_offset() Kevin Wolf
` (10 subsequent siblings)
14 siblings, 0 replies; 24+ messages in thread
From: Kevin Wolf @ 2017-09-06 14:02 UTC (permalink / raw)
To: qemu-block; +Cc: kwolf, qemu-devel
From: Manos Pitsidianakis <el13635@mail.ntua.gr>
bdrv_co_get_block_status_from_file() and
bdrv_co_get_block_status_from_backing() set *file to bs->file and
bs->backing respectively, so that bdrv_co_get_block_status() can recurse
to them. Future block drivers won't have to duplicate code to implement
this.
Reviewed-by: Fam Zheng <famz@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
include/block/block_int.h | 18 ++++++++++++++++++
block/blkdebug.c | 12 +-----------
block/commit.c | 12 +-----------
block/io.c | 26 ++++++++++++++++++++++++++
block/mirror.c | 12 +-----------
5 files changed, 47 insertions(+), 33 deletions(-)
diff --git a/include/block/block_int.h b/include/block/block_int.h
index ca4e7e5629..223801e4fb 100644
--- a/include/block/block_int.h
+++ b/include/block/block_int.h
@@ -964,6 +964,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);
diff --git a/block/blkdebug.c b/block/blkdebug.c
index f10e3e5638..46e53f2f09 100644
--- a/block/blkdebug.c
+++ b/block/blkdebug.c
@@ -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;
@@ -907,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,
diff --git a/block/commit.c b/block/commit.c
index c7857c3321..898d91f653 100644
--- a/block/commit.c
+++ b/block/commit.c
@@ -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,
diff --git a/block/io.c b/block/io.c
index 26003814eb..4378ae4c7d 100644
--- a/block/io.c
+++ b/block/io.c
@@ -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
diff --git a/block/mirror.c b/block/mirror.c
index 429751b9fe..6531652d73 100644
--- a/block/mirror.c
+++ b/block/mirror.c
@@ -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,
--
2.13.5
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [Qemu-devel] [PULL 05/14] qcow: Change signature of get_cluster_offset()
2017-09-06 14:02 [Qemu-devel] [PULL 00/14] Block layer patches Kevin Wolf
` (3 preceding siblings ...)
2017-09-06 14:02 ` [Qemu-devel] [PULL 04/14] block: add default implementations for bdrv_co_get_block_status() Kevin Wolf
@ 2017-09-06 14:02 ` Kevin Wolf
2017-09-06 14:02 ` [Qemu-devel] [PULL 06/14] qcow: Check failure of bdrv_getlength() and bdrv_truncate() Kevin Wolf
` (9 subsequent siblings)
14 siblings, 0 replies; 24+ messages in thread
From: Kevin Wolf @ 2017-09-06 14:02 UTC (permalink / raw)
To: qemu-block; +Cc: kwolf, qemu-devel
From: Eric Blake <eblake@redhat.com>
The old signature has an ambiguous meaning for a return of 0:
either no allocation was requested or necessary, or an error
occurred (but any errno associated with the error is lost to
the caller, which then has to assume EIO).
Better is to follow the example of qcow2, by changing the
signature to have a separate return value that cleanly
distinguishes between failure and success, along with a
parameter that cleanly holds a 64-bit value. Then update all
callers.
While auditing that all return paths return a negative errno
(rather than -1), I also simplified places where we can pass
NULL rather than a local Error that just gets thrown away.
Suggested-by: Kevin Wolf <kwolf@redhat.com>
Signed-off-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
block/qcow.c | 123 +++++++++++++++++++++++++++++++++++------------------------
1 file changed, 73 insertions(+), 50 deletions(-)
diff --git a/block/qcow.c b/block/qcow.c
index 63904a26ee..d07bef6306 100644
--- a/block/qcow.c
+++ b/block/qcow.c
@@ -347,19 +347,21 @@ 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;
+ int min_index, i, j, l1_index, l2_index, ret;
uint64_t l2_offset, *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;
@@ -373,10 +375,12 @@ static uint64_t get_cluster_offset(BlockDriverState *bs,
/* 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 +407,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,16 +434,18 @@ 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);
/* 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 (allocate == 1) {
@@ -459,13 +468,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 +487,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 +601,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 +622,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 +631,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 +656,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 +668,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 +676,6 @@ done:
}
return ret;
-
-fail:
- ret = -EIO;
- goto done;
}
static coroutine_fn int qcow_co_writev(BlockDriverState *bs, int64_t sector_num,
@@ -697,9 +714,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 +1015,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;
--
2.13.5
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [Qemu-devel] [PULL 06/14] qcow: Check failure of bdrv_getlength() and bdrv_truncate()
2017-09-06 14:02 [Qemu-devel] [PULL 00/14] Block layer patches Kevin Wolf
` (4 preceding siblings ...)
2017-09-06 14:02 ` [Qemu-devel] [PULL 05/14] qcow: Change signature of get_cluster_offset() Kevin Wolf
@ 2017-09-06 14:02 ` Kevin Wolf
2017-09-06 14:02 ` [Qemu-devel] [PULL 07/14] block: document semantics of bdrv_co_preadv|pwritev Kevin Wolf
` (8 subsequent siblings)
14 siblings, 0 replies; 24+ messages in thread
From: Kevin Wolf @ 2017-09-06 14:02 UTC (permalink / raw)
To: qemu-block; +Cc: kwolf, qemu-devel
From: Eric Blake <eblake@redhat.com>
Omitting the check for whether bdrv_getlength() and bdrv_truncate()
failed meant that it was theoretically possible to return an
incorrect offset to the caller. More likely, conditions for either
of these functions to fail would also cause one of our other calls
(such as bdrv_pread() or bdrv_pwrite_sync()) to also fail, but
auditing that we are safe is difficult compared to just patching
things to always forward on the error rather than ignoring it.
Use osdep.h macros instead of open-coded rounding while in the
area.
Reported-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
block/qcow.c | 30 ++++++++++++++++++++++--------
1 file changed, 22 insertions(+), 8 deletions(-)
diff --git a/block/qcow.c b/block/qcow.c
index d07bef6306..f450b00cfc 100644
--- a/block/qcow.c
+++ b/block/qcow.c
@@ -357,7 +357,8 @@ static int get_cluster_offset(BlockDriverState *bs,
{
BDRVQcowState *s = bs->opaque;
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;
int new_l2_table;
@@ -370,8 +371,11 @@ static int 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);
@@ -438,8 +442,10 @@ static int get_cluster_offset(BlockDriverState *bs,
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 */
ret = bdrv_pwrite(bs->file, cluster_offset, s->cluster_cache,
s->cluster_size);
@@ -448,12 +454,20 @@ static int get_cluster_offset(BlockDriverState *bs,
}
} 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 &&
--
2.13.5
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [Qemu-devel] [PULL 07/14] block: document semantics of bdrv_co_preadv|pwritev
2017-09-06 14:02 [Qemu-devel] [PULL 00/14] Block layer patches Kevin Wolf
` (5 preceding siblings ...)
2017-09-06 14:02 ` [Qemu-devel] [PULL 06/14] qcow: Check failure of bdrv_getlength() and bdrv_truncate() Kevin Wolf
@ 2017-09-06 14:02 ` Kevin Wolf
2017-09-06 14:02 ` [Qemu-devel] [PULL 08/14] block: move ThrottleGroup membership to ThrottleGroupMember Kevin Wolf
` (7 subsequent siblings)
14 siblings, 0 replies; 24+ messages in thread
From: Kevin Wolf @ 2017-09-06 14:02 UTC (permalink / raw)
To: qemu-block; +Cc: kwolf, qemu-devel
From: "Daniel P. Berrange" <berrange@redhat.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
include/block/block_int.h | 31 +++++++++++++++++++++++++++++++
1 file changed, 31 insertions(+)
diff --git a/include/block/block_int.h b/include/block/block_int.h
index 223801e4fb..ba4c383393 100644
--- a/include/block/block_int.h
+++ b/include/block/block_int.h
@@ -150,12 +150,43 @@ struct BlockDriver {
int coroutine_fn (*bdrv_co_readv)(BlockDriverState *bs,
int64_t sector_num, int nb_sectors, QEMUIOVector *qiov);
+
+ /**
+ * @offset: position in bytes to read at
+ * @bytes: number of bytes to read
+ * @qiov: the buffers to fill with read data
+ * @flags: currently unused, always 0
+ *
+ * @offset and @bytes will be a multiple of 'request_alignment',
+ * but the length of individual @qiov elements does not have to
+ * be a multiple.
+ *
+ * @bytes will always equal the total size of @qiov, and will be
+ * no larger than 'max_transfer'.
+ *
+ * The buffer in @qiov may point directly to guest memory.
+ */
int coroutine_fn (*bdrv_co_preadv)(BlockDriverState *bs,
uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags);
int coroutine_fn (*bdrv_co_writev)(BlockDriverState *bs,
int64_t sector_num, int nb_sectors, QEMUIOVector *qiov);
int coroutine_fn (*bdrv_co_writev_flags)(BlockDriverState *bs,
int64_t sector_num, int nb_sectors, QEMUIOVector *qiov, int flags);
+ /**
+ * @offset: position in bytes to write at
+ * @bytes: number of bytes to write
+ * @qiov: the buffers containing data to write
+ * @flags: zero or more bits allowed by 'supported_write_flags'
+ *
+ * @offset and @bytes will be a multiple of 'request_alignment',
+ * but the length of individual @qiov elements does not have to
+ * be a multiple.
+ *
+ * @bytes will always equal the total size of @qiov, and will be
+ * no larger than 'max_transfer'.
+ *
+ * The buffer in @qiov may point directly to guest memory.
+ */
int coroutine_fn (*bdrv_co_pwritev)(BlockDriverState *bs,
uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags);
--
2.13.5
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [Qemu-devel] [PULL 08/14] block: move ThrottleGroup membership to ThrottleGroupMember
2017-09-06 14:02 [Qemu-devel] [PULL 00/14] Block layer patches Kevin Wolf
` (6 preceding siblings ...)
2017-09-06 14:02 ` [Qemu-devel] [PULL 07/14] block: document semantics of bdrv_co_preadv|pwritev Kevin Wolf
@ 2017-09-06 14:02 ` Kevin Wolf
2017-09-06 14:02 ` [Qemu-devel] [PULL 09/14] block: add aio_context field in ThrottleGroupMember Kevin Wolf
` (6 subsequent siblings)
14 siblings, 0 replies; 24+ messages in thread
From: Kevin Wolf @ 2017-09-06 14:02 UTC (permalink / raw)
To: qemu-block; +Cc: kwolf, qemu-devel
From: Manos Pitsidianakis <el13635@mail.ntua.gr>
This commit eliminates the 1:1 relationship between BlockBackend and
throttle group state. Users will be able to create multiple throttle
nodes, each with its own throttle group state, in the future. The
throttle group state cannot be per-BlockBackend anymore, it must be
per-throttle node. This is done by gathering ThrottleGroup membership
details from BlockBackendPublic into ThrottleGroupMember and refactoring
existing code to use the structure.
Reviewed-by: Alberto Garcia <berto@igalia.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
include/block/throttle-groups.h | 39 +++++-
include/sysemu/block-backend.h | 20 +--
block/block-backend.c | 66 +++++----
block/qapi.c | 8 +-
block/throttle-groups.c | 288 ++++++++++++++++++++--------------------
blockdev.c | 4 +-
tests/test-throttle.c | 53 ++++----
7 files changed, 252 insertions(+), 226 deletions(-)
diff --git a/include/block/throttle-groups.h b/include/block/throttle-groups.h
index d983d34074..1a6bcdae74 100644
--- a/include/block/throttle-groups.h
+++ b/include/block/throttle-groups.h
@@ -28,19 +28,44 @@
#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 {
+ /* 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;
+
+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);
+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);
diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h
index aadc733daf..c4e52a5fa3 100644
--- a/include/sysemu/block-backend.h
+++ b/include/sysemu/block-backend.h
@@ -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);
diff --git a/block/block-backend.c b/block/block-backend.c
index 1031742401..3dacd7eca7 100644
--- a/block/block-backend.c
+++ b/block/block-backend.c
@@ -273,9 +273,9 @@ 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]);
+ qemu_co_mutex_init(&blk->public.throttle_group_member.throttled_reqs_lock);
+ qemu_co_queue_init(&blk->public.throttle_group_member.throttled_reqs[0]);
+ qemu_co_queue_init(&blk->public.throttle_group_member.throttled_reqs[1]);
block_acct_init(&blk->stats);
notifier_list_init(&blk->remove_bs_notifiers);
@@ -343,7 +343,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 +658,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 +685,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 +1050,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 +1075,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,15 +1747,17 @@ static AioContext *blk_aiocb_get_aio_context(BlockAIOCB *acb)
void blk_set_aio_context(BlockBackend *blk, AioContext *new_context)
{
BlockDriverState *bs = blk_bs(blk);
+ ThrottleTimers *tt;
if (bs) {
- 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);
}
bdrv_set_aio_context(bs, new_context);
- if (blk->public.throttle_state) {
- throttle_timers_attach_aio_context(&blk->public.throttle_timers,
- new_context);
+ if (blk->public.throttle_group_member.throttle_state) {
+ tt = &blk->public.throttle_group_member.throttle_timers;
+ throttle_timers_attach_aio_context(tt, new_context);
}
}
}
@@ -1969,33 +1976,34 @@ 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);
}
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 +2025,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 +2035,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) {
diff --git a/block/qapi.c b/block/qapi.c
index 5f1a71f5d2..7fa2437923 100644
--- a/block/qapi.c
+++ b/block/qapi.c
@@ -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);
diff --git a/block/throttle-groups.c b/block/throttle-groups.c
index 890bfded3f..c8ed16ddf8 100644
--- a/block/throttle-groups.c
+++ b/block/throttle-groups.c
@@ -30,7 +30,7 @@
#include "sysemu/qtest.h"
/* The ThrottleGroup structure (with its ThrottleState) is shared
- * among different BlockBackends and it's independent from
+ * among different ThrottleGroupMembers and it's independent from
* AioContext, so in order to use it from different threads it needs
* its own locking.
*
@@ -40,26 +40,26 @@
* The whole ThrottleGroup structure is private and invisible to
* outside users, that only use it through its ThrottleState.
*
- * In addition to the ThrottleGroup structure, BlockBackendPublic has
+ * In addition to the ThrottleGroup structure, ThrottleGroupMember has
* fields that need to be accessed by other members of the group and
* therefore also need to be protected by this lock. Once a
- * BlockBackend is registered in a group those fields can be accessed
+ * ThrottleGroupMember is registered in a group those fields can be accessed
* by other threads any time.
*
* Again, all this is handled internally and is mostly transparent to
* the outside. The 'throttle_timers' field however has an additional
* constraint because it may be temporarily invalid (see for example
* blk_set_aio_context()). Therefore in this file a thread will
- * access some other BlockBackend's timers only after verifying that
- * that BlockBackend has throttled requests in the queue.
+ * access some other ThrottleGroupMember's timers only after verifying that
+ * that ThrottleGroupMember has throttled requests in the queue.
*/
typedef struct ThrottleGroup {
char *name; /* This is constant during the lifetime of the group */
QemuMutex lock; /* This lock protects the following four fields */
ThrottleState ts;
- QLIST_HEAD(, BlockBackendPublic) head;
- BlockBackend *tokens[2];
+ QLIST_HEAD(, ThrottleGroupMember) head;
+ ThrottleGroupMember *tokens[2];
bool any_timer_armed[2];
QEMUClockType clock_type;
@@ -140,114 +140,112 @@ void throttle_group_unref(ThrottleState *ts)
qemu_mutex_unlock(&throttle_groups_lock);
}
-/* Get the name from a BlockBackend's ThrottleGroup. The name (and the pointer)
+/* Get the name from a ThrottleGroupMember's group. The name (and the pointer)
* is guaranteed to remain constant during the lifetime of the group.
*
- * @blk: a BlockBackend that is member of a throttling group
+ * @tgm: a ThrottleGroupMember
* @ret: the name of the group.
*/
-const char *throttle_group_get_name(BlockBackend *blk)
+const char *throttle_group_get_name(ThrottleGroupMember *tgm)
{
- BlockBackendPublic *blkp = blk_get_public(blk);
- ThrottleGroup *tg = container_of(blkp->throttle_state, ThrottleGroup, ts);
+ ThrottleGroup *tg = container_of(tgm->throttle_state, ThrottleGroup, ts);
return tg->name;
}
-/* Return the next BlockBackend in the round-robin sequence, simulating a
- * circular list.
+/* Return the next ThrottleGroupMember in the round-robin sequence, simulating
+ * a circular list.
*
* This assumes that tg->lock is held.
*
- * @blk: the current BlockBackend
- * @ret: the next BlockBackend in the sequence
+ * @tgm: the current ThrottleGroupMember
+ * @ret: the next ThrottleGroupMember in the sequence
*/
-static BlockBackend *throttle_group_next_blk(BlockBackend *blk)
+static ThrottleGroupMember *throttle_group_next_tgm(ThrottleGroupMember *tgm)
{
- BlockBackendPublic *blkp = blk_get_public(blk);
- ThrottleState *ts = blkp->throttle_state;
+ ThrottleState *ts = tgm->throttle_state;
ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
- BlockBackendPublic *next = QLIST_NEXT(blkp, round_robin);
+ ThrottleGroupMember *next = QLIST_NEXT(tgm, round_robin);
if (!next) {
next = QLIST_FIRST(&tg->head);
}
- return blk_by_public(next);
+ return next;
}
/*
- * Return whether a BlockBackend has pending requests.
+ * Return whether a ThrottleGroupMember has pending requests.
*
* This assumes that tg->lock is held.
*
- * @blk: the BlockBackend
- * @is_write: the type of operation (read/write)
- * @ret: whether the BlockBackend has pending requests.
+ * @tgm: the ThrottleGroupMember
+ * @is_write: the type of operation (read/write)
+ * @ret: whether the ThrottleGroupMember has pending requests.
*/
-static inline bool blk_has_pending_reqs(BlockBackend *blk,
+static inline bool tgm_has_pending_reqs(ThrottleGroupMember *tgm,
bool is_write)
{
- const BlockBackendPublic *blkp = blk_get_public(blk);
- return blkp->pending_reqs[is_write];
+ return tgm->pending_reqs[is_write];
}
-/* Return the next BlockBackend in the round-robin sequence with pending I/O
- * requests.
+/* Return the next ThrottleGroupMember in the round-robin sequence with pending
+ * I/O requests.
*
* This assumes that tg->lock is held.
*
- * @blk: the current BlockBackend
+ * @tgm: the current ThrottleGroupMember
* @is_write: the type of operation (read/write)
- * @ret: the next BlockBackend with pending requests, or blk if there is
- * none.
+ * @ret: the next ThrottleGroupMember with pending requests, or tgm if
+ * there is none.
*/
-static BlockBackend *next_throttle_token(BlockBackend *blk, bool is_write)
+static ThrottleGroupMember *next_throttle_token(ThrottleGroupMember *tgm,
+ bool is_write)
{
- BlockBackendPublic *blkp = blk_get_public(blk);
- ThrottleGroup *tg = container_of(blkp->throttle_state, ThrottleGroup, ts);
- BlockBackend *token, *start;
+ ThrottleState *ts = tgm->throttle_state;
+ ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
+ ThrottleGroupMember *token, *start;
start = token = tg->tokens[is_write];
/* get next bs round in round robin style */
- token = throttle_group_next_blk(token);
- while (token != start && !blk_has_pending_reqs(token, is_write)) {
- token = throttle_group_next_blk(token);
+ token = throttle_group_next_tgm(token);
+ while (token != start && !tgm_has_pending_reqs(token, is_write)) {
+ token = throttle_group_next_tgm(token);
}
/* If no IO are queued for scheduling on the next round robin token
- * then decide the token is the current bs because chances are
- * the current bs get the current request queued.
+ * then decide the token is the current tgm because chances are
+ * the current tgm got the current request queued.
*/
- if (token == start && !blk_has_pending_reqs(token, is_write)) {
- token = blk;
+ if (token == start && !tgm_has_pending_reqs(token, is_write)) {
+ token = tgm;
}
- /* Either we return the original BB, or one with pending requests */
- assert(token == blk || blk_has_pending_reqs(token, is_write));
+ /* Either we return the original TGM, or one with pending requests */
+ assert(token == tgm || tgm_has_pending_reqs(token, is_write));
return token;
}
-/* Check if the next I/O request for a BlockBackend needs to be throttled or
- * not. If there's no timer set in this group, set one and update the token
- * accordingly.
+/* Check if the next I/O request for a ThrottleGroupMember needs to be
+ * throttled or not. If there's no timer set in this group, set one and update
+ * the token accordingly.
*
* This assumes that tg->lock is held.
*
- * @blk: the current BlockBackend
+ * @tgm: the current ThrottleGroupMember
* @is_write: the type of operation (read/write)
* @ret: whether the I/O request needs to be throttled or not
*/
-static bool throttle_group_schedule_timer(BlockBackend *blk, bool is_write)
+static bool throttle_group_schedule_timer(ThrottleGroupMember *tgm,
+ bool is_write)
{
- BlockBackendPublic *blkp = blk_get_public(blk);
- ThrottleState *ts = blkp->throttle_state;
- ThrottleTimers *tt = &blkp->throttle_timers;
+ ThrottleState *ts = tgm->throttle_state;
ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
+ ThrottleTimers *tt = &tgm->throttle_timers;
bool must_wait;
- if (atomic_read(&blkp->io_limits_disabled)) {
+ if (atomic_read(&tgm->io_limits_disabled)) {
return false;
}
@@ -258,30 +256,29 @@ static bool throttle_group_schedule_timer(BlockBackend *blk, bool is_write)
must_wait = throttle_schedule_timer(ts, tt, is_write);
- /* If a timer just got armed, set blk as the current token */
+ /* If a timer just got armed, set tgm as the current token */
if (must_wait) {
- tg->tokens[is_write] = blk;
+ tg->tokens[is_write] = tgm;
tg->any_timer_armed[is_write] = true;
}
return must_wait;
}
-/* Start the next pending I/O request for a BlockBackend. Return whether
+/* Start the next pending I/O request for a ThrottleGroupMember. Return whether
* any request was actually pending.
*
- * @blk: the current BlockBackend
+ * @tgm: the current ThrottleGroupMember
* @is_write: the type of operation (read/write)
*/
-static bool coroutine_fn throttle_group_co_restart_queue(BlockBackend *blk,
+static bool coroutine_fn throttle_group_co_restart_queue(ThrottleGroupMember *tgm,
bool is_write)
{
- BlockBackendPublic *blkp = blk_get_public(blk);
bool ret;
- qemu_co_mutex_lock(&blkp->throttled_reqs_lock);
- ret = qemu_co_queue_next(&blkp->throttled_reqs[is_write]);
- qemu_co_mutex_unlock(&blkp->throttled_reqs_lock);
+ qemu_co_mutex_lock(&tgm->throttled_reqs_lock);
+ ret = qemu_co_queue_next(&tgm->throttled_reqs[is_write]);
+ qemu_co_mutex_unlock(&tgm->throttled_reqs_lock);
return ret;
}
@@ -290,19 +287,19 @@ static bool coroutine_fn throttle_group_co_restart_queue(BlockBackend *blk,
*
* This assumes that tg->lock is held.
*
- * @blk: the current BlockBackend
+ * @tgm: the current ThrottleGroupMember
* @is_write: the type of operation (read/write)
*/
-static void schedule_next_request(BlockBackend *blk, bool is_write)
+static void schedule_next_request(ThrottleGroupMember *tgm, bool is_write)
{
- BlockBackendPublic *blkp = blk_get_public(blk);
- ThrottleGroup *tg = container_of(blkp->throttle_state, ThrottleGroup, ts);
+ ThrottleState *ts = tgm->throttle_state;
+ ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
bool must_wait;
- BlockBackend *token;
+ ThrottleGroupMember *token;
/* Check if there's any pending request to schedule next */
- token = next_throttle_token(blk, is_write);
- if (!blk_has_pending_reqs(token, is_write)) {
+ token = next_throttle_token(tgm, is_write);
+ if (!tgm_has_pending_reqs(token, is_write)) {
return;
}
@@ -311,12 +308,12 @@ static void schedule_next_request(BlockBackend *blk, bool is_write)
/* If it doesn't have to wait, queue it for immediate execution */
if (!must_wait) {
- /* Give preference to requests from the current blk */
+ /* Give preference to requests from the current tgm */
if (qemu_in_coroutine() &&
- throttle_group_co_restart_queue(blk, is_write)) {
- token = blk;
+ throttle_group_co_restart_queue(tgm, is_write)) {
+ token = tgm;
} else {
- ThrottleTimers *tt = &blk_get_public(token)->throttle_timers;
+ ThrottleTimers *tt = &token->throttle_timers;
int64_t now = qemu_clock_get_ns(tg->clock_type);
timer_mod(tt->timers[is_write], now);
tg->any_timer_armed[is_write] = true;
@@ -329,76 +326,77 @@ static void schedule_next_request(BlockBackend *blk, bool is_write)
* if necessary, and schedule the next request using a round robin
* algorithm.
*
- * @blk: the current BlockBackend
+ * @tgm: the current ThrottleGroupMember
* @bytes: the number of bytes for this I/O
* @is_write: the type of operation (read/write)
*/
-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)
{
bool must_wait;
- BlockBackend *token;
-
- BlockBackendPublic *blkp = blk_get_public(blk);
- ThrottleGroup *tg = container_of(blkp->throttle_state, ThrottleGroup, ts);
+ ThrottleGroupMember *token;
+ ThrottleGroup *tg = container_of(tgm->throttle_state, ThrottleGroup, ts);
qemu_mutex_lock(&tg->lock);
/* First we check if this I/O has to be throttled. */
- token = next_throttle_token(blk, is_write);
+ token = next_throttle_token(tgm, is_write);
must_wait = throttle_group_schedule_timer(token, is_write);
/* Wait if there's a timer set or queued requests of this type */
- if (must_wait || blkp->pending_reqs[is_write]) {
- blkp->pending_reqs[is_write]++;
+ if (must_wait || tgm->pending_reqs[is_write]) {
+ tgm->pending_reqs[is_write]++;
qemu_mutex_unlock(&tg->lock);
- qemu_co_mutex_lock(&blkp->throttled_reqs_lock);
- qemu_co_queue_wait(&blkp->throttled_reqs[is_write],
- &blkp->throttled_reqs_lock);
- qemu_co_mutex_unlock(&blkp->throttled_reqs_lock);
+ qemu_co_mutex_lock(&tgm->throttled_reqs_lock);
+ qemu_co_queue_wait(&tgm->throttled_reqs[is_write],
+ &tgm->throttled_reqs_lock);
+ qemu_co_mutex_unlock(&tgm->throttled_reqs_lock);
qemu_mutex_lock(&tg->lock);
- blkp->pending_reqs[is_write]--;
+ tgm->pending_reqs[is_write]--;
}
/* The I/O will be executed, so do the accounting */
- throttle_account(blkp->throttle_state, is_write, bytes);
+ throttle_account(tgm->throttle_state, is_write, bytes);
/* Schedule the next request */
- schedule_next_request(blk, is_write);
+ schedule_next_request(tgm, is_write);
qemu_mutex_unlock(&tg->lock);
}
typedef struct {
- BlockBackend *blk;
+ ThrottleGroupMember *tgm;
bool is_write;
} RestartData;
static void coroutine_fn throttle_group_restart_queue_entry(void *opaque)
{
RestartData *data = opaque;
- BlockBackend *blk = data->blk;
+ ThrottleGroupMember *tgm = data->tgm;
+ ThrottleState *ts = tgm->throttle_state;
+ ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
bool is_write = data->is_write;
- BlockBackendPublic *blkp = blk_get_public(blk);
- ThrottleGroup *tg = container_of(blkp->throttle_state, ThrottleGroup, ts);
bool empty_queue;
- empty_queue = !throttle_group_co_restart_queue(blk, is_write);
+ empty_queue = !throttle_group_co_restart_queue(tgm, is_write);
/* If the request queue was empty then we have to take care of
* scheduling the next one */
if (empty_queue) {
qemu_mutex_lock(&tg->lock);
- schedule_next_request(blk, is_write);
+ schedule_next_request(tgm, is_write);
qemu_mutex_unlock(&tg->lock);
}
}
-static void throttle_group_restart_queue(BlockBackend *blk, bool is_write)
+static void throttle_group_restart_queue(ThrottleGroupMember *tgm, bool is_write)
{
+ BlockBackendPublic *blkp = container_of(tgm, BlockBackendPublic,
+ throttle_group_member);
+ BlockBackend *blk = blk_by_public(blkp);
Coroutine *co;
RestartData rd = {
- .blk = blk,
+ .tgm = tgm,
.is_write = is_write
};
@@ -406,13 +404,11 @@ static void throttle_group_restart_queue(BlockBackend *blk, bool is_write)
aio_co_enter(blk_get_aio_context(blk), co);
}
-void throttle_group_restart_blk(BlockBackend *blk)
+void throttle_group_restart_tgm(ThrottleGroupMember *tgm)
{
- BlockBackendPublic *blkp = blk_get_public(blk);
-
- if (blkp->throttle_state) {
- throttle_group_restart_queue(blk, 0);
- throttle_group_restart_queue(blk, 1);
+ if (tgm->throttle_state) {
+ throttle_group_restart_queue(tgm, 0);
+ throttle_group_restart_queue(tgm, 1);
}
}
@@ -420,32 +416,30 @@ void throttle_group_restart_blk(BlockBackend *blk)
* to throttle_config(), but guarantees atomicity within the
* throttling group.
*
- * @blk: a BlockBackend that is a member of the group
+ * @tgm: a ThrottleGroupMember that is a member of the group
* @cfg: the configuration to set
*/
-void throttle_group_config(BlockBackend *blk, ThrottleConfig *cfg)
+void throttle_group_config(ThrottleGroupMember *tgm, ThrottleConfig *cfg)
{
- BlockBackendPublic *blkp = blk_get_public(blk);
- ThrottleState *ts = blkp->throttle_state;
+ ThrottleState *ts = tgm->throttle_state;
ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
qemu_mutex_lock(&tg->lock);
throttle_config(ts, tg->clock_type, cfg);
qemu_mutex_unlock(&tg->lock);
- throttle_group_restart_blk(blk);
+ throttle_group_restart_tgm(tgm);
}
/* Get the throttle configuration from a particular group. Similar to
* throttle_get_config(), but guarantees atomicity within the
* throttling group.
*
- * @blk: a BlockBackend that is a member of the group
+ * @tgm: a ThrottleGroupMember that is a member of the group
* @cfg: the configuration will be written here
*/
-void throttle_group_get_config(BlockBackend *blk, ThrottleConfig *cfg)
+void throttle_group_get_config(ThrottleGroupMember *tgm, ThrottleConfig *cfg)
{
- BlockBackendPublic *blkp = blk_get_public(blk);
- ThrottleState *ts = blkp->throttle_state;
+ ThrottleState *ts = tgm->throttle_state;
ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
qemu_mutex_lock(&tg->lock);
throttle_get_config(ts, cfg);
@@ -461,7 +455,8 @@ void throttle_group_get_config(BlockBackend *blk, ThrottleConfig *cfg)
static void timer_cb(BlockBackend *blk, bool is_write)
{
BlockBackendPublic *blkp = blk_get_public(blk);
- ThrottleState *ts = blkp->throttle_state;
+ ThrottleGroupMember *tgm = &blkp->throttle_group_member;
+ ThrottleState *ts = tgm->throttle_state;
ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
/* The timer has just been fired, so we can update the flag */
@@ -470,7 +465,7 @@ static void timer_cb(BlockBackend *blk, bool is_write)
qemu_mutex_unlock(&tg->lock);
/* Run the request that was waiting for this timer */
- throttle_group_restart_queue(blk, is_write);
+ throttle_group_restart_queue(tgm, is_write);
}
static void read_timer_cb(void *opaque)
@@ -483,32 +478,36 @@ static void write_timer_cb(void *opaque)
timer_cb(opaque, true);
}
-/* Register a BlockBackend in the throttling group, also initializing its
- * timers and updating its throttle_state pointer to point to it. If a
+/* Register a ThrottleGroupMember from the throttling group, also initializing
+ * its timers and updating its throttle_state pointer to point to it. If a
* throttling group with that name does not exist yet, it will be created.
*
- * @blk: the BlockBackend to insert
+ * @tgm: the ThrottleGroupMember to insert
* @groupname: the name of the group
*/
-void throttle_group_register_blk(BlockBackend *blk, const char *groupname)
+void throttle_group_register_tgm(ThrottleGroupMember *tgm,
+ const char *groupname)
{
int i;
- BlockBackendPublic *blkp = blk_get_public(blk);
+ BlockBackendPublic *blkp = container_of(tgm, BlockBackendPublic,
+ throttle_group_member);
+ BlockBackend *blk = blk_by_public(blkp);
ThrottleState *ts = throttle_group_incref(groupname);
ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
- blkp->throttle_state = ts;
+
+ tgm->throttle_state = ts;
qemu_mutex_lock(&tg->lock);
- /* If the ThrottleGroup is new set this BlockBackend as the token */
+ /* If the ThrottleGroup is new set this ThrottleGroupMember as the token */
for (i = 0; i < 2; i++) {
if (!tg->tokens[i]) {
- tg->tokens[i] = blk;
+ tg->tokens[i] = tgm;
}
}
- QLIST_INSERT_HEAD(&tg->head, blkp, round_robin);
+ QLIST_INSERT_HEAD(&tg->head, tgm, round_robin);
- throttle_timers_init(&blkp->throttle_timers,
+ throttle_timers_init(&tgm->throttle_timers,
blk_get_aio_context(blk),
tg->clock_type,
read_timer_cb,
@@ -518,45 +517,46 @@ void throttle_group_register_blk(BlockBackend *blk, const char *groupname)
qemu_mutex_unlock(&tg->lock);
}
-/* Unregister a BlockBackend from its group, removing it from the list,
+/* Unregister a ThrottleGroupMember from its group, removing it from the list,
* destroying the timers and setting the throttle_state pointer to NULL.
*
- * The BlockBackend must not have pending throttled requests, so the caller has
- * to drain them first.
+ * The ThrottleGroupMember must not have pending throttled requests, so the
+ * caller has to drain them first.
*
* The group will be destroyed if it's empty after this operation.
*
- * @blk: the BlockBackend to remove
+ * @tgm the ThrottleGroupMember to remove
*/
-void throttle_group_unregister_blk(BlockBackend *blk)
+void throttle_group_unregister_tgm(ThrottleGroupMember *tgm)
{
- BlockBackendPublic *blkp = blk_get_public(blk);
- ThrottleGroup *tg = container_of(blkp->throttle_state, ThrottleGroup, ts);
+ ThrottleState *ts = tgm->throttle_state;
+ ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
+ ThrottleGroupMember *token;
int i;
- assert(blkp->pending_reqs[0] == 0 && blkp->pending_reqs[1] == 0);
- assert(qemu_co_queue_empty(&blkp->throttled_reqs[0]));
- assert(qemu_co_queue_empty(&blkp->throttled_reqs[1]));
+ assert(tgm->pending_reqs[0] == 0 && tgm->pending_reqs[1] == 0);
+ assert(qemu_co_queue_empty(&tgm->throttled_reqs[0]));
+ assert(qemu_co_queue_empty(&tgm->throttled_reqs[1]));
qemu_mutex_lock(&tg->lock);
for (i = 0; i < 2; i++) {
- if (tg->tokens[i] == blk) {
- BlockBackend *token = throttle_group_next_blk(blk);
- /* Take care of the case where this is the last blk in the group */
- if (token == blk) {
+ if (tg->tokens[i] == tgm) {
+ token = throttle_group_next_tgm(tgm);
+ /* Take care of the case where this is the last tgm in the group */
+ if (token == tgm) {
token = NULL;
}
tg->tokens[i] = token;
}
}
- /* remove the current blk from the list */
- QLIST_REMOVE(blkp, round_robin);
- throttle_timers_destroy(&blkp->throttle_timers);
+ /* remove the current tgm from the list */
+ QLIST_REMOVE(tgm, round_robin);
+ throttle_timers_destroy(&tgm->throttle_timers);
qemu_mutex_unlock(&tg->lock);
throttle_group_unref(&tg->ts);
- blkp->throttle_state = NULL;
+ tgm->throttle_state = NULL;
}
static void throttle_groups_init(void)
diff --git a/blockdev.c b/blockdev.c
index 796beaed94..56a6b24a0b 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -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);
}
diff --git a/tests/test-throttle.c b/tests/test-throttle.c
index bf7a5a648a..69cc8b18e4 100644
--- a/tests/test-throttle.c
+++ b/tests/test-throttle.c
@@ -669,6 +669,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 +680,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");
+ throttle_group_register_tgm(tgm2, "foo");
+ throttle_group_register_tgm(tgm3, "bar");
- 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 +706,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)
--
2.13.5
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [Qemu-devel] [PULL 09/14] block: add aio_context field in ThrottleGroupMember
2017-09-06 14:02 [Qemu-devel] [PULL 00/14] Block layer patches Kevin Wolf
` (7 preceding siblings ...)
2017-09-06 14:02 ` [Qemu-devel] [PULL 08/14] block: move ThrottleGroup membership to ThrottleGroupMember Kevin Wolf
@ 2017-09-06 14:02 ` Kevin Wolf
2017-09-06 14:02 ` [Qemu-devel] [PULL 10/14] block: tidy ThrottleGroupMember initializations Kevin Wolf
` (5 subsequent siblings)
14 siblings, 0 replies; 24+ messages in thread
From: Kevin Wolf @ 2017-09-06 14:02 UTC (permalink / raw)
To: qemu-block; +Cc: kwolf, qemu-devel
From: Manos Pitsidianakis <el13635@mail.ntua.gr>
timer_cb() needs to know about the current Aio context of the throttle
request that is woken up. In order to make ThrottleGroupMember backend
agnostic, this information is stored in an aio_context field instead of
accessing it from BlockBackend.
Reviewed-by: Alberto Garcia <berto@igalia.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
include/block/throttle-groups.h | 7 ++++-
block/block-backend.c | 15 ++++------
block/throttle-groups.c | 38 ++++++++++++++++---------
tests/test-throttle.c | 63 +++++++++++++++++++++--------------------
4 files changed, 69 insertions(+), 54 deletions(-)
diff --git a/include/block/throttle-groups.h b/include/block/throttle-groups.h
index 1a6bcdae74..a0f27cac63 100644
--- a/include/block/throttle-groups.h
+++ b/include/block/throttle-groups.h
@@ -33,6 +33,7 @@
*/
typedef struct ThrottleGroupMember {
+ AioContext *aio_context;
/* throttled_reqs_lock protects the CoQueues for throttled requests. */
CoMutex throttled_reqs_lock;
CoQueue throttled_reqs[2];
@@ -61,12 +62,16 @@ void throttle_group_config(ThrottleGroupMember *tgm, ThrottleConfig *cfg);
void throttle_group_get_config(ThrottleGroupMember *tgm, ThrottleConfig *cfg);
void throttle_group_register_tgm(ThrottleGroupMember *tgm,
- const char *groupname);
+ 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(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);
#endif
diff --git a/block/block-backend.c b/block/block-backend.c
index 3dacd7eca7..b4acd0f2ae 100644
--- a/block/block-backend.c
+++ b/block/block-backend.c
@@ -1747,18 +1747,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);
- ThrottleTimers *tt;
+ ThrottleGroupMember *tgm = &blk->public.throttle_group_member;
if (bs) {
- if (blk->public.throttle_group_member.throttle_state) {
- tt = &blk->public.throttle_group_member.throttle_timers;
- throttle_timers_detach_aio_context(tt);
+ 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_group_member.throttle_state) {
- tt = &blk->public.throttle_group_member.throttle_timers;
- throttle_timers_attach_aio_context(tt, new_context);
- }
}
}
@@ -1991,7 +1987,8 @@ void blk_io_limits_disable(BlockBackend *blk)
void blk_io_limits_enable(BlockBackend *blk, const char *group)
{
assert(!blk->public.throttle_group_member.throttle_state);
- throttle_group_register_tgm(&blk->public.throttle_group_member, 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)
diff --git a/block/throttle-groups.c b/block/throttle-groups.c
index c8ed16ddf8..3b07b25f39 100644
--- a/block/throttle-groups.c
+++ b/block/throttle-groups.c
@@ -391,9 +391,6 @@ static void coroutine_fn throttle_group_restart_queue_entry(void *opaque)
static void throttle_group_restart_queue(ThrottleGroupMember *tgm, bool is_write)
{
- BlockBackendPublic *blkp = container_of(tgm, BlockBackendPublic,
- throttle_group_member);
- BlockBackend *blk = blk_by_public(blkp);
Coroutine *co;
RestartData rd = {
.tgm = tgm,
@@ -401,7 +398,7 @@ static void throttle_group_restart_queue(ThrottleGroupMember *tgm, bool is_write
};
co = qemu_coroutine_create(throttle_group_restart_queue_entry, &rd);
- aio_co_enter(blk_get_aio_context(blk), co);
+ aio_co_enter(tgm->aio_context, co);
}
void throttle_group_restart_tgm(ThrottleGroupMember *tgm)
@@ -449,13 +446,11 @@ void throttle_group_get_config(ThrottleGroupMember *tgm, ThrottleConfig *cfg)
/* ThrottleTimers callback. This wakes up a request that was waiting
* because it had been throttled.
*
- * @blk: the BlockBackend whose request had been throttled
+ * @tgm: the ThrottleGroupMember whose request had been throttled
* @is_write: the type of operation (read/write)
*/
-static void timer_cb(BlockBackend *blk, bool is_write)
+static void timer_cb(ThrottleGroupMember *tgm, bool is_write)
{
- BlockBackendPublic *blkp = blk_get_public(blk);
- ThrottleGroupMember *tgm = &blkp->throttle_group_member;
ThrottleState *ts = tgm->throttle_state;
ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
@@ -484,18 +479,18 @@ static void write_timer_cb(void *opaque)
*
* @tgm: the ThrottleGroupMember to insert
* @groupname: the name of the group
+ * @ctx: the AioContext to use
*/
void throttle_group_register_tgm(ThrottleGroupMember *tgm,
- const char *groupname)
+ const char *groupname,
+ AioContext *ctx)
{
int i;
- BlockBackendPublic *blkp = container_of(tgm, BlockBackendPublic,
- throttle_group_member);
- BlockBackend *blk = blk_by_public(blkp);
ThrottleState *ts = throttle_group_incref(groupname);
ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
tgm->throttle_state = ts;
+ tgm->aio_context = ctx;
qemu_mutex_lock(&tg->lock);
/* If the ThrottleGroup is new set this ThrottleGroupMember as the token */
@@ -508,11 +503,11 @@ void throttle_group_register_tgm(ThrottleGroupMember *tgm,
QLIST_INSERT_HEAD(&tg->head, tgm, round_robin);
throttle_timers_init(&tgm->throttle_timers,
- blk_get_aio_context(blk),
+ tgm->aio_context,
tg->clock_type,
read_timer_cb,
write_timer_cb,
- blk);
+ tgm);
qemu_mutex_unlock(&tg->lock);
}
@@ -559,6 +554,21 @@ void throttle_group_unregister_tgm(ThrottleGroupMember *tgm)
tgm->throttle_state = NULL;
}
+void throttle_group_attach_aio_context(ThrottleGroupMember *tgm,
+ AioContext *new_context)
+{
+ ThrottleTimers *tt = &tgm->throttle_timers;
+ throttle_timers_attach_aio_context(tt, new_context);
+ tgm->aio_context = new_context;
+}
+
+void throttle_group_detach_aio_context(ThrottleGroupMember *tgm)
+{
+ ThrottleTimers *tt = &tgm->throttle_timers;
+ throttle_timers_detach_aio_context(tt);
+ tgm->aio_context = NULL;
+}
+
static void throttle_groups_init(void)
{
qemu_mutex_init(&throttle_groups_lock);
diff --git a/tests/test-throttle.c b/tests/test-throttle.c
index 69cc8b18e4..e3a45cc605 100644
--- a/tests/test-throttle.c
+++ b/tests/test-throttle.c
@@ -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;
}
@@ -688,9 +691,9 @@ static void test_groups(void)
g_assert(tgm2->throttle_state == NULL);
g_assert(tgm3->throttle_state == NULL);
- throttle_group_register_tgm(tgm1, "bar");
- throttle_group_register_tgm(tgm2, "foo");
- throttle_group_register_tgm(tgm3, "bar");
+ 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(tgm1->throttle_state != NULL);
g_assert(tgm2->throttle_state != NULL);
--
2.13.5
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [Qemu-devel] [PULL 10/14] block: tidy ThrottleGroupMember initializations
2017-09-06 14:02 [Qemu-devel] [PULL 00/14] Block layer patches Kevin Wolf
` (8 preceding siblings ...)
2017-09-06 14:02 ` [Qemu-devel] [PULL 09/14] block: add aio_context field in ThrottleGroupMember Kevin Wolf
@ 2017-09-06 14:02 ` Kevin Wolf
2017-09-06 14:02 ` [Qemu-devel] [PULL 11/14] block: convert ThrottleGroup to object with QOM Kevin Wolf
` (4 subsequent siblings)
14 siblings, 0 replies; 24+ messages in thread
From: Kevin Wolf @ 2017-09-06 14:02 UTC (permalink / raw)
To: qemu-block; +Cc: kwolf, qemu-devel
From: Manos Pitsidianakis <el13635@mail.ntua.gr>
Move the CoMutex and CoQueue inits inside throttle_group_register_tgm()
which is called whenever a ThrottleGroupMember is initialized. There's
no need for them to be separate.
Reviewed-by: Alberto Garcia <berto@igalia.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
block/block-backend.c | 3 ---
block/throttle-groups.c | 3 +++
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/block/block-backend.c b/block/block-backend.c
index b4acd0f2ae..45d9101be3 100644
--- a/block/block-backend.c
+++ b/block/block-backend.c
@@ -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.throttle_group_member.throttled_reqs_lock);
- qemu_co_queue_init(&blk->public.throttle_group_member.throttled_reqs[0]);
- qemu_co_queue_init(&blk->public.throttle_group_member.throttled_reqs[1]);
block_acct_init(&blk->stats);
notifier_list_init(&blk->remove_bs_notifiers);
diff --git a/block/throttle-groups.c b/block/throttle-groups.c
index 3b07b25f39..7749cf043f 100644
--- a/block/throttle-groups.c
+++ b/block/throttle-groups.c
@@ -508,6 +508,9 @@ void throttle_group_register_tgm(ThrottleGroupMember *tgm,
read_timer_cb,
write_timer_cb,
tgm);
+ qemu_co_mutex_init(&tgm->throttled_reqs_lock);
+ qemu_co_queue_init(&tgm->throttled_reqs[0]);
+ qemu_co_queue_init(&tgm->throttled_reqs[1]);
qemu_mutex_unlock(&tg->lock);
}
--
2.13.5
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [Qemu-devel] [PULL 11/14] block: convert ThrottleGroup to object with QOM
2017-09-06 14:02 [Qemu-devel] [PULL 00/14] Block layer patches Kevin Wolf
` (9 preceding siblings ...)
2017-09-06 14:02 ` [Qemu-devel] [PULL 10/14] block: tidy ThrottleGroupMember initializations Kevin Wolf
@ 2017-09-06 14:02 ` Kevin Wolf
2017-09-06 14:02 ` [Qemu-devel] [PULL 12/14] block: add throttle block filter driver Kevin Wolf
` (3 subsequent siblings)
14 siblings, 0 replies; 24+ messages in thread
From: Kevin Wolf @ 2017-09-06 14:02 UTC (permalink / raw)
To: qemu-block; +Cc: kwolf, qemu-devel
From: Manos Pitsidianakis <el13635@mail.ntua.gr>
ThrottleGroup is converted to an object. This will allow the future
throttle block filter drive easy creation and configuration of throttle
groups in QMP and cli.
A new QAPI struct, ThrottleLimits, is introduced to provide a shared
struct for all throttle configuration needs in QMP.
ThrottleGroups can be created via CLI as
-object throttle-group,id=foo,x-iops-total=100,x-..
where x-* are individual limit properties. Since we can't add non-scalar
properties in -object this interface must be used instead. However,
setting these properties must be disabled after initialization because
certain combinations of limits are forbidden and thus configuration
changes should be done in one transaction. The individual properties
will go away when support for non-scalar values in CLI is implemented
and thus are marked as experimental.
ThrottleGroup also has a `limits` property that uses the ThrottleLimits
struct. It can be used to create ThrottleGroups or set the
configuration in existing groups as follows:
{ "execute": "object-add",
"arguments": {
"qom-type": "throttle-group",
"id": "foo",
"props" : {
"limits": {
"iops-total": 100
}
}
}
}
{ "execute" : "qom-set",
"arguments" : {
"path" : "foo",
"property" : "limits",
"value" : {
"iops-total" : 99
}
}
}
This also means a group's configuration can be fetched with qom-get.
Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Alberto Garcia <berto@igalia.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
qapi/block-core.json | 48 +++++
include/block/throttle-groups.h | 3 +
include/qemu/throttle-options.h | 59 ++++--
include/qemu/throttle.h | 3 +
block/throttle-groups.c | 424 ++++++++++++++++++++++++++++++++++++----
tests/test-throttle.c | 1 +
util/throttle.c | 151 ++++++++++++++
7 files changed, 628 insertions(+), 61 deletions(-)
diff --git a/qapi/block-core.json b/qapi/block-core.json
index 28abb9e6cf..60bd7ec379 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -1906,6 +1906,54 @@
'*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:
#
# Copy data from a backing file into a block device.
diff --git a/include/block/throttle-groups.h b/include/block/throttle-groups.h
index a0f27cac63..82f030523f 100644
--- a/include/block/throttle-groups.h
+++ b/include/block/throttle-groups.h
@@ -53,6 +53,9 @@ typedef struct ThrottleGroupMember {
} 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);
diff --git a/include/qemu/throttle-options.h b/include/qemu/throttle-options.h
index 3133d1ca40..182b7896e1 100644
--- a/include/qemu/throttle-options.h
+++ b/include/qemu/throttle-options.h
@@ -10,81 +10,102 @@
#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 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",\
}
diff --git a/include/qemu/throttle.h b/include/qemu/throttle.h
index 8e01885d29..8c93237866 100644
--- a/include/qemu/throttle.h
+++ b/include/qemu/throttle.h
@@ -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
diff --git a/block/throttle-groups.c b/block/throttle-groups.c
index 7749cf043f..ed1817ec84 100644
--- a/block/throttle-groups.c
+++ b/block/throttle-groups.c
@@ -25,9 +25,17 @@
#include "qemu/osdep.h"
#include "sysemu/block-backend.h"
#include "block/throttle-groups.h"
+#include "qemu/throttle-options.h"
#include "qemu/queue.h"
#include "qemu/thread.h"
#include "sysemu/qtest.h"
+#include "qapi/error.h"
+#include "qapi-visit.h"
+#include "qom/object.h"
+#include "qom/object_interfaces.h"
+
+static void throttle_group_obj_init(Object *obj);
+static void throttle_group_obj_complete(UserCreatable *obj, Error **errp);
/* The ThrottleGroup structure (with its ThrottleState) is shared
* among different ThrottleGroupMembers and it's independent from
@@ -54,6 +62,10 @@
* that ThrottleGroupMember has throttled requests in the queue.
*/
typedef struct ThrottleGroup {
+ Object parent_obj;
+
+ /* refuse individual property change if initialization is complete */
+ bool is_initialized;
char *name; /* This is constant during the lifetime of the group */
QemuMutex lock; /* This lock protects the following four fields */
@@ -63,59 +75,60 @@ typedef struct ThrottleGroup {
bool any_timer_armed[2];
QEMUClockType clock_type;
- /* These two are protected by the global throttle_groups_lock */
- unsigned refcount;
+ /* This field is protected by the global QEMU mutex */
QTAILQ_ENTRY(ThrottleGroup) list;
} ThrottleGroup;
-static QemuMutex throttle_groups_lock;
+/* This is protected by the global QEMU mutex */
static QTAILQ_HEAD(, ThrottleGroup) throttle_groups =
QTAILQ_HEAD_INITIALIZER(throttle_groups);
+
+/* This function reads throttle_groups and must be called under the global
+ * mutex.
+ */
+static ThrottleGroup *throttle_group_by_name(const char *name)
+{
+ ThrottleGroup *iter;
+
+ /* Look for an existing group with that name */
+ QTAILQ_FOREACH(iter, &throttle_groups, list) {
+ if (!g_strcmp0(name, iter->name)) {
+ return iter;
+ }
+ }
+
+ return NULL;
+}
+
/* Increments the reference count of a ThrottleGroup given its name.
*
* If no ThrottleGroup is found with the given name a new one is
* created.
*
+ * This function edits throttle_groups and must be called under the global
+ * mutex.
+ *
* @name: the name of the ThrottleGroup
* @ret: the ThrottleState member of the ThrottleGroup
*/
ThrottleState *throttle_group_incref(const char *name)
{
ThrottleGroup *tg = NULL;
- ThrottleGroup *iter;
-
- qemu_mutex_lock(&throttle_groups_lock);
/* Look for an existing group with that name */
- QTAILQ_FOREACH(iter, &throttle_groups, list) {
- if (!strcmp(name, iter->name)) {
- tg = iter;
- break;
- }
- }
-
- /* Create a new one if not found */
- if (!tg) {
- tg = g_new0(ThrottleGroup, 1);
+ tg = throttle_group_by_name(name);
+
+ if (tg) {
+ object_ref(OBJECT(tg));
+ } else {
+ /* Create a new one if not found */
+ /* new ThrottleGroup obj will have a refcnt = 1 */
+ tg = THROTTLE_GROUP(object_new(TYPE_THROTTLE_GROUP));
tg->name = g_strdup(name);
- tg->clock_type = QEMU_CLOCK_REALTIME;
-
- if (qtest_enabled()) {
- /* For testing block IO throttling only */
- tg->clock_type = QEMU_CLOCK_VIRTUAL;
- }
- qemu_mutex_init(&tg->lock);
- throttle_init(&tg->ts);
- QLIST_INIT(&tg->head);
-
- QTAILQ_INSERT_TAIL(&throttle_groups, tg, list);
+ throttle_group_obj_complete(USER_CREATABLE(tg), &error_abort);
}
- tg->refcount++;
-
- qemu_mutex_unlock(&throttle_groups_lock);
-
return &tg->ts;
}
@@ -124,20 +137,15 @@ ThrottleState *throttle_group_incref(const char *name)
* When the reference count reaches zero the ThrottleGroup is
* destroyed.
*
+ * This function edits throttle_groups and must be called under the global
+ * mutex.
+ *
* @ts: The ThrottleGroup to unref, given by its ThrottleState member
*/
void throttle_group_unref(ThrottleState *ts)
{
ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
-
- qemu_mutex_lock(&throttle_groups_lock);
- if (--tg->refcount == 0) {
- QTAILQ_REMOVE(&throttle_groups, tg, list);
- qemu_mutex_destroy(&tg->lock);
- g_free(tg->name);
- g_free(tg);
- }
- qemu_mutex_unlock(&throttle_groups_lock);
+ object_unref(OBJECT(tg));
}
/* Get the name from a ThrottleGroupMember's group. The name (and the pointer)
@@ -477,6 +485,9 @@ static void write_timer_cb(void *opaque)
* its timers and updating its throttle_state pointer to point to it. If a
* throttling group with that name does not exist yet, it will be created.
*
+ * This function edits throttle_groups and must be called under the global
+ * mutex.
+ *
* @tgm: the ThrottleGroupMember to insert
* @groupname: the name of the group
* @ctx: the AioContext to use
@@ -572,9 +583,338 @@ void throttle_group_detach_aio_context(ThrottleGroupMember *tgm)
tgm->aio_context = NULL;
}
+#undef THROTTLE_OPT_PREFIX
+#define THROTTLE_OPT_PREFIX "x-"
+
+/* Helper struct and array for QOM property setter/getter */
+typedef struct {
+ const char *name;
+ BucketType type;
+ enum {
+ AVG,
+ MAX,
+ BURST_LENGTH,
+ IOPS_SIZE,
+ } category;
+} ThrottleParamInfo;
+
+static ThrottleParamInfo properties[] = {
+ {
+ THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_TOTAL,
+ THROTTLE_OPS_TOTAL, AVG,
+ },
+ {
+ THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_TOTAL_MAX,
+ THROTTLE_OPS_TOTAL, MAX,
+ },
+ {
+ THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_TOTAL_MAX_LENGTH,
+ THROTTLE_OPS_TOTAL, BURST_LENGTH,
+ },
+ {
+ THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_READ,
+ THROTTLE_OPS_READ, AVG,
+ },
+ {
+ THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_READ_MAX,
+ THROTTLE_OPS_READ, MAX,
+ },
+ {
+ THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_READ_MAX_LENGTH,
+ THROTTLE_OPS_READ, BURST_LENGTH,
+ },
+ {
+ THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_WRITE,
+ THROTTLE_OPS_WRITE, AVG,
+ },
+ {
+ THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_WRITE_MAX,
+ THROTTLE_OPS_WRITE, MAX,
+ },
+ {
+ THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_WRITE_MAX_LENGTH,
+ THROTTLE_OPS_WRITE, BURST_LENGTH,
+ },
+ {
+ THROTTLE_OPT_PREFIX QEMU_OPT_BPS_TOTAL,
+ THROTTLE_BPS_TOTAL, AVG,
+ },
+ {
+ THROTTLE_OPT_PREFIX QEMU_OPT_BPS_TOTAL_MAX,
+ THROTTLE_BPS_TOTAL, MAX,
+ },
+ {
+ THROTTLE_OPT_PREFIX QEMU_OPT_BPS_TOTAL_MAX_LENGTH,
+ THROTTLE_BPS_TOTAL, BURST_LENGTH,
+ },
+ {
+ THROTTLE_OPT_PREFIX QEMU_OPT_BPS_READ,
+ THROTTLE_BPS_READ, AVG,
+ },
+ {
+ THROTTLE_OPT_PREFIX QEMU_OPT_BPS_READ_MAX,
+ THROTTLE_BPS_READ, MAX,
+ },
+ {
+ THROTTLE_OPT_PREFIX QEMU_OPT_BPS_READ_MAX_LENGTH,
+ THROTTLE_BPS_READ, BURST_LENGTH,
+ },
+ {
+ THROTTLE_OPT_PREFIX QEMU_OPT_BPS_WRITE,
+ THROTTLE_BPS_WRITE, AVG,
+ },
+ {
+ THROTTLE_OPT_PREFIX QEMU_OPT_BPS_WRITE_MAX,
+ THROTTLE_BPS_WRITE, MAX,
+ },
+ {
+ THROTTLE_OPT_PREFIX QEMU_OPT_BPS_WRITE_MAX_LENGTH,
+ THROTTLE_BPS_WRITE, BURST_LENGTH,
+ },
+ {
+ THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_SIZE,
+ 0, IOPS_SIZE,
+ }
+};
+
+/* This function edits throttle_groups and must be called under the global
+ * mutex */
+static void throttle_group_obj_init(Object *obj)
+{
+ ThrottleGroup *tg = THROTTLE_GROUP(obj);
+
+ tg->clock_type = QEMU_CLOCK_REALTIME;
+ if (qtest_enabled()) {
+ /* For testing block IO throttling only */
+ tg->clock_type = QEMU_CLOCK_VIRTUAL;
+ }
+ tg->is_initialized = false;
+ qemu_mutex_init(&tg->lock);
+ throttle_init(&tg->ts);
+ QLIST_INIT(&tg->head);
+}
+
+/* This function edits throttle_groups and must be called under the global
+ * mutex */
+static void throttle_group_obj_complete(UserCreatable *obj, Error **errp)
+{
+ ThrottleGroup *tg = THROTTLE_GROUP(obj);
+ ThrottleConfig cfg;
+
+ /* set group name to object id if it exists */
+ if (!tg->name && tg->parent_obj.parent) {
+ tg->name = object_get_canonical_path_component(OBJECT(obj));
+ }
+ /* We must have a group name at this point */
+ assert(tg->name);
+
+ /* error if name is duplicate */
+ if (throttle_group_by_name(tg->name) != NULL) {
+ error_setg(errp, "A group with this name already exists");
+ return;
+ }
+
+ /* check validity */
+ throttle_get_config(&tg->ts, &cfg);
+ if (!throttle_is_valid(&cfg, errp)) {
+ return;
+ }
+ throttle_config(&tg->ts, tg->clock_type, &cfg);
+ QTAILQ_INSERT_TAIL(&throttle_groups, tg, list);
+ tg->is_initialized = true;
+}
+
+/* This function edits throttle_groups and must be called under the global
+ * mutex */
+static void throttle_group_obj_finalize(Object *obj)
+{
+ ThrottleGroup *tg = THROTTLE_GROUP(obj);
+ if (tg->is_initialized) {
+ QTAILQ_REMOVE(&throttle_groups, tg, list);
+ }
+ qemu_mutex_destroy(&tg->lock);
+ g_free(tg->name);
+}
+
+static void throttle_group_set(Object *obj, Visitor *v, const char * name,
+ void *opaque, Error **errp)
+
+{
+ ThrottleGroup *tg = THROTTLE_GROUP(obj);
+ ThrottleConfig *cfg;
+ ThrottleParamInfo *info = opaque;
+ Error *local_err = NULL;
+ int64_t value;
+
+ /* If we have finished initialization, don't accept individual property
+ * changes through QOM. Throttle configuration limits must be set in one
+ * transaction, as certain combinations are invalid.
+ */
+ if (tg->is_initialized) {
+ error_setg(&local_err, "Property cannot be set after initialization");
+ goto ret;
+ }
+
+ visit_type_int64(v, name, &value, &local_err);
+ if (local_err) {
+ goto ret;
+ }
+ if (value < 0) {
+ error_setg(&local_err, "Property values cannot be negative");
+ goto ret;
+ }
+
+ cfg = &tg->ts.cfg;
+ switch (info->category) {
+ case AVG:
+ cfg->buckets[info->type].avg = value;
+ break;
+ case MAX:
+ cfg->buckets[info->type].max = value;
+ break;
+ case BURST_LENGTH:
+ if (value > UINT_MAX) {
+ error_setg(&local_err, "%s value must be in the"
+ "range [0, %u]", info->name, UINT_MAX);
+ goto ret;
+ }
+ cfg->buckets[info->type].burst_length = value;
+ break;
+ case IOPS_SIZE:
+ cfg->op_size = value;
+ break;
+ }
+
+ret:
+ error_propagate(errp, local_err);
+ return;
+
+}
+
+static void throttle_group_get(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ ThrottleGroup *tg = THROTTLE_GROUP(obj);
+ ThrottleConfig cfg;
+ ThrottleParamInfo *info = opaque;
+ int64_t value;
+
+ throttle_get_config(&tg->ts, &cfg);
+ switch (info->category) {
+ case AVG:
+ value = cfg.buckets[info->type].avg;
+ break;
+ case MAX:
+ value = cfg.buckets[info->type].max;
+ break;
+ case BURST_LENGTH:
+ value = cfg.buckets[info->type].burst_length;
+ break;
+ case IOPS_SIZE:
+ value = cfg.op_size;
+ break;
+ }
+
+ visit_type_int64(v, name, &value, errp);
+}
+
+static void throttle_group_set_limits(Object *obj, Visitor *v,
+ const char *name, void *opaque,
+ Error **errp)
+
+{
+ ThrottleGroup *tg = THROTTLE_GROUP(obj);
+ ThrottleConfig cfg;
+ ThrottleLimits arg = { 0 };
+ ThrottleLimits *argp = &arg;
+ Error *local_err = NULL;
+
+ visit_type_ThrottleLimits(v, name, &argp, &local_err);
+ if (local_err) {
+ goto ret;
+ }
+ qemu_mutex_lock(&tg->lock);
+ throttle_get_config(&tg->ts, &cfg);
+ throttle_limits_to_config(argp, &cfg, &local_err);
+ if (local_err) {
+ goto unlock;
+ }
+ throttle_config(&tg->ts, tg->clock_type, &cfg);
+
+unlock:
+ qemu_mutex_unlock(&tg->lock);
+ret:
+ error_propagate(errp, local_err);
+ return;
+}
+
+static void throttle_group_get_limits(Object *obj, Visitor *v,
+ const char *name, void *opaque,
+ Error **errp)
+{
+ ThrottleGroup *tg = THROTTLE_GROUP(obj);
+ ThrottleConfig cfg;
+ ThrottleLimits arg = { 0 };
+ ThrottleLimits *argp = &arg;
+
+ qemu_mutex_lock(&tg->lock);
+ throttle_get_config(&tg->ts, &cfg);
+ qemu_mutex_unlock(&tg->lock);
+
+ throttle_config_to_limits(&cfg, argp);
+
+ visit_type_ThrottleLimits(v, name, &argp, errp);
+}
+
+static bool throttle_group_can_be_deleted(UserCreatable *uc)
+{
+ return OBJECT(uc)->ref == 1;
+}
+
+static void throttle_group_obj_class_init(ObjectClass *klass, void *class_data)
+{
+ size_t i = 0;
+ UserCreatableClass *ucc = USER_CREATABLE_CLASS(klass);
+
+ ucc->complete = throttle_group_obj_complete;
+ ucc->can_be_deleted = throttle_group_can_be_deleted;
+
+ /* individual properties */
+ for (i = 0; i < sizeof(properties) / sizeof(ThrottleParamInfo); i++) {
+ object_class_property_add(klass,
+ properties[i].name,
+ "int",
+ throttle_group_get,
+ throttle_group_set,
+ NULL, &properties[i],
+ &error_abort);
+ }
+
+ /* ThrottleLimits */
+ object_class_property_add(klass,
+ "limits", "ThrottleLimits",
+ throttle_group_get_limits,
+ throttle_group_set_limits,
+ NULL, NULL,
+ &error_abort);
+}
+
+static const TypeInfo throttle_group_info = {
+ .name = TYPE_THROTTLE_GROUP,
+ .parent = TYPE_OBJECT,
+ .class_init = throttle_group_obj_class_init,
+ .instance_size = sizeof(ThrottleGroup),
+ .instance_init = throttle_group_obj_init,
+ .instance_finalize = throttle_group_obj_finalize,
+ .interfaces = (InterfaceInfo[]) {
+ { TYPE_USER_CREATABLE },
+ { }
+ },
+};
+
static void throttle_groups_init(void)
{
- qemu_mutex_init(&throttle_groups_lock);
+ type_register_static(&throttle_group_info);
}
-block_init(throttle_groups_init);
+type_init(throttle_groups_init);
diff --git a/tests/test-throttle.c b/tests/test-throttle.c
index e3a45cc605..948a42c991 100644
--- a/tests/test-throttle.c
+++ b/tests/test-throttle.c
@@ -739,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));
diff --git a/util/throttle.c b/util/throttle.c
index b8c524336c..06bf916adc 100644
--- a/util/throttle.c
+++ b/util/throttle.c
@@ -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;
+}
--
2.13.5
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [Qemu-devel] [PULL 12/14] block: add throttle block filter driver
2017-09-06 14:02 [Qemu-devel] [PULL 00/14] Block layer patches Kevin Wolf
` (10 preceding siblings ...)
2017-09-06 14:02 ` [Qemu-devel] [PULL 11/14] block: convert ThrottleGroup to object with QOM Kevin Wolf
@ 2017-09-06 14:02 ` Kevin Wolf
2017-09-06 14:02 ` [Qemu-devel] [PULL 13/14] qemu-iotests: add 184 for throttle " Kevin Wolf
` (2 subsequent siblings)
14 siblings, 0 replies; 24+ messages in thread
From: Kevin Wolf @ 2017-09-06 14:02 UTC (permalink / raw)
To: qemu-block; +Cc: kwolf, qemu-devel
From: Manos Pitsidianakis <el13635@mail.ntua.gr>
block/throttle.c uses existing I/O throttle infrastructure inside a
block filter driver. I/O operations are intercepted in the filter's
read/write coroutines, and referred to block/throttle-groups.c
The driver can be used with the syntax
-drive driver=throttle,file.filename=foo.qcow2,throttle-group=bar
which registers the throttle filter node with the ThrottleGroup 'bar'. The
given group must be created beforehand with object-add or -object.
Reviewed-by: Alberto Garcia <berto@igalia.com>
Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
qapi/block-core.json | 18 ++-
include/block/throttle-groups.h | 5 +
include/qemu/throttle-options.h | 1 +
block/throttle-groups.c | 15 ++-
block/throttle.c | 237 ++++++++++++++++++++++++++++++++++++++++
block/Makefile.objs | 1 +
6 files changed, 275 insertions(+), 2 deletions(-)
create mode 100644 block/throttle.c
diff --git a/qapi/block-core.json b/qapi/block-core.json
index 60bd7ec379..bb11815608 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -2223,6 +2223,7 @@
# Drivers that are supported in block device operations.
#
# @vxhs: Since 2.10
+# @throttle: Since 2.11
#
# Since: 2.9
##
@@ -2232,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:
@@ -3096,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
@@ -3156,6 +3171,7 @@
'replication':'BlockdevOptionsReplication',
'sheepdog': 'BlockdevOptionsSheepdog',
'ssh': 'BlockdevOptionsSsh',
+ 'throttle': 'BlockdevOptionsThrottle',
'vdi': 'BlockdevOptionsGenericFormat',
'vhdx': 'BlockdevOptionsGenericFormat',
'vmdk': 'BlockdevOptionsGenericCOWFormat',
diff --git a/include/block/throttle-groups.h b/include/block/throttle-groups.h
index 82f030523f..e2fd0513c4 100644
--- a/include/block/throttle-groups.h
+++ b/include/block/throttle-groups.h
@@ -76,5 +76,10 @@ void coroutine_fn throttle_group_co_io_limits_intercept(ThrottleGroupMember *tgm
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
diff --git a/include/qemu/throttle-options.h b/include/qemu/throttle-options.h
index 182b7896e1..3528a8f4a2 100644
--- a/include/qemu/throttle-options.h
+++ b/include/qemu/throttle-options.h
@@ -29,6 +29,7 @@
#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 \
diff --git a/block/throttle-groups.c b/block/throttle-groups.c
index ed1817ec84..6ba992c8d7 100644
--- a/block/throttle-groups.c
+++ b/block/throttle-groups.c
@@ -101,6 +101,14 @@ static ThrottleGroup *throttle_group_by_name(const char *name)
return NULL;
}
+/* This function reads throttle_groups and must be called under the global
+ * mutex.
+ */
+bool throttle_group_exists(const char *name)
+{
+ return throttle_group_by_name(name) != NULL;
+}
+
/* Increments the reference count of a ThrottleGroup given its name.
*
* If no ThrottleGroup is found with the given name a new one is
@@ -543,6 +551,11 @@ void throttle_group_unregister_tgm(ThrottleGroupMember *tgm)
ThrottleGroupMember *token;
int i;
+ if (!ts) {
+ /* Discard already unregistered tgm */
+ return;
+ }
+
assert(tgm->pending_reqs[0] == 0 && tgm->pending_reqs[1] == 0);
assert(qemu_co_queue_empty(&tgm->throttled_reqs[0]));
assert(qemu_co_queue_empty(&tgm->throttled_reqs[1]));
@@ -709,7 +722,7 @@ static void throttle_group_obj_complete(UserCreatable *obj, Error **errp)
assert(tg->name);
/* error if name is duplicate */
- if (throttle_group_by_name(tg->name) != NULL) {
+ if (throttle_group_exists(tg->name)) {
error_setg(errp, "A group with this name already exists");
return;
}
diff --git a/block/throttle.c b/block/throttle.c
new file mode 100644
index 0000000000..5bca76300f
--- /dev/null
+++ b/block/throttle.c
@@ -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);
diff --git a/block/Makefile.objs b/block/Makefile.objs
index 2aaede4ae1..6eaf78a046 100644
--- a/block/Makefile.objs
+++ b/block/Makefile.objs
@@ -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
--
2.13.5
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [Qemu-devel] [PULL 13/14] qemu-iotests: add 184 for throttle filter driver
2017-09-06 14:02 [Qemu-devel] [PULL 00/14] Block layer patches Kevin Wolf
` (11 preceding siblings ...)
2017-09-06 14:02 ` [Qemu-devel] [PULL 12/14] block: add throttle block filter driver Kevin Wolf
@ 2017-09-06 14:02 ` Kevin Wolf
2017-09-06 14:02 ` [Qemu-devel] [PULL 14/14] qcow2: move qcow2_store_persistent_dirty_bitmaps() before cache flushing Kevin Wolf
2017-09-07 10:50 ` [Qemu-devel] [PULL 00/14] Block layer patches Peter Maydell
14 siblings, 0 replies; 24+ messages in thread
From: Kevin Wolf @ 2017-09-06 14:02 UTC (permalink / raw)
To: qemu-block; +Cc: kwolf, qemu-devel
From: Manos Pitsidianakis <el13635@mail.ntua.gr>
Reviewed-by: Alberto Garcia <berto@igalia.com>
Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
tests/qemu-iotests/184 | 205 ++++++++++++++++++++++++++++++
tests/qemu-iotests/184.out | 302 +++++++++++++++++++++++++++++++++++++++++++++
tests/qemu-iotests/group | 1 +
3 files changed, 508 insertions(+)
create mode 100755 tests/qemu-iotests/184
create mode 100644 tests/qemu-iotests/184.out
diff --git a/tests/qemu-iotests/184 b/tests/qemu-iotests/184
new file mode 100755
index 0000000000..704f38f936
--- /dev/null
+++ b/tests/qemu-iotests/184
@@ -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
diff --git a/tests/qemu-iotests/184.out b/tests/qemu-iotests/184.out
new file mode 100644
index 0000000000..0aed1a2220
--- /dev/null
+++ b/tests/qemu-iotests/184.out
@@ -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
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
index 4bd5017008..94e764865a 100644
--- a/tests/qemu-iotests/group
+++ b/tests/qemu-iotests/group
@@ -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
--
2.13.5
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [Qemu-devel] [PULL 14/14] qcow2: move qcow2_store_persistent_dirty_bitmaps() before cache flushing
2017-09-06 14:02 [Qemu-devel] [PULL 00/14] Block layer patches Kevin Wolf
` (12 preceding siblings ...)
2017-09-06 14:02 ` [Qemu-devel] [PULL 13/14] qemu-iotests: add 184 for throttle " Kevin Wolf
@ 2017-09-06 14:02 ` Kevin Wolf
2017-09-07 10:50 ` [Qemu-devel] [PULL 00/14] Block layer patches Peter Maydell
14 siblings, 0 replies; 24+ messages in thread
From: Kevin Wolf @ 2017-09-06 14:02 UTC (permalink / raw)
To: qemu-block; +Cc: kwolf, qemu-devel
From: Pavel Butsykin <pbutsykin@virtuozzo.com>
After calling qcow2_inactivate(), all qcow2 caches must be flushed, but this
may not happen, because the last call qcow2_store_persistent_dirty_bitmaps()
can lead to marking l2/refcont cache as dirty.
Let's move qcow2_store_persistent_dirty_bitmaps() before the caсhe flushing
to fix it.
Cc: qemu-stable@nongnu.org
Signed-off-by: Pavel Butsykin <pbutsykin@virtuozzo.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
block/qcow2.c | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/block/qcow2.c b/block/qcow2.c
index 2ec399663e..bae5893327 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -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);
}
--
2.13.5
^ permalink raw reply related [flat|nested] 24+ messages in thread
* Re: [Qemu-devel] [PULL 00/14] Block layer patches
2017-09-06 14:02 [Qemu-devel] [PULL 00/14] Block layer patches Kevin Wolf
` (13 preceding siblings ...)
2017-09-06 14:02 ` [Qemu-devel] [PULL 14/14] qcow2: move qcow2_store_persistent_dirty_bitmaps() before cache flushing Kevin Wolf
@ 2017-09-07 10:50 ` Peter Maydell
14 siblings, 0 replies; 24+ messages in thread
From: Peter Maydell @ 2017-09-07 10:50 UTC (permalink / raw)
To: Kevin Wolf; +Cc: Qemu-block, QEMU Developers
On 6 September 2017 at 15:02, Kevin Wolf <kwolf@redhat.com> wrote:
> The following changes since commit 98bfaac788be0ca63d7d010c8d4ba100ff1d8278:
>
> Merge remote-tracking branch 'remotes/armbru/tags/pull-qapi-2017-09-01-v3' into staging (2017-09-04 13:28:09 +0100)
>
> are available in the git repository at:
>
> git://repo.or.cz/qemu/kevin.git tags/for-upstream
>
> for you to fetch changes up to 83a8c775a8bf134eb18a719322939b74a818d750:
>
> qcow2: move qcow2_store_persistent_dirty_bitmaps() before cache flushing (2017-09-06 14:40:18 +0200)
>
> ----------------------------------------------------------------
> Block layer patches
>
Applied, thanks.
-- PMM
^ permalink raw reply [flat|nested] 24+ messages in thread
* [Qemu-devel] [PULL 00/14] Block layer patches
@ 2017-12-20 13:19 Kevin Wolf
2017-12-20 15:30 ` Peter Maydell
0 siblings, 1 reply; 24+ messages in thread
From: Kevin Wolf @ 2017-12-20 13:19 UTC (permalink / raw)
To: qemu-block; +Cc: kwolf, qemu-devel
The following changes since commit af352675efb7e92a1f5f6461a042a12015ab3d12:
Merge remote-tracking branch 'remotes/aurel/tags/pull-target-sh4-20171218' into staging (2017-12-19 19:11:11 +0000)
are available in the git repository at:
git://repo.or.cz/qemu/kevin.git tags/for-upstream
for you to fetch changes up to b4d526c87b26aff6d8b353951aa175a1236ad887:
nvme: Add tracing (2017-12-20 11:05:49 +0100)
----------------------------------------------------------------
Block layer patches
----------------------------------------------------------------
Doug Gale (1):
nvme: Add tracing
Edgar Kaziakhmedov (1):
qcow2: get rid of qcow2_backing_read1 routine
Fam Zheng (1):
qemu-img: Document --force-share / -U
John Snow (1):
iotests: fix 197 for vpc
Kevin Wolf (9):
block: Formats don't need CONSISTENT_READ with NO_IO
block: Make bdrv_drain_invoke() recursive
block: Call .drain_begin only once in bdrv_drain_all_begin()
test-bdrv-drain: Test BlockDriver callbacks for drain
block: bdrv_drain_recurse(): Remove unused begin parameter
block: Don't wait for requests in bdrv_drain*_end()
block: Unify order in drain functions
block: Don't acquire AioContext in hmp_qemu_io()
block: Document that x-blockdev-change breaks quorum children list
Mao Zhongyi (1):
hw/block/nvme: Convert to realize
qapi/block-core.json | 4 +
block/qcow2.h | 3 -
block.c | 6 +-
block/io.c | 31 ++--
block/qcow2.c | 51 +-----
hmp.c | 6 -
hw/block/nvme.c | 367 ++++++++++++++++++++++++++++++++-------
tests/test-bdrv-drain.c | 137 +++++++++++++++
hw/block/trace-events | 93 ++++++++++
qemu-img.texi | 9 +
tests/Makefile.include | 2 +
tests/qemu-iotests/197 | 4 +
tests/qemu-iotests/common.filter | 3 +-
13 files changed, 591 insertions(+), 125 deletions(-)
create mode 100644 tests/test-bdrv-drain.c
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [Qemu-devel] [PULL 00/14] Block layer patches
2017-12-20 13:19 Kevin Wolf
@ 2017-12-20 15:30 ` Peter Maydell
0 siblings, 0 replies; 24+ messages in thread
From: Peter Maydell @ 2017-12-20 15:30 UTC (permalink / raw)
To: Kevin Wolf; +Cc: Qemu-block, QEMU Developers
On 20 December 2017 at 13:19, Kevin Wolf <kwolf@redhat.com> wrote:
> The following changes since commit af352675efb7e92a1f5f6461a042a12015ab3d12:
>
> Merge remote-tracking branch 'remotes/aurel/tags/pull-target-sh4-20171218' into staging (2017-12-19 19:11:11 +0000)
>
> are available in the git repository at:
>
> git://repo.or.cz/qemu/kevin.git tags/for-upstream
>
> for you to fetch changes up to b4d526c87b26aff6d8b353951aa175a1236ad887:
>
> nvme: Add tracing (2017-12-20 11:05:49 +0100)
>
> ----------------------------------------------------------------
> Block layer patches
Hi; this has a merge conflict in hw/block/nvme.c where both
sets of changes are changing the way that the function
handles errors, and I'm not sure which is correct. Can you
resolve and resend, please?
thanks
-- PMM
^ permalink raw reply [flat|nested] 24+ messages in thread
* [Qemu-devel] [PULL 00/14] Block layer patches
@ 2018-11-12 17:05 Kevin Wolf
2018-11-13 10:14 ` Peter Maydell
0 siblings, 1 reply; 24+ messages in thread
From: Kevin Wolf @ 2018-11-12 17:05 UTC (permalink / raw)
To: qemu-block; +Cc: kwolf, peter.maydell, qemu-devel
The following changes since commit 5704c36d25ee84e7129722cb0db53df9faefe943:
Merge remote-tracking branch 'remotes/kraxel/tags/fixes-31-20181112-pull-request' into staging (2018-11-12 15:55:40 +0000)
are available in the Git repository at:
git://repo.or.cz/qemu/kevin.git tags/for-upstream
for you to fetch changes up to 1a42e5d8298d1b0f90d2254e7d559391dd3a45ca:
Merge remote-tracking branch 'mreitz/tags/pull-block-2018-11-12' into queue-block (2018-11-12 17:57:32 +0100)
----------------------------------------------------------------
Block layer patches:
- file-posix: Don't waste a file descriptor for locking, don't lock the
same bit multiple times
- nvme: Fix double free and memory leak
- Misc error handling fixes
- Added NULL checks found by static analysis
- Allow more block drivers to not be included in the qemu build
----------------------------------------------------------------
Fam Zheng (4):
file-posix: Use error API properly
file-posix: Skip effectiveless OFD lock operations
file-posix: Drop s->lock_fd
tests: Add unit tests for image locking
Jeff Cody (1):
block: Make more block drivers compile-time configurable
Kevin Wolf (1):
Merge remote-tracking branch 'mreitz/tags/pull-block-2018-11-12' into queue-block
Li Qiang (2):
nvme: don't unref ctrl_mem when device unrealized
nvme: free cmbuf in nvme_exit
Liam Merwick (5):
job: Fix off-by-one assert checks for JobSTT and JobVerbTable
block: Null pointer dereference in blk_root_get_parent_desc()
qemu-img: assert block_job_get() does not return NULL in img_commit()
block: Fix potential Null pointer dereferences in vvfat.c
qcow2: Read outside array bounds in qcow2_pre_write_overlap_check()
Peter Maydell (1):
blockdev: Consistently use snapshot_node_name in external_snapshot_prepare()
zhenwei pi (1):
blockdev: handle error on block latency histogram set error
configure | 91 ++++++++++++++++++++++++++
block/block-backend.c | 3 +-
block/file-posix.c | 122 ++++++++++++++++++++---------------
block/qcow2-refcount.c | 18 +++---
block/vvfat.c | 46 ++++++++-----
blockdev.c | 21 ++++--
hw/block/nvme.c | 6 +-
job.c | 4 +-
qemu-img.c | 1 +
tests/test-image-locking.c | 157 +++++++++++++++++++++++++++++++++++++++++++++
block/Makefile.objs | 22 +++++--
tests/Makefile.include | 2 +
12 files changed, 400 insertions(+), 93 deletions(-)
create mode 100644 tests/test-image-locking.c
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [Qemu-devel] [PULL 00/14] Block layer patches
2018-11-12 17:05 Kevin Wolf
@ 2018-11-13 10:14 ` Peter Maydell
0 siblings, 0 replies; 24+ messages in thread
From: Peter Maydell @ 2018-11-13 10:14 UTC (permalink / raw)
To: Kevin Wolf; +Cc: Qemu-block, QEMU Developers
On 12 November 2018 at 17:05, Kevin Wolf <kwolf@redhat.com> wrote:
> The following changes since commit 5704c36d25ee84e7129722cb0db53df9faefe943:
>
> Merge remote-tracking branch 'remotes/kraxel/tags/fixes-31-20181112-pull-request' into staging (2018-11-12 15:55:40 +0000)
>
> are available in the Git repository at:
>
> git://repo.or.cz/qemu/kevin.git tags/for-upstream
>
> for you to fetch changes up to 1a42e5d8298d1b0f90d2254e7d559391dd3a45ca:
>
> Merge remote-tracking branch 'mreitz/tags/pull-block-2018-11-12' into queue-block (2018-11-12 17:57:32 +0100)
>
> ----------------------------------------------------------------
> Block layer patches:
>
> - file-posix: Don't waste a file descriptor for locking, don't lock the
> same bit multiple times
> - nvme: Fix double free and memory leak
> - Misc error handling fixes
> - Added NULL checks found by static analysis
> - Allow more block drivers to not be included in the qemu build
>
Applied, thanks.
-- PMM
^ permalink raw reply [flat|nested] 24+ messages in thread
* [Qemu-devel] [PULL 00/14] Block layer patches
@ 2019-06-18 15:23 Kevin Wolf
2019-06-18 16:32 ` Peter Maydell
0 siblings, 1 reply; 24+ messages in thread
From: Kevin Wolf @ 2019-06-18 15:23 UTC (permalink / raw)
To: qemu-block; +Cc: kwolf, qemu-devel
The following changes since commit cdfaa2720f4a09e5254868bd1f6e33f3e9eae76f:
Merge remote-tracking branch 'remotes/armbru/tags/pull-monitor-2019-06-17-v2' into staging (2019-06-18 10:47:00 +0100)
are available in the Git repository at:
git://repo.or.cz/qemu/kevin.git tags/for-upstream
for you to fetch changes up to 128b05f7e00765d883164631b974a27af5b4b613:
block/null: Expose read-zeroes option in QAPI schema (2019-06-18 16:41:10 +0200)
----------------------------------------------------------------
Block layer patches:
- block: Remove bs->job
- block: Ignore loosening perm restrictions failures
- block/null: Expose read-zeroes option in QAPI schema
- iotests: Hide timestamps for skipped tests
----------------------------------------------------------------
Kevin Wolf (1):
block/null: Expose read-zeroes option in QAPI schema
Max Reitz (9):
iotests: Hide timestamps for skipped tests
file-posix: Update open_flags in raw_set_perm()
block: Add bdrv_child_refresh_perms()
block/mirror: Fix child permissions
block/commit: Drop bdrv_child_try_set_perm()
block: Fix order in bdrv_replace_child()
block: Add *tighten_restrictions to *check*_perm()
block: Ignore loosening perm restrictions failures
iotests: Test failure to loosen restrictions
Vladimir Sementsov-Ogievskiy (4):
block/replication: drop usage of bs->job
block/block-backend: blk_iostatus_reset: drop usage of bs->job
blockdev: blockdev_mark_auto_del: drop usage of bs->job
block: drop bs->job
qapi/block-core.json | 4 +-
include/block/block_int.h | 30 ++++++---
include/block/blockjob.h | 9 +++
block.c | 155 +++++++++++++++++++++++++++++++++++++--------
block/block-backend.c | 4 --
block/commit.c | 2 -
block/file-posix.c | 4 ++
block/mirror.c | 70 ++++++++++++--------
block/replication.c | 21 +++---
blockdev.c | 19 +++---
blockjob.c | 22 ++++---
monitor/qmp-cmds.c | 5 ++
tests/test-blockjob.c | 5 +-
block/trace-events | 2 +-
tests/qemu-iotests/182 | 21 ++++++
tests/qemu-iotests/182.out | 6 ++
tests/qemu-iotests/check | 1 +
17 files changed, 283 insertions(+), 97 deletions(-)
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [Qemu-devel] [PULL 00/14] Block layer patches
2019-06-18 15:23 Kevin Wolf
@ 2019-06-18 16:32 ` Peter Maydell
0 siblings, 0 replies; 24+ messages in thread
From: Peter Maydell @ 2019-06-18 16:32 UTC (permalink / raw)
To: Kevin Wolf; +Cc: QEMU Developers, Qemu-block
On Tue, 18 Jun 2019 at 16:59, Kevin Wolf <kwolf@redhat.com> wrote:
>
> The following changes since commit cdfaa2720f4a09e5254868bd1f6e33f3e9eae76f:
>
> Merge remote-tracking branch 'remotes/armbru/tags/pull-monitor-2019-06-17-v2' into staging (2019-06-18 10:47:00 +0100)
>
> are available in the Git repository at:
>
> git://repo.or.cz/qemu/kevin.git tags/for-upstream
>
> for you to fetch changes up to 128b05f7e00765d883164631b974a27af5b4b613:
>
> block/null: Expose read-zeroes option in QAPI schema (2019-06-18 16:41:10 +0200)
>
> ----------------------------------------------------------------
> Block layer patches:
>
> - block: Remove bs->job
> - block: Ignore loosening perm restrictions failures
> - block/null: Expose read-zeroes option in QAPI schema
> - iotests: Hide timestamps for skipped tests
>
Applied, thanks.
Please update the changelog at https://wiki.qemu.org/ChangeLog/4.1
for any user-visible changes.
-- PMM
^ permalink raw reply [flat|nested] 24+ messages in thread
end of thread, other threads:[~2019-06-18 17:16 UTC | newest]
Thread overview: 24+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2017-09-06 14:02 [Qemu-devel] [PULL 00/14] Block layer patches Kevin Wolf
2017-09-06 14:02 ` [Qemu-devel] [PULL 01/14] block: pass bdrv_* methods to bs->file by default in block filters Kevin Wolf
2017-09-06 14:02 ` [Qemu-devel] [PULL 02/14] block: remove unused bdrv_media_changed Kevin Wolf
2017-09-06 14:02 ` [Qemu-devel] [PULL 03/14] block: remove bdrv_truncate callback in blkdebug Kevin Wolf
2017-09-06 14:02 ` [Qemu-devel] [PULL 04/14] block: add default implementations for bdrv_co_get_block_status() Kevin Wolf
2017-09-06 14:02 ` [Qemu-devel] [PULL 05/14] qcow: Change signature of get_cluster_offset() Kevin Wolf
2017-09-06 14:02 ` [Qemu-devel] [PULL 06/14] qcow: Check failure of bdrv_getlength() and bdrv_truncate() Kevin Wolf
2017-09-06 14:02 ` [Qemu-devel] [PULL 07/14] block: document semantics of bdrv_co_preadv|pwritev Kevin Wolf
2017-09-06 14:02 ` [Qemu-devel] [PULL 08/14] block: move ThrottleGroup membership to ThrottleGroupMember Kevin Wolf
2017-09-06 14:02 ` [Qemu-devel] [PULL 09/14] block: add aio_context field in ThrottleGroupMember Kevin Wolf
2017-09-06 14:02 ` [Qemu-devel] [PULL 10/14] block: tidy ThrottleGroupMember initializations Kevin Wolf
2017-09-06 14:02 ` [Qemu-devel] [PULL 11/14] block: convert ThrottleGroup to object with QOM Kevin Wolf
2017-09-06 14:02 ` [Qemu-devel] [PULL 12/14] block: add throttle block filter driver Kevin Wolf
2017-09-06 14:02 ` [Qemu-devel] [PULL 13/14] qemu-iotests: add 184 for throttle " Kevin Wolf
2017-09-06 14:02 ` [Qemu-devel] [PULL 14/14] qcow2: move qcow2_store_persistent_dirty_bitmaps() before cache flushing Kevin Wolf
2017-09-07 10:50 ` [Qemu-devel] [PULL 00/14] Block layer patches Peter Maydell
-- strict thread matches above, loose matches on Subject: below --
2019-06-18 15:23 Kevin Wolf
2019-06-18 16:32 ` Peter Maydell
2018-11-12 17:05 Kevin Wolf
2018-11-13 10:14 ` Peter Maydell
2017-12-20 13:19 Kevin Wolf
2017-12-20 15:30 ` Peter Maydell
2017-01-09 13:44 Kevin Wolf
2017-01-09 15:30 ` Peter Maydell
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).