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: - block: AioContext management, part 2 - Avoid recursive block_status call (i.e. lseek() calls) if possible - linux-aio: Drop unused BlockAIOCB submission method - nvme: add Get/Set Feature Timestamp support - Fix crash on commit job start with active I/O on base node - Fix crash in bdrv_drained_end - Fix integer overflow in qcow2 discard # gpg: Signature made Tue 04 Jun 2019 16:20:02 BST # gpg: using RSA key 7F09B272C88F2FD6 # gpg: Good signature from "Kevin Wolf <kwolf@redhat.com>" [full] # Primary key fingerprint: DC3D EB15 9A9A F95D 3D74 56FE 7F09 B272 C88F 2FD6 * remotes/kevin/tags/for-upstream: (29 commits) iotests: Fix duplicated diff output on failure iotests: test big qcow2 shrink block/io: bdrv_pdiscard: support int64_t bytes parameter block/qcow2-refcount: add trace-point to qcow2_process_discards block: Remove bdrv_set_aio_context() test-bdrv-drain: Use bdrv_try_set_aio_context() iotests: Attach new devices to node in non-default iothread virtio-scsi-test: Test attaching new overlay with iothreads block: Remove wrong bdrv_set_aio_context() calls blockdev: Use bdrv_try_set_aio_context() for monitor commands block: Move node without parents to main AioContext test-block-iothread: BlockBackend AioContext across root node change test-block-iothread: Test adding parent to iothread node block: Adjust AioContexts when attaching nodes scsi-disk: Use qdev_prop_drive_iothread block: Add qdev_prop_drive_iothread property type block: Add BlockBackend.ctx block: Add Error to blk_set_aio_context() nbd-server: Call blk_set_allow_aio_context_change() test-block-iothread: Check filter node in test_propagate_mirror ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
@@ -2235,6 +2235,10 @@ static void bdrv_replace_child(BdrvChild *child, BlockDriverState *new_bs)
|
||||
bdrv_get_cumulative_perm(old_bs, &perm, &shared_perm);
|
||||
bdrv_check_perm(old_bs, NULL, perm, shared_perm, NULL, &error_abort);
|
||||
bdrv_set_perm(old_bs, perm, shared_perm);
|
||||
|
||||
/* When the parent requiring a non-default AioContext is removed, the
|
||||
* node moves back to the main AioContext */
|
||||
bdrv_try_set_aio_context(old_bs, qemu_get_aio_context(), NULL);
|
||||
}
|
||||
|
||||
if (new_bs) {
|
||||
@@ -2249,14 +2253,19 @@ static void bdrv_replace_child(BdrvChild *child, BlockDriverState *new_bs)
|
||||
*
|
||||
* On failure NULL is returned, errp is set and the reference to
|
||||
* child_bs is also dropped.
|
||||
*
|
||||
* The caller must hold the AioContext lock @child_bs, but not that of @ctx
|
||||
* (unless @child_bs is already in @ctx).
|
||||
*/
|
||||
BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs,
|
||||
const char *child_name,
|
||||
const BdrvChildRole *child_role,
|
||||
AioContext *ctx,
|
||||
uint64_t perm, uint64_t shared_perm,
|
||||
void *opaque, Error **errp)
|
||||
{
|
||||
BdrvChild *child;
|
||||
Error *local_err = NULL;
|
||||
int ret;
|
||||
|
||||
ret = bdrv_check_update_perm(child_bs, NULL, perm, shared_perm, NULL, errp);
|
||||
@@ -2276,6 +2285,31 @@ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs,
|
||||
.opaque = opaque,
|
||||
};
|
||||
|
||||
/* If the AioContexts don't match, first try to move the subtree of
|
||||
* child_bs into the AioContext of the new parent. If this doesn't work,
|
||||
* try moving the parent into the AioContext of child_bs instead. */
|
||||
if (bdrv_get_aio_context(child_bs) != ctx) {
|
||||
ret = bdrv_try_set_aio_context(child_bs, ctx, &local_err);
|
||||
if (ret < 0 && child_role->can_set_aio_ctx) {
|
||||
GSList *ignore = g_slist_prepend(NULL, child);;
|
||||
ctx = bdrv_get_aio_context(child_bs);
|
||||
if (child_role->can_set_aio_ctx(child, ctx, &ignore, NULL)) {
|
||||
error_free(local_err);
|
||||
ret = 0;
|
||||
g_slist_free(ignore);
|
||||
ignore = g_slist_prepend(NULL, child);;
|
||||
child_role->set_aio_ctx(child, ctx, &ignore);
|
||||
}
|
||||
g_slist_free(ignore);
|
||||
}
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
g_free(child);
|
||||
bdrv_abort_perm_update(child_bs);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* This performs the matching bdrv_set_perm() for the above check. */
|
||||
bdrv_replace_child(child, child_bs);
|
||||
|
||||
@@ -2289,6 +2323,9 @@ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs,
|
||||
*
|
||||
* On failure NULL is returned, errp is set and the reference to
|
||||
* child_bs is also dropped.
|
||||
*
|
||||
* If @parent_bs and @child_bs are in different AioContexts, the caller must
|
||||
* hold the AioContext lock for @child_bs, but not for @parent_bs.
|
||||
*/
|
||||
BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs,
|
||||
BlockDriverState *child_bs,
|
||||
@@ -2302,11 +2339,11 @@ BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs,
|
||||
bdrv_get_cumulative_perm(parent_bs, &perm, &shared_perm);
|
||||
|
||||
assert(parent_bs->drv);
|
||||
assert(bdrv_get_aio_context(parent_bs) == bdrv_get_aio_context(child_bs));
|
||||
bdrv_child_perm(parent_bs, child_bs, NULL, child_role, NULL,
|
||||
perm, shared_perm, &perm, &shared_perm);
|
||||
|
||||
child = bdrv_root_attach_child(child_bs, child_name, child_role,
|
||||
bdrv_get_aio_context(parent_bs),
|
||||
perm, shared_perm, parent_bs, errp);
|
||||
if (child == NULL) {
|
||||
return NULL;
|
||||
@@ -2517,7 +2554,6 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options,
|
||||
ret = -EINVAL;
|
||||
goto free_exit;
|
||||
}
|
||||
bdrv_set_aio_context(backing_hd, bdrv_get_aio_context(bs));
|
||||
|
||||
if (implicit_backing) {
|
||||
bdrv_refresh_filename(backing_hd);
|
||||
@@ -2884,7 +2920,7 @@ static BlockDriverState *bdrv_open_inherit(const char *filename,
|
||||
/* Not requesting BLK_PERM_CONSISTENT_READ because we're only
|
||||
* looking at the header to guess the image format. This works even
|
||||
* in cases where a guest would not see a consistent state. */
|
||||
file = blk_new(0, BLK_PERM_ALL);
|
||||
file = blk_new(bdrv_get_aio_context(file_bs), 0, BLK_PERM_ALL);
|
||||
blk_insert_bs(file, file_bs, &local_err);
|
||||
bdrv_unref(file_bs);
|
||||
if (local_err) {
|
||||
@@ -4017,13 +4053,13 @@ void bdrv_replace_node(BlockDriverState *from, BlockDriverState *to,
|
||||
uint64_t perm = 0, shared = BLK_PERM_ALL;
|
||||
int ret;
|
||||
|
||||
assert(!atomic_read(&from->in_flight));
|
||||
assert(!atomic_read(&to->in_flight));
|
||||
|
||||
/* Make sure that @from doesn't go away until we have successfully attached
|
||||
* all of its parents to @to. */
|
||||
bdrv_ref(from);
|
||||
|
||||
assert(qemu_get_current_aio_context() == qemu_get_aio_context());
|
||||
bdrv_drained_begin(from);
|
||||
|
||||
/* Put all parents into @list and calculate their cumulative permissions */
|
||||
QLIST_FOREACH_SAFE(c, &from->parents, next_parent, next) {
|
||||
assert(c->bs == from);
|
||||
@@ -4064,6 +4100,7 @@ void bdrv_replace_node(BlockDriverState *from, BlockDriverState *to,
|
||||
|
||||
out:
|
||||
g_slist_free(list);
|
||||
bdrv_drained_end(from);
|
||||
bdrv_unref(from);
|
||||
}
|
||||
|
||||
@@ -5752,8 +5789,17 @@ static void bdrv_attach_aio_context(BlockDriverState *bs,
|
||||
bs->walking_aio_notifiers = false;
|
||||
}
|
||||
|
||||
/* @ignore will accumulate all visited BdrvChild object. The caller is
|
||||
* responsible for freeing the list afterwards. */
|
||||
/*
|
||||
* Changes the AioContext used for fd handlers, timers, and BHs by this
|
||||
* BlockDriverState and all its children and parents.
|
||||
*
|
||||
* The caller must own the AioContext lock for the old AioContext of bs, but it
|
||||
* must not own the AioContext lock for new_context (unless new_context is the
|
||||
* same as the current context of bs).
|
||||
*
|
||||
* @ignore will accumulate all visited BdrvChild object. The caller is
|
||||
* responsible for freeing the list afterwards.
|
||||
*/
|
||||
void bdrv_set_aio_context_ignore(BlockDriverState *bs,
|
||||
AioContext *new_context, GSList **ignore)
|
||||
{
|
||||
@@ -5776,10 +5822,9 @@ void bdrv_set_aio_context_ignore(BlockDriverState *bs,
|
||||
if (g_slist_find(*ignore, child)) {
|
||||
continue;
|
||||
}
|
||||
if (child->role->set_aio_ctx) {
|
||||
*ignore = g_slist_prepend(*ignore, child);
|
||||
child->role->set_aio_ctx(child, new_context, ignore);
|
||||
}
|
||||
assert(child->role->set_aio_ctx);
|
||||
*ignore = g_slist_prepend(*ignore, child);
|
||||
child->role->set_aio_ctx(child, new_context, ignore);
|
||||
}
|
||||
|
||||
bdrv_detach_aio_context(bs);
|
||||
@@ -5793,16 +5838,6 @@ void bdrv_set_aio_context_ignore(BlockDriverState *bs,
|
||||
aio_context_release(new_context);
|
||||
}
|
||||
|
||||
/* The caller must own the AioContext lock for the old AioContext of bs, but it
|
||||
* must not own the AioContext lock for new_context (unless new_context is
|
||||
* the same as the current context of bs). */
|
||||
void bdrv_set_aio_context(BlockDriverState *bs, AioContext *new_context)
|
||||
{
|
||||
GSList *ignore_list = NULL;
|
||||
bdrv_set_aio_context_ignore(bs, new_context, &ignore_list);
|
||||
g_slist_free(ignore_list);
|
||||
}
|
||||
|
||||
static bool bdrv_parent_can_set_aio_context(BdrvChild *c, AioContext *ctx,
|
||||
GSList **ignore, Error **errp)
|
||||
{
|
||||
|
||||
+2
-1
@@ -627,7 +627,8 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
|
||||
}
|
||||
|
||||
/* The target must match the source in size, so no resize here either */
|
||||
job->target = blk_new(BLK_PERM_WRITE,
|
||||
job->target = blk_new(job->common.job.aio_context,
|
||||
BLK_PERM_WRITE,
|
||||
BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE |
|
||||
BLK_PERM_WRITE_UNCHANGED | BLK_PERM_GRAPH_MOD);
|
||||
ret = blk_insert_bs(job->target, target, errp);
|
||||
|
||||
+32
-15
@@ -42,6 +42,7 @@ struct BlockBackend {
|
||||
char *name;
|
||||
int refcnt;
|
||||
BdrvChild *root;
|
||||
AioContext *ctx;
|
||||
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 */
|
||||
@@ -322,12 +323,13 @@ static const BdrvChildRole child_root = {
|
||||
*
|
||||
* Return the new BlockBackend on success, null on failure.
|
||||
*/
|
||||
BlockBackend *blk_new(uint64_t perm, uint64_t shared_perm)
|
||||
BlockBackend *blk_new(AioContext *ctx, uint64_t perm, uint64_t shared_perm)
|
||||
{
|
||||
BlockBackend *blk;
|
||||
|
||||
blk = g_new0(BlockBackend, 1);
|
||||
blk->refcnt = 1;
|
||||
blk->ctx = ctx;
|
||||
blk->perm = perm;
|
||||
blk->shared_perm = shared_perm;
|
||||
blk_set_enable_write_cache(blk, true);
|
||||
@@ -347,6 +349,7 @@ BlockBackend *blk_new(uint64_t perm, uint64_t shared_perm)
|
||||
|
||||
/*
|
||||
* Creates a new BlockBackend, opens a new BlockDriverState, and connects both.
|
||||
* The new BlockBackend is in the main AioContext.
|
||||
*
|
||||
* Just as with bdrv_open(), after having called this function the reference to
|
||||
* @options belongs to the block layer (even on failure).
|
||||
@@ -382,14 +385,14 @@ BlockBackend *blk_new_open(const char *filename, const char *reference,
|
||||
perm |= BLK_PERM_RESIZE;
|
||||
}
|
||||
|
||||
blk = blk_new(perm, BLK_PERM_ALL);
|
||||
blk = blk_new(qemu_get_aio_context(), perm, BLK_PERM_ALL);
|
||||
bs = bdrv_open(filename, reference, options, flags, errp);
|
||||
if (!bs) {
|
||||
blk_unref(blk);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
blk->root = bdrv_root_attach_child(bs, "root", &child_root,
|
||||
blk->root = bdrv_root_attach_child(bs, "root", &child_root, blk->ctx,
|
||||
perm, BLK_PERM_ALL, blk, errp);
|
||||
if (!blk->root) {
|
||||
blk_unref(blk);
|
||||
@@ -800,7 +803,7 @@ int blk_insert_bs(BlockBackend *blk, BlockDriverState *bs, Error **errp)
|
||||
{
|
||||
ThrottleGroupMember *tgm = &blk->public.throttle_group_member;
|
||||
bdrv_ref(bs);
|
||||
blk->root = bdrv_root_attach_child(bs, "root", &child_root,
|
||||
blk->root = bdrv_root_attach_child(bs, "root", &child_root, blk->ctx,
|
||||
blk->perm, blk->shared_perm, blk, errp);
|
||||
if (blk->root == NULL) {
|
||||
return -EPERM;
|
||||
@@ -1856,7 +1859,14 @@ void blk_op_unblock_all(BlockBackend *blk, Error *reason)
|
||||
|
||||
AioContext *blk_get_aio_context(BlockBackend *blk)
|
||||
{
|
||||
return bdrv_get_aio_context(blk_bs(blk));
|
||||
BlockDriverState *bs = blk_bs(blk);
|
||||
|
||||
if (bs) {
|
||||
AioContext *ctx = bdrv_get_aio_context(blk_bs(blk));
|
||||
assert(ctx == blk->ctx);
|
||||
}
|
||||
|
||||
return blk->ctx;
|
||||
}
|
||||
|
||||
static AioContext *blk_aiocb_get_aio_context(BlockAIOCB *acb)
|
||||
@@ -1865,30 +1875,37 @@ static AioContext *blk_aiocb_get_aio_context(BlockAIOCB *acb)
|
||||
return blk_get_aio_context(blk_acb->blk);
|
||||
}
|
||||
|
||||
static void blk_do_set_aio_context(BlockBackend *blk, AioContext *new_context,
|
||||
bool update_root_node)
|
||||
static int blk_do_set_aio_context(BlockBackend *blk, AioContext *new_context,
|
||||
bool update_root_node, Error **errp)
|
||||
{
|
||||
BlockDriverState *bs = blk_bs(blk);
|
||||
ThrottleGroupMember *tgm = &blk->public.throttle_group_member;
|
||||
int ret;
|
||||
|
||||
if (bs) {
|
||||
if (update_root_node) {
|
||||
ret = bdrv_child_try_set_aio_context(bs, new_context, blk->root,
|
||||
errp);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
if (tgm->throttle_state) {
|
||||
bdrv_drained_begin(bs);
|
||||
throttle_group_detach_aio_context(tgm);
|
||||
throttle_group_attach_aio_context(tgm, new_context);
|
||||
bdrv_drained_end(bs);
|
||||
}
|
||||
if (update_root_node) {
|
||||
GSList *ignore = g_slist_prepend(NULL, blk->root);
|
||||
bdrv_set_aio_context_ignore(bs, new_context, &ignore);
|
||||
g_slist_free(ignore);
|
||||
}
|
||||
}
|
||||
|
||||
blk->ctx = new_context;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void blk_set_aio_context(BlockBackend *blk, AioContext *new_context)
|
||||
int blk_set_aio_context(BlockBackend *blk, AioContext *new_context,
|
||||
Error **errp)
|
||||
{
|
||||
blk_do_set_aio_context(blk, new_context, true);
|
||||
return blk_do_set_aio_context(blk, new_context, true, errp);
|
||||
}
|
||||
|
||||
static bool blk_root_can_set_aio_ctx(BdrvChild *child, AioContext *ctx,
|
||||
@@ -1915,7 +1932,7 @@ static void blk_root_set_aio_ctx(BdrvChild *child, AioContext *ctx,
|
||||
GSList **ignore)
|
||||
{
|
||||
BlockBackend *blk = child->opaque;
|
||||
blk_do_set_aio_context(blk, ctx, false);
|
||||
blk_do_set_aio_context(blk, ctx, false, &error_abort);
|
||||
}
|
||||
|
||||
void blk_add_aio_context_notifier(BlockBackend *blk,
|
||||
|
||||
+7
-6
@@ -301,7 +301,6 @@ void commit_start(const char *job_id, BlockDriverState *bs,
|
||||
commit_top_bs->implicit = true;
|
||||
}
|
||||
commit_top_bs->total_sectors = top->total_sectors;
|
||||
bdrv_set_aio_context(commit_top_bs, bdrv_get_aio_context(top));
|
||||
|
||||
bdrv_append(commit_top_bs, top, &local_err);
|
||||
if (local_err) {
|
||||
@@ -338,7 +337,8 @@ void commit_start(const char *job_id, BlockDriverState *bs,
|
||||
goto fail;
|
||||
}
|
||||
|
||||
s->base = blk_new(BLK_PERM_CONSISTENT_READ
|
||||
s->base = blk_new(s->common.job.aio_context,
|
||||
BLK_PERM_CONSISTENT_READ
|
||||
| BLK_PERM_WRITE
|
||||
| BLK_PERM_RESIZE,
|
||||
BLK_PERM_CONSISTENT_READ
|
||||
@@ -351,7 +351,7 @@ void commit_start(const char *job_id, BlockDriverState *bs,
|
||||
s->base_bs = base;
|
||||
|
||||
/* Required permissions are already taken with block_job_add_bdrv() */
|
||||
s->top = blk_new(0, BLK_PERM_ALL);
|
||||
s->top = blk_new(s->common.job.aio_context, 0, BLK_PERM_ALL);
|
||||
ret = blk_insert_bs(s->top, top, errp);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
@@ -395,6 +395,7 @@ int bdrv_commit(BlockDriverState *bs)
|
||||
BlockDriverState *backing_file_bs = NULL;
|
||||
BlockDriverState *commit_top_bs = NULL;
|
||||
BlockDriver *drv = bs->drv;
|
||||
AioContext *ctx;
|
||||
int64_t offset, length, backing_length;
|
||||
int ro;
|
||||
int64_t n;
|
||||
@@ -422,8 +423,9 @@ int bdrv_commit(BlockDriverState *bs)
|
||||
}
|
||||
}
|
||||
|
||||
src = blk_new(BLK_PERM_CONSISTENT_READ, BLK_PERM_ALL);
|
||||
backing = blk_new(BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
|
||||
ctx = bdrv_get_aio_context(bs);
|
||||
src = blk_new(ctx, BLK_PERM_CONSISTENT_READ, BLK_PERM_ALL);
|
||||
backing = blk_new(ctx, BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
|
||||
|
||||
ret = blk_insert_bs(src, bs, &local_err);
|
||||
if (ret < 0) {
|
||||
@@ -440,7 +442,6 @@ int bdrv_commit(BlockDriverState *bs)
|
||||
error_report_err(local_err);
|
||||
goto ro_cleanup;
|
||||
}
|
||||
bdrv_set_aio_context(commit_top_bs, bdrv_get_aio_context(backing_file_bs));
|
||||
|
||||
bdrv_set_backing_hd(commit_top_bs, backing_file_bs, &error_abort);
|
||||
bdrv_set_backing_hd(bs, commit_top_bs, &error_abort);
|
||||
|
||||
+2
-1
@@ -257,7 +257,8 @@ static int block_crypto_co_create_generic(BlockDriverState *bs,
|
||||
QCryptoBlock *crypto = NULL;
|
||||
struct BlockCryptoCreateData data;
|
||||
|
||||
blk = blk_new(BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
|
||||
blk = blk_new(bdrv_get_aio_context(bs),
|
||||
BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
|
||||
|
||||
ret = blk_insert_bs(blk, bs, errp);
|
||||
if (ret < 0) {
|
||||
|
||||
+18
-10
@@ -422,11 +422,12 @@ static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive,
|
||||
return;
|
||||
}
|
||||
assert(bs->quiesce_counter > 0);
|
||||
old_quiesce_counter = atomic_fetch_dec(&bs->quiesce_counter);
|
||||
|
||||
/* Re-enable things in child-to-parent order */
|
||||
bdrv_drain_invoke(bs, false);
|
||||
bdrv_parent_drained_end(bs, parent, ignore_bds_parents);
|
||||
|
||||
old_quiesce_counter = atomic_fetch_dec(&bs->quiesce_counter);
|
||||
if (old_quiesce_counter == 1) {
|
||||
aio_enable_external(bdrv_get_aio_context(bs));
|
||||
}
|
||||
@@ -2092,6 +2093,12 @@ static int coroutine_fn bdrv_co_block_status(BlockDriverState *bs,
|
||||
*/
|
||||
assert(*pnum && QEMU_IS_ALIGNED(*pnum, align) &&
|
||||
align > offset - aligned_offset);
|
||||
if (ret & BDRV_BLOCK_RECURSE) {
|
||||
assert(ret & BDRV_BLOCK_DATA);
|
||||
assert(ret & BDRV_BLOCK_OFFSET_VALID);
|
||||
assert(!(ret & BDRV_BLOCK_ZERO));
|
||||
}
|
||||
|
||||
*pnum -= offset - aligned_offset;
|
||||
if (*pnum > bytes) {
|
||||
*pnum = bytes;
|
||||
@@ -2122,7 +2129,8 @@ static int coroutine_fn bdrv_co_block_status(BlockDriverState *bs,
|
||||
}
|
||||
}
|
||||
|
||||
if (want_zero && local_file && local_file != bs &&
|
||||
if (want_zero && ret & BDRV_BLOCK_RECURSE &&
|
||||
local_file && local_file != bs &&
|
||||
(ret & BDRV_BLOCK_DATA) && !(ret & BDRV_BLOCK_ZERO) &&
|
||||
(ret & BDRV_BLOCK_OFFSET_VALID)) {
|
||||
int64_t file_pnum;
|
||||
@@ -2624,7 +2632,7 @@ int bdrv_flush(BlockDriverState *bs)
|
||||
typedef struct DiscardCo {
|
||||
BdrvChild *child;
|
||||
int64_t offset;
|
||||
int bytes;
|
||||
int64_t bytes;
|
||||
int ret;
|
||||
} DiscardCo;
|
||||
static void coroutine_fn bdrv_pdiscard_co_entry(void *opaque)
|
||||
@@ -2635,14 +2643,15 @@ static void coroutine_fn bdrv_pdiscard_co_entry(void *opaque)
|
||||
aio_wait_kick();
|
||||
}
|
||||
|
||||
int coroutine_fn bdrv_co_pdiscard(BdrvChild *child, int64_t offset, int bytes)
|
||||
int coroutine_fn bdrv_co_pdiscard(BdrvChild *child, int64_t offset,
|
||||
int64_t bytes)
|
||||
{
|
||||
BdrvTrackedRequest req;
|
||||
int max_pdiscard, ret;
|
||||
int head, tail, align;
|
||||
BlockDriverState *bs = child->bs;
|
||||
|
||||
if (!bs || !bs->drv) {
|
||||
if (!bs || !bs->drv || !bdrv_is_inserted(bs)) {
|
||||
return -ENOMEDIUM;
|
||||
}
|
||||
|
||||
@@ -2650,9 +2659,8 @@ int coroutine_fn bdrv_co_pdiscard(BdrvChild *child, int64_t offset, int bytes)
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
ret = bdrv_check_byte_request(bs, offset, bytes);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
if (offset < 0 || bytes < 0 || bytes > INT64_MAX - offset) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* Do nothing if disabled. */
|
||||
@@ -2687,7 +2695,7 @@ int coroutine_fn bdrv_co_pdiscard(BdrvChild *child, int64_t offset, int bytes)
|
||||
assert(max_pdiscard >= bs->bl.request_alignment);
|
||||
|
||||
while (bytes > 0) {
|
||||
int num = bytes;
|
||||
int64_t num = bytes;
|
||||
|
||||
if (head) {
|
||||
/* Make small requests to get to alignment boundaries. */
|
||||
@@ -2749,7 +2757,7 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int bdrv_pdiscard(BdrvChild *child, int64_t offset, int bytes)
|
||||
int bdrv_pdiscard(BdrvChild *child, int64_t offset, int64_t bytes)
|
||||
{
|
||||
Coroutine *co;
|
||||
DiscardCo rwco = {
|
||||
|
||||
+10
-62
@@ -30,7 +30,6 @@
|
||||
#define MAX_EVENTS 128
|
||||
|
||||
struct qemu_laiocb {
|
||||
BlockAIOCB common;
|
||||
Coroutine *co;
|
||||
LinuxAioState *ctx;
|
||||
struct iocb iocb;
|
||||
@@ -72,7 +71,7 @@ static inline ssize_t io_event_ret(struct io_event *ev)
|
||||
}
|
||||
|
||||
/*
|
||||
* Completes an AIO request (calls the callback and frees the ACB).
|
||||
* Completes an AIO request.
|
||||
*/
|
||||
static void qemu_laio_process_completion(struct qemu_laiocb *laiocb)
|
||||
{
|
||||
@@ -94,18 +93,15 @@ static void qemu_laio_process_completion(struct qemu_laiocb *laiocb)
|
||||
}
|
||||
|
||||
laiocb->ret = ret;
|
||||
if (laiocb->co) {
|
||||
/* If the coroutine is already entered it must be in ioq_submit() and
|
||||
* will notice laio->ret has been filled in when it eventually runs
|
||||
* later. Coroutines cannot be entered recursively so avoid doing
|
||||
* that!
|
||||
*/
|
||||
if (!qemu_coroutine_entered(laiocb->co)) {
|
||||
aio_co_wake(laiocb->co);
|
||||
}
|
||||
} else {
|
||||
laiocb->common.cb(laiocb->common.opaque, ret);
|
||||
qemu_aio_unref(laiocb);
|
||||
|
||||
/*
|
||||
* If the coroutine is already entered it must be in ioq_submit() and
|
||||
* will notice laio->ret has been filled in when it eventually runs
|
||||
* later. Coroutines cannot be entered recursively so avoid doing
|
||||
* that!
|
||||
*/
|
||||
if (!qemu_coroutine_entered(laiocb->co)) {
|
||||
aio_co_wake(laiocb->co);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -273,30 +269,6 @@ static bool qemu_laio_poll_cb(void *opaque)
|
||||
return true;
|
||||
}
|
||||
|
||||
static void laio_cancel(BlockAIOCB *blockacb)
|
||||
{
|
||||
struct qemu_laiocb *laiocb = (struct qemu_laiocb *)blockacb;
|
||||
struct io_event event;
|
||||
int ret;
|
||||
|
||||
if (laiocb->ret != -EINPROGRESS) {
|
||||
return;
|
||||
}
|
||||
ret = io_cancel(laiocb->ctx->ctx, &laiocb->iocb, &event);
|
||||
laiocb->ret = -ECANCELED;
|
||||
if (ret != 0) {
|
||||
/* iocb is not cancelled, cb will be called by the event loop later */
|
||||
return;
|
||||
}
|
||||
|
||||
laiocb->common.cb(laiocb->common.opaque, laiocb->ret);
|
||||
}
|
||||
|
||||
static const AIOCBInfo laio_aiocb_info = {
|
||||
.aiocb_size = sizeof(struct qemu_laiocb),
|
||||
.cancel_async = laio_cancel,
|
||||
};
|
||||
|
||||
static void ioq_init(LaioQueue *io_q)
|
||||
{
|
||||
QSIMPLEQ_INIT(&io_q->pending);
|
||||
@@ -431,30 +403,6 @@ int coroutine_fn laio_co_submit(BlockDriverState *bs, LinuxAioState *s, int fd,
|
||||
return laiocb.ret;
|
||||
}
|
||||
|
||||
BlockAIOCB *laio_submit(BlockDriverState *bs, LinuxAioState *s, int fd,
|
||||
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
|
||||
BlockCompletionFunc *cb, void *opaque, int type)
|
||||
{
|
||||
struct qemu_laiocb *laiocb;
|
||||
off_t offset = sector_num * BDRV_SECTOR_SIZE;
|
||||
int ret;
|
||||
|
||||
laiocb = qemu_aio_get(&laio_aiocb_info, bs, cb, opaque);
|
||||
laiocb->nbytes = nb_sectors * BDRV_SECTOR_SIZE;
|
||||
laiocb->ctx = s;
|
||||
laiocb->ret = -EINPROGRESS;
|
||||
laiocb->is_read = (type == QEMU_AIO_READ);
|
||||
laiocb->qiov = qiov;
|
||||
|
||||
ret = laio_do_submit(fd, laiocb, offset, type);
|
||||
if (ret < 0) {
|
||||
qemu_aio_unref(laiocb);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return &laiocb->common;
|
||||
}
|
||||
|
||||
void laio_detach_aio_context(LinuxAioState *s, AioContext *old_context)
|
||||
{
|
||||
aio_set_event_notifier(old_context, &s->e, false, NULL, NULL);
|
||||
|
||||
+2
-2
@@ -1543,7 +1543,6 @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs,
|
||||
BDRV_REQ_NO_FALLBACK;
|
||||
bs_opaque = g_new0(MirrorBDSOpaque, 1);
|
||||
mirror_top_bs->opaque = bs_opaque;
|
||||
bdrv_set_aio_context(mirror_top_bs, bdrv_get_aio_context(bs));
|
||||
|
||||
/* bdrv_append takes ownership of the mirror_top_bs reference, need to keep
|
||||
* it alive until block_job_create() succeeds even if bs has no parent. */
|
||||
@@ -1584,7 +1583,8 @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs,
|
||||
* We can allow anything except resize there.*/
|
||||
target_is_backing = bdrv_chain_contains(bs, target);
|
||||
target_graph_mod = (backing_mode != MIRROR_LEAVE_BACKING_CHAIN);
|
||||
s->target = blk_new(BLK_PERM_WRITE | BLK_PERM_RESIZE |
|
||||
s->target = blk_new(s->common.job.aio_context,
|
||||
BLK_PERM_WRITE | BLK_PERM_RESIZE |
|
||||
(target_graph_mod ? BLK_PERM_GRAPH_MOD : 0),
|
||||
BLK_PERM_WRITE_UNCHANGED |
|
||||
(target_is_backing ? BLK_PERM_CONSISTENT_READ |
|
||||
|
||||
+2
-1
@@ -554,7 +554,8 @@ static int coroutine_fn parallels_co_create(BlockdevCreateOptions* opts,
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
blk = blk_new(BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
|
||||
blk = blk_new(bdrv_get_aio_context(bs),
|
||||
BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
|
||||
ret = blk_insert_bs(blk, bs, errp);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
|
||||
+2
-1
@@ -844,7 +844,8 @@ static int coroutine_fn qcow_co_create(BlockdevCreateOptions *opts,
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
qcow_blk = blk_new(BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
|
||||
qcow_blk = blk_new(bdrv_get_aio_context(bs),
|
||||
BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
|
||||
ret = blk_insert_bs(qcow_blk, bs, errp);
|
||||
if (ret < 0) {
|
||||
goto exit;
|
||||
|
||||
+38
-1
@@ -29,6 +29,7 @@
|
||||
#include "qemu/range.h"
|
||||
#include "qemu/bswap.h"
|
||||
#include "qemu/cutils.h"
|
||||
#include "trace.h"
|
||||
|
||||
static int64_t alloc_clusters_noref(BlockDriverState *bs, uint64_t size,
|
||||
uint64_t max);
|
||||
@@ -737,7 +738,11 @@ void qcow2_process_discards(BlockDriverState *bs, int ret)
|
||||
|
||||
/* Discard is optional, ignore the return value */
|
||||
if (ret >= 0) {
|
||||
bdrv_pdiscard(bs->file, d->offset, d->bytes);
|
||||
int r2 = bdrv_pdiscard(bs->file, d->offset, d->bytes);
|
||||
if (r2 < 0) {
|
||||
trace_qcow2_process_discards_failed_region(d->offset, d->bytes,
|
||||
r2);
|
||||
}
|
||||
}
|
||||
|
||||
g_free(d);
|
||||
@@ -3444,3 +3449,35 @@ int64_t qcow2_get_last_cluster(BlockDriverState *bs, int64_t size)
|
||||
"There are no references in the refcount table.");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
int qcow2_detect_metadata_preallocation(BlockDriverState *bs)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
int64_t i, end_cluster, cluster_count = 0, threshold;
|
||||
int64_t file_length, real_allocation, real_clusters;
|
||||
|
||||
file_length = bdrv_getlength(bs->file->bs);
|
||||
if (file_length < 0) {
|
||||
return file_length;
|
||||
}
|
||||
|
||||
real_allocation = bdrv_get_allocated_file_size(bs->file->bs);
|
||||
if (real_allocation < 0) {
|
||||
return real_allocation;
|
||||
}
|
||||
|
||||
real_clusters = real_allocation / s->cluster_size;
|
||||
threshold = MAX(real_clusters * 10 / 9, real_clusters + 2);
|
||||
|
||||
end_cluster = size_to_clusters(s, file_length);
|
||||
for (i = 0; i < end_cluster && cluster_count < threshold; i++) {
|
||||
uint64_t refcount;
|
||||
int ret = qcow2_get_refcount(bs, i, &refcount);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
cluster_count += !!refcount;
|
||||
}
|
||||
|
||||
return cluster_count >= threshold;
|
||||
}
|
||||
|
||||
+15
-2
@@ -1895,6 +1895,12 @@ static int coroutine_fn qcow2_co_block_status(BlockDriverState *bs,
|
||||
unsigned int bytes;
|
||||
int status = 0;
|
||||
|
||||
if (!s->metadata_preallocation_checked) {
|
||||
ret = qcow2_detect_metadata_preallocation(bs);
|
||||
s->metadata_preallocation = (ret == 1);
|
||||
s->metadata_preallocation_checked = true;
|
||||
}
|
||||
|
||||
bytes = MIN(INT_MAX, count);
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
ret = qcow2_get_cluster_offset(bs, offset, &bytes, &cluster_offset);
|
||||
@@ -1917,6 +1923,11 @@ static int coroutine_fn qcow2_co_block_status(BlockDriverState *bs,
|
||||
} else if (ret != QCOW2_CLUSTER_UNALLOCATED) {
|
||||
status |= BDRV_BLOCK_DATA;
|
||||
}
|
||||
if (s->metadata_preallocation && (status & BDRV_BLOCK_DATA) &&
|
||||
(status & BDRV_BLOCK_OFFSET_VALID))
|
||||
{
|
||||
status |= BDRV_BLOCK_RECURSE;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -3163,7 +3174,8 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp)
|
||||
}
|
||||
|
||||
/* Create BlockBackend to write to the image */
|
||||
blk = blk_new(BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
|
||||
blk = blk_new(bdrv_get_aio_context(bs),
|
||||
BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
|
||||
ret = blk_insert_bs(blk, bs, errp);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
@@ -4995,7 +5007,8 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
|
||||
}
|
||||
|
||||
if (new_size) {
|
||||
BlockBackend *blk = blk_new(BLK_PERM_RESIZE, BLK_PERM_ALL);
|
||||
BlockBackend *blk = blk_new(bdrv_get_aio_context(bs),
|
||||
BLK_PERM_RESIZE, BLK_PERM_ALL);
|
||||
ret = blk_insert_bs(blk, bs, errp);
|
||||
if (ret < 0) {
|
||||
blk_unref(blk);
|
||||
|
||||
@@ -356,6 +356,9 @@ typedef struct BDRVQcow2State {
|
||||
int nb_threads;
|
||||
|
||||
BdrvChild *data_file;
|
||||
|
||||
bool metadata_preallocation_checked;
|
||||
bool metadata_preallocation;
|
||||
} BDRVQcow2State;
|
||||
|
||||
typedef struct Qcow2COWRegion {
|
||||
@@ -655,6 +658,7 @@ int qcow2_change_refcount_order(BlockDriverState *bs, int refcount_order,
|
||||
void *cb_opaque, Error **errp);
|
||||
int qcow2_shrink_reftable(BlockDriverState *bs);
|
||||
int64_t qcow2_get_last_cluster(BlockDriverState *bs, int64_t size);
|
||||
int qcow2_detect_metadata_preallocation(BlockDriverState *bs);
|
||||
|
||||
/* qcow2-cluster.c functions */
|
||||
int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size,
|
||||
|
||||
+2
-1
@@ -649,7 +649,8 @@ static int coroutine_fn bdrv_qed_co_create(BlockdevCreateOptions *opts,
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
blk = blk_new(BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
|
||||
blk = blk_new(bdrv_get_aio_context(bs),
|
||||
BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
|
||||
ret = blk_insert_bs(blk, bs, errp);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
|
||||
+2
-1
@@ -1800,7 +1800,8 @@ static int sd_prealloc(BlockDriverState *bs, int64_t old_size, int64_t new_size,
|
||||
void *buf = NULL;
|
||||
int ret;
|
||||
|
||||
blk = blk_new(BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE | BLK_PERM_RESIZE,
|
||||
blk = blk_new(bdrv_get_aio_context(bs),
|
||||
BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE | BLK_PERM_RESIZE,
|
||||
BLK_PERM_ALL);
|
||||
|
||||
ret = blk_insert_bs(blk, bs, errp);
|
||||
|
||||
@@ -92,6 +92,9 @@ qcow2_cache_get_done(void *co, int c, int i) "co %p is_l2_cache %d index %d"
|
||||
qcow2_cache_flush(void *co, int c) "co %p is_l2_cache %d"
|
||||
qcow2_cache_entry_flush(void *co, int c, int i) "co %p is_l2_cache %d index %d"
|
||||
|
||||
# qcow2-refcount.c
|
||||
qcow2_process_discards_failed_region(uint64_t offset, uint64_t bytes, int ret) "offset 0x%" PRIx64 " bytes 0x%" PRIx64 " ret %d"
|
||||
|
||||
# qed-l2-cache.c
|
||||
qed_alloc_l2_cache_entry(void *l2_cache, void *entry) "l2_cache %p entry %p"
|
||||
qed_unref_l2_cache_entry(void *entry, int ref) "entry %p ref %d"
|
||||
|
||||
+2
-1
@@ -803,7 +803,8 @@ static int coroutine_fn vdi_co_do_create(BlockdevCreateOptions *create_options,
|
||||
goto exit;
|
||||
}
|
||||
|
||||
blk = blk_new(BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
|
||||
blk = blk_new(bdrv_get_aio_context(bs_file),
|
||||
BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
|
||||
ret = blk_insert_bs(blk, bs_file, errp);
|
||||
if (ret < 0) {
|
||||
goto exit;
|
||||
|
||||
+2
-1
@@ -1900,7 +1900,8 @@ static int coroutine_fn vhdx_co_create(BlockdevCreateOptions *opts,
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
blk = blk_new(BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
|
||||
blk = blk_new(bdrv_get_aio_context(bs),
|
||||
BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
|
||||
ret = blk_insert_bs(blk, bs, errp);
|
||||
if (ret < 0) {
|
||||
goto delete_and_exit;
|
||||
|
||||
+2
-1
@@ -2356,7 +2356,8 @@ static BlockBackend *vmdk_co_create_cb(int64_t size, int idx,
|
||||
if (!bs) {
|
||||
return NULL;
|
||||
}
|
||||
blk = blk_new(BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE | BLK_PERM_RESIZE,
|
||||
blk = blk_new(bdrv_get_aio_context(bs),
|
||||
BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE | BLK_PERM_RESIZE,
|
||||
BLK_PERM_ALL);
|
||||
if (blk_insert_bs(blk, bs, errp)) {
|
||||
bdrv_unref(bs);
|
||||
|
||||
+2
-1
@@ -1011,7 +1011,8 @@ static int coroutine_fn vpc_co_create(BlockdevCreateOptions *opts,
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
blk = blk_new(BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
|
||||
blk = blk_new(bdrv_get_aio_context(bs),
|
||||
BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
|
||||
ret = blk_insert_bs(blk, bs, errp);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user