mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
Merge remote-tracking branch 'remotes/kevin/tags/for-upstream' into staging
Block layer patches # gpg: Signature made Thu 19 May 2016 16:09:27 BST using RSA key ID C88F2FD6 # gpg: Good signature from "Kevin Wolf <kwolf@redhat.com>" * remotes/kevin/tags/for-upstream: (31 commits) qemu-iotests: Fix regression in 136 on aio_read invalid qemu-iotests: Simplify 109 with unaligned qemu-img compare qemu-io: Fix recent UI updates block: clarify error message for qmp-eject qemu-iotests: Some more write_zeroes tests qcow2: Fix write_zeroes with partially allocated backing file cluster qcow2: fix condition in is_zero_cluster block: Propagate AioContext change to all children block: Remove BlockDriverState.blk block: Don't return throttling info in query-named-block-nodes block: Avoid bs->blk in bdrv_next() block: Add bdrv_has_blk() block: Remove bdrv_aio_multiwrite() blockjob: Don't touch BDS iostatus blockjob: Don't set iostatus of target block: User BdrvChild callback for device name block: Use BdrvChild callbacks for change_media/resize block: Don't check throttled reqs in bdrv_requests_pending() Revert "block: Forbid I/O throttling on nodes with multiple parents for 2.6" block: Remove bdrv_move_feature_fields() ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
@@ -38,7 +38,6 @@
|
||||
#include "qmp-commands.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "qapi-event.h"
|
||||
#include "block/throttle-groups.h"
|
||||
#include "qemu/cutils.h"
|
||||
#include "qemu/id.h"
|
||||
|
||||
@@ -237,8 +236,6 @@ BlockDriverState *bdrv_new(void)
|
||||
QLIST_INIT(&bs->op_blockers[i]);
|
||||
}
|
||||
notifier_with_return_list_init(&bs->before_write_notifiers);
|
||||
qemu_co_queue_init(&bs->throttled_reqs[0]);
|
||||
qemu_co_queue_init(&bs->throttled_reqs[1]);
|
||||
bs->refcnt = 1;
|
||||
bs->aio_context = qemu_get_aio_context();
|
||||
|
||||
@@ -1217,6 +1214,27 @@ void bdrv_unref_child(BlockDriverState *parent, BdrvChild *child)
|
||||
bdrv_root_unref_child(child);
|
||||
}
|
||||
|
||||
|
||||
static void bdrv_parent_cb_change_media(BlockDriverState *bs, bool load)
|
||||
{
|
||||
BdrvChild *c;
|
||||
QLIST_FOREACH(c, &bs->parents, next_parent) {
|
||||
if (c->role->change_media) {
|
||||
c->role->change_media(c, load);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void bdrv_parent_cb_resize(BlockDriverState *bs)
|
||||
{
|
||||
BdrvChild *c;
|
||||
QLIST_FOREACH(c, &bs->parents, next_parent) {
|
||||
if (c->role->resize) {
|
||||
c->role->resize(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets the backing file link of a BDS. A new reference is created; callers
|
||||
* which don't need their own reference any more must call bdrv_unref().
|
||||
@@ -1525,12 +1543,6 @@ static int bdrv_open_inherit(BlockDriverState **pbs, const char *filename,
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (bs->throttle_state) {
|
||||
error_setg(errp, "Cannot reference an existing block device for "
|
||||
"which I/O throttling is enabled");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
bdrv_ref(bs);
|
||||
*pbs = bs;
|
||||
return 0;
|
||||
@@ -1682,9 +1694,7 @@ static int bdrv_open_inherit(BlockDriverState **pbs, const char *filename,
|
||||
}
|
||||
|
||||
if (!bdrv_key_required(bs)) {
|
||||
if (bs->blk) {
|
||||
blk_dev_change_media_cb(bs->blk, true);
|
||||
}
|
||||
bdrv_parent_cb_change_media(bs, true);
|
||||
} else if (!runstate_check(RUN_STATE_PRELAUNCH)
|
||||
&& !runstate_check(RUN_STATE_INMIGRATE)
|
||||
&& !runstate_check(RUN_STATE_PAUSED)) { /* HACK */
|
||||
@@ -2123,11 +2133,6 @@ static void bdrv_close(BlockDriverState *bs)
|
||||
|
||||
assert(!bs->job);
|
||||
|
||||
/* Disable I/O limits and drain all pending throttled requests */
|
||||
if (bs->throttle_state) {
|
||||
bdrv_io_limits_disable(bs);
|
||||
}
|
||||
|
||||
bdrv_drained_begin(bs); /* complete I/O */
|
||||
bdrv_flush(bs);
|
||||
bdrv_drain(bs); /* in case flush left pending I/O */
|
||||
@@ -2135,9 +2140,7 @@ static void bdrv_close(BlockDriverState *bs)
|
||||
bdrv_release_named_dirty_bitmaps(bs);
|
||||
assert(QLIST_EMPTY(&bs->dirty_bitmaps));
|
||||
|
||||
if (bs->blk) {
|
||||
blk_dev_change_media_cb(bs->blk, false);
|
||||
}
|
||||
bdrv_parent_cb_change_media(bs, false);
|
||||
|
||||
if (bs->drv) {
|
||||
BdrvChild *child, *next;
|
||||
@@ -2218,26 +2221,11 @@ void bdrv_close_all(void)
|
||||
}
|
||||
}
|
||||
|
||||
/* Fields that need to stay with the top-level BDS */
|
||||
static void bdrv_move_feature_fields(BlockDriverState *bs_dest,
|
||||
BlockDriverState *bs_src)
|
||||
{
|
||||
/* move some fields that need to stay attached to the device */
|
||||
}
|
||||
|
||||
static void change_parent_backing_link(BlockDriverState *from,
|
||||
BlockDriverState *to)
|
||||
{
|
||||
BdrvChild *c, *next;
|
||||
|
||||
if (from->blk) {
|
||||
/* FIXME We bypass blk_set_bs(), so we need to make these updates
|
||||
* manually. The root problem is not in this change function, but the
|
||||
* existence of BlockDriverState.blk. */
|
||||
to->blk = from->blk;
|
||||
from->blk = NULL;
|
||||
}
|
||||
|
||||
QLIST_FOREACH_SAFE(c, &from->parents, next_parent, next) {
|
||||
assert(c->role != &child_backing);
|
||||
c->bs = to;
|
||||
@@ -2248,22 +2236,6 @@ static void change_parent_backing_link(BlockDriverState *from,
|
||||
}
|
||||
}
|
||||
|
||||
static void swap_feature_fields(BlockDriverState *bs_top,
|
||||
BlockDriverState *bs_new)
|
||||
{
|
||||
BlockDriverState tmp;
|
||||
|
||||
bdrv_move_feature_fields(&tmp, bs_top);
|
||||
bdrv_move_feature_fields(bs_top, bs_new);
|
||||
bdrv_move_feature_fields(bs_new, &tmp);
|
||||
|
||||
assert(!bs_new->throttle_state);
|
||||
if (bs_top->throttle_state) {
|
||||
bdrv_io_limits_enable(bs_new, throttle_group_get_name(bs_top));
|
||||
bdrv_io_limits_disable(bs_top);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Add new bs contents at the top of an image chain while the chain is
|
||||
* live, while keeping required fields on the top layer.
|
||||
@@ -2286,11 +2258,8 @@ void bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top)
|
||||
assert(!bdrv_requests_pending(bs_new));
|
||||
|
||||
bdrv_ref(bs_top);
|
||||
|
||||
change_parent_backing_link(bs_top, bs_new);
|
||||
|
||||
/* Some fields always stay on top of the backing file chain */
|
||||
swap_feature_fields(bs_top, bs_new);
|
||||
|
||||
bdrv_set_backing_hd(bs_new, bs_top);
|
||||
bdrv_unref(bs_top);
|
||||
|
||||
@@ -2306,16 +2275,6 @@ void bdrv_replace_in_backing_chain(BlockDriverState *old, BlockDriverState *new)
|
||||
|
||||
bdrv_ref(old);
|
||||
|
||||
if (old->blk) {
|
||||
/* As long as these fields aren't in BlockBackend, but in the top-level
|
||||
* BlockDriverState, it's not possible for a BDS to have two BBs.
|
||||
*
|
||||
* We really want to copy the fields from old to new, but we go for a
|
||||
* swap instead so that pointers aren't duplicated and cause trouble.
|
||||
* (Also, bdrv_swap() used to do the same.) */
|
||||
assert(!new->blk);
|
||||
swap_feature_fields(old, new);
|
||||
}
|
||||
change_parent_backing_link(old, new);
|
||||
|
||||
/* Change backing files if a previously independent node is added to the
|
||||
@@ -2624,9 +2583,7 @@ int bdrv_truncate(BlockDriverState *bs, int64_t offset)
|
||||
if (ret == 0) {
|
||||
ret = refresh_total_sectors(bs, offset >> BDRV_SECTOR_BITS);
|
||||
bdrv_dirty_bitmap_truncate(bs);
|
||||
if (bs->blk) {
|
||||
blk_dev_resize_cb(bs->blk);
|
||||
}
|
||||
bdrv_parent_cb_resize(bs);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -2736,11 +2693,9 @@ int bdrv_set_key(BlockDriverState *bs, const char *key)
|
||||
if (ret < 0) {
|
||||
bs->valid_key = 0;
|
||||
} else if (!bs->valid_key) {
|
||||
/* call the change callback now, we skipped it on open */
|
||||
bs->valid_key = 1;
|
||||
if (bs->blk) {
|
||||
/* call the change callback now, we skipped it on open */
|
||||
blk_dev_change_media_cb(bs->blk, true);
|
||||
}
|
||||
bdrv_parent_cb_change_media(bs, true);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -2907,34 +2862,33 @@ BlockDriverState *bdrv_next_node(BlockDriverState *bs)
|
||||
return QTAILQ_NEXT(bs, node_list);
|
||||
}
|
||||
|
||||
/* Iterates over all top-level BlockDriverStates, i.e. BDSs that are owned by
|
||||
* the monitor or attached to a BlockBackend */
|
||||
BlockDriverState *bdrv_next(BlockDriverState *bs)
|
||||
{
|
||||
if (!bs || bs->blk) {
|
||||
bs = blk_next_root_bs(bs);
|
||||
if (bs) {
|
||||
return bs;
|
||||
}
|
||||
}
|
||||
|
||||
/* Ignore all BDSs that are attached to a BlockBackend here; they have been
|
||||
* handled by the above block already */
|
||||
do {
|
||||
bs = bdrv_next_monitor_owned(bs);
|
||||
} while (bs && bs->blk);
|
||||
return bs;
|
||||
}
|
||||
|
||||
const char *bdrv_get_node_name(const BlockDriverState *bs)
|
||||
{
|
||||
return bs->node_name;
|
||||
}
|
||||
|
||||
const char *bdrv_get_parent_name(const BlockDriverState *bs)
|
||||
{
|
||||
BdrvChild *c;
|
||||
const char *name;
|
||||
|
||||
/* If multiple parents have a name, just pick the first one. */
|
||||
QLIST_FOREACH(c, &bs->parents, next_parent) {
|
||||
if (c->role->get_name) {
|
||||
name = c->role->get_name(c);
|
||||
if (name && *name) {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* TODO check what callers really want: bs->node_name or blk_name() */
|
||||
const char *bdrv_get_device_name(const BlockDriverState *bs)
|
||||
{
|
||||
return bs->blk ? blk_name(bs->blk) : "";
|
||||
return bdrv_get_parent_name(bs) ?: "";
|
||||
}
|
||||
|
||||
/* This can be used to identify nodes that might not have a device
|
||||
@@ -2943,7 +2897,7 @@ const char *bdrv_get_device_name(const BlockDriverState *bs)
|
||||
* absent, then this returns an empty (non-null) string. */
|
||||
const char *bdrv_get_device_or_node_name(const BlockDriverState *bs)
|
||||
{
|
||||
return bs->blk ? blk_name(bs->blk) : bs->node_name;
|
||||
return bdrv_get_parent_name(bs) ?: bs->node_name;
|
||||
}
|
||||
|
||||
int bdrv_get_flags(BlockDriverState *bs)
|
||||
@@ -3239,10 +3193,11 @@ void bdrv_invalidate_cache(BlockDriverState *bs, Error **errp)
|
||||
|
||||
void bdrv_invalidate_cache_all(Error **errp)
|
||||
{
|
||||
BlockDriverState *bs = NULL;
|
||||
BlockDriverState *bs;
|
||||
Error *local_err = NULL;
|
||||
BdrvNextIterator *it = NULL;
|
||||
|
||||
while ((bs = bdrv_next(bs)) != NULL) {
|
||||
while ((it = bdrv_next(it, &bs)) != NULL) {
|
||||
AioContext *aio_context = bdrv_get_aio_context(bs);
|
||||
|
||||
aio_context_acquire(aio_context);
|
||||
@@ -3284,10 +3239,11 @@ static int bdrv_inactivate_recurse(BlockDriverState *bs,
|
||||
int bdrv_inactivate_all(void)
|
||||
{
|
||||
BlockDriverState *bs = NULL;
|
||||
BdrvNextIterator *it = NULL;
|
||||
int ret = 0;
|
||||
int pass;
|
||||
|
||||
while ((bs = bdrv_next(bs)) != NULL) {
|
||||
while ((it = bdrv_next(it, &bs)) != NULL) {
|
||||
aio_context_acquire(bdrv_get_aio_context(bs));
|
||||
}
|
||||
|
||||
@@ -3296,8 +3252,8 @@ int bdrv_inactivate_all(void)
|
||||
* the second pass sets the BDRV_O_INACTIVE flag so that no further write
|
||||
* is allowed. */
|
||||
for (pass = 0; pass < 2; pass++) {
|
||||
bs = NULL;
|
||||
while ((bs = bdrv_next(bs)) != NULL) {
|
||||
it = NULL;
|
||||
while ((it = bdrv_next(it, &bs)) != NULL) {
|
||||
ret = bdrv_inactivate_recurse(bs, pass);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
@@ -3306,8 +3262,8 @@ int bdrv_inactivate_all(void)
|
||||
}
|
||||
|
||||
out:
|
||||
bs = NULL;
|
||||
while ((bs = bdrv_next(bs)) != NULL) {
|
||||
it = NULL;
|
||||
while ((it = bdrv_next(it, &bs)) != NULL) {
|
||||
aio_context_release(bdrv_get_aio_context(bs));
|
||||
}
|
||||
|
||||
@@ -3653,6 +3609,7 @@ AioContext *bdrv_get_aio_context(BlockDriverState *bs)
|
||||
void bdrv_detach_aio_context(BlockDriverState *bs)
|
||||
{
|
||||
BdrvAioNotifier *baf;
|
||||
BdrvChild *child;
|
||||
|
||||
if (!bs->drv) {
|
||||
return;
|
||||
@@ -3662,17 +3619,11 @@ void bdrv_detach_aio_context(BlockDriverState *bs)
|
||||
baf->detach_aio_context(baf->opaque);
|
||||
}
|
||||
|
||||
if (bs->throttle_state) {
|
||||
throttle_timers_detach_aio_context(&bs->throttle_timers);
|
||||
}
|
||||
if (bs->drv->bdrv_detach_aio_context) {
|
||||
bs->drv->bdrv_detach_aio_context(bs);
|
||||
}
|
||||
if (bs->file) {
|
||||
bdrv_detach_aio_context(bs->file->bs);
|
||||
}
|
||||
if (bs->backing) {
|
||||
bdrv_detach_aio_context(bs->backing->bs);
|
||||
QLIST_FOREACH(child, &bs->children, next) {
|
||||
bdrv_detach_aio_context(child->bs);
|
||||
}
|
||||
|
||||
bs->aio_context = NULL;
|
||||
@@ -3682,6 +3633,7 @@ void bdrv_attach_aio_context(BlockDriverState *bs,
|
||||
AioContext *new_context)
|
||||
{
|
||||
BdrvAioNotifier *ban;
|
||||
BdrvChild *child;
|
||||
|
||||
if (!bs->drv) {
|
||||
return;
|
||||
@@ -3689,18 +3641,12 @@ void bdrv_attach_aio_context(BlockDriverState *bs,
|
||||
|
||||
bs->aio_context = new_context;
|
||||
|
||||
if (bs->backing) {
|
||||
bdrv_attach_aio_context(bs->backing->bs, new_context);
|
||||
}
|
||||
if (bs->file) {
|
||||
bdrv_attach_aio_context(bs->file->bs, new_context);
|
||||
QLIST_FOREACH(child, &bs->children, next) {
|
||||
bdrv_attach_aio_context(child->bs, new_context);
|
||||
}
|
||||
if (bs->drv->bdrv_attach_aio_context) {
|
||||
bs->drv->bdrv_attach_aio_context(bs, new_context);
|
||||
}
|
||||
if (bs->throttle_state) {
|
||||
throttle_timers_attach_aio_context(&bs->throttle_timers, new_context);
|
||||
}
|
||||
|
||||
QLIST_FOREACH(ban, &bs->aio_notifiers, list) {
|
||||
ban->attached_aio_context(new_context, ban->opaque);
|
||||
@@ -3806,10 +3752,11 @@ bool bdrv_recurse_is_first_non_filter(BlockDriverState *bs,
|
||||
*/
|
||||
bool bdrv_is_first_non_filter(BlockDriverState *candidate)
|
||||
{
|
||||
BlockDriverState *bs = NULL;
|
||||
BlockDriverState *bs;
|
||||
BdrvNextIterator *it = NULL;
|
||||
|
||||
/* walk down the bs forest recursively */
|
||||
while ((bs = bdrv_next(bs)) != NULL) {
|
||||
while ((it = bdrv_next(it, &bs)) != NULL) {
|
||||
bool perm;
|
||||
|
||||
/* try to recurse in this top level bs */
|
||||
|
||||
+4
-30
@@ -218,15 +218,6 @@ static void backup_set_speed(BlockJob *job, int64_t speed, Error **errp)
|
||||
ratelimit_set_speed(&s->limit, speed / BDRV_SECTOR_SIZE, SLICE_TIME);
|
||||
}
|
||||
|
||||
static void backup_iostatus_reset(BlockJob *job)
|
||||
{
|
||||
BackupBlockJob *s = container_of(job, BackupBlockJob, common);
|
||||
|
||||
if (s->target->blk) {
|
||||
blk_iostatus_reset(s->target->blk);
|
||||
}
|
||||
}
|
||||
|
||||
static void backup_cleanup_sync_bitmap(BackupBlockJob *job, int ret)
|
||||
{
|
||||
BdrvDirtyBitmap *bm;
|
||||
@@ -263,7 +254,6 @@ static const BlockJobDriver backup_job_driver = {
|
||||
.instance_size = sizeof(BackupBlockJob),
|
||||
.job_type = BLOCK_JOB_TYPE_BACKUP,
|
||||
.set_speed = backup_set_speed,
|
||||
.iostatus_reset = backup_iostatus_reset,
|
||||
.commit = backup_commit,
|
||||
.abort = backup_abort,
|
||||
};
|
||||
@@ -272,11 +262,11 @@ static BlockErrorAction backup_error_action(BackupBlockJob *job,
|
||||
bool read, int error)
|
||||
{
|
||||
if (read) {
|
||||
return block_job_error_action(&job->common, job->common.bs,
|
||||
job->on_source_error, true, error);
|
||||
return block_job_error_action(&job->common, job->on_source_error,
|
||||
true, error);
|
||||
} else {
|
||||
return block_job_error_action(&job->common, job->target,
|
||||
job->on_target_error, false, error);
|
||||
return block_job_error_action(&job->common, job->on_target_error,
|
||||
false, error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -388,7 +378,6 @@ static void coroutine_fn backup_run(void *opaque)
|
||||
BackupCompleteData *data;
|
||||
BlockDriverState *bs = job->common.bs;
|
||||
BlockDriverState *target = job->target;
|
||||
BlockdevOnError on_target_error = job->on_target_error;
|
||||
NotifierWithReturn before_write = {
|
||||
.notify = backup_before_write_notify,
|
||||
};
|
||||
@@ -404,11 +393,6 @@ static void coroutine_fn backup_run(void *opaque)
|
||||
|
||||
job->done_bitmap = bitmap_new(end);
|
||||
|
||||
if (target->blk) {
|
||||
blk_set_on_error(target->blk, on_target_error, on_target_error);
|
||||
blk_iostatus_enable(target->blk);
|
||||
}
|
||||
|
||||
bdrv_add_before_write_notifier(bs, &before_write);
|
||||
|
||||
if (job->sync_mode == MIRROR_SYNC_MODE_NONE) {
|
||||
@@ -484,9 +468,6 @@ static void coroutine_fn backup_run(void *opaque)
|
||||
qemu_co_rwlock_unlock(&job->flush_rwlock);
|
||||
g_free(job->done_bitmap);
|
||||
|
||||
if (target->blk) {
|
||||
blk_iostatus_disable(target->blk);
|
||||
}
|
||||
bdrv_op_unblock_all(target, job->common.blocker);
|
||||
|
||||
data = g_malloc(sizeof(*data));
|
||||
@@ -515,13 +496,6 @@ void backup_start(BlockDriverState *bs, BlockDriverState *target,
|
||||
return;
|
||||
}
|
||||
|
||||
if ((on_source_error == BLOCKDEV_ON_ERROR_STOP ||
|
||||
on_source_error == BLOCKDEV_ON_ERROR_ENOSPC) &&
|
||||
(!bs->blk || !blk_iostatus_is_enabled(bs->blk))) {
|
||||
error_setg(errp, QERR_INVALID_PARAMETER, "on-source-error");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!bdrv_is_inserted(bs)) {
|
||||
error_setg(errp, "Device is not inserted: %s",
|
||||
bdrv_get_device_name(bs));
|
||||
|
||||
@@ -293,22 +293,6 @@ static bool blkverify_recurse_is_first_non_filter(BlockDriverState *bs,
|
||||
return bdrv_recurse_is_first_non_filter(s->test_file->bs, candidate);
|
||||
}
|
||||
|
||||
/* Propagate AioContext changes to ->test_file */
|
||||
static void blkverify_detach_aio_context(BlockDriverState *bs)
|
||||
{
|
||||
BDRVBlkverifyState *s = bs->opaque;
|
||||
|
||||
bdrv_detach_aio_context(s->test_file->bs);
|
||||
}
|
||||
|
||||
static void blkverify_attach_aio_context(BlockDriverState *bs,
|
||||
AioContext *new_context)
|
||||
{
|
||||
BDRVBlkverifyState *s = bs->opaque;
|
||||
|
||||
bdrv_attach_aio_context(s->test_file->bs, new_context);
|
||||
}
|
||||
|
||||
static void blkverify_refresh_filename(BlockDriverState *bs, QDict *options)
|
||||
{
|
||||
BDRVBlkverifyState *s = bs->opaque;
|
||||
@@ -356,9 +340,6 @@ static BlockDriver bdrv_blkverify = {
|
||||
.bdrv_aio_writev = blkverify_aio_writev,
|
||||
.bdrv_aio_flush = blkverify_aio_flush,
|
||||
|
||||
.bdrv_attach_aio_context = blkverify_attach_aio_context,
|
||||
.bdrv_detach_aio_context = blkverify_detach_aio_context,
|
||||
|
||||
.is_filter = true,
|
||||
.bdrv_recurse_is_first_non_filter = blkverify_recurse_is_first_non_filter,
|
||||
};
|
||||
|
||||
+191
-62
@@ -34,6 +34,7 @@ struct BlockBackend {
|
||||
DriveInfo *legacy_dinfo; /* null unless created by drive_new() */
|
||||
QTAILQ_ENTRY(BlockBackend) link; /* for block_backends */
|
||||
QTAILQ_ENTRY(BlockBackend) monitor_link; /* for monitor_block_backends */
|
||||
BlockBackendPublic public;
|
||||
|
||||
void *dev; /* attached device model, if any */
|
||||
/* TODO change to DeviceState when all users are qdevified */
|
||||
@@ -74,6 +75,7 @@ static const AIOCBInfo block_backend_aiocb_info = {
|
||||
};
|
||||
|
||||
static void drive_info_del(DriveInfo *dinfo);
|
||||
static BlockBackend *bdrv_first_blk(BlockDriverState *bs);
|
||||
|
||||
/* All BlockBackends */
|
||||
static QTAILQ_HEAD(, BlockBackend) block_backends =
|
||||
@@ -90,9 +92,26 @@ static void blk_root_inherit_options(int *child_flags, QDict *child_options,
|
||||
/* We're not supposed to call this function for root nodes */
|
||||
abort();
|
||||
}
|
||||
static void blk_root_drained_begin(BdrvChild *child);
|
||||
static void blk_root_drained_end(BdrvChild *child);
|
||||
|
||||
static void blk_root_change_media(BdrvChild *child, bool load);
|
||||
static void blk_root_resize(BdrvChild *child);
|
||||
|
||||
static const char *blk_root_get_name(BdrvChild *child)
|
||||
{
|
||||
return blk_name(child->opaque);
|
||||
}
|
||||
|
||||
static const BdrvChildRole child_root = {
|
||||
.inherit_options = blk_root_inherit_options,
|
||||
.inherit_options = blk_root_inherit_options,
|
||||
|
||||
.change_media = blk_root_change_media,
|
||||
.resize = blk_root_resize,
|
||||
.get_name = blk_root_get_name,
|
||||
|
||||
.drained_begin = blk_root_drained_begin,
|
||||
.drained_end = blk_root_drained_end,
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -106,8 +125,12 @@ BlockBackend *blk_new(Error **errp)
|
||||
|
||||
blk = g_new0(BlockBackend, 1);
|
||||
blk->refcnt = 1;
|
||||
qemu_co_queue_init(&blk->public.throttled_reqs[0]);
|
||||
qemu_co_queue_init(&blk->public.throttled_reqs[1]);
|
||||
|
||||
notifier_list_init(&blk->remove_bs_notifiers);
|
||||
notifier_list_init(&blk->insert_bs_notifiers);
|
||||
|
||||
QTAILQ_INSERT_TAIL(&block_backends, blk, link);
|
||||
return blk;
|
||||
}
|
||||
@@ -128,7 +151,7 @@ BlockBackend *blk_new_with_bs(Error **errp)
|
||||
|
||||
bs = bdrv_new_root();
|
||||
blk->root = bdrv_root_attach_child(bs, "root", &child_root);
|
||||
bs->blk = blk;
|
||||
blk->root->opaque = blk;
|
||||
return blk;
|
||||
}
|
||||
|
||||
@@ -177,10 +200,6 @@ static void blk_delete(BlockBackend *blk)
|
||||
}
|
||||
assert(QLIST_EMPTY(&blk->remove_bs_notifiers.notifiers));
|
||||
assert(QLIST_EMPTY(&blk->insert_bs_notifiers.notifiers));
|
||||
if (blk->root_state.throttle_state) {
|
||||
g_free(blk->root_state.throttle_group);
|
||||
throttle_group_unref(blk->root_state.throttle_state);
|
||||
}
|
||||
QTAILQ_REMOVE(&block_backends, blk, link);
|
||||
drive_info_del(blk->legacy_dinfo);
|
||||
block_acct_cleanup(&blk->stats);
|
||||
@@ -267,28 +286,50 @@ BlockBackend *blk_next(BlockBackend *blk)
|
||||
: QTAILQ_FIRST(&monitor_block_backends);
|
||||
}
|
||||
|
||||
/*
|
||||
* Iterates over all BlockDriverStates which are attached to a BlockBackend.
|
||||
* This function is for use by bdrv_next().
|
||||
*
|
||||
* @bs must be NULL or a BDS that is attached to a BB.
|
||||
*/
|
||||
BlockDriverState *blk_next_root_bs(BlockDriverState *bs)
|
||||
{
|
||||
struct BdrvNextIterator {
|
||||
enum {
|
||||
BDRV_NEXT_BACKEND_ROOTS,
|
||||
BDRV_NEXT_MONITOR_OWNED,
|
||||
} phase;
|
||||
BlockBackend *blk;
|
||||
BlockDriverState *bs;
|
||||
};
|
||||
|
||||
if (bs) {
|
||||
assert(bs->blk);
|
||||
blk = bs->blk;
|
||||
} else {
|
||||
blk = NULL;
|
||||
/* Iterates over all top-level BlockDriverStates, i.e. BDSs that are owned by
|
||||
* the monitor or attached to a BlockBackend */
|
||||
BdrvNextIterator *bdrv_next(BdrvNextIterator *it, BlockDriverState **bs)
|
||||
{
|
||||
if (!it) {
|
||||
it = g_new(BdrvNextIterator, 1);
|
||||
*it = (BdrvNextIterator) {
|
||||
.phase = BDRV_NEXT_BACKEND_ROOTS,
|
||||
};
|
||||
}
|
||||
|
||||
do {
|
||||
blk = blk_all_next(blk);
|
||||
} while (blk && !blk->root);
|
||||
/* First, return all root nodes of BlockBackends. In order to avoid
|
||||
* returning a BDS twice when multiple BBs refer to it, we only return it
|
||||
* if the BB is the first one in the parent list of the BDS. */
|
||||
if (it->phase == BDRV_NEXT_BACKEND_ROOTS) {
|
||||
do {
|
||||
it->blk = blk_all_next(it->blk);
|
||||
*bs = it->blk ? blk_bs(it->blk) : NULL;
|
||||
} while (it->blk && (*bs == NULL || bdrv_first_blk(*bs) != it->blk));
|
||||
|
||||
return blk ? blk->root->bs : NULL;
|
||||
if (*bs) {
|
||||
return it;
|
||||
}
|
||||
it->phase = BDRV_NEXT_MONITOR_OWNED;
|
||||
}
|
||||
|
||||
/* Then return the monitor-owned BDSes without a BB attached. Ignore all
|
||||
* BDSes that are attached to a BlockBackend here; they have been handled
|
||||
* by the above block already */
|
||||
do {
|
||||
it->bs = bdrv_next_monitor_owned(it->bs);
|
||||
*bs = it->bs;
|
||||
} while (*bs && bdrv_has_blk(*bs));
|
||||
|
||||
return *bs ? it : NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -375,6 +416,26 @@ BlockDriverState *blk_bs(BlockBackend *blk)
|
||||
return blk->root ? blk->root->bs : NULL;
|
||||
}
|
||||
|
||||
static BlockBackend *bdrv_first_blk(BlockDriverState *bs)
|
||||
{
|
||||
BdrvChild *child;
|
||||
QLIST_FOREACH(child, &bs->parents, next_parent) {
|
||||
if (child->role == &child_root) {
|
||||
return child->opaque;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true if @bs has an associated BlockBackend.
|
||||
*/
|
||||
bool bdrv_has_blk(BlockDriverState *bs)
|
||||
{
|
||||
return bdrv_first_blk(bs) != NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return @blk's DriveInfo if any, else null.
|
||||
*/
|
||||
@@ -410,18 +471,34 @@ BlockBackend *blk_by_legacy_dinfo(DriveInfo *dinfo)
|
||||
abort();
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns a pointer to the publicly accessible fields of @blk.
|
||||
*/
|
||||
BlockBackendPublic *blk_get_public(BlockBackend *blk)
|
||||
{
|
||||
return &blk->public;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns a BlockBackend given the associated @public fields.
|
||||
*/
|
||||
BlockBackend *blk_by_public(BlockBackendPublic *public)
|
||||
{
|
||||
return container_of(public, BlockBackend, public);
|
||||
}
|
||||
|
||||
/*
|
||||
* Disassociates the currently associated BlockDriverState from @blk.
|
||||
*/
|
||||
void blk_remove_bs(BlockBackend *blk)
|
||||
{
|
||||
assert(blk->root->bs->blk == blk);
|
||||
|
||||
notifier_list_notify(&blk->remove_bs_notifiers, blk);
|
||||
if (blk->public.throttle_state) {
|
||||
throttle_timers_detach_aio_context(&blk->public.throttle_timers);
|
||||
}
|
||||
|
||||
blk_update_root_state(blk);
|
||||
|
||||
blk->root->bs->blk = NULL;
|
||||
bdrv_root_unref_child(blk->root);
|
||||
blk->root = NULL;
|
||||
}
|
||||
@@ -431,12 +508,15 @@ void blk_remove_bs(BlockBackend *blk)
|
||||
*/
|
||||
void blk_insert_bs(BlockBackend *blk, BlockDriverState *bs)
|
||||
{
|
||||
assert(!blk->root && !bs->blk);
|
||||
bdrv_ref(bs);
|
||||
blk->root = bdrv_root_attach_child(bs, "root", &child_root);
|
||||
bs->blk = blk;
|
||||
blk->root->opaque = blk;
|
||||
|
||||
notifier_list_notify(&blk->insert_bs_notifiers, blk);
|
||||
if (blk->public.throttle_state) {
|
||||
throttle_timers_attach_aio_context(
|
||||
&blk->public.throttle_timers, bdrv_get_aio_context(bs));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -525,6 +605,11 @@ void blk_dev_change_media_cb(BlockBackend *blk, bool load)
|
||||
}
|
||||
}
|
||||
|
||||
static void blk_root_change_media(BdrvChild *child, bool load)
|
||||
{
|
||||
blk_dev_change_media_cb(child->opaque, load);
|
||||
}
|
||||
|
||||
/*
|
||||
* Does @blk's attached device model have removable media?
|
||||
* %true if no device model is attached.
|
||||
@@ -579,8 +664,10 @@ bool blk_dev_is_medium_locked(BlockBackend *blk)
|
||||
/*
|
||||
* Notify @blk's attached device model of a backend size change.
|
||||
*/
|
||||
void blk_dev_resize_cb(BlockBackend *blk)
|
||||
static void blk_root_resize(BdrvChild *child)
|
||||
{
|
||||
BlockBackend *blk = child->opaque;
|
||||
|
||||
if (blk->dev_ops && blk->dev_ops->resize_cb) {
|
||||
blk->dev_ops->resize_cb(blk->dev_opaque);
|
||||
}
|
||||
@@ -692,6 +779,11 @@ static int coroutine_fn blk_co_preadv(BlockBackend *blk, int64_t offset,
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* throttling disk I/O */
|
||||
if (blk->public.throttle_state) {
|
||||
throttle_group_co_io_limits_intercept(blk, bytes, false);
|
||||
}
|
||||
|
||||
return bdrv_co_preadv(blk_bs(blk), offset, bytes, qiov, flags);
|
||||
}
|
||||
|
||||
@@ -706,6 +798,11 @@ static int coroutine_fn blk_co_pwritev(BlockBackend *blk, int64_t offset,
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* throttling disk I/O */
|
||||
if (blk->public.throttle_state) {
|
||||
throttle_group_co_io_limits_intercept(blk, bytes, true);
|
||||
}
|
||||
|
||||
if (!blk->enable_write_cache) {
|
||||
flags |= BDRV_REQ_FUA;
|
||||
}
|
||||
@@ -775,7 +872,6 @@ static int blk_prw(BlockBackend *blk, int64_t offset, uint8_t *buf,
|
||||
int blk_pread_unthrottled(BlockBackend *blk, int64_t offset, uint8_t *buf,
|
||||
int count)
|
||||
{
|
||||
BlockDriverState *bs = blk_bs(blk);
|
||||
int ret;
|
||||
|
||||
ret = blk_check_byte_request(blk, offset, count);
|
||||
@@ -783,9 +879,9 @@ int blk_pread_unthrottled(BlockBackend *blk, int64_t offset, uint8_t *buf,
|
||||
return ret;
|
||||
}
|
||||
|
||||
bdrv_no_throttling_begin(bs);
|
||||
blk_root_drained_begin(blk->root);
|
||||
ret = blk_pread(blk, offset, buf, count);
|
||||
bdrv_no_throttling_end(bs);
|
||||
blk_root_drained_end(blk->root);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1008,20 +1104,6 @@ void blk_aio_cancel_async(BlockAIOCB *acb)
|
||||
bdrv_aio_cancel_async(acb);
|
||||
}
|
||||
|
||||
int blk_aio_multiwrite(BlockBackend *blk, BlockRequest *reqs, int num_reqs)
|
||||
{
|
||||
int i, ret;
|
||||
|
||||
for (i = 0; i < num_reqs; i++) {
|
||||
ret = blk_check_request(blk, reqs[i].sector, reqs[i].nb_sectors);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return bdrv_aio_multiwrite(blk_bs(blk), reqs, num_reqs);
|
||||
}
|
||||
|
||||
int blk_ioctl(BlockBackend *blk, unsigned long int req, void *buf)
|
||||
{
|
||||
if (!blk_is_available(blk)) {
|
||||
@@ -1334,7 +1416,14 @@ void blk_set_aio_context(BlockBackend *blk, AioContext *new_context)
|
||||
BlockDriverState *bs = blk_bs(blk);
|
||||
|
||||
if (bs) {
|
||||
if (blk->public.throttle_state) {
|
||||
throttle_timers_detach_aio_context(&blk->public.throttle_timers);
|
||||
}
|
||||
bdrv_set_aio_context(bs, new_context);
|
||||
if (blk->public.throttle_state) {
|
||||
throttle_timers_attach_aio_context(&blk->public.throttle_timers,
|
||||
new_context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1499,19 +1588,6 @@ void blk_update_root_state(BlockBackend *blk)
|
||||
blk->root_state.open_flags = blk->root->bs->open_flags;
|
||||
blk->root_state.read_only = blk->root->bs->read_only;
|
||||
blk->root_state.detect_zeroes = blk->root->bs->detect_zeroes;
|
||||
|
||||
if (blk->root_state.throttle_group) {
|
||||
g_free(blk->root_state.throttle_group);
|
||||
throttle_group_unref(blk->root_state.throttle_state);
|
||||
}
|
||||
if (blk->root->bs->throttle_state) {
|
||||
const char *name = throttle_group_get_name(blk->root->bs);
|
||||
blk->root_state.throttle_group = g_strdup(name);
|
||||
blk->root_state.throttle_state = throttle_group_incref(name);
|
||||
} else {
|
||||
blk->root_state.throttle_group = NULL;
|
||||
blk->root_state.throttle_state = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1522,9 +1598,6 @@ void blk_update_root_state(BlockBackend *blk)
|
||||
void blk_apply_root_state(BlockBackend *blk, BlockDriverState *bs)
|
||||
{
|
||||
bs->detect_zeroes = blk->root_state.detect_zeroes;
|
||||
if (blk->root_state.throttle_group) {
|
||||
bdrv_io_limits_enable(bs, blk->root_state.throttle_group);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1587,3 +1660,59 @@ int blk_flush_all(void)
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/* throttling disk I/O limits */
|
||||
void blk_set_io_limits(BlockBackend *blk, ThrottleConfig *cfg)
|
||||
{
|
||||
throttle_group_config(blk, cfg);
|
||||
}
|
||||
|
||||
void blk_io_limits_disable(BlockBackend *blk)
|
||||
{
|
||||
assert(blk->public.throttle_state);
|
||||
bdrv_drained_begin(blk_bs(blk));
|
||||
throttle_group_unregister_blk(blk);
|
||||
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);
|
||||
}
|
||||
|
||||
void blk_io_limits_update_group(BlockBackend *blk, const char *group)
|
||||
{
|
||||
/* this BB is not part of any group */
|
||||
if (!blk->public.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)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* need to change the group this bs belong to */
|
||||
blk_io_limits_disable(blk);
|
||||
blk_io_limits_enable(blk, group);
|
||||
}
|
||||
|
||||
static void blk_root_drained_begin(BdrvChild *child)
|
||||
{
|
||||
BlockBackend *blk = child->opaque;
|
||||
|
||||
if (blk->public.io_limits_disabled++ == 0) {
|
||||
throttle_group_restart_blk(blk);
|
||||
}
|
||||
}
|
||||
|
||||
static void blk_root_drained_end(BdrvChild *child)
|
||||
{
|
||||
BlockBackend *blk = child->opaque;
|
||||
|
||||
assert(blk->public.io_limits_disabled);
|
||||
--blk->public.io_limits_disabled;
|
||||
}
|
||||
|
||||
@@ -214,13 +214,6 @@ void commit_start(BlockDriverState *bs, BlockDriverState *base,
|
||||
BlockDriverState *overlay_bs;
|
||||
Error *local_err = NULL;
|
||||
|
||||
if ((on_error == BLOCKDEV_ON_ERROR_STOP ||
|
||||
on_error == BLOCKDEV_ON_ERROR_ENOSPC) &&
|
||||
(!bs->blk || !blk_iostatus_is_enabled(bs->blk))) {
|
||||
error_setg(errp, "Invalid parameter combination");
|
||||
return;
|
||||
}
|
||||
|
||||
assert(top != bs);
|
||||
if (top == base) {
|
||||
error_setg(errp, "Invalid files for merge: top and base are the same");
|
||||
|
||||
+28
-265
@@ -27,7 +27,6 @@
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "block/blockjob.h"
|
||||
#include "block/block_int.h"
|
||||
#include "block/throttle-groups.h"
|
||||
#include "qemu/cutils.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/error-report.h"
|
||||
@@ -46,56 +45,26 @@ static void coroutine_fn bdrv_co_do_rw(void *opaque);
|
||||
static int coroutine_fn bdrv_co_do_write_zeroes(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors, BdrvRequestFlags flags);
|
||||
|
||||
/* throttling disk I/O limits */
|
||||
void bdrv_set_io_limits(BlockDriverState *bs,
|
||||
ThrottleConfig *cfg)
|
||||
static void bdrv_parent_drained_begin(BlockDriverState *bs)
|
||||
{
|
||||
throttle_group_config(bs, cfg);
|
||||
}
|
||||
BdrvChild *c;
|
||||
|
||||
void bdrv_no_throttling_begin(BlockDriverState *bs)
|
||||
{
|
||||
if (bs->io_limits_disabled++ == 0) {
|
||||
throttle_group_restart_bs(bs);
|
||||
QLIST_FOREACH(c, &bs->parents, next_parent) {
|
||||
if (c->role->drained_begin) {
|
||||
c->role->drained_begin(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void bdrv_no_throttling_end(BlockDriverState *bs)
|
||||
static void bdrv_parent_drained_end(BlockDriverState *bs)
|
||||
{
|
||||
assert(bs->io_limits_disabled);
|
||||
--bs->io_limits_disabled;
|
||||
}
|
||||
BdrvChild *c;
|
||||
|
||||
void bdrv_io_limits_disable(BlockDriverState *bs)
|
||||
{
|
||||
assert(bs->throttle_state);
|
||||
bdrv_no_throttling_begin(bs);
|
||||
throttle_group_unregister_bs(bs);
|
||||
bdrv_no_throttling_end(bs);
|
||||
}
|
||||
|
||||
/* should be called before bdrv_set_io_limits if a limit is set */
|
||||
void bdrv_io_limits_enable(BlockDriverState *bs, const char *group)
|
||||
{
|
||||
assert(!bs->throttle_state);
|
||||
throttle_group_register_bs(bs, group);
|
||||
}
|
||||
|
||||
void bdrv_io_limits_update_group(BlockDriverState *bs, const char *group)
|
||||
{
|
||||
/* this bs is not part of any group */
|
||||
if (!bs->throttle_state) {
|
||||
return;
|
||||
QLIST_FOREACH(c, &bs->parents, next_parent) {
|
||||
if (c->role->drained_end) {
|
||||
c->role->drained_end(c);
|
||||
}
|
||||
}
|
||||
|
||||
/* this bs is a part of the same group than the one we want */
|
||||
if (!g_strcmp0(throttle_group_get_name(bs), group)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* need to change the group this bs belong to */
|
||||
bdrv_io_limits_disable(bs);
|
||||
bdrv_io_limits_enable(bs, group);
|
||||
}
|
||||
|
||||
void bdrv_refresh_limits(BlockDriverState *bs, Error **errp)
|
||||
@@ -182,12 +151,6 @@ bool bdrv_requests_pending(BlockDriverState *bs)
|
||||
if (!QLIST_EMPTY(&bs->tracked_requests)) {
|
||||
return true;
|
||||
}
|
||||
if (!qemu_co_queue_empty(&bs->throttled_reqs[0])) {
|
||||
return true;
|
||||
}
|
||||
if (!qemu_co_queue_empty(&bs->throttled_reqs[1])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
QLIST_FOREACH(child, &bs->children, next) {
|
||||
if (bdrv_requests_pending(child->bs)) {
|
||||
@@ -275,17 +238,17 @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs)
|
||||
*/
|
||||
void coroutine_fn bdrv_co_drain(BlockDriverState *bs)
|
||||
{
|
||||
bdrv_no_throttling_begin(bs);
|
||||
bdrv_parent_drained_begin(bs);
|
||||
bdrv_io_unplugged_begin(bs);
|
||||
bdrv_drain_recurse(bs);
|
||||
bdrv_co_yield_to_drain(bs);
|
||||
bdrv_io_unplugged_end(bs);
|
||||
bdrv_no_throttling_end(bs);
|
||||
bdrv_parent_drained_end(bs);
|
||||
}
|
||||
|
||||
void bdrv_drain(BlockDriverState *bs)
|
||||
{
|
||||
bdrv_no_throttling_begin(bs);
|
||||
bdrv_parent_drained_begin(bs);
|
||||
bdrv_io_unplugged_begin(bs);
|
||||
bdrv_drain_recurse(bs);
|
||||
if (qemu_in_coroutine()) {
|
||||
@@ -294,7 +257,7 @@ void bdrv_drain(BlockDriverState *bs)
|
||||
bdrv_drain_poll(bs);
|
||||
}
|
||||
bdrv_io_unplugged_end(bs);
|
||||
bdrv_no_throttling_end(bs);
|
||||
bdrv_parent_drained_end(bs);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -307,17 +270,18 @@ void bdrv_drain_all(void)
|
||||
{
|
||||
/* Always run first iteration so any pending completion BHs run */
|
||||
bool busy = true;
|
||||
BlockDriverState *bs = NULL;
|
||||
BlockDriverState *bs;
|
||||
BdrvNextIterator *it = NULL;
|
||||
GSList *aio_ctxs = NULL, *ctx;
|
||||
|
||||
while ((bs = bdrv_next(bs))) {
|
||||
while ((it = bdrv_next(it, &bs))) {
|
||||
AioContext *aio_context = bdrv_get_aio_context(bs);
|
||||
|
||||
aio_context_acquire(aio_context);
|
||||
if (bs->job) {
|
||||
block_job_pause(bs->job);
|
||||
}
|
||||
bdrv_no_throttling_begin(bs);
|
||||
bdrv_parent_drained_begin(bs);
|
||||
bdrv_io_unplugged_begin(bs);
|
||||
bdrv_drain_recurse(bs);
|
||||
aio_context_release(aio_context);
|
||||
@@ -338,10 +302,10 @@ void bdrv_drain_all(void)
|
||||
|
||||
for (ctx = aio_ctxs; ctx != NULL; ctx = ctx->next) {
|
||||
AioContext *aio_context = ctx->data;
|
||||
bs = NULL;
|
||||
it = NULL;
|
||||
|
||||
aio_context_acquire(aio_context);
|
||||
while ((bs = bdrv_next(bs))) {
|
||||
while ((it = bdrv_next(it, &bs))) {
|
||||
if (aio_context == bdrv_get_aio_context(bs)) {
|
||||
if (bdrv_requests_pending(bs)) {
|
||||
busy = true;
|
||||
@@ -354,13 +318,13 @@ void bdrv_drain_all(void)
|
||||
}
|
||||
}
|
||||
|
||||
bs = NULL;
|
||||
while ((bs = bdrv_next(bs))) {
|
||||
it = NULL;
|
||||
while ((it = bdrv_next(it, &bs))) {
|
||||
AioContext *aio_context = bdrv_get_aio_context(bs);
|
||||
|
||||
aio_context_acquire(aio_context);
|
||||
bdrv_io_unplugged_end(bs);
|
||||
bdrv_no_throttling_end(bs);
|
||||
bdrv_parent_drained_end(bs);
|
||||
if (bs->job) {
|
||||
block_job_resume(bs->job);
|
||||
}
|
||||
@@ -1069,11 +1033,6 @@ int coroutine_fn bdrv_co_preadv(BlockDriverState *bs,
|
||||
flags |= BDRV_REQ_COPY_ON_READ;
|
||||
}
|
||||
|
||||
/* throttling disk I/O */
|
||||
if (bs->throttle_state) {
|
||||
throttle_group_co_io_limits_intercept(bs, bytes, false);
|
||||
}
|
||||
|
||||
/* Align read if necessary by padding qiov */
|
||||
if (offset & (align - 1)) {
|
||||
head_buf = qemu_blockalign(bs, align);
|
||||
@@ -1430,11 +1389,6 @@ int coroutine_fn bdrv_co_pwritev(BlockDriverState *bs,
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* throttling disk I/O */
|
||||
if (bs->throttle_state) {
|
||||
throttle_group_co_io_limits_intercept(bs, bytes, true);
|
||||
}
|
||||
|
||||
/*
|
||||
* Align write if necessary by performing a read-modify-write cycle.
|
||||
* Pad qiov with the read parts and be sure to have a tracked request not
|
||||
@@ -1925,200 +1879,6 @@ BlockAIOCB *bdrv_aio_write_zeroes(BlockDriverState *bs,
|
||||
cb, opaque, true);
|
||||
}
|
||||
|
||||
|
||||
typedef struct MultiwriteCB {
|
||||
int error;
|
||||
int num_requests;
|
||||
int num_callbacks;
|
||||
struct {
|
||||
BlockCompletionFunc *cb;
|
||||
void *opaque;
|
||||
QEMUIOVector *free_qiov;
|
||||
} callbacks[];
|
||||
} MultiwriteCB;
|
||||
|
||||
static void multiwrite_user_cb(MultiwriteCB *mcb)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < mcb->num_callbacks; i++) {
|
||||
mcb->callbacks[i].cb(mcb->callbacks[i].opaque, mcb->error);
|
||||
if (mcb->callbacks[i].free_qiov) {
|
||||
qemu_iovec_destroy(mcb->callbacks[i].free_qiov);
|
||||
}
|
||||
g_free(mcb->callbacks[i].free_qiov);
|
||||
}
|
||||
}
|
||||
|
||||
static void multiwrite_cb(void *opaque, int ret)
|
||||
{
|
||||
MultiwriteCB *mcb = opaque;
|
||||
|
||||
trace_multiwrite_cb(mcb, ret);
|
||||
|
||||
if (ret < 0 && !mcb->error) {
|
||||
mcb->error = ret;
|
||||
}
|
||||
|
||||
mcb->num_requests--;
|
||||
if (mcb->num_requests == 0) {
|
||||
multiwrite_user_cb(mcb);
|
||||
g_free(mcb);
|
||||
}
|
||||
}
|
||||
|
||||
static int multiwrite_req_compare(const void *a, const void *b)
|
||||
{
|
||||
const BlockRequest *req1 = a, *req2 = b;
|
||||
|
||||
/*
|
||||
* Note that we can't simply subtract req2->sector from req1->sector
|
||||
* here as that could overflow the return value.
|
||||
*/
|
||||
if (req1->sector > req2->sector) {
|
||||
return 1;
|
||||
} else if (req1->sector < req2->sector) {
|
||||
return -1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Takes a bunch of requests and tries to merge them. Returns the number of
|
||||
* requests that remain after merging.
|
||||
*/
|
||||
static int multiwrite_merge(BlockDriverState *bs, BlockRequest *reqs,
|
||||
int num_reqs, MultiwriteCB *mcb)
|
||||
{
|
||||
int i, outidx;
|
||||
|
||||
// Sort requests by start sector
|
||||
qsort(reqs, num_reqs, sizeof(*reqs), &multiwrite_req_compare);
|
||||
|
||||
// Check if adjacent requests touch the same clusters. If so, combine them,
|
||||
// filling up gaps with zero sectors.
|
||||
outidx = 0;
|
||||
for (i = 1; i < num_reqs; i++) {
|
||||
int merge = 0;
|
||||
int64_t oldreq_last = reqs[outidx].sector + reqs[outidx].nb_sectors;
|
||||
|
||||
// Handle exactly sequential writes and overlapping writes.
|
||||
if (reqs[i].sector <= oldreq_last) {
|
||||
merge = 1;
|
||||
}
|
||||
|
||||
if (reqs[outidx].qiov->niov + reqs[i].qiov->niov + 1 >
|
||||
bs->bl.max_iov) {
|
||||
merge = 0;
|
||||
}
|
||||
|
||||
if (bs->bl.max_transfer_length && reqs[outidx].nb_sectors +
|
||||
reqs[i].nb_sectors > bs->bl.max_transfer_length) {
|
||||
merge = 0;
|
||||
}
|
||||
|
||||
if (merge) {
|
||||
size_t size;
|
||||
QEMUIOVector *qiov = g_malloc0(sizeof(*qiov));
|
||||
qemu_iovec_init(qiov,
|
||||
reqs[outidx].qiov->niov + reqs[i].qiov->niov + 1);
|
||||
|
||||
// Add the first request to the merged one. If the requests are
|
||||
// overlapping, drop the last sectors of the first request.
|
||||
size = (reqs[i].sector - reqs[outidx].sector) << 9;
|
||||
qemu_iovec_concat(qiov, reqs[outidx].qiov, 0, size);
|
||||
|
||||
// We should need to add any zeros between the two requests
|
||||
assert (reqs[i].sector <= oldreq_last);
|
||||
|
||||
// Add the second request
|
||||
qemu_iovec_concat(qiov, reqs[i].qiov, 0, reqs[i].qiov->size);
|
||||
|
||||
// Add tail of first request, if necessary
|
||||
if (qiov->size < reqs[outidx].qiov->size) {
|
||||
qemu_iovec_concat(qiov, reqs[outidx].qiov, qiov->size,
|
||||
reqs[outidx].qiov->size - qiov->size);
|
||||
}
|
||||
|
||||
reqs[outidx].nb_sectors = qiov->size >> 9;
|
||||
reqs[outidx].qiov = qiov;
|
||||
|
||||
mcb->callbacks[i].free_qiov = reqs[outidx].qiov;
|
||||
} else {
|
||||
outidx++;
|
||||
reqs[outidx].sector = reqs[i].sector;
|
||||
reqs[outidx].nb_sectors = reqs[i].nb_sectors;
|
||||
reqs[outidx].qiov = reqs[i].qiov;
|
||||
}
|
||||
}
|
||||
|
||||
if (bs->blk) {
|
||||
block_acct_merge_done(blk_get_stats(bs->blk), BLOCK_ACCT_WRITE,
|
||||
num_reqs - outidx - 1);
|
||||
}
|
||||
|
||||
return outidx + 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Submit multiple AIO write requests at once.
|
||||
*
|
||||
* On success, the function returns 0 and all requests in the reqs array have
|
||||
* been submitted. In error case this function returns -1, and any of the
|
||||
* requests may or may not be submitted yet. In particular, this means that the
|
||||
* callback will be called for some of the requests, for others it won't. The
|
||||
* caller must check the error field of the BlockRequest to wait for the right
|
||||
* callbacks (if error != 0, no callback will be called).
|
||||
*
|
||||
* The implementation may modify the contents of the reqs array, e.g. to merge
|
||||
* requests. However, the fields opaque and error are left unmodified as they
|
||||
* are used to signal failure for a single request to the caller.
|
||||
*/
|
||||
int bdrv_aio_multiwrite(BlockDriverState *bs, BlockRequest *reqs, int num_reqs)
|
||||
{
|
||||
MultiwriteCB *mcb;
|
||||
int i;
|
||||
|
||||
/* don't submit writes if we don't have a medium */
|
||||
if (bs->drv == NULL) {
|
||||
for (i = 0; i < num_reqs; i++) {
|
||||
reqs[i].error = -ENOMEDIUM;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (num_reqs == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Create MultiwriteCB structure
|
||||
mcb = g_malloc0(sizeof(*mcb) + num_reqs * sizeof(*mcb->callbacks));
|
||||
mcb->num_requests = 0;
|
||||
mcb->num_callbacks = num_reqs;
|
||||
|
||||
for (i = 0; i < num_reqs; i++) {
|
||||
mcb->callbacks[i].cb = reqs[i].cb;
|
||||
mcb->callbacks[i].opaque = reqs[i].opaque;
|
||||
}
|
||||
|
||||
// Check for mergable requests
|
||||
num_reqs = multiwrite_merge(bs, reqs, num_reqs, mcb);
|
||||
|
||||
trace_bdrv_aio_multiwrite(mcb, mcb->num_callbacks, num_reqs);
|
||||
|
||||
/* Run the aio requests. */
|
||||
mcb->num_requests = num_reqs;
|
||||
for (i = 0; i < num_reqs; i++) {
|
||||
bdrv_co_aio_rw_vector(bs, reqs[i].sector, reqs[i].qiov,
|
||||
reqs[i].nb_sectors, reqs[i].flags,
|
||||
multiwrite_cb, mcb,
|
||||
true);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void bdrv_aio_cancel(BlockAIOCB *acb)
|
||||
{
|
||||
qemu_aio_ref(acb);
|
||||
@@ -2789,11 +2549,14 @@ void bdrv_drained_begin(BlockDriverState *bs)
|
||||
if (!bs->quiesce_counter++) {
|
||||
aio_disable_external(bdrv_get_aio_context(bs));
|
||||
}
|
||||
bdrv_parent_drained_begin(bs);
|
||||
bdrv_drain(bs);
|
||||
}
|
||||
|
||||
void bdrv_drained_end(BlockDriverState *bs)
|
||||
{
|
||||
bdrv_parent_drained_end(bs);
|
||||
|
||||
assert(bs->quiesce_counter > 0);
|
||||
if (--bs->quiesce_counter > 0) {
|
||||
return;
|
||||
|
||||
+6
-32
@@ -80,11 +80,11 @@ static BlockErrorAction mirror_error_action(MirrorBlockJob *s, bool read,
|
||||
{
|
||||
s->synced = false;
|
||||
if (read) {
|
||||
return block_job_error_action(&s->common, s->common.bs,
|
||||
s->on_source_error, true, error);
|
||||
return block_job_error_action(&s->common, s->on_source_error,
|
||||
true, error);
|
||||
} else {
|
||||
return block_job_error_action(&s->common, s->target,
|
||||
s->on_target_error, false, error);
|
||||
return block_job_error_action(&s->common, s->on_target_error,
|
||||
false, error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -468,7 +468,7 @@ static void mirror_exit(BlockJob *job, void *opaque)
|
||||
|
||||
/* This was checked in mirror_start_job(), but meanwhile one of the
|
||||
* nodes could have been newly attached to a BlockBackend. */
|
||||
if (to_replace->blk && s->target->blk) {
|
||||
if (bdrv_has_blk(to_replace) && bdrv_has_blk(s->target)) {
|
||||
error_report("block job: Can't create node with two BlockBackends");
|
||||
data->ret = -EINVAL;
|
||||
goto out;
|
||||
@@ -710,9 +710,6 @@ immediate_exit:
|
||||
g_free(s->cow_bitmap);
|
||||
g_free(s->in_flight_bitmap);
|
||||
bdrv_release_dirty_bitmap(bs, s->dirty_bitmap);
|
||||
if (s->target->blk) {
|
||||
blk_iostatus_disable(s->target->blk);
|
||||
}
|
||||
|
||||
data = g_malloc(sizeof(*data));
|
||||
data->ret = ret;
|
||||
@@ -739,15 +736,6 @@ static void mirror_set_speed(BlockJob *job, int64_t speed, Error **errp)
|
||||
ratelimit_set_speed(&s->limit, speed / BDRV_SECTOR_SIZE, SLICE_TIME);
|
||||
}
|
||||
|
||||
static void mirror_iostatus_reset(BlockJob *job)
|
||||
{
|
||||
MirrorBlockJob *s = container_of(job, MirrorBlockJob, common);
|
||||
|
||||
if (s->target->blk) {
|
||||
blk_iostatus_reset(s->target->blk);
|
||||
}
|
||||
}
|
||||
|
||||
static void mirror_complete(BlockJob *job, Error **errp)
|
||||
{
|
||||
MirrorBlockJob *s = container_of(job, MirrorBlockJob, common);
|
||||
@@ -793,7 +781,6 @@ static const BlockJobDriver mirror_job_driver = {
|
||||
.instance_size = sizeof(MirrorBlockJob),
|
||||
.job_type = BLOCK_JOB_TYPE_MIRROR,
|
||||
.set_speed = mirror_set_speed,
|
||||
.iostatus_reset= mirror_iostatus_reset,
|
||||
.complete = mirror_complete,
|
||||
};
|
||||
|
||||
@@ -801,8 +788,6 @@ static const BlockJobDriver commit_active_job_driver = {
|
||||
.instance_size = sizeof(MirrorBlockJob),
|
||||
.job_type = BLOCK_JOB_TYPE_COMMIT,
|
||||
.set_speed = mirror_set_speed,
|
||||
.iostatus_reset
|
||||
= mirror_iostatus_reset,
|
||||
.complete = mirror_complete,
|
||||
};
|
||||
|
||||
@@ -827,13 +812,6 @@ static void mirror_start_job(BlockDriverState *bs, BlockDriverState *target,
|
||||
|
||||
assert ((granularity & (granularity - 1)) == 0);
|
||||
|
||||
if ((on_source_error == BLOCKDEV_ON_ERROR_STOP ||
|
||||
on_source_error == BLOCKDEV_ON_ERROR_ENOSPC) &&
|
||||
(!bs->blk || !blk_iostatus_is_enabled(bs->blk))) {
|
||||
error_setg(errp, QERR_INVALID_PARAMETER, "on-source-error");
|
||||
return;
|
||||
}
|
||||
|
||||
if (buf_size < 0) {
|
||||
error_setg(errp, "Invalid parameter 'buf-size'");
|
||||
return;
|
||||
@@ -853,7 +831,7 @@ static void mirror_start_job(BlockDriverState *bs, BlockDriverState *target,
|
||||
} else {
|
||||
replaced_bs = bs;
|
||||
}
|
||||
if (replaced_bs->blk && target->blk) {
|
||||
if (bdrv_has_blk(replaced_bs) && bdrv_has_blk(target)) {
|
||||
error_setg(errp, "Can't create node with two BlockBackends");
|
||||
return;
|
||||
}
|
||||
@@ -882,10 +860,6 @@ static void mirror_start_job(BlockDriverState *bs, BlockDriverState *target,
|
||||
|
||||
bdrv_op_block_all(s->target, s->common.blocker);
|
||||
|
||||
if (s->target->blk) {
|
||||
blk_set_on_error(s->target->blk, on_target_error, on_target_error);
|
||||
blk_iostatus_enable(s->target->blk);
|
||||
}
|
||||
s->common.co = qemu_coroutine_create(mirror_run);
|
||||
trace_mirror_start(bs, s, s->common.co, opaque);
|
||||
qemu_coroutine_enter(s->common.co, s);
|
||||
|
||||
+3
-3
@@ -67,10 +67,10 @@ BlockDeviceInfo *bdrv_block_device_info(BlockBackend *blk,
|
||||
info->backing_file_depth = bdrv_get_backing_file_depth(bs);
|
||||
info->detect_zeroes = bs->detect_zeroes;
|
||||
|
||||
if (bs->throttle_state) {
|
||||
if (blk && blk_get_public(blk)->throttle_state) {
|
||||
ThrottleConfig cfg;
|
||||
|
||||
throttle_group_get_config(bs, &cfg);
|
||||
throttle_group_get_config(blk, &cfg);
|
||||
|
||||
info->bps = cfg.buckets[THROTTLE_BPS_TOTAL].avg;
|
||||
info->bps_rd = cfg.buckets[THROTTLE_BPS_READ].avg;
|
||||
@@ -118,7 +118,7 @@ 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(bs));
|
||||
info->group = g_strdup(throttle_group_get_name(blk));
|
||||
}
|
||||
|
||||
info->write_threshold = bdrv_write_threshold_get(bs);
|
||||
|
||||
+2
-1
@@ -2413,7 +2413,7 @@ static bool is_zero_cluster(BlockDriverState *bs, int64_t start)
|
||||
BlockDriverState *file;
|
||||
int64_t res = bdrv_get_block_status_above(bs, NULL, start,
|
||||
s->cluster_sectors, &nr, &file);
|
||||
return res >= 0 && ((res & BDRV_BLOCK_ZERO) || !(res & BDRV_BLOCK_DATA));
|
||||
return res >= 0 && (res & BDRV_BLOCK_ZERO) && nr == s->cluster_sectors;
|
||||
}
|
||||
|
||||
static bool is_zero_cluster_top_locked(BlockDriverState *bs, int64_t start)
|
||||
@@ -2424,6 +2424,7 @@ static bool is_zero_cluster_top_locked(BlockDriverState *bs, int64_t start)
|
||||
int ret;
|
||||
|
||||
ret = qcow2_get_cluster_offset(bs, start << BDRV_SECTOR_BITS, &nr, &off);
|
||||
assert(nr == s->cluster_sectors);
|
||||
return ret == QCOW2_CLUSTER_UNALLOCATED || ret == QCOW2_CLUSTER_ZERO;
|
||||
}
|
||||
|
||||
|
||||
@@ -989,27 +989,6 @@ static void quorum_close(BlockDriverState *bs)
|
||||
g_free(s->children);
|
||||
}
|
||||
|
||||
static void quorum_detach_aio_context(BlockDriverState *bs)
|
||||
{
|
||||
BDRVQuorumState *s = bs->opaque;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < s->num_children; i++) {
|
||||
bdrv_detach_aio_context(s->children[i]->bs);
|
||||
}
|
||||
}
|
||||
|
||||
static void quorum_attach_aio_context(BlockDriverState *bs,
|
||||
AioContext *new_context)
|
||||
{
|
||||
BDRVQuorumState *s = bs->opaque;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < s->num_children; i++) {
|
||||
bdrv_attach_aio_context(s->children[i]->bs, new_context);
|
||||
}
|
||||
}
|
||||
|
||||
static void quorum_add_child(BlockDriverState *bs, BlockDriverState *child_bs,
|
||||
Error **errp)
|
||||
{
|
||||
@@ -1127,9 +1106,6 @@ static BlockDriver bdrv_quorum = {
|
||||
.bdrv_aio_readv = quorum_aio_readv,
|
||||
.bdrv_aio_writev = quorum_aio_writev,
|
||||
|
||||
.bdrv_detach_aio_context = quorum_detach_aio_context,
|
||||
.bdrv_attach_aio_context = quorum_attach_aio_context,
|
||||
|
||||
.bdrv_add_child = quorum_add_child,
|
||||
.bdrv_del_child = quorum_del_child,
|
||||
|
||||
|
||||
+18
-12
@@ -373,9 +373,10 @@ int bdrv_snapshot_load_tmp_by_id_or_name(BlockDriverState *bs,
|
||||
bool bdrv_all_can_snapshot(BlockDriverState **first_bad_bs)
|
||||
{
|
||||
bool ok = true;
|
||||
BlockDriverState *bs = NULL;
|
||||
BlockDriverState *bs;
|
||||
BdrvNextIterator *it = NULL;
|
||||
|
||||
while (ok && (bs = bdrv_next(bs))) {
|
||||
while (ok && (it = bdrv_next(it, &bs))) {
|
||||
AioContext *ctx = bdrv_get_aio_context(bs);
|
||||
|
||||
aio_context_acquire(ctx);
|
||||
@@ -393,10 +394,11 @@ int bdrv_all_delete_snapshot(const char *name, BlockDriverState **first_bad_bs,
|
||||
Error **err)
|
||||
{
|
||||
int ret = 0;
|
||||
BlockDriverState *bs = NULL;
|
||||
BlockDriverState *bs;
|
||||
BdrvNextIterator *it = NULL;
|
||||
QEMUSnapshotInfo sn1, *snapshot = &sn1;
|
||||
|
||||
while (ret == 0 && (bs = bdrv_next(bs))) {
|
||||
while (ret == 0 && (it = bdrv_next(it, &bs))) {
|
||||
AioContext *ctx = bdrv_get_aio_context(bs);
|
||||
|
||||
aio_context_acquire(ctx);
|
||||
@@ -415,9 +417,10 @@ int bdrv_all_delete_snapshot(const char *name, BlockDriverState **first_bad_bs,
|
||||
int bdrv_all_goto_snapshot(const char *name, BlockDriverState **first_bad_bs)
|
||||
{
|
||||
int err = 0;
|
||||
BlockDriverState *bs = NULL;
|
||||
BlockDriverState *bs;
|
||||
BdrvNextIterator *it = NULL;
|
||||
|
||||
while (err == 0 && (bs = bdrv_next(bs))) {
|
||||
while (err == 0 && (it = bdrv_next(it, &bs))) {
|
||||
AioContext *ctx = bdrv_get_aio_context(bs);
|
||||
|
||||
aio_context_acquire(ctx);
|
||||
@@ -435,9 +438,10 @@ int bdrv_all_find_snapshot(const char *name, BlockDriverState **first_bad_bs)
|
||||
{
|
||||
QEMUSnapshotInfo sn;
|
||||
int err = 0;
|
||||
BlockDriverState *bs = NULL;
|
||||
BlockDriverState *bs;
|
||||
BdrvNextIterator *it = NULL;
|
||||
|
||||
while (err == 0 && (bs = bdrv_next(bs))) {
|
||||
while (err == 0 && (it = bdrv_next(it, &bs))) {
|
||||
AioContext *ctx = bdrv_get_aio_context(bs);
|
||||
|
||||
aio_context_acquire(ctx);
|
||||
@@ -457,9 +461,10 @@ int bdrv_all_create_snapshot(QEMUSnapshotInfo *sn,
|
||||
BlockDriverState **first_bad_bs)
|
||||
{
|
||||
int err = 0;
|
||||
BlockDriverState *bs = NULL;
|
||||
BlockDriverState *bs;
|
||||
BdrvNextIterator *it = NULL;
|
||||
|
||||
while (err == 0 && (bs = bdrv_next(bs))) {
|
||||
while (err == 0 && (it = bdrv_next(it, &bs))) {
|
||||
AioContext *ctx = bdrv_get_aio_context(bs);
|
||||
|
||||
aio_context_acquire(ctx);
|
||||
@@ -480,9 +485,10 @@ int bdrv_all_create_snapshot(QEMUSnapshotInfo *sn,
|
||||
BlockDriverState *bdrv_all_find_vmstate_bs(void)
|
||||
{
|
||||
bool not_found = true;
|
||||
BlockDriverState *bs = NULL;
|
||||
BlockDriverState *bs;
|
||||
BdrvNextIterator *it = NULL;
|
||||
|
||||
while (not_found && (bs = bdrv_next(bs))) {
|
||||
while (not_found && (it = bdrv_next(it, &bs))) {
|
||||
AioContext *ctx = bdrv_get_aio_context(bs);
|
||||
|
||||
aio_context_acquire(ctx);
|
||||
|
||||
+1
-9
@@ -163,8 +163,7 @@ wait:
|
||||
}
|
||||
if (ret < 0) {
|
||||
BlockErrorAction action =
|
||||
block_job_error_action(&s->common, s->common.bs, s->on_error,
|
||||
true, -ret);
|
||||
block_job_error_action(&s->common, s->on_error, true, -ret);
|
||||
if (action == BLOCK_ERROR_ACTION_STOP) {
|
||||
n = 0;
|
||||
continue;
|
||||
@@ -224,13 +223,6 @@ void stream_start(BlockDriverState *bs, BlockDriverState *base,
|
||||
{
|
||||
StreamBlockJob *s;
|
||||
|
||||
if ((on_error == BLOCKDEV_ON_ERROR_STOP ||
|
||||
on_error == BLOCKDEV_ON_ERROR_ENOSPC) &&
|
||||
(!bs->blk || !blk_iostatus_is_enabled(bs->blk))) {
|
||||
error_setg(errp, QERR_INVALID_PARAMETER, "on-error");
|
||||
return;
|
||||
}
|
||||
|
||||
s = block_job_create(&stream_job_driver, bs, speed, cb, opaque, errp);
|
||||
if (!s) {
|
||||
return;
|
||||
|
||||
+126
-118
@@ -23,13 +23,14 @@
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "block/throttle-groups.h"
|
||||
#include "qemu/queue.h"
|
||||
#include "qemu/thread.h"
|
||||
#include "sysemu/qtest.h"
|
||||
|
||||
/* The ThrottleGroup structure (with its ThrottleState) is shared
|
||||
* among different BlockDriverState and it's independent from
|
||||
* among different BlockBackends and it's independent from
|
||||
* AioContext, so in order to use it from different threads it needs
|
||||
* its own locking.
|
||||
*
|
||||
@@ -39,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, BlockDriverState has
|
||||
* In addition to the ThrottleGroup structure, BlockBackendPublic 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 BDS is
|
||||
* registered in a group those fields can be accessed by other threads
|
||||
* any time.
|
||||
* therefore also need to be protected by this lock. Once a
|
||||
* BlockBackend 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
|
||||
* bdrv_set_aio_context()). Therefore in this file a thread will
|
||||
* access some other BDS's timers only after verifying that that BDS
|
||||
* has throttled requests in the queue.
|
||||
* access some other BlockBackend's timers only after verifying that
|
||||
* that BlockBackend 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(, BlockDriverState) head;
|
||||
BlockDriverState *tokens[2];
|
||||
QLIST_HEAD(, BlockBackendPublic) head;
|
||||
BlockBackend *tokens[2];
|
||||
bool any_timer_armed[2];
|
||||
|
||||
/* These two are protected by the global throttle_groups_lock */
|
||||
@@ -132,94 +133,95 @@ void throttle_group_unref(ThrottleState *ts)
|
||||
qemu_mutex_unlock(&throttle_groups_lock);
|
||||
}
|
||||
|
||||
/* Get the name from a BlockDriverState's ThrottleGroup. The name (and
|
||||
* the pointer) is guaranteed to remain constant during the lifetime
|
||||
* of the group.
|
||||
/* Get the name from a BlockBackend's ThrottleGroup. The name (and the pointer)
|
||||
* is guaranteed to remain constant during the lifetime of the group.
|
||||
*
|
||||
* @bs: a BlockDriverState that is member of a throttling group
|
||||
* @blk: a BlockBackend that is member of a throttling group
|
||||
* @ret: the name of the group.
|
||||
*/
|
||||
const char *throttle_group_get_name(BlockDriverState *bs)
|
||||
const char *throttle_group_get_name(BlockBackend *blk)
|
||||
{
|
||||
ThrottleGroup *tg = container_of(bs->throttle_state, ThrottleGroup, ts);
|
||||
BlockBackendPublic *blkp = blk_get_public(blk);
|
||||
ThrottleGroup *tg = container_of(blkp->throttle_state, ThrottleGroup, ts);
|
||||
return tg->name;
|
||||
}
|
||||
|
||||
/* Return the next BlockDriverState in the round-robin sequence,
|
||||
* simulating a circular list.
|
||||
/* Return the next BlockBackend in the round-robin sequence, simulating a
|
||||
* circular list.
|
||||
*
|
||||
* This assumes that tg->lock is held.
|
||||
*
|
||||
* @bs: the current BlockDriverState
|
||||
* @ret: the next BlockDriverState in the sequence
|
||||
* @blk: the current BlockBackend
|
||||
* @ret: the next BlockBackend in the sequence
|
||||
*/
|
||||
static BlockDriverState *throttle_group_next_bs(BlockDriverState *bs)
|
||||
static BlockBackend *throttle_group_next_blk(BlockBackend *blk)
|
||||
{
|
||||
ThrottleState *ts = bs->throttle_state;
|
||||
BlockBackendPublic *blkp = blk_get_public(blk);
|
||||
ThrottleState *ts = blkp->throttle_state;
|
||||
ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
|
||||
BlockDriverState *next = QLIST_NEXT(bs, round_robin);
|
||||
BlockBackendPublic *next = QLIST_NEXT(blkp, round_robin);
|
||||
|
||||
if (!next) {
|
||||
return QLIST_FIRST(&tg->head);
|
||||
next = QLIST_FIRST(&tg->head);
|
||||
}
|
||||
|
||||
return next;
|
||||
return blk_by_public(next);
|
||||
}
|
||||
|
||||
/* Return the next BlockDriverState in the round-robin sequence with
|
||||
* pending I/O requests.
|
||||
/* Return the next BlockBackend in the round-robin sequence with pending I/O
|
||||
* requests.
|
||||
*
|
||||
* This assumes that tg->lock is held.
|
||||
*
|
||||
* @bs: the current BlockDriverState
|
||||
* @blk: the current BlockBackend
|
||||
* @is_write: the type of operation (read/write)
|
||||
* @ret: the next BlockDriverState with pending requests, or bs
|
||||
* if there is none.
|
||||
* @ret: the next BlockBackend with pending requests, or blk if there is
|
||||
* none.
|
||||
*/
|
||||
static BlockDriverState *next_throttle_token(BlockDriverState *bs,
|
||||
bool is_write)
|
||||
static BlockBackend *next_throttle_token(BlockBackend *blk, bool is_write)
|
||||
{
|
||||
ThrottleGroup *tg = container_of(bs->throttle_state, ThrottleGroup, ts);
|
||||
BlockDriverState *token, *start;
|
||||
BlockBackendPublic *blkp = blk_get_public(blk);
|
||||
ThrottleGroup *tg = container_of(blkp->throttle_state, ThrottleGroup, ts);
|
||||
BlockBackend *token, *start;
|
||||
|
||||
start = token = tg->tokens[is_write];
|
||||
|
||||
/* get next bs round in round robin style */
|
||||
token = throttle_group_next_bs(token);
|
||||
while (token != start && !token->pending_reqs[is_write]) {
|
||||
token = throttle_group_next_bs(token);
|
||||
token = throttle_group_next_blk(token);
|
||||
while (token != start && !blkp->pending_reqs[is_write]) {
|
||||
token = throttle_group_next_blk(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.
|
||||
*/
|
||||
if (token == start && !token->pending_reqs[is_write]) {
|
||||
token = bs;
|
||||
if (token == start && !blkp->pending_reqs[is_write]) {
|
||||
token = blk;
|
||||
}
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
/* Check if the next I/O request for a BlockDriverState 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 BlockBackend 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.
|
||||
*
|
||||
* @bs: the current BlockDriverState
|
||||
* @blk: the current BlockBackend
|
||||
* @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(BlockDriverState *bs,
|
||||
bool is_write)
|
||||
static bool throttle_group_schedule_timer(BlockBackend *blk, bool is_write)
|
||||
{
|
||||
ThrottleState *ts = bs->throttle_state;
|
||||
ThrottleTimers *tt = &bs->throttle_timers;
|
||||
BlockBackendPublic *blkp = blk_get_public(blk);
|
||||
ThrottleState *ts = blkp->throttle_state;
|
||||
ThrottleTimers *tt = &blkp->throttle_timers;
|
||||
ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
|
||||
bool must_wait;
|
||||
|
||||
if (bs->io_limits_disabled) {
|
||||
if (blkp->io_limits_disabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -230,9 +232,9 @@ static bool throttle_group_schedule_timer(BlockDriverState *bs,
|
||||
|
||||
must_wait = throttle_schedule_timer(ts, tt, is_write);
|
||||
|
||||
/* If a timer just got armed, set bs as the current token */
|
||||
/* If a timer just got armed, set blk as the current token */
|
||||
if (must_wait) {
|
||||
tg->tokens[is_write] = bs;
|
||||
tg->tokens[is_write] = blk;
|
||||
tg->any_timer_armed[is_write] = true;
|
||||
}
|
||||
|
||||
@@ -243,18 +245,19 @@ static bool throttle_group_schedule_timer(BlockDriverState *bs,
|
||||
*
|
||||
* This assumes that tg->lock is held.
|
||||
*
|
||||
* @bs: the current BlockDriverState
|
||||
* @blk: the current BlockBackend
|
||||
* @is_write: the type of operation (read/write)
|
||||
*/
|
||||
static void schedule_next_request(BlockDriverState *bs, bool is_write)
|
||||
static void schedule_next_request(BlockBackend *blk, bool is_write)
|
||||
{
|
||||
ThrottleGroup *tg = container_of(bs->throttle_state, ThrottleGroup, ts);
|
||||
BlockBackendPublic *blkp = blk_get_public(blk);
|
||||
ThrottleGroup *tg = container_of(blkp->throttle_state, ThrottleGroup, ts);
|
||||
bool must_wait;
|
||||
BlockDriverState *token;
|
||||
BlockBackend *token;
|
||||
|
||||
/* Check if there's any pending request to schedule next */
|
||||
token = next_throttle_token(bs, is_write);
|
||||
if (!token->pending_reqs[is_write]) {
|
||||
token = next_throttle_token(blk, is_write);
|
||||
if (!blkp->pending_reqs[is_write]) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -263,12 +266,12 @@ static void schedule_next_request(BlockDriverState *bs, 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 bs */
|
||||
/* Give preference to requests from the current blk */
|
||||
if (qemu_in_coroutine() &&
|
||||
qemu_co_queue_next(&bs->throttled_reqs[is_write])) {
|
||||
token = bs;
|
||||
qemu_co_queue_next(&blkp->throttled_reqs[is_write])) {
|
||||
token = blk;
|
||||
} else {
|
||||
ThrottleTimers *tt = &token->throttle_timers;
|
||||
ThrottleTimers *tt = &blkp->throttle_timers;
|
||||
int64_t now = qemu_clock_get_ns(tt->clock_type);
|
||||
timer_mod(tt->timers[is_write], now + 1);
|
||||
tg->any_timer_armed[is_write] = true;
|
||||
@@ -281,48 +284,50 @@ static void schedule_next_request(BlockDriverState *bs, bool is_write)
|
||||
* if necessary, and schedule the next request using a round robin
|
||||
* algorithm.
|
||||
*
|
||||
* @bs: the current BlockDriverState
|
||||
* @blk: the current BlockBackend
|
||||
* @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(BlockDriverState *bs,
|
||||
void coroutine_fn throttle_group_co_io_limits_intercept(BlockBackend *blk,
|
||||
unsigned int bytes,
|
||||
bool is_write)
|
||||
{
|
||||
bool must_wait;
|
||||
BlockDriverState *token;
|
||||
BlockBackend *token;
|
||||
|
||||
ThrottleGroup *tg = container_of(bs->throttle_state, ThrottleGroup, ts);
|
||||
BlockBackendPublic *blkp = blk_get_public(blk);
|
||||
ThrottleGroup *tg = container_of(blkp->throttle_state, ThrottleGroup, ts);
|
||||
qemu_mutex_lock(&tg->lock);
|
||||
|
||||
/* First we check if this I/O has to be throttled. */
|
||||
token = next_throttle_token(bs, is_write);
|
||||
token = next_throttle_token(blk, 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 || bs->pending_reqs[is_write]) {
|
||||
bs->pending_reqs[is_write]++;
|
||||
if (must_wait || blkp->pending_reqs[is_write]) {
|
||||
blkp->pending_reqs[is_write]++;
|
||||
qemu_mutex_unlock(&tg->lock);
|
||||
qemu_co_queue_wait(&bs->throttled_reqs[is_write]);
|
||||
qemu_co_queue_wait(&blkp->throttled_reqs[is_write]);
|
||||
qemu_mutex_lock(&tg->lock);
|
||||
bs->pending_reqs[is_write]--;
|
||||
blkp->pending_reqs[is_write]--;
|
||||
}
|
||||
|
||||
/* The I/O will be executed, so do the accounting */
|
||||
throttle_account(bs->throttle_state, is_write, bytes);
|
||||
throttle_account(blkp->throttle_state, is_write, bytes);
|
||||
|
||||
/* Schedule the next request */
|
||||
schedule_next_request(bs, is_write);
|
||||
schedule_next_request(blk, is_write);
|
||||
|
||||
qemu_mutex_unlock(&tg->lock);
|
||||
}
|
||||
|
||||
void throttle_group_restart_bs(BlockDriverState *bs)
|
||||
void throttle_group_restart_blk(BlockBackend *blk)
|
||||
{
|
||||
BlockBackendPublic *blkp = blk_get_public(blk);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
while (qemu_co_enter_next(&bs->throttled_reqs[i])) {
|
||||
while (qemu_co_enter_next(&blkp->throttled_reqs[i])) {
|
||||
;
|
||||
}
|
||||
}
|
||||
@@ -332,13 +337,14 @@ void throttle_group_restart_bs(BlockDriverState *bs)
|
||||
* to throttle_config(), but guarantees atomicity within the
|
||||
* throttling group.
|
||||
*
|
||||
* @bs: a BlockDriverState that is member of the group
|
||||
* @blk: a BlockBackend that is a member of the group
|
||||
* @cfg: the configuration to set
|
||||
*/
|
||||
void throttle_group_config(BlockDriverState *bs, ThrottleConfig *cfg)
|
||||
void throttle_group_config(BlockBackend *blk, ThrottleConfig *cfg)
|
||||
{
|
||||
ThrottleTimers *tt = &bs->throttle_timers;
|
||||
ThrottleState *ts = bs->throttle_state;
|
||||
BlockBackendPublic *blkp = blk_get_public(blk);
|
||||
ThrottleTimers *tt = &blkp->throttle_timers;
|
||||
ThrottleState *ts = blkp->throttle_state;
|
||||
ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
|
||||
qemu_mutex_lock(&tg->lock);
|
||||
/* throttle_config() cancels the timers */
|
||||
@@ -351,20 +357,21 @@ void throttle_group_config(BlockDriverState *bs, ThrottleConfig *cfg)
|
||||
throttle_config(ts, tt, cfg);
|
||||
qemu_mutex_unlock(&tg->lock);
|
||||
|
||||
qemu_co_enter_next(&bs->throttled_reqs[0]);
|
||||
qemu_co_enter_next(&bs->throttled_reqs[1]);
|
||||
qemu_co_enter_next(&blkp->throttled_reqs[0]);
|
||||
qemu_co_enter_next(&blkp->throttled_reqs[1]);
|
||||
}
|
||||
|
||||
/* Get the throttle configuration from a particular group. Similar to
|
||||
* throttle_get_config(), but guarantees atomicity within the
|
||||
* throttling group.
|
||||
*
|
||||
* @bs: a BlockDriverState that is member of the group
|
||||
* @blk: a BlockBackend that is a member of the group
|
||||
* @cfg: the configuration will be written here
|
||||
*/
|
||||
void throttle_group_get_config(BlockDriverState *bs, ThrottleConfig *cfg)
|
||||
void throttle_group_get_config(BlockBackend *blk, ThrottleConfig *cfg)
|
||||
{
|
||||
ThrottleState *ts = bs->throttle_state;
|
||||
BlockBackendPublic *blkp = blk_get_public(blk);
|
||||
ThrottleState *ts = blkp->throttle_state;
|
||||
ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
|
||||
qemu_mutex_lock(&tg->lock);
|
||||
throttle_get_config(ts, cfg);
|
||||
@@ -374,12 +381,13 @@ void throttle_group_get_config(BlockDriverState *bs, ThrottleConfig *cfg)
|
||||
/* ThrottleTimers callback. This wakes up a request that was waiting
|
||||
* because it had been throttled.
|
||||
*
|
||||
* @bs: the BlockDriverState whose request had been throttled
|
||||
* @blk: the BlockBackend whose request had been throttled
|
||||
* @is_write: the type of operation (read/write)
|
||||
*/
|
||||
static void timer_cb(BlockDriverState *bs, bool is_write)
|
||||
static void timer_cb(BlockBackend *blk, bool is_write)
|
||||
{
|
||||
ThrottleState *ts = bs->throttle_state;
|
||||
BlockBackendPublic *blkp = blk_get_public(blk);
|
||||
ThrottleState *ts = blkp->throttle_state;
|
||||
ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
|
||||
bool empty_queue;
|
||||
|
||||
@@ -389,13 +397,13 @@ static void timer_cb(BlockDriverState *bs, bool is_write)
|
||||
qemu_mutex_unlock(&tg->lock);
|
||||
|
||||
/* Run the request that was waiting for this timer */
|
||||
empty_queue = !qemu_co_enter_next(&bs->throttled_reqs[is_write]);
|
||||
empty_queue = !qemu_co_enter_next(&blkp->throttled_reqs[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(bs, is_write);
|
||||
schedule_next_request(blk, is_write);
|
||||
qemu_mutex_unlock(&tg->lock);
|
||||
}
|
||||
}
|
||||
@@ -410,17 +418,17 @@ static void write_timer_cb(void *opaque)
|
||||
timer_cb(opaque, true);
|
||||
}
|
||||
|
||||
/* Register a BlockDriverState in 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.
|
||||
/* Register a BlockBackend in 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.
|
||||
*
|
||||
* @bs: the BlockDriverState to insert
|
||||
* @blk: the BlockBackend to insert
|
||||
* @groupname: the name of the group
|
||||
*/
|
||||
void throttle_group_register_bs(BlockDriverState *bs, const char *groupname)
|
||||
void throttle_group_register_blk(BlockBackend *blk, const char *groupname)
|
||||
{
|
||||
int i;
|
||||
BlockBackendPublic *blkp = blk_get_public(blk);
|
||||
ThrottleState *ts = throttle_group_incref(groupname);
|
||||
ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
|
||||
int clock_type = QEMU_CLOCK_REALTIME;
|
||||
@@ -430,67 +438,67 @@ void throttle_group_register_bs(BlockDriverState *bs, const char *groupname)
|
||||
clock_type = QEMU_CLOCK_VIRTUAL;
|
||||
}
|
||||
|
||||
bs->throttle_state = ts;
|
||||
blkp->throttle_state = ts;
|
||||
|
||||
qemu_mutex_lock(&tg->lock);
|
||||
/* If the ThrottleGroup is new set this BlockDriverState as the token */
|
||||
/* If the ThrottleGroup is new set this BlockBackend as the token */
|
||||
for (i = 0; i < 2; i++) {
|
||||
if (!tg->tokens[i]) {
|
||||
tg->tokens[i] = bs;
|
||||
tg->tokens[i] = blk;
|
||||
}
|
||||
}
|
||||
|
||||
QLIST_INSERT_HEAD(&tg->head, bs, round_robin);
|
||||
QLIST_INSERT_HEAD(&tg->head, blkp, round_robin);
|
||||
|
||||
throttle_timers_init(&bs->throttle_timers,
|
||||
bdrv_get_aio_context(bs),
|
||||
throttle_timers_init(&blkp->throttle_timers,
|
||||
blk_get_aio_context(blk),
|
||||
clock_type,
|
||||
read_timer_cb,
|
||||
write_timer_cb,
|
||||
bs);
|
||||
blk);
|
||||
|
||||
qemu_mutex_unlock(&tg->lock);
|
||||
}
|
||||
|
||||
/* Unregister a BlockDriverState from its group, removing it from the
|
||||
* list, destroying the timers and setting the throttle_state pointer
|
||||
* to NULL.
|
||||
/* Unregister a BlockBackend from its group, removing it from the list,
|
||||
* destroying the timers and setting the throttle_state pointer to NULL.
|
||||
*
|
||||
* The BlockDriverState must not have pending throttled requests, so
|
||||
* the caller has to drain them first.
|
||||
* The BlockBackend 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.
|
||||
*
|
||||
* @bs: the BlockDriverState to remove
|
||||
* @blk: the BlockBackend to remove
|
||||
*/
|
||||
void throttle_group_unregister_bs(BlockDriverState *bs)
|
||||
void throttle_group_unregister_blk(BlockBackend *blk)
|
||||
{
|
||||
ThrottleGroup *tg = container_of(bs->throttle_state, ThrottleGroup, ts);
|
||||
BlockBackendPublic *blkp = blk_get_public(blk);
|
||||
ThrottleGroup *tg = container_of(blkp->throttle_state, ThrottleGroup, ts);
|
||||
int i;
|
||||
|
||||
assert(bs->pending_reqs[0] == 0 && bs->pending_reqs[1] == 0);
|
||||
assert(qemu_co_queue_empty(&bs->throttled_reqs[0]));
|
||||
assert(qemu_co_queue_empty(&bs->throttled_reqs[1]));
|
||||
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]));
|
||||
|
||||
qemu_mutex_lock(&tg->lock);
|
||||
for (i = 0; i < 2; i++) {
|
||||
if (tg->tokens[i] == bs) {
|
||||
BlockDriverState *token = throttle_group_next_bs(bs);
|
||||
/* Take care of the case where this is the last bs in the group */
|
||||
if (token == bs) {
|
||||
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) {
|
||||
token = NULL;
|
||||
}
|
||||
tg->tokens[i] = token;
|
||||
}
|
||||
}
|
||||
|
||||
/* remove the current bs from the list */
|
||||
QLIST_REMOVE(bs, round_robin);
|
||||
throttle_timers_destroy(&bs->throttle_timers);
|
||||
/* remove the current blk from the list */
|
||||
QLIST_REMOVE(blkp, round_robin);
|
||||
throttle_timers_destroy(&blkp->throttle_timers);
|
||||
qemu_mutex_unlock(&tg->lock);
|
||||
|
||||
throttle_group_unref(&tg->ts);
|
||||
bs->throttle_state = NULL;
|
||||
blkp->throttle_state = NULL;
|
||||
}
|
||||
|
||||
static void throttle_groups_init(void)
|
||||
|
||||
@@ -2344,27 +2344,6 @@ static int vmdk_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void vmdk_detach_aio_context(BlockDriverState *bs)
|
||||
{
|
||||
BDRVVmdkState *s = bs->opaque;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < s->num_extents; i++) {
|
||||
bdrv_detach_aio_context(s->extents[i].file->bs);
|
||||
}
|
||||
}
|
||||
|
||||
static void vmdk_attach_aio_context(BlockDriverState *bs,
|
||||
AioContext *new_context)
|
||||
{
|
||||
BDRVVmdkState *s = bs->opaque;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < s->num_extents; i++) {
|
||||
bdrv_attach_aio_context(s->extents[i].file->bs, new_context);
|
||||
}
|
||||
}
|
||||
|
||||
static QemuOptsList vmdk_create_opts = {
|
||||
.name = "vmdk-create-opts",
|
||||
.head = QTAILQ_HEAD_INITIALIZER(vmdk_create_opts.head),
|
||||
@@ -2434,8 +2413,6 @@ static BlockDriver bdrv_vmdk = {
|
||||
.bdrv_get_specific_info = vmdk_get_specific_info,
|
||||
.bdrv_refresh_limits = vmdk_refresh_limits,
|
||||
.bdrv_get_info = vmdk_get_info,
|
||||
.bdrv_detach_aio_context = vmdk_detach_aio_context,
|
||||
.bdrv_attach_aio_context = vmdk_attach_aio_context,
|
||||
|
||||
.supports_backing = true,
|
||||
.create_opts = &vmdk_create_opts,
|
||||
|
||||
+67
-53
@@ -577,15 +577,6 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts,
|
||||
blk_rs->read_only = !(bdrv_flags & BDRV_O_RDWR);
|
||||
blk_rs->detect_zeroes = detect_zeroes;
|
||||
|
||||
if (throttle_enabled(&cfg)) {
|
||||
if (!throttling_group) {
|
||||
throttling_group = blk_name(blk);
|
||||
}
|
||||
blk_rs->throttle_group = g_strdup(throttling_group);
|
||||
blk_rs->throttle_state = throttle_group_incref(throttling_group);
|
||||
blk_rs->throttle_state->cfg = cfg;
|
||||
}
|
||||
|
||||
QDECREF(bs_opts);
|
||||
} else {
|
||||
if (file && !*file) {
|
||||
@@ -611,15 +602,6 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts,
|
||||
|
||||
bs->detect_zeroes = detect_zeroes;
|
||||
|
||||
/* disk I/O throttling */
|
||||
if (throttle_enabled(&cfg)) {
|
||||
if (!throttling_group) {
|
||||
throttling_group = blk_name(blk);
|
||||
}
|
||||
bdrv_io_limits_enable(bs, throttling_group);
|
||||
bdrv_set_io_limits(bs, &cfg);
|
||||
}
|
||||
|
||||
if (bdrv_key_required(bs)) {
|
||||
autostart = 0;
|
||||
}
|
||||
@@ -633,6 +615,15 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts,
|
||||
}
|
||||
}
|
||||
|
||||
/* disk I/O throttling */
|
||||
if (throttle_enabled(&cfg)) {
|
||||
if (!throttling_group) {
|
||||
throttling_group = blk_name(blk);
|
||||
}
|
||||
blk_io_limits_enable(blk, throttling_group);
|
||||
blk_set_io_limits(blk, &cfg);
|
||||
}
|
||||
|
||||
blk_set_enable_write_cache(blk, !writethrough);
|
||||
blk_set_on_error(blk, on_read_error, on_write_error);
|
||||
|
||||
@@ -1785,9 +1776,9 @@ static void external_snapshot_prepare(BlkActionState *common,
|
||||
return;
|
||||
}
|
||||
|
||||
if (state->new_bs->blk != NULL) {
|
||||
if (bdrv_has_blk(state->new_bs)) {
|
||||
error_setg(errp, "The snapshot is already in use by %s",
|
||||
blk_name(state->new_bs->blk));
|
||||
bdrv_get_parent_name(state->new_bs));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2290,16 +2281,29 @@ exit:
|
||||
block_job_txn_unref(block_job_txn);
|
||||
}
|
||||
|
||||
static int do_open_tray(const char *device, bool force, Error **errp);
|
||||
|
||||
void qmp_eject(const char *device, bool has_force, bool force, Error **errp)
|
||||
{
|
||||
Error *local_err = NULL;
|
||||
int rc;
|
||||
|
||||
qmp_blockdev_open_tray(device, has_force, force, &local_err);
|
||||
if (!has_force) {
|
||||
force = false;
|
||||
}
|
||||
|
||||
rc = do_open_tray(device, force, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (rc == EINPROGRESS) {
|
||||
error_setg(errp, "Device '%s' is locked and force was not specified, "
|
||||
"wait for tray to open and try again", device);
|
||||
return;
|
||||
}
|
||||
|
||||
qmp_x_blockdev_remove_medium(device, errp);
|
||||
}
|
||||
|
||||
@@ -2327,35 +2331,36 @@ void qmp_block_passwd(bool has_device, const char *device,
|
||||
aio_context_release(aio_context);
|
||||
}
|
||||
|
||||
void qmp_blockdev_open_tray(const char *device, bool has_force, bool force,
|
||||
Error **errp)
|
||||
/**
|
||||
* returns -errno on fatal error, +errno for non-fatal situations.
|
||||
* errp will always be set when the return code is negative.
|
||||
* May return +ENOSYS if the device has no tray,
|
||||
* or +EINPROGRESS if the tray is locked and the guest has been notified.
|
||||
*/
|
||||
static int do_open_tray(const char *device, bool force, Error **errp)
|
||||
{
|
||||
BlockBackend *blk;
|
||||
bool locked;
|
||||
|
||||
if (!has_force) {
|
||||
force = false;
|
||||
}
|
||||
|
||||
blk = blk_by_name(device);
|
||||
if (!blk) {
|
||||
error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
|
||||
"Device '%s' not found", device);
|
||||
return;
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (!blk_dev_has_removable_media(blk)) {
|
||||
error_setg(errp, "Device '%s' is not removable", device);
|
||||
return;
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
if (!blk_dev_has_tray(blk)) {
|
||||
/* Ignore this command on tray-less devices */
|
||||
return;
|
||||
return ENOSYS;
|
||||
}
|
||||
|
||||
if (blk_dev_is_tray_open(blk)) {
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
locked = blk_dev_is_medium_locked(blk);
|
||||
@@ -2366,6 +2371,21 @@ void qmp_blockdev_open_tray(const char *device, bool has_force, bool force,
|
||||
if (!locked || force) {
|
||||
blk_dev_change_media_cb(blk, false);
|
||||
}
|
||||
|
||||
if (locked && !force) {
|
||||
return EINPROGRESS;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void qmp_blockdev_open_tray(const char *device, bool has_force, bool force,
|
||||
Error **errp)
|
||||
{
|
||||
if (!has_force) {
|
||||
force = false;
|
||||
}
|
||||
do_open_tray(device, force, errp);
|
||||
}
|
||||
|
||||
void qmp_blockdev_close_tray(const char *device, Error **errp)
|
||||
@@ -2503,9 +2523,9 @@ void qmp_x_blockdev_insert_medium(const char *device, const char *node_name,
|
||||
return;
|
||||
}
|
||||
|
||||
if (bs->blk) {
|
||||
if (bdrv_has_blk(bs)) {
|
||||
error_setg(errp, "Node '%s' is already in use by '%s'", node_name,
|
||||
blk_name(bs->blk));
|
||||
bdrv_get_parent_name(bs));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2570,8 +2590,6 @@ void qmp_blockdev_change_medium(const char *device, const char *filename,
|
||||
goto fail;
|
||||
}
|
||||
|
||||
blk_apply_root_state(blk, medium_bs);
|
||||
|
||||
bdrv_add_key(medium_bs, NULL, &err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
@@ -2596,6 +2614,8 @@ void qmp_blockdev_change_medium(const char *device, const char *filename,
|
||||
goto fail;
|
||||
}
|
||||
|
||||
blk_apply_root_state(blk, medium_bs);
|
||||
|
||||
qmp_blockdev_close_tray(device, errp);
|
||||
|
||||
fail:
|
||||
@@ -2661,13 +2681,6 @@ void qmp_block_set_io_throttle(const char *device, int64_t bps, int64_t bps_rd,
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* The BlockBackend must be the only parent */
|
||||
assert(QLIST_FIRST(&bs->parents));
|
||||
if (QLIST_NEXT(QLIST_FIRST(&bs->parents), next_parent)) {
|
||||
error_setg(errp, "Cannot throttle device with multiple parents");
|
||||
goto out;
|
||||
}
|
||||
|
||||
throttle_config_init(&cfg);
|
||||
cfg.buckets[THROTTLE_BPS_TOTAL].avg = bps;
|
||||
cfg.buckets[THROTTLE_BPS_READ].avg = bps_rd;
|
||||
@@ -2726,16 +2739,16 @@ void qmp_block_set_io_throttle(const char *device, int64_t bps, int64_t bps_rd,
|
||||
if (throttle_enabled(&cfg)) {
|
||||
/* Enable I/O limits if they're not enabled yet, otherwise
|
||||
* just update the throttling group. */
|
||||
if (!bs->throttle_state) {
|
||||
bdrv_io_limits_enable(bs, has_group ? group : device);
|
||||
if (!blk_get_public(blk)->throttle_state) {
|
||||
blk_io_limits_enable(blk, has_group ? group : device);
|
||||
} else if (has_group) {
|
||||
bdrv_io_limits_update_group(bs, group);
|
||||
blk_io_limits_update_group(blk, group);
|
||||
}
|
||||
/* Set the new throttling configuration */
|
||||
bdrv_set_io_limits(bs, &cfg);
|
||||
} else if (bs->throttle_state) {
|
||||
blk_set_io_limits(blk, &cfg);
|
||||
} else if (blk_get_public(blk)->throttle_state) {
|
||||
/* If all throttling settings are set to 0, disable I/O limits */
|
||||
bdrv_io_limits_disable(bs);
|
||||
blk_io_limits_disable(blk);
|
||||
}
|
||||
|
||||
out:
|
||||
@@ -3457,7 +3470,7 @@ static void blockdev_mirror_common(BlockDriverState *bs,
|
||||
if (bdrv_op_is_blocked(target, BLOCK_OP_TYPE_MIRROR_TARGET, errp)) {
|
||||
return;
|
||||
}
|
||||
if (target->blk) {
|
||||
if (bdrv_has_blk(target)) {
|
||||
error_setg(errp, "Cannot mirror to an attached block device");
|
||||
return;
|
||||
}
|
||||
@@ -4046,15 +4059,15 @@ void qmp_x_blockdev_del(bool has_id, const char *id,
|
||||
bs = blk_bs(blk);
|
||||
aio_context = blk_get_aio_context(blk);
|
||||
} else {
|
||||
blk = NULL;
|
||||
bs = bdrv_find_node(node_name);
|
||||
if (!bs) {
|
||||
error_setg(errp, "Cannot find node %s", node_name);
|
||||
return;
|
||||
}
|
||||
blk = bs->blk;
|
||||
if (blk) {
|
||||
if (bdrv_has_blk(bs)) {
|
||||
error_setg(errp, "Node %s is in use by %s",
|
||||
node_name, blk_name(blk));
|
||||
node_name, bdrv_get_parent_name(bs));
|
||||
return;
|
||||
}
|
||||
aio_context = bdrv_get_aio_context(bs);
|
||||
@@ -4151,8 +4164,9 @@ BlockJobInfoList *qmp_query_block_jobs(Error **errp)
|
||||
{
|
||||
BlockJobInfoList *head = NULL, **p_next = &head;
|
||||
BlockDriverState *bs;
|
||||
BdrvNextIterator *it = NULL;
|
||||
|
||||
for (bs = bdrv_next(NULL); bs; bs = bdrv_next(bs)) {
|
||||
while ((it = bdrv_next(it, &bs))) {
|
||||
AioContext *aio_context = bdrv_get_aio_context(bs);
|
||||
|
||||
aio_context_acquire(aio_context);
|
||||
|
||||
+1
-5
@@ -411,8 +411,7 @@ void block_job_event_ready(BlockJob *job)
|
||||
job->speed, &error_abort);
|
||||
}
|
||||
|
||||
BlockErrorAction block_job_error_action(BlockJob *job, BlockDriverState *bs,
|
||||
BlockdevOnError on_err,
|
||||
BlockErrorAction block_job_error_action(BlockJob *job, BlockdevOnError on_err,
|
||||
int is_read, int error)
|
||||
{
|
||||
BlockErrorAction action;
|
||||
@@ -443,9 +442,6 @@ BlockErrorAction block_job_error_action(BlockJob *job, BlockDriverState *bs,
|
||||
job->user_paused = true;
|
||||
block_job_pause(job);
|
||||
block_job_iostatus_set_err(job, error);
|
||||
if (bs->blk && bs != job->bs) {
|
||||
blk_iostatus_set_err(bs->blk, error);
|
||||
}
|
||||
}
|
||||
return action;
|
||||
}
|
||||
|
||||
+4
-10
@@ -17,6 +17,7 @@ typedef struct BlockJob BlockJob;
|
||||
typedef struct BdrvChild BdrvChild;
|
||||
typedef struct BdrvChildRole BdrvChildRole;
|
||||
typedef struct BlockJobTxn BlockJobTxn;
|
||||
typedef struct BdrvNextIterator BdrvNextIterator;
|
||||
|
||||
typedef struct BlockDriverInfo {
|
||||
/* in bytes, 0 if irrelevant */
|
||||
@@ -187,10 +188,6 @@ void bdrv_stats_print(Monitor *mon, const QObject *data);
|
||||
void bdrv_info_stats(Monitor *mon, QObject **ret_data);
|
||||
|
||||
/* disk I/O throttling */
|
||||
void bdrv_io_limits_enable(BlockDriverState *bs, const char *group);
|
||||
void bdrv_io_limits_disable(BlockDriverState *bs);
|
||||
void bdrv_io_limits_update_group(BlockDriverState *bs, const char *group);
|
||||
|
||||
void bdrv_init(void);
|
||||
void bdrv_init_with_whitelist(void);
|
||||
bool bdrv_uses_whitelist(void);
|
||||
@@ -333,7 +330,7 @@ void bdrv_aio_cancel(BlockAIOCB *acb);
|
||||
void bdrv_aio_cancel_async(BlockAIOCB *acb);
|
||||
|
||||
typedef struct BlockRequest {
|
||||
/* Fields to be filled by multiwrite caller */
|
||||
/* Fields to be filled by caller */
|
||||
union {
|
||||
struct {
|
||||
int64_t sector;
|
||||
@@ -349,13 +346,10 @@ typedef struct BlockRequest {
|
||||
BlockCompletionFunc *cb;
|
||||
void *opaque;
|
||||
|
||||
/* Filled by multiwrite implementation */
|
||||
/* Filled by block layer */
|
||||
int error;
|
||||
} BlockRequest;
|
||||
|
||||
int bdrv_aio_multiwrite(BlockDriverState *bs, BlockRequest *reqs,
|
||||
int num_reqs);
|
||||
|
||||
/* sg packet commands */
|
||||
int bdrv_ioctl(BlockDriverState *bs, unsigned long int req, void *buf);
|
||||
BlockAIOCB *bdrv_aio_ioctl(BlockDriverState *bs,
|
||||
@@ -408,7 +402,7 @@ BlockDriverState *bdrv_lookup_bs(const char *device,
|
||||
Error **errp);
|
||||
bool bdrv_chain_contains(BlockDriverState *top, BlockDriverState *base);
|
||||
BlockDriverState *bdrv_next_node(BlockDriverState *bs);
|
||||
BlockDriverState *bdrv_next(BlockDriverState *bs);
|
||||
BdrvNextIterator *bdrv_next(BdrvNextIterator *it, BlockDriverState **bs);
|
||||
BlockDriverState *bdrv_next_monitor_owned(BlockDriverState *bs);
|
||||
int bdrv_is_encrypted(BlockDriverState *bs);
|
||||
int bdrv_key_required(BlockDriverState *bs);
|
||||
|
||||
+21
-26
@@ -26,7 +26,6 @@
|
||||
|
||||
#include "block/accounting.h"
|
||||
#include "block/block.h"
|
||||
#include "block/throttle-groups.h"
|
||||
#include "qemu/option.h"
|
||||
#include "qemu/queue.h"
|
||||
#include "qemu/coroutine.h"
|
||||
@@ -365,6 +364,25 @@ typedef struct BdrvAioNotifier {
|
||||
struct BdrvChildRole {
|
||||
void (*inherit_options)(int *child_flags, QDict *child_options,
|
||||
int parent_flags, QDict *parent_options);
|
||||
|
||||
void (*change_media)(BdrvChild *child, bool load);
|
||||
void (*resize)(BdrvChild *child);
|
||||
|
||||
/* Returns a name that is supposedly more useful for human users than the
|
||||
* node name for identifying the node in question (in particular, a BB
|
||||
* name), or NULL if the parent can't provide a better name. */
|
||||
const char* (*get_name)(BdrvChild *child);
|
||||
|
||||
/*
|
||||
* If this pair of functions is implemented, the parent doesn't issue new
|
||||
* requests after returning from .drained_begin() until .drained_end() is
|
||||
* called.
|
||||
*
|
||||
* Note that this can be nested. If drained_begin() was called twice, new
|
||||
* I/O is allowed only after drained_end() was called twice, too.
|
||||
*/
|
||||
void (*drained_begin)(BdrvChild *child);
|
||||
void (*drained_end)(BdrvChild *child);
|
||||
};
|
||||
|
||||
extern const BdrvChildRole child_file;
|
||||
@@ -374,6 +392,7 @@ struct BdrvChild {
|
||||
BlockDriverState *bs;
|
||||
char *name;
|
||||
const BdrvChildRole *role;
|
||||
void *opaque;
|
||||
QLIST_ENTRY(BdrvChild) next;
|
||||
QLIST_ENTRY(BdrvChild) next_parent;
|
||||
};
|
||||
@@ -399,8 +418,6 @@ struct BlockDriverState {
|
||||
BlockDriver *drv; /* NULL means no media */
|
||||
void *opaque;
|
||||
|
||||
BlockBackend *blk; /* owning backend, if any */
|
||||
|
||||
AioContext *aio_context; /* event loop used for fd handlers, timers, etc */
|
||||
/* long-running tasks intended to always use the same AioContext as this
|
||||
* BDS may register themselves in this list to be notified of changes
|
||||
@@ -424,19 +441,6 @@ struct BlockDriverState {
|
||||
/* number of in-flight serialising requests */
|
||||
unsigned int serialising_in_flight;
|
||||
|
||||
/* I/O throttling.
|
||||
* throttle_state tells us if this BDS has I/O limits configured.
|
||||
* io_limits_disabled tells us if they are currently being enforced */
|
||||
CoQueue throttled_reqs[2];
|
||||
unsigned int io_limits_disabled;
|
||||
|
||||
/* The following fields are protected by the ThrottleGroup lock.
|
||||
* See the ThrottleGroup documentation for details. */
|
||||
ThrottleState *throttle_state;
|
||||
ThrottleTimers throttle_timers;
|
||||
unsigned pending_reqs[2];
|
||||
QLIST_ENTRY(BlockDriverState) round_robin;
|
||||
|
||||
/* Offset after the highest byte written to */
|
||||
uint64_t wr_highest_offset;
|
||||
|
||||
@@ -502,9 +506,6 @@ struct BlockBackendRootState {
|
||||
int open_flags;
|
||||
bool read_only;
|
||||
BlockdevDetectZeroesOptions detect_zeroes;
|
||||
|
||||
char *throttle_group;
|
||||
ThrottleState *throttle_state;
|
||||
};
|
||||
|
||||
static inline BlockDriverState *backing_bs(BlockDriverState *bs)
|
||||
@@ -539,9 +540,6 @@ int get_tmp_filename(char *filename, int size);
|
||||
BlockDriver *bdrv_probe_all(const uint8_t *buf, int buf_size,
|
||||
const char *filename);
|
||||
|
||||
void bdrv_set_io_limits(BlockDriverState *bs,
|
||||
ThrottleConfig *cfg);
|
||||
|
||||
|
||||
/**
|
||||
* bdrv_add_before_write_notifier:
|
||||
@@ -724,16 +722,13 @@ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs,
|
||||
const BdrvChildRole *child_role);
|
||||
void bdrv_root_unref_child(BdrvChild *child);
|
||||
|
||||
void bdrv_no_throttling_begin(BlockDriverState *bs);
|
||||
void bdrv_no_throttling_end(BlockDriverState *bs);
|
||||
|
||||
const char *bdrv_get_parent_name(const BlockDriverState *bs);
|
||||
void blk_dev_change_media_cb(BlockBackend *blk, bool load);
|
||||
bool blk_dev_has_removable_media(BlockBackend *blk);
|
||||
bool blk_dev_has_tray(BlockBackend *blk);
|
||||
void blk_dev_eject_request(BlockBackend *blk, bool force);
|
||||
bool blk_dev_is_tray_open(BlockBackend *blk);
|
||||
bool blk_dev_is_medium_locked(BlockBackend *blk);
|
||||
void blk_dev_resize_cb(BlockBackend *blk);
|
||||
|
||||
void bdrv_set_dirty(BlockDriverState *bs, int64_t cur_sector, int nr_sectors);
|
||||
bool bdrv_requests_pending(BlockDriverState *bs);
|
||||
|
||||
@@ -383,7 +383,6 @@ void block_job_iostatus_reset(BlockJob *job);
|
||||
/**
|
||||
* block_job_error_action:
|
||||
* @job: The job to signal an error for.
|
||||
* @bs: The block device on which to set an I/O error.
|
||||
* @on_err: The error action setting.
|
||||
* @is_read: Whether the operation was a read.
|
||||
* @error: The error that was reported.
|
||||
@@ -391,8 +390,7 @@ void block_job_iostatus_reset(BlockJob *job);
|
||||
* Report an I/O error for a block job and possibly stop the VM. Return the
|
||||
* action that was selected based on @on_err and @error.
|
||||
*/
|
||||
BlockErrorAction block_job_error_action(BlockJob *job, BlockDriverState *bs,
|
||||
BlockdevOnError on_err,
|
||||
BlockErrorAction block_job_error_action(BlockJob *job, BlockdevOnError on_err,
|
||||
int is_read, int error);
|
||||
|
||||
typedef void BlockJobDeferToMainLoopFn(BlockJob *job, void *opaque);
|
||||
|
||||
@@ -28,19 +28,19 @@
|
||||
#include "qemu/throttle.h"
|
||||
#include "block/block_int.h"
|
||||
|
||||
const char *throttle_group_get_name(BlockDriverState *bs);
|
||||
const char *throttle_group_get_name(BlockBackend *blk);
|
||||
|
||||
ThrottleState *throttle_group_incref(const char *name);
|
||||
void throttle_group_unref(ThrottleState *ts);
|
||||
|
||||
void throttle_group_config(BlockDriverState *bs, ThrottleConfig *cfg);
|
||||
void throttle_group_get_config(BlockDriverState *bs, ThrottleConfig *cfg);
|
||||
void throttle_group_config(BlockBackend *blk, ThrottleConfig *cfg);
|
||||
void throttle_group_get_config(BlockBackend *blk, ThrottleConfig *cfg);
|
||||
|
||||
void throttle_group_register_bs(BlockDriverState *bs, const char *groupname);
|
||||
void throttle_group_unregister_bs(BlockDriverState *bs);
|
||||
void throttle_group_restart_bs(BlockDriverState *bs);
|
||||
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 coroutine_fn throttle_group_co_io_limits_intercept(BlockDriverState *bs,
|
||||
void coroutine_fn throttle_group_co_io_limits_intercept(BlockBackend *blk,
|
||||
unsigned int bytes,
|
||||
bool is_write);
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user