2017-08-25 21:20:27 +08:00
|
|
|
/*
|
|
|
|
* 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"
|
2022-12-21 21:35:49 +08:00
|
|
|
#include "block/block-io.h"
|
|
|
|
#include "block/block_int.h"
|
2017-08-25 21:20:27 +08:00
|
|
|
#include "block/throttle-groups.h"
|
2019-05-23 22:35:07 +08:00
|
|
|
#include "qemu/module.h"
|
2018-02-01 19:18:46 +08:00
|
|
|
#include "qemu/option.h"
|
2017-08-25 21:20:27 +08:00
|
|
|
#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 */ }
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
throttle: Fix crash on reopen
The throttle block filter can be reopened, and with this it is
possible to change the throttle group that the filter belongs to.
The way the code does that is the following:
- On throttle_reopen_prepare(): create a new ThrottleGroupMember
and attach it to the new throttle group.
- On throttle_reopen_commit(): detach the old ThrottleGroupMember,
delete it and replace it with the new one.
The problem with this is that by replacing the ThrottleGroupMember the
previous value of io_limits_disabled is lost, causing an assertion
failure in throttle_co_drain_end().
This problem can be reproduced by reopening a throttle node:
$QEMU -monitor stdio
-object throttle-group,id=tg0,x-iops-total=1000 \
-blockdev node-name=hd0,driver=qcow2,file.driver=file,file.filename=hd.qcow2 \
-blockdev node-name=root,driver=throttle,throttle-group=tg0,file=hd0,read-only=on
(qemu) block_stream root
block/throttle.c:214: throttle_co_drain_end: Assertion `tgm->io_limits_disabled' failed.
Since we only want to change the throttle group on reopen there's no
need to create a ThrottleGroupMember and discard the old one. It's
easier if we simply detach it from its current group and attach it to
the new one.
Signed-off-by: Alberto Garcia <berto@igalia.com>
Message-id: 20180608151536.7378-1-berto@igalia.com
Signed-off-by: Max Reitz <mreitz@redhat.com>
2018-06-08 23:15:36 +08:00
|
|
|
/*
|
|
|
|
* If this function succeeds then the throttle group name is stored in
|
|
|
|
* @group and must be freed by the caller.
|
|
|
|
* If there's an error then @group remains unmodified.
|
|
|
|
*/
|
|
|
|
static int throttle_parse_options(QDict *options, char **group, Error **errp)
|
2017-08-25 21:20:27 +08:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
const char *group_name;
|
|
|
|
QemuOpts *opts = qemu_opts_create(&throttle_opts, NULL, 0, &error_abort);
|
|
|
|
|
2020-07-08 00:06:03 +08:00
|
|
|
if (!qemu_opts_absorb_qdict(opts, options, errp)) {
|
2017-08-25 21:20:27 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
throttle: Fix crash on reopen
The throttle block filter can be reopened, and with this it is
possible to change the throttle group that the filter belongs to.
The way the code does that is the following:
- On throttle_reopen_prepare(): create a new ThrottleGroupMember
and attach it to the new throttle group.
- On throttle_reopen_commit(): detach the old ThrottleGroupMember,
delete it and replace it with the new one.
The problem with this is that by replacing the ThrottleGroupMember the
previous value of io_limits_disabled is lost, causing an assertion
failure in throttle_co_drain_end().
This problem can be reproduced by reopening a throttle node:
$QEMU -monitor stdio
-object throttle-group,id=tg0,x-iops-total=1000 \
-blockdev node-name=hd0,driver=qcow2,file.driver=file,file.filename=hd.qcow2 \
-blockdev node-name=root,driver=throttle,throttle-group=tg0,file=hd0,read-only=on
(qemu) block_stream root
block/throttle.c:214: throttle_co_drain_end: Assertion `tgm->io_limits_disabled' failed.
Since we only want to change the throttle group on reopen there's no
need to create a ThrottleGroupMember and discard the old one. It's
easier if we simply detach it from its current group and attach it to
the new one.
Signed-off-by: Alberto Garcia <berto@igalia.com>
Message-id: 20180608151536.7378-1-berto@igalia.com
Signed-off-by: Max Reitz <mreitz@redhat.com>
2018-06-08 23:15:36 +08:00
|
|
|
*group = g_strdup(group_name);
|
2017-08-25 21:20:27 +08:00
|
|
|
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;
|
throttle: Fix crash on reopen
The throttle block filter can be reopened, and with this it is
possible to change the throttle group that the filter belongs to.
The way the code does that is the following:
- On throttle_reopen_prepare(): create a new ThrottleGroupMember
and attach it to the new throttle group.
- On throttle_reopen_commit(): detach the old ThrottleGroupMember,
delete it and replace it with the new one.
The problem with this is that by replacing the ThrottleGroupMember the
previous value of io_limits_disabled is lost, causing an assertion
failure in throttle_co_drain_end().
This problem can be reproduced by reopening a throttle node:
$QEMU -monitor stdio
-object throttle-group,id=tg0,x-iops-total=1000 \
-blockdev node-name=hd0,driver=qcow2,file.driver=file,file.filename=hd.qcow2 \
-blockdev node-name=root,driver=throttle,throttle-group=tg0,file=hd0,read-only=on
(qemu) block_stream root
block/throttle.c:214: throttle_co_drain_end: Assertion `tgm->io_limits_disabled' failed.
Since we only want to change the throttle group on reopen there's no
need to create a ThrottleGroupMember and discard the old one. It's
easier if we simply detach it from its current group and attach it to
the new one.
Signed-off-by: Alberto Garcia <berto@igalia.com>
Message-id: 20180608151536.7378-1-berto@igalia.com
Signed-off-by: Max Reitz <mreitz@redhat.com>
2018-06-08 23:15:36 +08:00
|
|
|
char *group;
|
|
|
|
int ret;
|
2017-08-25 21:20:27 +08:00
|
|
|
|
2022-07-27 04:11:21 +08:00
|
|
|
ret = bdrv_open_file_child(NULL, options, "file", bs, errp);
|
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
2017-08-25 21:20:27 +08:00
|
|
|
}
|
2018-04-21 21:29:26 +08:00
|
|
|
bs->supported_write_flags = bs->file->bs->supported_write_flags |
|
|
|
|
BDRV_REQ_WRITE_UNCHANGED;
|
|
|
|
bs->supported_zero_flags = bs->file->bs->supported_zero_flags |
|
|
|
|
BDRV_REQ_WRITE_UNCHANGED;
|
2017-08-25 21:20:27 +08:00
|
|
|
|
throttle: Fix crash on reopen
The throttle block filter can be reopened, and with this it is
possible to change the throttle group that the filter belongs to.
The way the code does that is the following:
- On throttle_reopen_prepare(): create a new ThrottleGroupMember
and attach it to the new throttle group.
- On throttle_reopen_commit(): detach the old ThrottleGroupMember,
delete it and replace it with the new one.
The problem with this is that by replacing the ThrottleGroupMember the
previous value of io_limits_disabled is lost, causing an assertion
failure in throttle_co_drain_end().
This problem can be reproduced by reopening a throttle node:
$QEMU -monitor stdio
-object throttle-group,id=tg0,x-iops-total=1000 \
-blockdev node-name=hd0,driver=qcow2,file.driver=file,file.filename=hd.qcow2 \
-blockdev node-name=root,driver=throttle,throttle-group=tg0,file=hd0,read-only=on
(qemu) block_stream root
block/throttle.c:214: throttle_co_drain_end: Assertion `tgm->io_limits_disabled' failed.
Since we only want to change the throttle group on reopen there's no
need to create a ThrottleGroupMember and discard the old one. It's
easier if we simply detach it from its current group and attach it to
the new one.
Signed-off-by: Alberto Garcia <berto@igalia.com>
Message-id: 20180608151536.7378-1-berto@igalia.com
Signed-off-by: Max Reitz <mreitz@redhat.com>
2018-06-08 23:15:36 +08:00
|
|
|
ret = throttle_parse_options(options, &group, errp);
|
|
|
|
if (ret == 0) {
|
|
|
|
/* Register membership to group with name group_name */
|
|
|
|
throttle_group_register_tgm(tgm, group, bdrv_get_aio_context(bs));
|
|
|
|
g_free(group);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
2017-08-25 21:20:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void throttle_close(BlockDriverState *bs)
|
|
|
|
{
|
|
|
|
ThrottleGroupMember *tgm = bs->opaque;
|
|
|
|
throttle_group_unregister_tgm(tgm);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-02-03 23:22:02 +08:00
|
|
|
static int64_t coroutine_fn GRAPH_RDLOCK
|
|
|
|
throttle_co_getlength(BlockDriverState *bs)
|
2017-08-25 21:20:27 +08:00
|
|
|
{
|
2023-01-14 04:42:04 +08:00
|
|
|
return bdrv_co_getlength(bs->file->bs);
|
2017-08-25 21:20:27 +08:00
|
|
|
}
|
|
|
|
|
2023-02-03 23:21:50 +08:00
|
|
|
static int coroutine_fn GRAPH_RDLOCK
|
|
|
|
throttle_co_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes,
|
|
|
|
QEMUIOVector *qiov, BdrvRequestFlags flags)
|
2017-08-25 21:20:27 +08:00
|
|
|
{
|
|
|
|
|
|
|
|
ThrottleGroupMember *tgm = bs->opaque;
|
block/throttle-groups: Use ThrottleDirection instread of bool is_write
'bool is_write' style is obsolete from throttle framework, adapt
block throttle groups to the new style:
- use ThrottleDirection instead of 'bool is_write'. Ex,
schedule_next_request(ThrottleGroupMember *tgm, bool is_write)
-> schedule_next_request(ThrottleGroupMember *tgm, ThrottleDirection direction)
- use THROTTLE_MAX instead of hard code. Ex, ThrottleGroupMember *tokens[2]
-> ThrottleGroupMember *tokens[THROTTLE_MAX]
- use ThrottleDirection instead of hard code on iteration. Ex, (i = 0; i < 2; i++)
-> for (dir = THROTTLE_READ; dir < THROTTLE_MAX; dir++)
Use a simple python script to test the new style:
#!/usr/bin/python3
import subprocess
import random
import time
commands = ['virsh blkdeviotune jammy vda --write-bytes-sec ', \
'virsh blkdeviotune jammy vda --write-iops-sec ', \
'virsh blkdeviotune jammy vda --read-bytes-sec ', \
'virsh blkdeviotune jammy vda --read-iops-sec ']
for loop in range(1, 1000):
time.sleep(random.randrange(3, 5))
command = commands[random.randrange(0, 3)] + str(random.randrange(0, 1000000))
subprocess.run(command, shell=True, check=True)
This works fine.
Signed-off-by: zhenwei pi <pizhenwei@bytedance.com>
Message-Id: <20230728022006.1098509-10-pizhenwei@bytedance.com>
Reviewed-by: Hanna Czenczek <hreitz@redhat.com>
Signed-off-by: Hanna Czenczek <hreitz@redhat.com>
2023-07-28 10:20:06 +08:00
|
|
|
throttle_group_co_io_limits_intercept(tgm, bytes, THROTTLE_READ);
|
2017-08-25 21:20:27 +08:00
|
|
|
|
|
|
|
return bdrv_co_preadv(bs->file, offset, bytes, qiov, flags);
|
|
|
|
}
|
|
|
|
|
2023-02-03 23:21:50 +08:00
|
|
|
static int coroutine_fn GRAPH_RDLOCK
|
|
|
|
throttle_co_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes,
|
|
|
|
QEMUIOVector *qiov, BdrvRequestFlags flags)
|
2017-08-25 21:20:27 +08:00
|
|
|
{
|
|
|
|
ThrottleGroupMember *tgm = bs->opaque;
|
block/throttle-groups: Use ThrottleDirection instread of bool is_write
'bool is_write' style is obsolete from throttle framework, adapt
block throttle groups to the new style:
- use ThrottleDirection instead of 'bool is_write'. Ex,
schedule_next_request(ThrottleGroupMember *tgm, bool is_write)
-> schedule_next_request(ThrottleGroupMember *tgm, ThrottleDirection direction)
- use THROTTLE_MAX instead of hard code. Ex, ThrottleGroupMember *tokens[2]
-> ThrottleGroupMember *tokens[THROTTLE_MAX]
- use ThrottleDirection instead of hard code on iteration. Ex, (i = 0; i < 2; i++)
-> for (dir = THROTTLE_READ; dir < THROTTLE_MAX; dir++)
Use a simple python script to test the new style:
#!/usr/bin/python3
import subprocess
import random
import time
commands = ['virsh blkdeviotune jammy vda --write-bytes-sec ', \
'virsh blkdeviotune jammy vda --write-iops-sec ', \
'virsh blkdeviotune jammy vda --read-bytes-sec ', \
'virsh blkdeviotune jammy vda --read-iops-sec ']
for loop in range(1, 1000):
time.sleep(random.randrange(3, 5))
command = commands[random.randrange(0, 3)] + str(random.randrange(0, 1000000))
subprocess.run(command, shell=True, check=True)
This works fine.
Signed-off-by: zhenwei pi <pizhenwei@bytedance.com>
Message-Id: <20230728022006.1098509-10-pizhenwei@bytedance.com>
Reviewed-by: Hanna Czenczek <hreitz@redhat.com>
Signed-off-by: Hanna Czenczek <hreitz@redhat.com>
2023-07-28 10:20:06 +08:00
|
|
|
throttle_group_co_io_limits_intercept(tgm, bytes, THROTTLE_WRITE);
|
2017-08-25 21:20:27 +08:00
|
|
|
|
|
|
|
return bdrv_co_pwritev(bs->file, offset, bytes, qiov, flags);
|
|
|
|
}
|
|
|
|
|
2023-02-03 23:21:48 +08:00
|
|
|
static int coroutine_fn GRAPH_RDLOCK
|
|
|
|
throttle_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int64_t bytes,
|
|
|
|
BdrvRequestFlags flags)
|
2017-08-25 21:20:27 +08:00
|
|
|
{
|
|
|
|
ThrottleGroupMember *tgm = bs->opaque;
|
block/throttle-groups: Use ThrottleDirection instread of bool is_write
'bool is_write' style is obsolete from throttle framework, adapt
block throttle groups to the new style:
- use ThrottleDirection instead of 'bool is_write'. Ex,
schedule_next_request(ThrottleGroupMember *tgm, bool is_write)
-> schedule_next_request(ThrottleGroupMember *tgm, ThrottleDirection direction)
- use THROTTLE_MAX instead of hard code. Ex, ThrottleGroupMember *tokens[2]
-> ThrottleGroupMember *tokens[THROTTLE_MAX]
- use ThrottleDirection instead of hard code on iteration. Ex, (i = 0; i < 2; i++)
-> for (dir = THROTTLE_READ; dir < THROTTLE_MAX; dir++)
Use a simple python script to test the new style:
#!/usr/bin/python3
import subprocess
import random
import time
commands = ['virsh blkdeviotune jammy vda --write-bytes-sec ', \
'virsh blkdeviotune jammy vda --write-iops-sec ', \
'virsh blkdeviotune jammy vda --read-bytes-sec ', \
'virsh blkdeviotune jammy vda --read-iops-sec ']
for loop in range(1, 1000):
time.sleep(random.randrange(3, 5))
command = commands[random.randrange(0, 3)] + str(random.randrange(0, 1000000))
subprocess.run(command, shell=True, check=True)
This works fine.
Signed-off-by: zhenwei pi <pizhenwei@bytedance.com>
Message-Id: <20230728022006.1098509-10-pizhenwei@bytedance.com>
Reviewed-by: Hanna Czenczek <hreitz@redhat.com>
Signed-off-by: Hanna Czenczek <hreitz@redhat.com>
2023-07-28 10:20:06 +08:00
|
|
|
throttle_group_co_io_limits_intercept(tgm, bytes, THROTTLE_WRITE);
|
2017-08-25 21:20:27 +08:00
|
|
|
|
|
|
|
return bdrv_co_pwrite_zeroes(bs->file, offset, bytes, flags);
|
|
|
|
}
|
|
|
|
|
2023-02-03 23:21:47 +08:00
|
|
|
static int coroutine_fn GRAPH_RDLOCK
|
|
|
|
throttle_co_pdiscard(BlockDriverState *bs, int64_t offset, int64_t bytes)
|
2017-08-25 21:20:27 +08:00
|
|
|
{
|
|
|
|
ThrottleGroupMember *tgm = bs->opaque;
|
block/throttle-groups: Use ThrottleDirection instread of bool is_write
'bool is_write' style is obsolete from throttle framework, adapt
block throttle groups to the new style:
- use ThrottleDirection instead of 'bool is_write'. Ex,
schedule_next_request(ThrottleGroupMember *tgm, bool is_write)
-> schedule_next_request(ThrottleGroupMember *tgm, ThrottleDirection direction)
- use THROTTLE_MAX instead of hard code. Ex, ThrottleGroupMember *tokens[2]
-> ThrottleGroupMember *tokens[THROTTLE_MAX]
- use ThrottleDirection instead of hard code on iteration. Ex, (i = 0; i < 2; i++)
-> for (dir = THROTTLE_READ; dir < THROTTLE_MAX; dir++)
Use a simple python script to test the new style:
#!/usr/bin/python3
import subprocess
import random
import time
commands = ['virsh blkdeviotune jammy vda --write-bytes-sec ', \
'virsh blkdeviotune jammy vda --write-iops-sec ', \
'virsh blkdeviotune jammy vda --read-bytes-sec ', \
'virsh blkdeviotune jammy vda --read-iops-sec ']
for loop in range(1, 1000):
time.sleep(random.randrange(3, 5))
command = commands[random.randrange(0, 3)] + str(random.randrange(0, 1000000))
subprocess.run(command, shell=True, check=True)
This works fine.
Signed-off-by: zhenwei pi <pizhenwei@bytedance.com>
Message-Id: <20230728022006.1098509-10-pizhenwei@bytedance.com>
Reviewed-by: Hanna Czenczek <hreitz@redhat.com>
Signed-off-by: Hanna Czenczek <hreitz@redhat.com>
2023-07-28 10:20:06 +08:00
|
|
|
throttle_group_co_io_limits_intercept(tgm, bytes, THROTTLE_WRITE);
|
2017-08-25 21:20:27 +08:00
|
|
|
|
2018-07-10 14:31:17 +08:00
|
|
|
return bdrv_co_pdiscard(bs->file, offset, bytes);
|
2017-08-25 21:20:27 +08:00
|
|
|
}
|
|
|
|
|
2023-02-03 23:21:50 +08:00
|
|
|
static int coroutine_fn GRAPH_RDLOCK
|
|
|
|
throttle_co_pwritev_compressed(BlockDriverState *bs, int64_t offset,
|
|
|
|
int64_t bytes, QEMUIOVector *qiov)
|
2019-06-13 04:52:27 +08:00
|
|
|
{
|
|
|
|
return throttle_co_pwritev(bs, offset, bytes, qiov,
|
|
|
|
BDRV_REQ_WRITE_COMPRESSED);
|
|
|
|
}
|
|
|
|
|
2023-02-03 23:21:46 +08:00
|
|
|
static int coroutine_fn GRAPH_RDLOCK throttle_co_flush(BlockDriverState *bs)
|
2017-08-25 21:20:27 +08:00
|
|
|
{
|
|
|
|
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)
|
|
|
|
{
|
throttle: Fix crash on reopen
The throttle block filter can be reopened, and with this it is
possible to change the throttle group that the filter belongs to.
The way the code does that is the following:
- On throttle_reopen_prepare(): create a new ThrottleGroupMember
and attach it to the new throttle group.
- On throttle_reopen_commit(): detach the old ThrottleGroupMember,
delete it and replace it with the new one.
The problem with this is that by replacing the ThrottleGroupMember the
previous value of io_limits_disabled is lost, causing an assertion
failure in throttle_co_drain_end().
This problem can be reproduced by reopening a throttle node:
$QEMU -monitor stdio
-object throttle-group,id=tg0,x-iops-total=1000 \
-blockdev node-name=hd0,driver=qcow2,file.driver=file,file.filename=hd.qcow2 \
-blockdev node-name=root,driver=throttle,throttle-group=tg0,file=hd0,read-only=on
(qemu) block_stream root
block/throttle.c:214: throttle_co_drain_end: Assertion `tgm->io_limits_disabled' failed.
Since we only want to change the throttle group on reopen there's no
need to create a ThrottleGroupMember and discard the old one. It's
easier if we simply detach it from its current group and attach it to
the new one.
Signed-off-by: Alberto Garcia <berto@igalia.com>
Message-id: 20180608151536.7378-1-berto@igalia.com
Signed-off-by: Max Reitz <mreitz@redhat.com>
2018-06-08 23:15:36 +08:00
|
|
|
int ret;
|
|
|
|
char *group = NULL;
|
2017-08-25 21:20:27 +08:00
|
|
|
|
|
|
|
assert(reopen_state != NULL);
|
|
|
|
assert(reopen_state->bs != NULL);
|
|
|
|
|
throttle: Fix crash on reopen
The throttle block filter can be reopened, and with this it is
possible to change the throttle group that the filter belongs to.
The way the code does that is the following:
- On throttle_reopen_prepare(): create a new ThrottleGroupMember
and attach it to the new throttle group.
- On throttle_reopen_commit(): detach the old ThrottleGroupMember,
delete it and replace it with the new one.
The problem with this is that by replacing the ThrottleGroupMember the
previous value of io_limits_disabled is lost, causing an assertion
failure in throttle_co_drain_end().
This problem can be reproduced by reopening a throttle node:
$QEMU -monitor stdio
-object throttle-group,id=tg0,x-iops-total=1000 \
-blockdev node-name=hd0,driver=qcow2,file.driver=file,file.filename=hd.qcow2 \
-blockdev node-name=root,driver=throttle,throttle-group=tg0,file=hd0,read-only=on
(qemu) block_stream root
block/throttle.c:214: throttle_co_drain_end: Assertion `tgm->io_limits_disabled' failed.
Since we only want to change the throttle group on reopen there's no
need to create a ThrottleGroupMember and discard the old one. It's
easier if we simply detach it from its current group and attach it to
the new one.
Signed-off-by: Alberto Garcia <berto@igalia.com>
Message-id: 20180608151536.7378-1-berto@igalia.com
Signed-off-by: Max Reitz <mreitz@redhat.com>
2018-06-08 23:15:36 +08:00
|
|
|
ret = throttle_parse_options(reopen_state->options, &group, errp);
|
|
|
|
reopen_state->opaque = group;
|
|
|
|
return ret;
|
2017-08-25 21:20:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void throttle_reopen_commit(BDRVReopenState *reopen_state)
|
|
|
|
{
|
throttle: Fix crash on reopen
The throttle block filter can be reopened, and with this it is
possible to change the throttle group that the filter belongs to.
The way the code does that is the following:
- On throttle_reopen_prepare(): create a new ThrottleGroupMember
and attach it to the new throttle group.
- On throttle_reopen_commit(): detach the old ThrottleGroupMember,
delete it and replace it with the new one.
The problem with this is that by replacing the ThrottleGroupMember the
previous value of io_limits_disabled is lost, causing an assertion
failure in throttle_co_drain_end().
This problem can be reproduced by reopening a throttle node:
$QEMU -monitor stdio
-object throttle-group,id=tg0,x-iops-total=1000 \
-blockdev node-name=hd0,driver=qcow2,file.driver=file,file.filename=hd.qcow2 \
-blockdev node-name=root,driver=throttle,throttle-group=tg0,file=hd0,read-only=on
(qemu) block_stream root
block/throttle.c:214: throttle_co_drain_end: Assertion `tgm->io_limits_disabled' failed.
Since we only want to change the throttle group on reopen there's no
need to create a ThrottleGroupMember and discard the old one. It's
easier if we simply detach it from its current group and attach it to
the new one.
Signed-off-by: Alberto Garcia <berto@igalia.com>
Message-id: 20180608151536.7378-1-berto@igalia.com
Signed-off-by: Max Reitz <mreitz@redhat.com>
2018-06-08 23:15:36 +08:00
|
|
|
BlockDriverState *bs = reopen_state->bs;
|
|
|
|
ThrottleGroupMember *tgm = bs->opaque;
|
|
|
|
char *group = reopen_state->opaque;
|
|
|
|
|
|
|
|
assert(group);
|
2017-08-25 21:20:27 +08:00
|
|
|
|
throttle: Fix crash on reopen
The throttle block filter can be reopened, and with this it is
possible to change the throttle group that the filter belongs to.
The way the code does that is the following:
- On throttle_reopen_prepare(): create a new ThrottleGroupMember
and attach it to the new throttle group.
- On throttle_reopen_commit(): detach the old ThrottleGroupMember,
delete it and replace it with the new one.
The problem with this is that by replacing the ThrottleGroupMember the
previous value of io_limits_disabled is lost, causing an assertion
failure in throttle_co_drain_end().
This problem can be reproduced by reopening a throttle node:
$QEMU -monitor stdio
-object throttle-group,id=tg0,x-iops-total=1000 \
-blockdev node-name=hd0,driver=qcow2,file.driver=file,file.filename=hd.qcow2 \
-blockdev node-name=root,driver=throttle,throttle-group=tg0,file=hd0,read-only=on
(qemu) block_stream root
block/throttle.c:214: throttle_co_drain_end: Assertion `tgm->io_limits_disabled' failed.
Since we only want to change the throttle group on reopen there's no
need to create a ThrottleGroupMember and discard the old one. It's
easier if we simply detach it from its current group and attach it to
the new one.
Signed-off-by: Alberto Garcia <berto@igalia.com>
Message-id: 20180608151536.7378-1-berto@igalia.com
Signed-off-by: Max Reitz <mreitz@redhat.com>
2018-06-08 23:15:36 +08:00
|
|
|
if (strcmp(group, throttle_group_get_name(tgm))) {
|
|
|
|
throttle_group_unregister_tgm(tgm);
|
|
|
|
throttle_group_register_tgm(tgm, group, bdrv_get_aio_context(bs));
|
|
|
|
}
|
|
|
|
g_free(reopen_state->opaque);
|
2017-08-25 21:20:27 +08:00
|
|
|
reopen_state->opaque = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void throttle_reopen_abort(BDRVReopenState *reopen_state)
|
|
|
|
{
|
throttle: Fix crash on reopen
The throttle block filter can be reopened, and with this it is
possible to change the throttle group that the filter belongs to.
The way the code does that is the following:
- On throttle_reopen_prepare(): create a new ThrottleGroupMember
and attach it to the new throttle group.
- On throttle_reopen_commit(): detach the old ThrottleGroupMember,
delete it and replace it with the new one.
The problem with this is that by replacing the ThrottleGroupMember the
previous value of io_limits_disabled is lost, causing an assertion
failure in throttle_co_drain_end().
This problem can be reproduced by reopening a throttle node:
$QEMU -monitor stdio
-object throttle-group,id=tg0,x-iops-total=1000 \
-blockdev node-name=hd0,driver=qcow2,file.driver=file,file.filename=hd.qcow2 \
-blockdev node-name=root,driver=throttle,throttle-group=tg0,file=hd0,read-only=on
(qemu) block_stream root
block/throttle.c:214: throttle_co_drain_end: Assertion `tgm->io_limits_disabled' failed.
Since we only want to change the throttle group on reopen there's no
need to create a ThrottleGroupMember and discard the old one. It's
easier if we simply detach it from its current group and attach it to
the new one.
Signed-off-by: Alberto Garcia <berto@igalia.com>
Message-id: 20180608151536.7378-1-berto@igalia.com
Signed-off-by: Max Reitz <mreitz@redhat.com>
2018-06-08 23:15:36 +08:00
|
|
|
g_free(reopen_state->opaque);
|
2017-08-25 21:20:27 +08:00
|
|
|
reopen_state->opaque = NULL;
|
|
|
|
}
|
|
|
|
|
2022-11-19 01:40:58 +08:00
|
|
|
static void throttle_drain_begin(BlockDriverState *bs)
|
2017-09-23 19:14:11 +08:00
|
|
|
{
|
|
|
|
ThrottleGroupMember *tgm = bs->opaque;
|
2020-09-23 18:56:46 +08:00
|
|
|
if (qatomic_fetch_inc(&tgm->io_limits_disabled) == 0) {
|
2017-09-23 19:14:11 +08:00
|
|
|
throttle_group_restart_tgm(tgm);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-19 01:40:58 +08:00
|
|
|
static void throttle_drain_end(BlockDriverState *bs)
|
2017-09-23 19:14:11 +08:00
|
|
|
{
|
|
|
|
ThrottleGroupMember *tgm = bs->opaque;
|
|
|
|
assert(tgm->io_limits_disabled);
|
2020-09-23 18:56:46 +08:00
|
|
|
qatomic_dec(&tgm->io_limits_disabled);
|
2017-09-23 19:14:11 +08:00
|
|
|
}
|
|
|
|
|
2019-02-02 03:29:25 +08:00
|
|
|
static const char *const throttle_strong_runtime_opts[] = {
|
|
|
|
QEMU_OPT_THROTTLE_GROUP_NAME,
|
|
|
|
|
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
2017-08-25 21:20:27 +08:00
|
|
|
static BlockDriver bdrv_throttle = {
|
|
|
|
.format_name = "throttle",
|
|
|
|
.instance_size = sizeof(ThrottleGroupMember),
|
|
|
|
|
2018-03-13 06:07:51 +08:00
|
|
|
.bdrv_open = throttle_open,
|
2017-08-25 21:20:27 +08:00
|
|
|
.bdrv_close = throttle_close,
|
|
|
|
.bdrv_co_flush = throttle_co_flush,
|
|
|
|
|
2020-05-13 19:05:39 +08:00
|
|
|
.bdrv_child_perm = bdrv_default_perms,
|
2017-08-25 21:20:27 +08:00
|
|
|
|
2023-01-14 04:42:04 +08:00
|
|
|
.bdrv_co_getlength = throttle_co_getlength,
|
2017-08-25 21:20:27 +08:00
|
|
|
|
|
|
|
.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,
|
2019-06-13 04:52:27 +08:00
|
|
|
.bdrv_co_pwritev_compressed = throttle_co_pwritev_compressed,
|
2017-08-25 21:20:27 +08:00
|
|
|
|
|
|
|
.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,
|
|
|
|
|
2022-11-19 01:40:58 +08:00
|
|
|
.bdrv_drain_begin = throttle_drain_begin,
|
|
|
|
.bdrv_drain_end = throttle_drain_end,
|
2017-09-23 19:14:11 +08:00
|
|
|
|
2017-08-25 21:20:27 +08:00
|
|
|
.is_filter = true,
|
2019-02-02 03:29:25 +08:00
|
|
|
.strong_runtime_opts = throttle_strong_runtime_opts,
|
2017-08-25 21:20:27 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
static void bdrv_throttle_init(void)
|
|
|
|
{
|
|
|
|
bdrv_register(&bdrv_throttle);
|
|
|
|
}
|
|
|
|
|
|
|
|
block_init(bdrv_throttle_init);
|