Merge QEMU v10.2.0

This commit is contained in:
Matt Borgerson
2026-01-18 16:36:55 -07:00
5172 changed files with 276005 additions and 130634 deletions
+16 -3
View File
@@ -27,7 +27,8 @@
#include "block/accounting.h"
#include "block/block_int.h"
#include "qemu/timer.h"
#include "sysemu/qtest.h"
#include "system/qtest.h"
#include "qapi/error.h"
static QEMUClockType clock_type = QEMU_CLOCK_REALTIME;
static const int qtest_latency_ns = NANOSECONDS_PER_SECOND / 1000;
@@ -56,13 +57,25 @@ static bool bool_from_onoffauto(OnOffAuto val, bool def)
}
}
void block_acct_setup(BlockAcctStats *stats, enum OnOffAuto account_invalid,
enum OnOffAuto account_failed)
bool block_acct_setup(BlockAcctStats *stats, enum OnOffAuto account_invalid,
enum OnOffAuto account_failed, uint32_t *stats_intervals,
uint32_t num_stats_intervals, Error **errp)
{
stats->account_invalid = bool_from_onoffauto(account_invalid,
stats->account_invalid);
stats->account_failed = bool_from_onoffauto(account_failed,
stats->account_failed);
if (stats_intervals) {
for (int i = 0; i < num_stats_intervals; i++) {
if (stats_intervals[i] <= 0) {
error_setg(errp, "Invalid interval length: %u", stats_intervals[i]);
return false;
}
block_acct_add_interval(stats, stats_intervals[i]);
}
g_free(stats_intervals);
}
return true;
}
void block_acct_cleanup(BlockAcctStats *stats)
+4 -3
View File
@@ -23,7 +23,7 @@
#include "block/dirty-bitmap.h"
#include "qapi/error.h"
#include "qemu/cutils.h"
#include "sysemu/block-backend.h"
#include "system/block-backend.h"
#include "qemu/bitmap.h"
#include "qemu/error-report.h"
@@ -361,6 +361,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
BackupPerf *perf,
BlockdevOnError on_source_error,
BlockdevOnError on_target_error,
OnCbwError on_cbw_error,
int creation_flags,
BlockCompletionFunc *cb, void *opaque,
JobTxn *txn, Error **errp)
@@ -458,7 +459,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
}
cbw = bdrv_cbw_append(bs, target, filter_node_name, discard_source,
perf->min_cluster_size, &bcs, errp);
perf->min_cluster_size, &bcs, on_cbw_error, errp);
if (!cbw) {
goto error;
}
@@ -497,7 +498,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
block_copy_set_speed(bcs, speed);
/* Required permissions are taken by copy-before-write filter target */
bdrv_graph_wrlock();
bdrv_graph_wrlock_drained();
block_job_add_bdrv(&job->common, "target", target, 0, BLK_PERM_ALL,
&error_abort);
bdrv_graph_wrunlock();
+7 -7
View File
@@ -33,11 +33,11 @@
#include "qemu/module.h"
#include "qemu/option.h"
#include "qapi/qapi-visit-block-core.h"
#include "qapi/qmp/qdict.h"
#include "qapi/qmp/qlist.h"
#include "qapi/qmp/qstring.h"
#include "qobject/qdict.h"
#include "qobject/qlist.h"
#include "qobject/qstring.h"
#include "qapi/qobject-input-visitor.h"
#include "sysemu/qtest.h"
#include "system/qtest.h"
/* All APIs are thread-safe */
@@ -751,9 +751,9 @@ blkdebug_co_pdiscard(BlockDriverState *bs, int64_t offset, int64_t bytes)
}
static int coroutine_fn GRAPH_RDLOCK
blkdebug_co_block_status(BlockDriverState *bs, bool want_zero, int64_t offset,
int64_t bytes, int64_t *pnum, int64_t *map,
BlockDriverState **file)
blkdebug_co_block_status(BlockDriverState *bs, unsigned int mode,
int64_t offset, int64_t bytes, int64_t *pnum,
int64_t *map, BlockDriverState **file)
{
int err;
+4 -4
View File
@@ -11,15 +11,15 @@
#include "qemu/osdep.h"
#include <blkio.h>
#include "block/block_int.h"
#include "exec/memory.h"
#include "system/memory.h"
#include "exec/cpu-common.h" /* for qemu_ram_get_fd() */
#include "qemu/defer-call.h"
#include "qapi/error.h"
#include "qemu/error-report.h"
#include "qapi/qmp/qdict.h"
#include "qobject/qdict.h"
#include "qemu/module.h"
#include "sysemu/block-backend.h"
#include "exec/memory.h" /* for ram_block_discard_disable() */
#include "system/block-backend.h"
#include "system/memory.h" /* for ram_block_discard_disable() */
#include "block/block-io.h"
+4 -4
View File
@@ -14,8 +14,8 @@
#include "qemu/sockets.h" /* for EINPROGRESS on Windows */
#include "block/block-io.h"
#include "block/block_int.h"
#include "qapi/qmp/qdict.h"
#include "qapi/qmp/qstring.h"
#include "qobject/qdict.h"
#include "qobject/qstring.h"
#include "qemu/cutils.h"
#include "qemu/module.h"
#include "qemu/option.h"
@@ -281,7 +281,7 @@ static int blk_log_writes_open(BlockDriverState *bs, QDict *options, int flags,
ret = 0;
fail_log:
if (ret < 0) {
bdrv_graph_wrlock();
bdrv_graph_wrlock_drained();
bdrv_unref_child(bs, s->log_file);
bdrv_graph_wrunlock();
s->log_file = NULL;
@@ -296,7 +296,7 @@ static void blk_log_writes_close(BlockDriverState *bs)
{
BDRVBlkLogWritesState *s = bs->opaque;
bdrv_graph_wrlock();
bdrv_graph_wrlock_drained();
bdrv_unref_child(bs, s->log_file);
s->log_file = NULL;
bdrv_graph_wrunlock();
+3 -2
View File
@@ -13,7 +13,7 @@
#include "qemu/module.h"
#include "block/block-io.h"
#include "block/block_int.h"
#include "sysemu/replay.h"
#include "system/replay.h"
#include "qapi/error.h"
typedef struct Request {
@@ -63,9 +63,10 @@ static void block_request_create(uint64_t reqid, BlockDriverState *bs,
Coroutine *co)
{
Request *req = g_new(Request, 1);
AioContext *ctx = qemu_coroutine_get_aio_context(co);
*req = (Request) {
.co = co,
.bh = aio_bh_new(bdrv_get_aio_context(bs), blkreplay_bh_cb, req),
.bh = aio_bh_new(ctx, blkreplay_bh_cb, req),
};
replay_block_event(req->bh, reqid);
}
+3 -3
View File
@@ -12,8 +12,8 @@
#include "qemu/sockets.h" /* for EINPROGRESS on Windows */
#include "block/block-io.h"
#include "block/block_int.h"
#include "qapi/qmp/qdict.h"
#include "qapi/qmp/qstring.h"
#include "qobject/qdict.h"
#include "qobject/qstring.h"
#include "qemu/cutils.h"
#include "qemu/module.h"
#include "qemu/option.h"
@@ -151,7 +151,7 @@ static void blkverify_close(BlockDriverState *bs)
{
BDRVBlkverifyState *s = bs->opaque;
bdrv_graph_wrlock();
bdrv_graph_wrlock_drained();
bdrv_unref_child(bs, s->test_file);
s->test_file = NULL;
bdrv_graph_wrunlock();
+48 -33
View File
@@ -11,15 +11,15 @@
*/
#include "qemu/osdep.h"
#include "sysemu/block-backend.h"
#include "system/block-backend.h"
#include "block/block_int.h"
#include "block/blockjob.h"
#include "block/coroutines.h"
#include "block/throttle-groups.h"
#include "hw/qdev-core.h"
#include "sysemu/blockdev.h"
#include "sysemu/runstate.h"
#include "sysemu/replay.h"
#include "system/blockdev.h"
#include "system/runstate.h"
#include "system/replay.h"
#include "qapi/error.h"
#include "qapi/qapi-events-block.h"
#include "qemu/id.h"
@@ -136,9 +136,9 @@ 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 bool blk_root_change_aio_ctx(BdrvChild *child, AioContext *ctx,
GHashTable *visited, Transaction *tran,
Error **errp);
static bool GRAPH_RDLOCK
blk_root_change_aio_ctx(BdrvChild *child, AioContext *ctx, GHashTable *visited,
Transaction *tran, Error **errp);
static char *blk_root_get_parent_desc(BdrvChild *child)
{
@@ -253,7 +253,7 @@ static bool blk_can_inactivate(BlockBackend *blk)
* guest. For block job BBs that satisfy this, we can just allow
* it. This is the case for mirror job source, which is required
* by libvirt non-shared block migration. */
if (!(blk->perm & (BLK_PERM_WRITE | BLK_PERM_WRITE_UNCHANGED))) {
if (!(blk->perm & ~BLK_PERM_CONSISTENT_READ)) {
return true;
}
@@ -889,7 +889,7 @@ void blk_remove_bs(BlockBackend *blk)
root = blk->root;
blk->root = NULL;
bdrv_graph_wrlock();
bdrv_graph_wrlock_drained();
bdrv_root_unref_child(root);
bdrv_graph_wrunlock();
}
@@ -900,14 +900,24 @@ void blk_remove_bs(BlockBackend *blk)
int blk_insert_bs(BlockBackend *blk, BlockDriverState *bs, Error **errp)
{
ThrottleGroupMember *tgm = &blk->public.throttle_group_member;
uint64_t perm, shared_perm;
GLOBAL_STATE_CODE();
bdrv_ref(bs);
bdrv_graph_wrlock();
bdrv_graph_wrlock_drained();
if ((bs->open_flags & BDRV_O_INACTIVE) && blk_can_inactivate(blk)) {
blk->disable_perm = true;
perm = 0;
shared_perm = BLK_PERM_ALL;
} else {
perm = blk->perm;
shared_perm = blk->shared_perm;
}
blk->root = bdrv_root_attach_child(bs, "root", &child_root,
BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY,
blk->perm, blk->shared_perm,
blk, errp);
perm, shared_perm, blk, errp);
bdrv_graph_wrunlock();
if (blk->root == NULL) {
return -EPERM;
@@ -1019,6 +1029,10 @@ DeviceState *blk_get_attached_dev(BlockBackend *blk)
return blk->dev;
}
/*
* The caller is responsible for releasing the value returned
* with g_free() after use.
*/
static char *blk_get_attached_dev_id_or_path(BlockBackend *blk, bool want_id)
{
DeviceState *dev = blk->dev;
@@ -1033,15 +1047,15 @@ static char *blk_get_attached_dev_id_or_path(BlockBackend *blk, bool want_id)
return object_get_canonical_path(OBJECT(dev)) ?: g_strdup("");
}
/*
* Return the qdev ID, or if no ID is assigned the QOM path, of the block
* device attached to the BlockBackend.
*/
char *blk_get_attached_dev_id(BlockBackend *blk)
{
return blk_get_attached_dev_id_or_path(blk, true);
}
/*
* The caller is responsible for releasing the value returned
* with g_free() after use.
*/
static char *blk_get_attached_dev_path(BlockBackend *blk)
{
return blk_get_attached_dev_id_or_path(blk, false);
@@ -1304,9 +1318,9 @@ static void coroutine_fn blk_wait_while_drained(BlockBackend *blk)
* section.
*/
qemu_mutex_lock(&blk->queued_requests_lock);
/* blk_root_drained_end() has the corresponding blk_inc_in_flight() */
blk_dec_in_flight(blk);
qemu_co_queue_wait(&blk->queued_requests, &blk->queued_requests_lock);
blk_inc_in_flight(blk);
qemu_mutex_unlock(&blk->queued_requests_lock);
}
}
@@ -2134,10 +2148,10 @@ static void send_qmp_error_event(BlockBackend *blk,
{
IoOperationType optype;
BlockDriverState *bs = blk_bs(blk);
g_autofree char *path = blk_get_attached_dev_path(blk);
optype = is_read ? IO_OPERATION_TYPE_READ : IO_OPERATION_TYPE_WRITE;
qapi_event_send_block_io_error(blk_name(blk),
blk_get_attached_dev_path(blk),
qapi_event_send_block_io_error(path, blk_name(blk),
bs ? bdrv_get_node_name(bs) : NULL, optype,
action, blk_iostatus_is_enabled(blk),
error == ENOSPC, strerror(error));
@@ -2291,6 +2305,17 @@ uint32_t blk_get_request_alignment(BlockBackend *blk)
return bs ? bs->bl.request_alignment : BDRV_SECTOR_SIZE;
}
/* Returns the optimal write zeroes alignment, in bytes; guaranteed nonzero */
uint32_t blk_get_pwrite_zeroes_alignment(BlockBackend *blk)
{
BlockDriverState *bs = blk_bs(blk);
IO_CODE();
if (!bs) {
return BDRV_SECTOR_SIZE;
}
return bs->bl.pwrite_zeroes_alignment ?: bs->bl.request_alignment;
}
/* Returns the maximum hardware transfer length, in bytes; guaranteed nonzero */
uint64_t blk_get_max_hw_transfer(BlockBackend *blk)
{
@@ -2343,18 +2368,6 @@ void *blk_blockalign(BlockBackend *blk, size_t size)
return qemu_blockalign(blk ? blk_bs(blk) : NULL, size);
}
bool blk_op_is_blocked(BlockBackend *blk, BlockOpType op, Error **errp)
{
BlockDriverState *bs = blk_bs(blk);
GLOBAL_STATE_CODE();
GRAPH_RDLOCK_GUARD_MAINLOOP();
if (!bs) {
return false;
}
return bdrv_op_is_blocked(bs, op, errp);
}
/**
* Return BB's current AioContext. Note that this context may change
@@ -2765,9 +2778,11 @@ static void blk_root_drained_end(BdrvChild *child)
blk->dev_ops->drained_end(blk->dev_opaque);
}
qemu_mutex_lock(&blk->queued_requests_lock);
while (qemu_co_enter_next(&blk->queued_requests,
&blk->queued_requests_lock)) {
while (!qemu_co_queue_empty(&blk->queued_requests)) {
/* Resume all queued requests */
blk_inc_in_flight(blk);
qemu_co_enter_next(&blk->queued_requests,
&blk->queued_requests_lock);
}
qemu_mutex_unlock(&blk->queued_requests_lock);
}
+1 -1
View File
@@ -20,7 +20,7 @@
#include "block/block_int-io.h"
#include "block/dirty-bitmap.h"
#include "block/reqlist.h"
#include "sysemu/block-backend.h"
#include "system/block-backend.h"
#include "qemu/units.h"
#include "qemu/co-shared-resource.h"
#include "qemu/coroutine.h"
+2 -2
View File
@@ -5,8 +5,8 @@
*/
#include "qemu/osdep.h"
#include "sysemu/block-backend.h"
#include "sysemu/block-ram-registrar.h"
#include "system/block-backend.h"
#include "system/block-ram-registrar.h"
#include "qapi/error.h"
static void ram_block_added(RAMBlockNotifier *n, void *host, size_t size,
+7 -7
View File
@@ -300,15 +300,15 @@ static void bochs_close(BlockDriverState *bs)
}
static BlockDriver bdrv_bochs = {
.format_name = "bochs",
.instance_size = sizeof(BDRVBochsState),
.bdrv_probe = bochs_probe,
.bdrv_open = bochs_open,
.format_name = "bochs",
.instance_size = sizeof(BDRVBochsState),
.bdrv_probe = bochs_probe,
.bdrv_open = bochs_open,
.bdrv_child_perm = bdrv_default_perms,
.bdrv_refresh_limits = bochs_refresh_limits,
.bdrv_co_preadv = bochs_co_preadv,
.bdrv_close = bochs_close,
.is_format = true,
.bdrv_co_preadv = bochs_co_preadv,
.bdrv_close = bochs_close,
.is_format = true,
};
static void bdrv_bochs_init(void)
+108 -40
View File
@@ -15,12 +15,14 @@
#include "qemu/osdep.h"
#include "qemu/cutils.h"
#include "trace.h"
#include "block/block-common.h"
#include "block/coroutines.h"
#include "block/block_int.h"
#include "block/blockjob_int.h"
#include "qapi/error.h"
#include "qemu/ratelimit.h"
#include "qemu/memalign.h"
#include "sysemu/block-backend.h"
#include "system/block-backend.h"
enum {
/*
@@ -66,7 +68,7 @@ static int commit_prepare(Job *job)
s->backing_mask_protocol);
}
static void commit_abort(Job *job)
static void GRAPH_UNLOCKED commit_abort(Job *job)
{
CommitBlockJob *s = container_of(job, CommitBlockJob, common.job);
BlockDriverState *top_bs = blk_bs(s->top);
@@ -126,6 +128,84 @@ static void commit_clean(Job *job)
blk_unref(s->top);
}
static int commit_iteration(CommitBlockJob *s, int64_t offset,
int64_t *requested_bytes, void *buf)
{
BlockErrorAction action;
int64_t bytes = *requested_bytes;
int ret = 0;
bool error_in_source = true;
/* Copy if allocated above the base */
WITH_GRAPH_RDLOCK_GUARD() {
ret = bdrv_co_common_block_status_above(blk_bs(s->top),
s->base_overlay, true, true, offset, COMMIT_BUFFER_SIZE,
&bytes, NULL, NULL, NULL);
}
trace_commit_one_iteration(s, offset, bytes, ret);
if (ret < 0) {
goto fail;
}
if (ret & BDRV_BLOCK_ALLOCATED) {
if (ret & BDRV_BLOCK_ZERO) {
/*
* If the top (sub)clusters are smaller than the base
* (sub)clusters, this will not unmap unless the underlying device
* does some tracking of these requests. Ideally, we would find
* the maximal extent of the zero clusters.
*/
ret = blk_co_pwrite_zeroes(s->base, offset, bytes,
BDRV_REQ_MAY_UNMAP);
if (ret < 0) {
error_in_source = false;
goto fail;
}
} else {
assert(bytes < SIZE_MAX);
ret = blk_co_pread(s->top, offset, bytes, buf, 0);
if (ret < 0) {
goto fail;
}
ret = blk_co_pwrite(s->base, offset, bytes, buf, 0);
if (ret < 0) {
error_in_source = false;
goto fail;
}
}
/*
* Whether zeroes actually end up on disk depends on the details of
* the underlying driver. Therefore, this might rate limit more than
* is necessary.
*/
block_job_ratelimit_processed_bytes(&s->common, bytes);
}
/* Publish progress */
job_progress_update(&s->common.job, bytes);
*requested_bytes = bytes;
return 0;
fail:
action = block_job_error_action(&s->common, s->on_error,
error_in_source, -ret);
if (action == BLOCK_ERROR_ACTION_REPORT) {
return ret;
}
*requested_bytes = 0;
return 0;
}
static int coroutine_fn commit_run(Job *job, Error **errp)
{
CommitBlockJob *s = container_of(job, CommitBlockJob, common.job);
@@ -156,9 +236,6 @@ static int coroutine_fn commit_run(Job *job, Error **errp)
buf = blk_blockalign(s->top, COMMIT_BUFFER_SIZE);
for (offset = 0; offset < len; offset += n) {
bool copy;
bool error_in_source = true;
/* Note that even when no rate limit is applied we need to yield
* with no pending I/O here so that bdrv_drain_all() returns.
*/
@@ -166,38 +243,11 @@ static int coroutine_fn commit_run(Job *job, Error **errp)
if (job_is_cancelled(&s->common.job)) {
break;
}
/* Copy if allocated above the base */
ret = blk_co_is_allocated_above(s->top, s->base_overlay, true,
offset, COMMIT_BUFFER_SIZE, &n);
copy = (ret > 0);
trace_commit_one_iteration(s, offset, n, ret);
if (copy) {
assert(n < SIZE_MAX);
ret = blk_co_pread(s->top, offset, n, buf, 0);
if (ret >= 0) {
ret = blk_co_pwrite(s->base, offset, n, buf, 0);
if (ret < 0) {
error_in_source = false;
}
}
}
ret = commit_iteration(s, offset, &n, buf);
if (ret < 0) {
BlockErrorAction action =
block_job_error_action(&s->common, s->on_error,
error_in_source, -ret);
if (action == BLOCK_ERROR_ACTION_REPORT) {
return ret;
} else {
n = 0;
continue;
}
}
/* Publish progress */
job_progress_update(&s->common.job, n);
if (copy) {
block_job_ratelimit_processed_bytes(&s->common, n);
return ret;
}
}
@@ -342,7 +392,7 @@ void commit_start(const char *job_id, BlockDriverState *bs,
* this is the responsibility of the interface (i.e. whoever calls
* commit_start()).
*/
bdrv_graph_wrlock();
bdrv_graph_wrlock_drained();
s->base_overlay = bdrv_find_overlay(top, base);
assert(s->base_overlay);
@@ -464,28 +514,32 @@ int bdrv_commit(BlockDriverState *bs)
Error *local_err = NULL;
GLOBAL_STATE_CODE();
GRAPH_RDLOCK_GUARD_MAINLOOP();
if (!drv)
return -ENOMEDIUM;
bdrv_graph_rdlock_main_loop();
backing_file_bs = bdrv_cow_bs(bs);
if (!backing_file_bs) {
return -ENOTSUP;
ret = -ENOTSUP;
goto out;
}
if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_COMMIT_SOURCE, NULL) ||
bdrv_op_is_blocked(backing_file_bs, BLOCK_OP_TYPE_COMMIT_TARGET, NULL))
{
return -EBUSY;
ret = -EBUSY;
goto out;
}
ro = bdrv_is_read_only(backing_file_bs);
if (ro) {
if (bdrv_reopen_set_read_only(backing_file_bs, false, NULL)) {
return -EACCES;
ret = -EACCES;
goto out;
}
}
@@ -509,8 +563,14 @@ int bdrv_commit(BlockDriverState *bs)
goto ro_cleanup;
}
bdrv_graph_rdunlock_main_loop();
bdrv_graph_wrlock_drained();
bdrv_set_backing_hd(commit_top_bs, backing_file_bs, &error_abort);
bdrv_set_backing_hd(bs, commit_top_bs, &error_abort);
bdrv_graph_wrunlock();
bdrv_graph_rdlock_main_loop();
ret = blk_insert_bs(backing, backing_file_bs, &local_err);
if (ret < 0) {
@@ -585,9 +645,14 @@ int bdrv_commit(BlockDriverState *bs)
ret = 0;
ro_cleanup:
blk_unref(backing);
bdrv_graph_rdunlock_main_loop();
bdrv_graph_wrlock_drained();
if (bdrv_cow_bs(bs) != backing_file_bs) {
bdrv_set_backing_hd(bs, backing_file_bs, &error_abort);
}
bdrv_graph_wrunlock();
bdrv_graph_rdlock_main_loop();
bdrv_unref(commit_top_bs);
blk_unref(src);
@@ -596,5 +661,8 @@ ro_cleanup:
bdrv_reopen_set_read_only(backing_file_bs, true, NULL);
}
out:
bdrv_graph_rdunlock_main_loop();
return ret;
}
+6 -4
View File
@@ -24,9 +24,9 @@
*/
#include "qemu/osdep.h"
#include "qapi/qmp/qjson.h"
#include "qobject/qjson.h"
#include "sysemu/block-backend.h"
#include "system/block-backend.h"
#include "qemu/cutils.h"
#include "qapi/error.h"
#include "block/block_int.h"
@@ -291,8 +291,8 @@ cbw_co_preadv_snapshot(BlockDriverState *bs, int64_t offset, int64_t bytes,
}
static int coroutine_fn GRAPH_RDLOCK
cbw_co_snapshot_block_status(BlockDriverState *bs,
bool want_zero, int64_t offset, int64_t bytes,
cbw_co_snapshot_block_status(BlockDriverState *bs, unsigned int mode,
int64_t offset, int64_t bytes,
int64_t *pnum, int64_t *map,
BlockDriverState **file)
{
@@ -551,6 +551,7 @@ BlockDriverState *bdrv_cbw_append(BlockDriverState *source,
bool discard_source,
uint64_t min_cluster_size,
BlockCopyState **bcs,
OnCbwError on_cbw_error,
Error **errp)
{
BDRVCopyBeforeWriteState *state;
@@ -568,6 +569,7 @@ BlockDriverState *bdrv_cbw_append(BlockDriverState *source,
}
qdict_put_str(opts, "file", bdrv_get_node_name(source));
qdict_put_str(opts, "target", bdrv_get_node_name(target));
qdict_put_str(opts, "on-cbw-error", OnCbwError_str(on_cbw_error));
if (min_cluster_size > INT64_MAX) {
error_setg(errp, "min-cluster-size too large: %" PRIu64 " > %" PRIi64,
+1
View File
@@ -42,6 +42,7 @@ BlockDriverState *bdrv_cbw_append(BlockDriverState *source,
bool discard_source,
uint64_t min_cluster_size,
BlockCopyState **bcs,
OnCbwError on_cbw_error,
Error **errp);
void bdrv_cbw_drop(BlockDriverState *bs);
+1 -1
View File
@@ -25,7 +25,7 @@
#include "block/block_int.h"
#include "qemu/module.h"
#include "qapi/error.h"
#include "qapi/qmp/qdict.h"
#include "qobject/qdict.h"
#include "block/copy-on-read.h"
+3 -3
View File
@@ -28,7 +28,7 @@
#include "block/block_int.h"
/* For blk_bs() in generated block/block-gen.c */
#include "sysemu/block-backend.h"
#include "system/block-backend.h"
/*
* I/O API functions. These functions are thread-safe.
@@ -47,7 +47,7 @@ int coroutine_fn GRAPH_RDLOCK
bdrv_co_common_block_status_above(BlockDriverState *bs,
BlockDriverState *base,
bool include_base,
bool want_zero,
unsigned int mode,
int64_t offset,
int64_t bytes,
int64_t *pnum,
@@ -78,7 +78,7 @@ int co_wrapper_mixed_bdrv_rdlock
bdrv_common_block_status_above(BlockDriverState *bs,
BlockDriverState *base,
bool include_base,
bool want_zero,
unsigned int mode,
int64_t offset,
int64_t bytes,
int64_t *pnum,
+25 -11
View File
@@ -22,7 +22,7 @@
#include "block/block_int.h"
#include "block/qdict.h"
#include "sysemu/block-backend.h"
#include "system/block-backend.h"
#include "crypto/block.h"
#include "qapi/opts-visitor.h"
#include "qapi/qapi-visit-crypto.h"
@@ -67,11 +67,18 @@ static int block_crypto_read_func(QCryptoBlock *block,
BlockCrypto *crypto = bs->opaque;
ssize_t ret;
GLOBAL_STATE_CODE();
GRAPH_RDLOCK_GUARD_MAINLOOP();
if (qemu_in_coroutine()) {
GRAPH_RDLOCK_GUARD();
ret = bdrv_pread(crypto->header ? crypto->header : bs->file,
offset, buflen, buf, 0);
ret = bdrv_co_pread(crypto->header ? crypto->header : bs->file,
offset, buflen, buf, 0);
} else {
GLOBAL_STATE_CODE();
GRAPH_RDLOCK_GUARD_MAINLOOP();
ret = bdrv_pread(crypto->header ? crypto->header : bs->file,
offset, buflen, buf, 0);
}
if (ret < 0) {
error_setg_errno(errp, -ret, "Could not read encryption header");
return ret;
@@ -90,11 +97,18 @@ static int block_crypto_write_func(QCryptoBlock *block,
BlockCrypto *crypto = bs->opaque;
ssize_t ret;
GLOBAL_STATE_CODE();
GRAPH_RDLOCK_GUARD_MAINLOOP();
if (qemu_in_coroutine()) {
GRAPH_RDLOCK_GUARD();
ret = bdrv_pwrite(crypto->header ? crypto->header : bs->file,
offset, buflen, buf, 0);
ret = bdrv_co_pwrite(crypto->header ? crypto->header : bs->file,
offset, buflen, buf, 0);
} else {
GLOBAL_STATE_CODE();
GRAPH_RDLOCK_GUARD_MAINLOOP();
ret = bdrv_pwrite(crypto->header ? crypto->header : bs->file,
offset, buflen, buf, 0);
}
if (ret < 0) {
error_setg_errno(errp, -ret, "Could not write encryption header");
return ret;
@@ -792,7 +806,7 @@ block_crypto_co_create_opts_luks(BlockDriver *drv, const char *filename,
char *buf = NULL;
int64_t size;
bool detached_hdr =
qemu_opt_get_bool(opts, "detached-header", false);
qemu_opt_get_bool_del(opts, "detached-header", false);
unsigned int cflags = 0;
int ret;
Error *local_err = NULL;
@@ -821,7 +835,7 @@ block_crypto_co_create_opts_luks(BlockDriver *drv, const char *filename,
}
/* Create protocol layer */
ret = bdrv_co_create_file(filename, opts, errp);
ret = bdrv_co_create_file(filename, opts, true, errp);
if (ret < 0) {
goto fail;
}
+42 -39
View File
@@ -29,8 +29,8 @@
#include "qemu/option.h"
#include "block/block-io.h"
#include "block/block_int.h"
#include "qapi/qmp/qdict.h"
#include "qapi/qmp/qstring.h"
#include "qobject/qdict.h"
#include "qobject/qstring.h"
#include "crypto/secret.h"
#include <curl/curl.h>
#include "qemu/cutils.h"
@@ -162,13 +162,9 @@ static int curl_timer_cb(CURLM *multi, long timeout_ms, void *opaque)
static int curl_sock_cb(CURL *curl, curl_socket_t fd, int action,
void *userp, void *sp)
{
BDRVCURLState *s;
CURLState *state = NULL;
BDRVCURLState *s = userp;
CURLSocket *socket;
curl_easy_getinfo(curl, CURLINFO_PRIVATE, (char **)&state);
s = state->s;
socket = g_hash_table_lookup(s->sockets, GINT_TO_POINTER(fd));
if (!socket) {
socket = g_new0(CURLSocket, 1);
@@ -262,8 +258,8 @@ read_end:
}
/* Called with s->mutex held. */
static bool curl_find_buf(BDRVCURLState *s, uint64_t start, uint64_t len,
CURLAIOCB *acb)
static bool coroutine_fn
curl_find_buf(BDRVCURLState *s, uint64_t start, uint64_t len, CURLAIOCB *acb)
{
int i;
uint64_t end = start + len;
@@ -311,6 +307,10 @@ static bool curl_find_buf(BDRVCURLState *s, uint64_t start, uint64_t len,
for (j=0; j<CURL_NUM_ACB; j++) {
if (!state->acb[j]) {
state->acb[j] = acb;
/* Await ongoing request */
qemu_mutex_unlock(&s->mutex);
qemu_coroutine_yield();
qemu_mutex_lock(&s->mutex);
return true;
}
}
@@ -382,6 +382,16 @@ static void curl_multi_check_completion(BDRVCURLState *s)
acb->ret = error ? -EIO : 0;
state->acb[i] = NULL;
qemu_mutex_unlock(&s->mutex);
/*
* Current AioContext is the BDS context, which may or may not
* be the request (coroutine) context.
* - If it is, the coroutine must have yielded or the FD handler
* (curl_multi_do()/curl_multi_timeout_do()) could not have
* been called and we would not be here
* - If it is not, it doesn't matter whether it has already
* yielded or not; it will be scheduled once it does yield
* So aio_co_wake() is safe to call.
*/
aio_co_wake(acb->co);
qemu_mutex_lock(&s->mutex);
}
@@ -475,11 +485,11 @@ static int curl_init_state(BDRVCURLState *s, CURLState *state)
(void *)curl_read_cb) ||
curl_easy_setopt(state->curl, CURLOPT_WRITEDATA, (void *)state) ||
curl_easy_setopt(state->curl, CURLOPT_PRIVATE, (void *)state) ||
curl_easy_setopt(state->curl, CURLOPT_AUTOREFERER, 1) ||
curl_easy_setopt(state->curl, CURLOPT_FOLLOWLOCATION, 1) ||
curl_easy_setopt(state->curl, CURLOPT_NOSIGNAL, 1) ||
curl_easy_setopt(state->curl, CURLOPT_AUTOREFERER, 1L) ||
curl_easy_setopt(state->curl, CURLOPT_FOLLOWLOCATION, 1L) ||
curl_easy_setopt(state->curl, CURLOPT_NOSIGNAL, 1L) ||
curl_easy_setopt(state->curl, CURLOPT_ERRORBUFFER, state->errmsg) ||
curl_easy_setopt(state->curl, CURLOPT_FAILONERROR, 1)) {
curl_easy_setopt(state->curl, CURLOPT_FAILONERROR, 1L)) {
goto err;
}
if (s->username) {
@@ -520,7 +530,7 @@ static int curl_init_state(BDRVCURLState *s, CURLState *state)
CURLOPT_REDIR_PROTOCOLS_STR, PROTOCOLS)) {
goto err;
}
#elif LIBCURL_VERSION_NUM >= 0x071304
#else
if (curl_easy_setopt(state->curl, CURLOPT_PROTOCOLS, PROTOCOLS) ||
curl_easy_setopt(state->curl, CURLOPT_REDIR_PROTOCOLS, PROTOCOLS)) {
goto err;
@@ -528,7 +538,7 @@ static int curl_init_state(BDRVCURLState *s, CURLState *state)
#endif
#ifdef DEBUG_VERBOSE
if (curl_easy_setopt(state->curl, CURLOPT_VERBOSE, 1)) {
if (curl_easy_setopt(state->curl, CURLOPT_VERBOSE, 1L)) {
goto err;
}
#endif
@@ -605,6 +615,7 @@ static void curl_attach_aio_context(BlockDriverState *bs,
assert(!s->multi);
s->multi = curl_multi_init();
s->aio_context = new_context;
curl_multi_setopt(s->multi, CURLMOPT_SOCKETDATA, s);
curl_multi_setopt(s->multi, CURLMOPT_SOCKETFUNCTION, curl_sock_cb);
curl_multi_setopt(s->multi, CURLMOPT_TIMERDATA, s);
curl_multi_setopt(s->multi, CURLMOPT_TIMERFUNCTION, curl_timer_cb);
@@ -803,7 +814,7 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags,
}
s->accept_range = false;
if (curl_easy_setopt(state->curl, CURLOPT_NOBODY, 1) ||
if (curl_easy_setopt(state->curl, CURLOPT_NOBODY, 1L) ||
curl_easy_setopt(state->curl, CURLOPT_HEADERFUNCTION, curl_header_cb) ||
curl_easy_setopt(state->curl, CURLOPT_HEADERDATA, s)) {
pstrcpy(state->errmsg, CURL_ERROR_SIZE,
@@ -824,22 +835,11 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags,
goto out;
}
#endif
/* Prior CURL 7.19.4 return value of 0 could mean that the file size is not
* know or the size is zero. From 7.19.4 CURL returns -1 if size is not
* known and zero if it is really zero-length file. */
#if LIBCURL_VERSION_NUM >= 0x071304
if (cl < 0) {
pstrcpy(state->errmsg, CURL_ERROR_SIZE,
"Server didn't report file size.");
goto out;
}
#else
if (cl <= 0) {
pstrcpy(state->errmsg, CURL_ERROR_SIZE,
"Unknown file size or zero-length file.");
goto out;
}
#endif
s->len = cl;
@@ -882,7 +882,7 @@ out_noclean:
return -EINVAL;
}
static void coroutine_fn curl_setup_preadv(BlockDriverState *bs, CURLAIOCB *acb)
static void coroutine_fn curl_do_preadv(BlockDriverState *bs, CURLAIOCB *acb)
{
CURLState *state;
int running;
@@ -894,10 +894,13 @@ static void coroutine_fn curl_setup_preadv(BlockDriverState *bs, CURLAIOCB *acb)
qemu_mutex_lock(&s->mutex);
// In case we have the requested data already (e.g. read-ahead),
// we can just call the callback and be done.
/*
* In case we have the requested data already (e.g. read-ahead),
* we can just call the callback and be done. This may have to
* await an ongoing request, in which case it itself will yield.
*/
if (curl_find_buf(s, start, acb->bytes, acb)) {
goto out;
goto dont_yield;
}
// No cache found, so let's start a new request
@@ -912,7 +915,7 @@ static void coroutine_fn curl_setup_preadv(BlockDriverState *bs, CURLAIOCB *acb)
if (curl_init_state(s, state) < 0) {
curl_clean_state(state);
acb->ret = -EIO;
goto out;
goto dont_yield;
}
acb->start = 0;
@@ -927,7 +930,7 @@ static void coroutine_fn curl_setup_preadv(BlockDriverState *bs, CURLAIOCB *acb)
if (state->buf_len && state->orig_buf == NULL) {
curl_clean_state(state);
acb->ret = -ENOMEM;
goto out;
goto dont_yield;
}
state->acb[0] = acb;
@@ -939,13 +942,16 @@ static void coroutine_fn curl_setup_preadv(BlockDriverState *bs, CURLAIOCB *acb)
acb->ret = -EIO;
curl_clean_state(state);
goto out;
goto dont_yield;
}
/* Tell curl it needs to kick things off */
curl_multi_socket_action(s->multi, CURL_SOCKET_TIMEOUT, 0, &running);
qemu_mutex_unlock(&s->mutex);
qemu_coroutine_yield();
return;
out:
dont_yield:
qemu_mutex_unlock(&s->mutex);
}
@@ -961,10 +967,7 @@ static int coroutine_fn curl_co_preadv(BlockDriverState *bs,
.bytes = bytes
};
curl_setup_preadv(bs, &acb);
while (acb.ret == -EINPROGRESS) {
qemu_coroutine_yield();
}
curl_do_preadv(bs, &acb);
return acb.ret;
}
+24 -9
View File
@@ -14,8 +14,8 @@
#include "qemu/osdep.h"
#include "block/block.h"
#include "sysemu/block-backend.h"
#include "sysemu/iothread.h"
#include "system/block-backend.h"
#include "system/iothread.h"
#include "block/export.h"
#include "block/fuse.h"
#include "block/nbd.h"
@@ -75,6 +75,7 @@ static const BlockExportDriver *blk_exp_find_driver(BlockExportType type)
BlockExport *blk_exp_add(BlockExportOptions *export, Error **errp)
{
bool fixed_iothread = export->has_fixed_iothread && export->fixed_iothread;
bool allow_inactive = export->has_allow_inactive && export->allow_inactive;
const BlockExportDriver *drv;
BlockExport *exp = NULL;
BlockDriverState *bs;
@@ -138,14 +139,25 @@ BlockExport *blk_exp_add(BlockExportOptions *export, Error **errp)
}
}
/*
* Block exports are used for non-shared storage migration. Make sure
* that BDRV_O_INACTIVE is cleared and the image is ready for write
* access since the export could be available before migration handover.
* ctx was acquired in the caller.
*/
bdrv_graph_rdlock_main_loop();
bdrv_activate(bs, NULL);
if (allow_inactive) {
if (!drv->supports_inactive) {
error_setg(errp, "Export type does not support inactive exports");
bdrv_graph_rdunlock_main_loop();
goto fail;
}
} else {
/*
* Block exports are used for non-shared storage migration. Make sure
* that BDRV_O_INACTIVE is cleared and the image is ready for write
* access since the export could be available before migration handover.
*/
ret = bdrv_activate(bs, errp);
if (ret < 0) {
bdrv_graph_rdunlock_main_loop();
goto fail;
}
}
bdrv_graph_rdunlock_main_loop();
perm = BLK_PERM_CONSISTENT_READ;
@@ -158,6 +170,9 @@ BlockExport *blk_exp_add(BlockExportOptions *export, Error **errp)
if (!fixed_iothread) {
blk_set_allow_aio_context_change(blk, true);
}
if (allow_inactive) {
blk_set_force_allow_inactivate(blk);
}
ret = blk_insert_bs(blk, bs, errp);
if (ret < 0) {
+1 -1
View File
@@ -28,7 +28,7 @@
#include "qapi/error.h"
#include "qapi/qapi-commands-block.h"
#include "qemu/main-loop.h"
#include "sysemu/block-backend.h"
#include "system/block-backend.h"
#include <fuse.h>
#include <fuse_lowlevel.h>
+1 -1
View File
@@ -13,7 +13,7 @@
#ifndef VIRTIO_BLK_HANDLER_H
#define VIRTIO_BLK_HANDLER_H
#include "sysemu/block-backend.h"
#include "system/block-backend.h"
#define VIRTIO_BLK_SECTOR_BITS 9
#define VIRTIO_BLK_SECTOR_SIZE (1ULL << VIRTIO_BLK_SECTOR_BITS)
+298 -93
View File
@@ -36,11 +36,12 @@
#include "block/thread-pool.h"
#include "qemu/iov.h"
#include "block/raw-aio.h"
#include "qapi/qmp/qdict.h"
#include "qapi/qmp/qstring.h"
#include "qobject/qdict.h"
#include "qobject/qstring.h"
#include "scsi/pr-manager.h"
#include "scsi/constants.h"
#include "scsi/utils.h"
#if defined(__APPLE__) && (__MACH__)
#include <sys/ioctl.h>
@@ -72,6 +73,7 @@
#include <linux/blkzoned.h>
#endif
#include <linux/cdrom.h>
#include <linux/dm-ioctl.h>
#include <linux/fd.h>
#include <linux/fs.h>
#include <linux/hdreg.h>
@@ -110,6 +112,10 @@
#include <sys/diskslice.h>
#endif
#ifdef EMSCRIPTEN
#include <sys/ioctl.h>
#endif
/* OS X does not have O_DSYNC */
#ifndef O_DSYNC
#ifdef O_SYNC
@@ -127,13 +133,29 @@
#define FTYPE_FILE 0
#define FTYPE_CD 1
#define MAX_BLOCKSIZE 4096
#define MAX_BLOCKSIZE 4096
/* Posix file locking bytes. Libvirt takes byte 0, we start from higher bytes,
* leaving a few more bytes for its future use. */
#define RAW_LOCK_PERM_BASE 100
#define RAW_LOCK_SHARED_BASE 200
/*
* Multiple retries are mostly meant for two separate scenarios:
*
* - DM_MPATH_PROBE_PATHS returns success, but before SG_IO completes, another
* path goes down.
*
* - DM_MPATH_PROBE_PATHS failed all paths in the current path group, so we have
* to send another SG_IO to switch to another path group to probe the paths in
* it.
*
* Even if each path is in a separate path group (path_grouping_policy set to
* failover), it's rare to have more than eight path groups - and even then
* pretty unlikely that only bad path groups would be chosen in eight retries.
*/
#define SG_IO_MAX_RETRIES 8
typedef struct BDRVRawState {
int fd;
bool use_lock;
@@ -161,6 +183,7 @@ typedef struct BDRVRawState {
bool use_linux_aio:1;
bool has_laio_fdsync:1;
bool use_linux_io_uring:1;
bool use_mpath:1;
int page_cache_inconsistent; /* errno from fdatasync failure */
bool has_fallocate;
bool needs_alignment;
@@ -194,6 +217,7 @@ static int fd_open(BlockDriverState *bs)
}
static int64_t raw_getlength(BlockDriverState *bs);
static int coroutine_fn raw_co_flush_to_disk(BlockDriverState *bs);
typedef struct RawPosixAIOData {
BlockDriverState *bs;
@@ -731,14 +755,23 @@ static int raw_open_common(BlockDriverState *bs, QDict *options,
}
#endif /* !defined(CONFIG_LINUX_AIO) */
#ifndef CONFIG_LINUX_IO_URING
if (s->use_linux_io_uring) {
#ifdef CONFIG_LINUX_IO_URING
if (!aio_has_io_uring()) {
error_setg(errp, "aio=io_uring was specified, but is not "
"available (disabled via io_uring_disabled "
"sysctl or blocked by container runtime "
"seccomp policy?)");
ret = -EINVAL;
goto fail;
}
#else
error_setg(errp, "aio=io_uring was specified, but is not supported "
"in this build.");
"in this build");
ret = -EINVAL;
goto fail;
}
#endif /* !defined(CONFIG_LINUX_IO_URING) */
}
s->has_discard = true;
s->has_write_zeroes = true;
@@ -780,17 +813,6 @@ static int raw_open_common(BlockDriverState *bs, QDict *options,
}
#endif
if (S_ISBLK(st.st_mode)) {
#ifdef __linux__
/* On Linux 3.10, BLKDISCARD leaves stale data in the page cache. Do
* not rely on the contents of discarded blocks unless using O_DIRECT.
* Same for BLKZEROOUT.
*/
if (!(bs->open_flags & BDRV_O_NOCACHE)) {
s->has_write_zeroes = false;
}
#endif
}
#ifdef __FreeBSD__
if (S_ISCHR(st.st_mode)) {
/*
@@ -804,6 +826,13 @@ static int raw_open_common(BlockDriverState *bs, QDict *options,
#endif
s->needs_alignment = raw_needs_alignment(bs);
bs->supported_write_flags = BDRV_REQ_FUA;
if (s->use_linux_aio && !laio_has_fua()) {
bs->supported_write_flags &= ~BDRV_REQ_FUA;
} else if (s->use_linux_io_uring && !luring_has_fua()) {
bs->supported_write_flags &= ~BDRV_REQ_FUA;
}
bs->supported_zero_flags = BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK;
if (S_ISREG(st.st_mode)) {
/* When extending regular files, we get zeros from the OS */
@@ -1268,10 +1297,10 @@ static int get_sysfs_zoned_model(struct stat *st, BlockZoneModel *zoned)
}
#endif /* defined(CONFIG_BLKZONED) */
#ifdef CONFIG_LINUX
/*
* Get a sysfs attribute value as a long integer.
*/
#ifdef CONFIG_LINUX
static long get_sysfs_long_val(struct stat *st, const char *attribute)
{
g_autofree char *str = NULL;
@@ -1291,6 +1320,30 @@ static long get_sysfs_long_val(struct stat *st, const char *attribute)
}
return ret;
}
/*
* Get a sysfs attribute value as a uint32_t.
*/
static int get_sysfs_u32_val(struct stat *st, const char *attribute,
uint32_t *u32)
{
g_autofree char *str = NULL;
const char *end;
unsigned int val;
int ret;
ret = get_sysfs_str_val(st, attribute, &str);
if (ret < 0) {
return ret;
}
/* The file is ended with '\n', pass 'end' to accept that. */
ret = qemu_strtoui(str, &end, 10, &val);
if (ret == 0 && end && *end == '\0') {
*u32 = val;
}
return ret;
}
#endif
static int hdev_get_max_segments(int fd, struct stat *st)
@@ -1310,6 +1363,23 @@ static int hdev_get_max_segments(int fd, struct stat *st)
#endif
}
/*
* Fills in *dalign with the discard alignment and returns 0 on success,
* -errno otherwise.
*/
static int hdev_get_pdiscard_alignment(struct stat *st, uint32_t *dalign)
{
#ifdef CONFIG_LINUX
/*
* Note that Linux "discard_granularity" is QEMU "discard_alignment". Linux
* "discard_alignment" is something else.
*/
return get_sysfs_u32_val(st, "discard_granularity", dalign);
#else
return -ENOTSUP;
#endif
}
#if defined(CONFIG_BLKZONED)
/*
* If the reset_all flag is true, then the wps of zone whose state is
@@ -1519,6 +1589,46 @@ static void raw_refresh_limits(BlockDriverState *bs, Error **errp)
}
}
if (S_ISBLK(st.st_mode)) {
uint32_t dalign = 0;
int ret;
ret = hdev_get_pdiscard_alignment(&st, &dalign);
if (ret == 0 && dalign != 0) {
uint32_t ralign = bs->bl.request_alignment;
/* Probably never happens, but handle it just in case */
if (dalign < ralign && (ralign % dalign == 0)) {
dalign = ralign;
}
/* The block layer requires a multiple of request_alignment */
if (dalign % ralign != 0) {
error_setg(errp, "Invalid pdiscard_alignment limit %u is not a "
"multiple of request_alignment %u", dalign, ralign);
return;
}
bs->bl.pdiscard_alignment = dalign;
}
#ifdef __linux__
/*
* Linux requires logical block size alignment for write zeroes even
* when normal reads/writes do not require alignment.
*/
if (!s->needs_alignment) {
ret = probe_logical_blocksize(s->fd,
&bs->bl.pwrite_zeroes_alignment);
if (ret < 0) {
error_setg_errno(errp, -ret,
"Failed to probe logical block size");
return;
}
}
#endif /* __linux__ */
}
raw_refresh_zoned_limits(bs, &st, errp);
}
@@ -2003,8 +2113,11 @@ static int handle_aiocb_write_zeroes_unmap(void *opaque)
}
#ifndef HAVE_COPY_FILE_RANGE
static off_t copy_file_range(int in_fd, off_t *in_off, int out_fd,
off_t *out_off, size_t len, unsigned int flags)
#ifndef EMSCRIPTEN
static
#endif
ssize_t copy_file_range(int in_fd, off_t *in_off, int out_fd,
off_t *out_off, size_t len, unsigned int flags)
{
#ifdef __NR_copy_file_range
return syscall(__NR_copy_file_range, in_fd, in_off, out_fd,
@@ -2434,27 +2547,6 @@ static bool bdrv_qiov_is_aligned(BlockDriverState *bs, QEMUIOVector *qiov)
return true;
}
#ifdef CONFIG_LINUX_IO_URING
static inline bool raw_check_linux_io_uring(BDRVRawState *s)
{
Error *local_err = NULL;
AioContext *ctx;
if (!s->use_linux_io_uring) {
return false;
}
ctx = qemu_get_current_aio_context();
if (unlikely(!aio_setup_linux_io_uring(ctx, &local_err))) {
error_reportf_err(local_err, "Unable to use linux io_uring, "
"falling back to thread pool: ");
s->use_linux_io_uring = false;
return false;
}
return true;
}
#endif
#ifdef CONFIG_LINUX_AIO
static inline bool raw_check_linux_aio(BDRVRawState *s)
{
@@ -2476,8 +2568,9 @@ static inline bool raw_check_linux_aio(BDRVRawState *s)
}
#endif
static int coroutine_fn raw_co_prw(BlockDriverState *bs, int64_t *offset_ptr,
uint64_t bytes, QEMUIOVector *qiov, int type)
static int coroutine_fn GRAPH_RDLOCK
raw_co_prw(BlockDriverState *bs, int64_t *offset_ptr, uint64_t bytes,
QEMUIOVector *qiov, int type, int flags)
{
BDRVRawState *s = bs->opaque;
RawPosixAIOData acb;
@@ -2506,15 +2599,15 @@ static int coroutine_fn raw_co_prw(BlockDriverState *bs, int64_t *offset_ptr,
if (s->needs_alignment && !bdrv_qiov_is_aligned(bs, qiov)) {
type |= QEMU_AIO_MISALIGNED;
#ifdef CONFIG_LINUX_IO_URING
} else if (raw_check_linux_io_uring(s)) {
} else if (s->use_linux_io_uring) {
assert(qiov->size == bytes);
ret = luring_co_submit(bs, s->fd, offset, qiov, type);
ret = luring_co_submit(bs, s->fd, offset, qiov, type, flags);
goto out;
#endif
#ifdef CONFIG_LINUX_AIO
} else if (raw_check_linux_aio(s)) {
assert(qiov->size == bytes);
ret = laio_co_submit(s->fd, offset, qiov, type,
ret = laio_co_submit(s->fd, offset, qiov, type, flags,
s->aio_max_batch);
goto out;
#endif
@@ -2534,6 +2627,10 @@ static int coroutine_fn raw_co_prw(BlockDriverState *bs, int64_t *offset_ptr,
assert(qiov->size == bytes);
ret = raw_thread_pool_submit(handle_aiocb_rw, &acb);
if (ret == 0 && (flags & BDRV_REQ_FUA)) {
/* TODO Use pwritev2() instead if it's available */
ret = bdrv_co_flush(bs);
}
goto out; /* Avoid the compiler err of unused label */
out:
@@ -2567,18 +2664,18 @@ out:
return ret;
}
static int coroutine_fn raw_co_preadv(BlockDriverState *bs, int64_t offset,
int64_t bytes, QEMUIOVector *qiov,
BdrvRequestFlags flags)
static int coroutine_fn GRAPH_RDLOCK
raw_co_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes,
QEMUIOVector *qiov, BdrvRequestFlags flags)
{
return raw_co_prw(bs, &offset, bytes, qiov, QEMU_AIO_READ);
return raw_co_prw(bs, &offset, bytes, qiov, QEMU_AIO_READ, flags);
}
static int coroutine_fn raw_co_pwritev(BlockDriverState *bs, int64_t offset,
int64_t bytes, QEMUIOVector *qiov,
BdrvRequestFlags flags)
static int coroutine_fn GRAPH_RDLOCK
raw_co_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes,
QEMUIOVector *qiov, BdrvRequestFlags flags)
{
return raw_co_prw(bs, &offset, bytes, qiov, QEMU_AIO_WRITE);
return raw_co_prw(bs, &offset, bytes, qiov, QEMU_AIO_WRITE, flags);
}
static int coroutine_fn raw_co_flush_to_disk(BlockDriverState *bs)
@@ -2599,13 +2696,13 @@ static int coroutine_fn raw_co_flush_to_disk(BlockDriverState *bs)
};
#ifdef CONFIG_LINUX_IO_URING
if (raw_check_linux_io_uring(s)) {
return luring_co_submit(bs, s->fd, 0, NULL, QEMU_AIO_FLUSH);
if (s->use_linux_io_uring) {
return luring_co_submit(bs, s->fd, 0, NULL, QEMU_AIO_FLUSH, 0);
}
#endif
#ifdef CONFIG_LINUX_AIO
if (s->has_laio_fdsync && raw_check_linux_aio(s)) {
return laio_co_submit(s->fd, 0, NULL, QEMU_AIO_FLUSH, 0);
return laio_co_submit(s->fd, 0, NULL, QEMU_AIO_FLUSH, 0, 0);
}
#endif
return raw_thread_pool_submit(handle_aiocb_flush, &acb);
@@ -3188,7 +3285,7 @@ static int find_allocation(BlockDriverState *bs, off_t start,
* well exceed it.
*/
static int coroutine_fn raw_co_block_status(BlockDriverState *bs,
bool want_zero,
unsigned int mode,
int64_t offset,
int64_t bytes, int64_t *pnum,
int64_t *map,
@@ -3204,7 +3301,8 @@ static int coroutine_fn raw_co_block_status(BlockDriverState *bs,
return ret;
}
if (!want_zero) {
if (!(mode & BDRV_WANT_ZERO)) {
/* There is no backing file - all bytes are allocated in this file. */
*pnum = bytes;
*map = offset;
*file = bs;
@@ -3512,10 +3610,11 @@ static int coroutine_fn raw_co_zone_mgmt(BlockDriverState *bs, BlockZoneOp op,
#endif
#if defined(CONFIG_BLKZONED)
static int coroutine_fn raw_co_zone_append(BlockDriverState *bs,
int64_t *offset,
QEMUIOVector *qiov,
BdrvRequestFlags flags) {
static int coroutine_fn GRAPH_RDLOCK
raw_co_zone_append(BlockDriverState *bs,
int64_t *offset,
QEMUIOVector *qiov,
BdrvRequestFlags flags) {
assert(flags == 0);
int64_t zone_size_mask = bs->bl.zone_size - 1;
int64_t iov_len = 0;
@@ -3540,7 +3639,7 @@ static int coroutine_fn raw_co_zone_append(BlockDriverState *bs,
}
trace_zbd_zone_append(bs, *offset >> BDRV_SECTOR_BITS);
return raw_co_prw(bs, offset, len, qiov, QEMU_AIO_ZONE_APPEND);
return raw_co_prw(bs, offset, len, qiov, QEMU_AIO_ZONE_APPEND, 0);
}
#endif
@@ -4178,15 +4277,89 @@ hdev_open_Mac_error:
/* Since this does ioctl the device must be already opened */
bs->sg = hdev_is_sg(bs);
/* sg devices aren't even block devices and can't use dm-mpath */
s->use_mpath = !bs->sg;
return ret;
}
#if defined(__linux__)
#if defined(DM_MPATH_PROBE_PATHS)
static bool coroutine_fn sgio_path_error(int ret, sg_io_hdr_t *io_hdr)
{
if (ret < 0) {
/* Path errors sometimes result in -ENODEV */
return ret == -ENODEV;
}
if (io_hdr->host_status != SCSI_HOST_OK) {
return true;
}
switch (io_hdr->status) {
case GOOD:
case CONDITION_GOOD:
case INTERMEDIATE_GOOD:
case INTERMEDIATE_C_GOOD:
case RESERVATION_CONFLICT:
case COMMAND_TERMINATED:
return false;
case CHECK_CONDITION:
return !scsi_sense_buf_is_guest_recoverable(io_hdr->sbp,
io_hdr->mx_sb_len);
default:
return true;
}
}
static bool coroutine_fn hdev_co_ioctl_sgio_retry(RawPosixAIOData *acb, int ret)
{
BDRVRawState *s = acb->bs->opaque;
RawPosixAIOData probe_acb;
if (!s->use_mpath) {
return false;
}
if (!sgio_path_error(ret, acb->ioctl.buf)) {
return false;
}
probe_acb = (RawPosixAIOData) {
.bs = acb->bs,
.aio_type = QEMU_AIO_IOCTL,
.aio_fildes = s->fd,
.aio_offset = 0,
.ioctl = {
.buf = NULL,
.cmd = DM_MPATH_PROBE_PATHS,
},
};
ret = raw_thread_pool_submit(handle_aiocb_ioctl, &probe_acb);
if (ret == -ENOTTY) {
s->use_mpath = false;
} else if (ret == -EAGAIN) {
/* The device might be suspended for a table reload, worth retrying */
return true;
}
return ret == 0;
}
#else
static bool coroutine_fn hdev_co_ioctl_sgio_retry(RawPosixAIOData *acb, int ret)
{
return false;
}
#endif /* DM_MPATH_PROBE_PATHS */
static int coroutine_fn
hdev_co_ioctl(BlockDriverState *bs, unsigned long int req, void *buf)
{
BDRVRawState *s = bs->opaque;
RawPosixAIOData acb;
uint64_t eagain_sleep_ns = 1 * SCALE_MS;
int retries = SG_IO_MAX_RETRIES;
int ret;
ret = fd_open(bs);
@@ -4214,7 +4387,39 @@ hdev_co_ioctl(BlockDriverState *bs, unsigned long int req, void *buf)
},
};
return raw_thread_pool_submit(handle_aiocb_ioctl, &acb);
retry:
ret = raw_thread_pool_submit(handle_aiocb_ioctl, &acb);
if (req == SG_IO && s->use_mpath) {
if (ret == -EAGAIN && eagain_sleep_ns < NANOSECONDS_PER_SECOND) {
/*
* If this is a multipath device, it is probably suspended.
*
* This can happen while the dm table is reloaded, e.g. because a
* path is added or removed. This is an operation that should
* complete within 1ms, so just wait a bit and retry.
*
* There are also some cases in which libmpathpersist must recover
* from path failure during its operation, which can leave the
* device suspended for a bit longer while the library brings back
* reservations into the expected state.
*
* Use increasing delays to cover both cases without waiting
* excessively, and stop after a bit more than a second (1023 ms).
* This is a tolerable delay before we return an error and
* potentially stop the VM.
*/
qemu_co_sleep_ns(QEMU_CLOCK_REALTIME, eagain_sleep_ns);
eagain_sleep_ns *= 2;
goto retry;
}
/* Even for ret == 0, the SG_IO header can contain an error */
if (retries-- && hdev_co_ioctl_sgio_retry(&acb, ret)) {
goto retry;
}
}
return ret;
}
#endif /* linux */
@@ -4385,20 +4590,20 @@ static void coroutine_fn cdrom_co_lock_medium(BlockDriverState *bs, bool locked)
}
static BlockDriver bdrv_host_cdrom = {
.format_name = "host_cdrom",
.protocol_name = "host_cdrom",
.instance_size = sizeof(BDRVRawState),
.bdrv_needs_filename = true,
.bdrv_probe_device = cdrom_probe_device,
.bdrv_parse_filename = cdrom_parse_filename,
.bdrv_open = cdrom_open,
.bdrv_close = raw_close,
.bdrv_reopen_prepare = raw_reopen_prepare,
.bdrv_reopen_commit = raw_reopen_commit,
.bdrv_reopen_abort = raw_reopen_abort,
.bdrv_co_create_opts = bdrv_co_create_opts_simple,
.create_opts = &bdrv_create_opts_simple,
.mutable_opts = mutable_opts,
.format_name = "host_cdrom",
.protocol_name = "host_cdrom",
.instance_size = sizeof(BDRVRawState),
.bdrv_needs_filename = true,
.bdrv_probe_device = cdrom_probe_device,
.bdrv_parse_filename = cdrom_parse_filename,
.bdrv_open = cdrom_open,
.bdrv_close = raw_close,
.bdrv_reopen_prepare = raw_reopen_prepare,
.bdrv_reopen_commit = raw_reopen_commit,
.bdrv_reopen_abort = raw_reopen_abort,
.bdrv_co_create_opts = bdrv_co_create_opts_simple,
.create_opts = &bdrv_create_opts_simple,
.mutable_opts = mutable_opts,
.bdrv_co_invalidate_cache = raw_co_invalidate_cache,
.bdrv_co_preadv = raw_co_preadv,
@@ -4511,20 +4716,20 @@ static void coroutine_fn cdrom_co_lock_medium(BlockDriverState *bs, bool locked)
}
static BlockDriver bdrv_host_cdrom = {
.format_name = "host_cdrom",
.protocol_name = "host_cdrom",
.instance_size = sizeof(BDRVRawState),
.bdrv_needs_filename = true,
.bdrv_probe_device = cdrom_probe_device,
.bdrv_parse_filename = cdrom_parse_filename,
.bdrv_open = cdrom_open,
.bdrv_close = raw_close,
.bdrv_reopen_prepare = raw_reopen_prepare,
.bdrv_reopen_commit = raw_reopen_commit,
.bdrv_reopen_abort = raw_reopen_abort,
.bdrv_co_create_opts = bdrv_co_create_opts_simple,
.create_opts = &bdrv_create_opts_simple,
.mutable_opts = mutable_opts,
.format_name = "host_cdrom",
.protocol_name = "host_cdrom",
.instance_size = sizeof(BDRVRawState),
.bdrv_needs_filename = true,
.bdrv_probe_device = cdrom_probe_device,
.bdrv_parse_filename = cdrom_parse_filename,
.bdrv_open = cdrom_open,
.bdrv_close = raw_close,
.bdrv_reopen_prepare = raw_reopen_prepare,
.bdrv_reopen_commit = raw_reopen_commit,
.bdrv_reopen_abort = raw_reopen_abort,
.bdrv_co_create_opts = bdrv_co_create_opts_simple,
.create_opts = &bdrv_create_opts_simple,
.mutable_opts = mutable_opts,
.bdrv_co_preadv = raw_co_preadv,
.bdrv_co_pwritev = raw_co_pwritev,
+21 -21
View File
@@ -33,8 +33,8 @@
#include "trace.h"
#include "block/thread-pool.h"
#include "qemu/iov.h"
#include "qapi/qmp/qdict.h"
#include "qapi/qmp/qstring.h"
#include "qobject/qdict.h"
#include "qobject/qstring.h"
#include <windows.h>
#include <winioctl.h>
#include <assert.h>
@@ -777,16 +777,16 @@ static QemuOptsList raw_create_opts = {
};
BlockDriver bdrv_file = {
.format_name = "file",
.protocol_name = "file",
.instance_size = sizeof(BDRVRawState),
.bdrv_needs_filename = true,
.bdrv_parse_filename = raw_parse_filename,
.bdrv_open = raw_open,
.bdrv_refresh_limits = raw_probe_alignment,
.bdrv_close = raw_close,
.bdrv_co_create_opts = raw_co_create_opts,
.bdrv_has_zero_init = bdrv_has_zero_init_1,
.format_name = "file",
.protocol_name = "file",
.instance_size = sizeof(BDRVRawState),
.bdrv_needs_filename = true,
.bdrv_parse_filename = raw_parse_filename,
.bdrv_open = raw_open,
.bdrv_refresh_limits = raw_probe_alignment,
.bdrv_close = raw_close,
.bdrv_co_create_opts = raw_co_create_opts,
.bdrv_has_zero_init = bdrv_has_zero_init_1,
.bdrv_reopen_prepare = raw_reopen_prepare,
.bdrv_reopen_commit = raw_reopen_commit,
@@ -956,15 +956,15 @@ done:
}
static BlockDriver bdrv_host_device = {
.format_name = "host_device",
.protocol_name = "host_device",
.instance_size = sizeof(BDRVRawState),
.bdrv_needs_filename = true,
.bdrv_parse_filename = hdev_parse_filename,
.bdrv_probe_device = hdev_probe_device,
.bdrv_open = hdev_open,
.bdrv_close = raw_close,
.bdrv_refresh_limits = hdev_refresh_limits,
.format_name = "host_device",
.protocol_name = "host_device",
.instance_size = sizeof(BDRVRawState),
.bdrv_needs_filename = true,
.bdrv_parse_filename = hdev_parse_filename,
.bdrv_probe_device = hdev_probe_device,
.bdrv_open = hdev_open,
.bdrv_close = raw_close,
.bdrv_refresh_limits = hdev_refresh_limits,
.bdrv_aio_preadv = raw_aio_preadv,
.bdrv_aio_pwritev = raw_aio_pwritev,
+14 -13
View File
@@ -15,7 +15,7 @@
#include "block/block_int.h"
#include "block/qdict.h"
#include "qapi/error.h"
#include "qapi/qmp/qdict.h"
#include "qobject/qdict.h"
#include "qapi/qmp/qerror.h"
#include "qemu/error-report.h"
#include "qemu/module.h"
@@ -56,7 +56,6 @@ typedef struct GlusterAIOCB {
int64_t size;
int ret;
Coroutine *coroutine;
AioContext *aio_context;
} GlusterAIOCB;
typedef struct BDRVGlusterState {
@@ -743,7 +742,17 @@ static void gluster_finish_aiocb(struct glfs_fd *fd, ssize_t ret,
acb->ret = -EIO; /* Partial read/write - fail it */
}
aio_co_schedule(acb->aio_context, acb->coroutine);
/*
* Safe to call: The coroutine will yield exactly once awaiting this
* scheduling, and the context is its own context, so it will be scheduled
* once it does yield.
*
* (aio_co_wake() would call qemu_get_current_aio_context() to check whether
* we are in the same context, but we are not in a qemu thread, so we cannot
* do that. Use aio_co_schedule() directly.)
*/
aio_co_schedule(qemu_coroutine_get_aio_context(acb->coroutine),
acb->coroutine);
}
static void qemu_gluster_parse_flags(int bdrv_flags, int *open_flags)
@@ -972,8 +981,6 @@ static void qemu_gluster_reopen_commit(BDRVReopenState *state)
g_free(state->opaque);
state->opaque = NULL;
return;
}
@@ -993,8 +1000,6 @@ static void qemu_gluster_reopen_abort(BDRVReopenState *state)
g_free(state->opaque);
state->opaque = NULL;
return;
}
#ifdef CONFIG_GLUSTERFS_ZEROFILL
@@ -1010,7 +1015,6 @@ static coroutine_fn int qemu_gluster_co_pwrite_zeroes(BlockDriverState *bs,
acb.size = bytes;
acb.ret = 0;
acb.coroutine = qemu_coroutine_self();
acb.aio_context = bdrv_get_aio_context(bs);
ret = glfs_zerofill_async(s->fd, offset, bytes, gluster_finish_aiocb, &acb);
if (ret < 0) {
@@ -1188,7 +1192,6 @@ static coroutine_fn int qemu_gluster_co_rw(BlockDriverState *bs,
acb.size = size;
acb.ret = 0;
acb.coroutine = qemu_coroutine_self();
acb.aio_context = bdrv_get_aio_context(bs);
if (write) {
ret = glfs_pwritev_async(s->fd, qiov->iov, qiov->niov, offset, 0,
@@ -1255,7 +1258,6 @@ static coroutine_fn int qemu_gluster_co_flush_to_disk(BlockDriverState *bs)
acb.size = 0;
acb.ret = 0;
acb.coroutine = qemu_coroutine_self();
acb.aio_context = bdrv_get_aio_context(bs);
ret = glfs_fsync_async(s->fd, gluster_finish_aiocb, &acb);
if (ret < 0) {
@@ -1303,7 +1305,6 @@ static coroutine_fn int qemu_gluster_co_pdiscard(BlockDriverState *bs,
acb.size = 0;
acb.ret = 0;
acb.coroutine = qemu_coroutine_self();
acb.aio_context = bdrv_get_aio_context(bs);
ret = glfs_discard_async(s->fd, offset, bytes, gluster_finish_aiocb, &acb);
if (ret < 0) {
@@ -1465,7 +1466,7 @@ exit:
* (Based on raw_co_block_status() from file-posix.c.)
*/
static int coroutine_fn qemu_gluster_co_block_status(BlockDriverState *bs,
bool want_zero,
unsigned int mode,
int64_t offset,
int64_t bytes,
int64_t *pnum,
@@ -1482,7 +1483,7 @@ static int coroutine_fn qemu_gluster_co_block_status(BlockDriverState *bs,
return ret;
}
if (!want_zero) {
if (!(mode & BDRV_WANT_ZERO)) {
*pnum = bytes;
*map = offset;
*file = bs;
+37 -3
View File
@@ -33,6 +33,17 @@ static QemuMutex aio_context_list_lock;
/* Written and read with atomic operations. */
static int has_writer;
/*
* Many write-locked sections are also drained sections. There is a convenience
* wrapper bdrv_graph_wrlock_drained() which begins a drained section before
* acquiring the lock. This variable here is used so bdrv_graph_wrunlock() knows
* if it also needs to end such a drained section. It needs to be a counter,
* because the aio_poll() call in bdrv_graph_wrlock() might re-enter
* bdrv_graph_wrlock_drained(). And note that aio_bh_poll() in
* bdrv_graph_wrunlock() might also re-enter a write-locked section.
*/
static int wrlock_quiesced_counter;
/*
* A reader coroutine could move from an AioContext to another.
* If this happens, there is no problem from the point of view of
@@ -112,8 +123,14 @@ void no_coroutine_fn bdrv_graph_wrlock(void)
assert(!qatomic_read(&has_writer));
assert(!qemu_in_coroutine());
/* Make sure that constantly arriving new I/O doesn't cause starvation */
bdrv_drain_all_begin_nopoll();
bool need_drain = wrlock_quiesced_counter == 0;
if (need_drain) {
/*
* Make sure that constantly arriving new I/O doesn't cause starvation
*/
bdrv_drain_all_begin_nopoll();
}
/*
* reader_count == 0: this means writer will read has_reader as 1
@@ -139,7 +156,18 @@ void no_coroutine_fn bdrv_graph_wrlock(void)
smp_mb();
} while (reader_count() >= 1);
bdrv_drain_all_end();
if (need_drain) {
bdrv_drain_all_end();
}
}
void no_coroutine_fn bdrv_graph_wrlock_drained(void)
{
GLOBAL_STATE_CODE();
bdrv_drain_all_begin();
wrlock_quiesced_counter++;
bdrv_graph_wrlock();
}
void no_coroutine_fn bdrv_graph_wrunlock(void)
@@ -168,6 +196,12 @@ void no_coroutine_fn bdrv_graph_wrunlock(void)
* progress.
*/
aio_bh_poll(qemu_get_aio_context());
if (wrlock_quiesced_counter > 0) {
bdrv_drain_all_end();
wrlock_quiesced_counter--;
}
}
void coroutine_fn bdrv_graph_co_rdlock(void)
+121 -66
View File
@@ -24,7 +24,7 @@
#include "qemu/osdep.h"
#include "trace.h"
#include "sysemu/block-backend.h"
#include "system/block-backend.h"
#include "block/aio-wait.h"
#include "block/blockjob.h"
#include "block/blockjob_int.h"
@@ -37,13 +37,14 @@
#include "qapi/error.h"
#include "qemu/error-report.h"
#include "qemu/main-loop.h"
#include "sysemu/replay.h"
#include "system/replay.h"
#include "qemu/units.h"
/* Maximum bounce buffer for copy-on-read and write zeroes, in bytes */
#define MAX_BOUNCE_BUFFER (32768 << BDRV_SECTOR_BITS)
static void coroutine_fn GRAPH_RDLOCK
bdrv_parent_cb_resize(BlockDriverState *bs);
/* Maximum read size for checking if data reads as zero, in bytes */
#define MAX_ZERO_CHECK_BUFFER (128 * KiB)
static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs,
int64_t offset, int64_t bytes, BdrvRequestFlags flags);
@@ -357,7 +358,7 @@ static void bdrv_do_drained_begin(BlockDriverState *bs, BdrvChild *parent,
GLOBAL_STATE_CODE();
/* Stop things in parent-to-child order */
if (qatomic_fetch_inc(&bs->quiesce_counter) == 0) {
if (bs->quiesce_counter++ == 0) {
GRAPH_RDLOCK_GUARD_MAINLOOP();
bdrv_parent_drained_begin(bs, parent);
if (bs->drv && bs->drv->bdrv_drain_begin) {
@@ -397,8 +398,6 @@ bdrv_drained_begin(BlockDriverState *bs)
*/
static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent)
{
int old_quiesce_counter;
IO_OR_GS_CODE();
if (qemu_in_coroutine()) {
@@ -409,11 +408,9 @@ static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent)
/* At this point, we should be always running in the main loop. */
GLOBAL_STATE_CODE();
assert(bs->quiesce_counter > 0);
GLOBAL_STATE_CODE();
/* Re-enable things in child-to-parent order */
old_quiesce_counter = qatomic_fetch_dec(&bs->quiesce_counter);
if (old_quiesce_counter == 1) {
if (--bs->quiesce_counter == 0) {
GRAPH_RDLOCK_GUARD_MAINLOOP();
if (bs->drv && bs->drv->bdrv_drain_end) {
bs->drv->bdrv_drain_end(bs);
@@ -721,11 +718,14 @@ BdrvTrackedRequest *coroutine_fn bdrv_co_get_self_request(BlockDriverState *bs)
Coroutine *self = qemu_coroutine_self();
IO_CODE();
qemu_mutex_lock(&bs->reqs_lock);
QLIST_FOREACH(req, &bs->tracked_requests, list) {
if (req->co == self) {
qemu_mutex_unlock(&bs->reqs_lock);
return req;
}
}
qemu_mutex_unlock(&bs->reqs_lock);
return NULL;
}
@@ -1058,6 +1058,10 @@ bdrv_driver_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes,
return -ENOMEDIUM;
}
if (bs->open_flags & BDRV_O_NO_FLUSH) {
flags &= ~BDRV_REQ_FUA;
}
if ((flags & BDRV_REQ_FUA) &&
(~bs->supported_write_flags & BDRV_REQ_FUA)) {
flags &= ~BDRV_REQ_FUA;
@@ -2034,7 +2038,7 @@ bdrv_co_write_req_finish(BdrvChild *child, int64_t offset, int64_t bytes,
end_sector > bs->total_sectors) &&
req->type != BDRV_TRACKED_DISCARD) {
bs->total_sectors = end_sector;
bdrv_parent_cb_resize(bs);
bdrv_co_parent_cb_resize(bs);
bdrv_dirty_bitmap_truncate(bs, end_sector << BDRV_SECTOR_BITS);
}
if (req->bytes) {
@@ -2360,10 +2364,8 @@ int bdrv_flush_all(void)
* Drivers not implementing the functionality are assumed to not support
* backing files, hence all their sectors are reported as allocated.
*
* If 'want_zero' is true, the caller is querying for mapping
* purposes, with a focus on valid BDRV_BLOCK_OFFSET_VALID, _DATA, and
* _ZERO where possible; otherwise, the result favors larger 'pnum',
* with a focus on accurate BDRV_BLOCK_ALLOCATED.
* 'mode' serves as a hint as to which results are favored; see the
* BDRV_WANT_* macros for details.
*
* If 'offset' is beyond the end of the disk image the return value is
* BDRV_BLOCK_EOF and 'pnum' is set to 0.
@@ -2383,7 +2385,7 @@ int bdrv_flush_all(void)
* set to the host mapping and BDS corresponding to the guest offset.
*/
static int coroutine_fn GRAPH_RDLOCK
bdrv_co_do_block_status(BlockDriverState *bs, bool want_zero,
bdrv_co_do_block_status(BlockDriverState *bs, unsigned int mode,
int64_t offset, int64_t bytes,
int64_t *pnum, int64_t *map, BlockDriverState **file)
{
@@ -2472,7 +2474,7 @@ bdrv_co_do_block_status(BlockDriverState *bs, bool want_zero,
local_file = bs;
local_map = aligned_offset;
} else {
ret = bs->drv->bdrv_co_block_status(bs, want_zero, aligned_offset,
ret = bs->drv->bdrv_co_block_status(bs, mode, aligned_offset,
aligned_bytes, pnum, &local_map,
&local_file);
@@ -2484,10 +2486,10 @@ bdrv_co_do_block_status(BlockDriverState *bs, bool want_zero,
* the cache requires an RCU update, so double check here to avoid
* such an update if possible.
*
* Check want_zero, because we only want to update the cache when we
* Check mode, because we only want to update the cache when we
* have accurate information about what is zero and what is data.
*/
if (want_zero &&
if (mode == BDRV_WANT_PRECISE &&
ret == (BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID) &&
QLIST_EMPTY(&bs->children))
{
@@ -2544,7 +2546,7 @@ bdrv_co_do_block_status(BlockDriverState *bs, bool want_zero,
if (ret & BDRV_BLOCK_RAW) {
assert(ret & BDRV_BLOCK_OFFSET_VALID && local_file);
ret = bdrv_co_do_block_status(local_file, want_zero, local_map,
ret = bdrv_co_do_block_status(local_file, mode, local_map,
*pnum, pnum, &local_map, &local_file);
goto out;
}
@@ -2556,7 +2558,7 @@ bdrv_co_do_block_status(BlockDriverState *bs, bool want_zero,
if (!cow_bs) {
ret |= BDRV_BLOCK_ZERO;
} else if (want_zero) {
} else if (mode == BDRV_WANT_PRECISE) {
int64_t size2 = bdrv_co_getlength(cow_bs);
if (size2 >= 0 && offset >= size2) {
@@ -2565,14 +2567,14 @@ bdrv_co_do_block_status(BlockDriverState *bs, bool want_zero,
}
}
if (want_zero && ret & BDRV_BLOCK_RECURSE &&
if (mode == BDRV_WANT_PRECISE && 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;
int ret2;
ret2 = bdrv_co_do_block_status(local_file, want_zero, local_map,
ret2 = bdrv_co_do_block_status(local_file, mode, local_map,
*pnum, &file_pnum, NULL, NULL);
if (ret2 >= 0) {
/* Ignore errors. This is just providing extra information, it
@@ -2623,7 +2625,7 @@ int coroutine_fn
bdrv_co_common_block_status_above(BlockDriverState *bs,
BlockDriverState *base,
bool include_base,
bool want_zero,
unsigned int mode,
int64_t offset,
int64_t bytes,
int64_t *pnum,
@@ -2650,7 +2652,7 @@ bdrv_co_common_block_status_above(BlockDriverState *bs,
return 0;
}
ret = bdrv_co_do_block_status(bs, want_zero, offset, bytes, pnum,
ret = bdrv_co_do_block_status(bs, mode, offset, bytes, pnum,
map, file);
++*depth;
if (ret < 0 || *pnum == 0 || ret & BDRV_BLOCK_ALLOCATED || bs == base) {
@@ -2667,7 +2669,7 @@ bdrv_co_common_block_status_above(BlockDriverState *bs,
for (p = bdrv_filter_or_cow_bs(bs); include_base || p != base;
p = bdrv_filter_or_cow_bs(p))
{
ret = bdrv_co_do_block_status(p, want_zero, offset, bytes, pnum,
ret = bdrv_co_do_block_status(p, mode, offset, bytes, pnum,
map, file);
++*depth;
if (ret < 0) {
@@ -2730,7 +2732,8 @@ int coroutine_fn bdrv_co_block_status_above(BlockDriverState *bs,
BlockDriverState **file)
{
IO_CODE();
return bdrv_co_common_block_status_above(bs, base, false, true, offset,
return bdrv_co_common_block_status_above(bs, base, false,
BDRV_WANT_PRECISE, offset,
bytes, pnum, map, file, NULL);
}
@@ -2748,27 +2751,89 @@ int coroutine_fn bdrv_co_block_status(BlockDriverState *bs, int64_t offset,
* by @offset and @bytes is known to read as zeroes.
* Return 1 if that is the case, 0 otherwise and -errno on error.
* This test is meant to be fast rather than accurate so returning 0
* does not guarantee non-zero data.
* does not guarantee non-zero data; but a return of 1 is reliable.
*/
int coroutine_fn bdrv_co_is_zero_fast(BlockDriverState *bs, int64_t offset,
int64_t bytes)
{
int ret;
int64_t pnum = bytes;
int64_t pnum;
IO_CODE();
if (!bytes) {
return 1;
while (bytes) {
ret = bdrv_co_common_block_status_above(bs, NULL, false,
BDRV_WANT_ZERO, offset, bytes,
&pnum, NULL, NULL, NULL);
if (ret < 0) {
return ret;
}
if (!(ret & BDRV_BLOCK_ZERO)) {
return 0;
}
offset += pnum;
bytes -= pnum;
}
ret = bdrv_co_common_block_status_above(bs, NULL, false, false, offset,
bytes, &pnum, NULL, NULL, NULL);
return 1;
}
/*
* Check @bs (and its backing chain) to see if the entire image is known
* to read as zeroes.
* Return 1 if that is the case, 0 otherwise and -errno on error.
* This test is meant to be fast rather than accurate so returning 0
* does not guarantee non-zero data; however, a return of 1 is reliable,
* and this function can report 1 in more cases than bdrv_co_is_zero_fast.
*/
int coroutine_fn bdrv_co_is_all_zeroes(BlockDriverState *bs)
{
int ret;
int64_t pnum, bytes;
char *buf;
QEMUIOVector local_qiov;
IO_CODE();
bytes = bdrv_co_getlength(bs);
if (bytes < 0) {
return bytes;
}
/* First probe - see if the entire image reads as zero */
ret = bdrv_co_common_block_status_above(bs, NULL, false, BDRV_WANT_ZERO,
0, bytes, &pnum, NULL, NULL,
NULL);
if (ret < 0) {
return ret;
}
if (ret & BDRV_BLOCK_ZERO) {
return bdrv_co_is_zero_fast(bs, pnum, bytes - pnum);
}
return (pnum == bytes) && (ret & BDRV_BLOCK_ZERO);
/*
* Because of the way 'blockdev-create' works, raw files tend to
* be created with a non-sparse region at the front to make
* alignment probing easier. If the block starts with only a
* small allocated region, it is still worth the effort to see if
* the rest of the image is still sparse, coupled with manually
* reading the first region to see if it reads zero after all.
*/
if (pnum > MAX_ZERO_CHECK_BUFFER) {
return 0;
}
ret = bdrv_co_is_zero_fast(bs, pnum, bytes - pnum);
if (ret <= 0) {
return ret;
}
/* Only the head of the image is unknown, and it's small. Read it. */
buf = qemu_blockalign(bs, pnum);
qemu_iovec_init_buf(&local_qiov, buf, pnum);
ret = bdrv_driver_preadv(bs, 0, pnum, &local_qiov, 0, 0);
if (ret >= 0) {
ret = buffer_is_zero(buf, pnum);
}
qemu_vfree(buf);
return ret;
}
int coroutine_fn bdrv_co_is_allocated(BlockDriverState *bs, int64_t offset,
@@ -2778,9 +2843,9 @@ int coroutine_fn bdrv_co_is_allocated(BlockDriverState *bs, int64_t offset,
int64_t dummy;
IO_CODE();
ret = bdrv_co_common_block_status_above(bs, bs, true, false, offset,
bytes, pnum ? pnum : &dummy, NULL,
NULL, NULL);
ret = bdrv_co_common_block_status_above(bs, bs, true, BDRV_WANT_ALLOCATED,
offset, bytes, pnum ? pnum : &dummy,
NULL, NULL, NULL);
if (ret < 0) {
return ret;
}
@@ -2813,7 +2878,8 @@ int coroutine_fn bdrv_co_is_allocated_above(BlockDriverState *bs,
int ret;
IO_CODE();
ret = bdrv_co_common_block_status_above(bs, base, include_base, false,
ret = bdrv_co_common_block_status_above(bs, base, include_base,
BDRV_WANT_ALLOCATED,
offset, bytes, pnum, NULL, NULL,
&depth);
if (ret < 0) {
@@ -3098,18 +3164,19 @@ int coroutine_fn bdrv_co_pdiscard(BdrvChild *child, int64_t offset,
return 0;
}
if (!bs->drv->bdrv_co_pdiscard && !bs->drv->bdrv_aio_pdiscard) {
if (!bs->drv->bdrv_co_pdiscard) {
return 0;
}
/* Invalidate the cached block-status data range if this discard overlaps */
bdrv_bsc_invalidate_range(bs, offset, bytes);
/* Discard is advisory, but some devices track and coalesce
/*
* Discard is advisory, but some devices track and coalesce
* unaligned requests, so we must pass everything down rather than
* round here. Still, most devices will just silently ignore
* unaligned requests (by returning -ENOTSUP), so we must fragment
* the request accordingly. */
* round here. Still, most devices reject unaligned requests with
* -EINVAL or -ENOTSUP, so we must fragment the request accordingly.
*/
align = MAX(bs->bl.pdiscard_alignment, bs->bl.request_alignment);
assert(align % bs->bl.request_alignment == 0);
head = offset % align;
@@ -3157,26 +3224,14 @@ int coroutine_fn bdrv_co_pdiscard(BdrvChild *child, int64_t offset,
ret = -ENOMEDIUM;
goto out;
}
if (bs->drv->bdrv_co_pdiscard) {
ret = bs->drv->bdrv_co_pdiscard(bs, offset, num);
} else {
BlockAIOCB *acb;
CoroutineIOCompletion co = {
.coroutine = qemu_coroutine_self(),
};
acb = bs->drv->bdrv_aio_pdiscard(bs, offset, num,
bdrv_co_io_em_complete, &co);
if (acb == NULL) {
ret = -EIO;
goto out;
} else {
qemu_coroutine_yield();
ret = co.ret;
}
}
ret = bs->drv->bdrv_co_pdiscard(bs, offset, num);
if (ret && ret != -ENOTSUP) {
goto out;
if (ret == -EINVAL && (offset % align != 0 || num % align != 0)) {
/* Silently skip rejected unaligned head/tail requests */
} else {
goto out; /* bail out */
}
}
offset += num;
@@ -3515,11 +3570,11 @@ int coroutine_fn bdrv_co_copy_range(BdrvChild *src, int64_t src_offset,
bytes, read_flags, write_flags);
}
static void coroutine_fn GRAPH_RDLOCK
bdrv_parent_cb_resize(BlockDriverState *bs)
void coroutine_fn bdrv_co_parent_cb_resize(BlockDriverState *bs)
{
BdrvChild *c;
IO_CODE();
assert_bdrv_graph_readable();
QLIST_FOREACH(c, &bs->parents, next_parent) {
@@ -3705,8 +3760,8 @@ bdrv_co_preadv_snapshot(BdrvChild *child, int64_t offset, int64_t bytes,
}
int coroutine_fn
bdrv_co_snapshot_block_status(BlockDriverState *bs,
bool want_zero, int64_t offset, int64_t bytes,
bdrv_co_snapshot_block_status(BlockDriverState *bs, unsigned int mode,
int64_t offset, int64_t bytes,
int64_t *pnum, int64_t *map,
BlockDriverState **file)
{
@@ -3724,7 +3779,7 @@ bdrv_co_snapshot_block_status(BlockDriverState *bs,
}
bdrv_inc_in_flight(bs);
ret = drv->bdrv_co_snapshot_block_status(bs, want_zero, offset, bytes,
ret = drv->bdrv_co_snapshot_block_status(bs, mode, offset, bytes,
pnum, map, file);
bdrv_dec_in_flight(bs);
+138 -364
View File
@@ -11,28 +11,20 @@
#include "qemu/osdep.h"
#include <liburing.h>
#include "block/aio.h"
#include "qemu/queue.h"
#include "block/block.h"
#include "block/raw-aio.h"
#include "qemu/coroutine.h"
#include "qemu/defer-call.h"
#include "qapi/error.h"
#include "sysemu/block-backend.h"
#include "system/block-backend.h"
#include "trace.h"
/* Only used for assertions. */
#include "qemu/coroutine_int.h"
/* io_uring ring size */
#define MAX_ENTRIES 128
typedef struct LuringAIOCB {
typedef struct {
Coroutine *co;
struct io_uring_sqe sqeq;
ssize_t ret;
QEMUIOVector *qiov;
bool is_read;
QSIMPLEQ_ENTRY(LuringAIOCB) next;
uint64_t offset;
ssize_t ret;
int type;
int fd;
BdrvRequestFlags flags;
/*
* Buffered reads may require resubmission, see
@@ -40,36 +32,69 @@ typedef struct LuringAIOCB {
*/
int total_read;
QEMUIOVector resubmit_qiov;
} LuringAIOCB;
typedef struct LuringQueue {
unsigned int in_queue;
unsigned int in_flight;
bool blocked;
QSIMPLEQ_HEAD(, LuringAIOCB) submit_queue;
} LuringQueue;
CqeHandler cqe_handler;
} LuringRequest;
struct LuringState {
AioContext *aio_context;
struct io_uring ring;
/* No locking required, only accessed from AioContext home thread */
LuringQueue io_q;
QEMUBH *completion_bh;
};
/**
* luring_resubmit:
*
* Resubmit a request by appending it to submit_queue. The caller must ensure
* that ioq_submit() is called later so that submit_queue requests are started.
*/
static void luring_resubmit(LuringState *s, LuringAIOCB *luringcb)
static void luring_prep_sqe(struct io_uring_sqe *sqe, void *opaque)
{
QSIMPLEQ_INSERT_TAIL(&s->io_q.submit_queue, luringcb, next);
s->io_q.in_queue++;
LuringRequest *req = opaque;
QEMUIOVector *qiov = req->qiov;
uint64_t offset = req->offset;
int fd = req->fd;
BdrvRequestFlags flags = req->flags;
switch (req->type) {
case QEMU_AIO_WRITE:
{
int luring_flags = (flags & BDRV_REQ_FUA) ? RWF_DSYNC : 0;
if (luring_flags != 0 || qiov->niov > 1) {
#ifdef HAVE_IO_URING_PREP_WRITEV2
io_uring_prep_writev2(sqe, fd, qiov->iov,
qiov->niov, offset, luring_flags);
#else
/*
* FUA should only be enabled with HAVE_IO_URING_PREP_WRITEV2, see
* luring_has_fua().
*/
assert(luring_flags == 0);
io_uring_prep_writev(sqe, fd, qiov->iov, qiov->niov, offset);
#endif
} else {
/* The man page says non-vectored is faster than vectored */
struct iovec *iov = qiov->iov;
io_uring_prep_write(sqe, fd, iov->iov_base, iov->iov_len, offset);
}
break;
}
case QEMU_AIO_ZONE_APPEND:
io_uring_prep_writev(sqe, fd, qiov->iov, qiov->niov, offset);
break;
case QEMU_AIO_READ:
{
if (req->resubmit_qiov.iov != NULL) {
qiov = &req->resubmit_qiov;
}
if (qiov->niov > 1) {
io_uring_prep_readv(sqe, fd, qiov->iov, qiov->niov,
offset + req->total_read);
} else {
/* The man page says non-vectored is faster than vectored */
struct iovec *iov = qiov->iov;
io_uring_prep_read(sqe, fd, iov->iov_base, iov->iov_len,
offset + req->total_read);
}
break;
}
case QEMU_AIO_FLUSH:
io_uring_prep_fsync(sqe, fd, IORING_FSYNC_DATASYNC);
break;
default:
fprintf(stderr, "%s: invalid AIO request type, aborting 0x%x.\n",
__func__, req->type);
abort();
}
}
/**
@@ -78,373 +103,122 @@ static void luring_resubmit(LuringState *s, LuringAIOCB *luringcb)
* Short reads are rare but may occur. The remaining read request needs to be
* resubmitted.
*/
static void luring_resubmit_short_read(LuringState *s, LuringAIOCB *luringcb,
int nread)
static void luring_resubmit_short_read(LuringRequest *req, int nread)
{
QEMUIOVector *resubmit_qiov;
size_t remaining;
trace_luring_resubmit_short_read(s, luringcb, nread);
trace_luring_resubmit_short_read(req, nread);
/* Update read position */
luringcb->total_read += nread;
remaining = luringcb->qiov->size - luringcb->total_read;
req->total_read += nread;
remaining = req->qiov->size - req->total_read;
/* Shorten qiov */
resubmit_qiov = &luringcb->resubmit_qiov;
resubmit_qiov = &req->resubmit_qiov;
if (resubmit_qiov->iov == NULL) {
qemu_iovec_init(resubmit_qiov, luringcb->qiov->niov);
qemu_iovec_init(resubmit_qiov, req->qiov->niov);
} else {
qemu_iovec_reset(resubmit_qiov);
}
qemu_iovec_concat(resubmit_qiov, luringcb->qiov, luringcb->total_read,
remaining);
qemu_iovec_concat(resubmit_qiov, req->qiov, req->total_read, remaining);
/* Update sqe */
luringcb->sqeq.off += nread;
luringcb->sqeq.addr = (uintptr_t)luringcb->resubmit_qiov.iov;
luringcb->sqeq.len = luringcb->resubmit_qiov.niov;
luring_resubmit(s, luringcb);
aio_add_sqe(luring_prep_sqe, req, &req->cqe_handler);
}
/**
* luring_process_completions:
* @s: AIO state
*
* Fetches completed I/O requests, consumes cqes and invokes their callbacks
* The function is somewhat tricky because it supports nested event loops, for
* example when a request callback invokes aio_poll().
*
* Function schedules BH completion so it can be called again in a nested
* event loop. When there are no events left to complete the BH is being
* canceled.
*
*/
static void luring_process_completions(LuringState *s)
static void luring_cqe_handler(CqeHandler *cqe_handler)
{
struct io_uring_cqe *cqes;
int total_bytes;
LuringRequest *req = container_of(cqe_handler, LuringRequest, cqe_handler);
int ret = cqe_handler->cqe.res;
defer_call_begin();
trace_luring_cqe_handler(req, ret);
/*
* Request completion callbacks can run the nested event loop.
* Schedule ourselves so the nested event loop will "see" remaining
* completed requests and process them. Without this, completion
* callbacks that wait for other requests using a nested event loop
* would hang forever.
*
* This workaround is needed because io_uring uses poll_wait, which
* is woken up when new events are added to the uring, thus polling on
* the same uring fd will block unless more events are received.
*
* Other leaf block drivers (drivers that access the data themselves)
* are networking based, so they poll sockets for data and run the
* correct coroutine.
*/
qemu_bh_schedule(s->completion_bh);
while (io_uring_peek_cqe(&s->ring, &cqes) == 0) {
LuringAIOCB *luringcb;
int ret;
if (!cqes) {
break;
if (ret < 0) {
/*
* Only writev/readv/fsync requests on regular files or host block
* devices are submitted. Therefore -EAGAIN is not expected but it's
* known to happen sometimes with Linux SCSI. Submit again and hope
* the request completes successfully.
*
* For more information, see:
* https://lore.kernel.org/io-uring/20210727165811.284510-3-axboe@kernel.dk/T/#u
*
* If the code is changed to submit other types of requests in the
* future, then this workaround may need to be extended to deal with
* genuine -EAGAIN results that should not be resubmitted
* immediately.
*/
if (ret == -EINTR || ret == -EAGAIN) {
aio_add_sqe(luring_prep_sqe, req, &req->cqe_handler);
return;
}
luringcb = io_uring_cqe_get_data(cqes);
ret = cqes->res;
io_uring_cqe_seen(&s->ring, cqes);
cqes = NULL;
/* Change counters one-by-one because we can be nested. */
s->io_q.in_flight--;
trace_luring_process_completion(s, luringcb, ret);
} else if (req->qiov) {
/* total_read is non-zero only for resubmitted read requests */
total_bytes = ret + luringcb->total_read;
int total_bytes = ret + req->total_read;
if (ret < 0) {
/*
* Only writev/readv/fsync requests on regular files or host block
* devices are submitted. Therefore -EAGAIN is not expected but it's
* known to happen sometimes with Linux SCSI. Submit again and hope
* the request completes successfully.
*
* For more information, see:
* https://lore.kernel.org/io-uring/20210727165811.284510-3-axboe@kernel.dk/T/#u
*
* If the code is changed to submit other types of requests in the
* future, then this workaround may need to be extended to deal with
* genuine -EAGAIN results that should not be resubmitted
* immediately.
*/
if (ret == -EINTR || ret == -EAGAIN) {
luring_resubmit(s, luringcb);
continue;
}
} else if (!luringcb->qiov) {
goto end;
} else if (total_bytes == luringcb->qiov->size) {
if (total_bytes == req->qiov->size) {
ret = 0;
/* Only read/write */
} else {
/* Short Read/Write */
if (luringcb->is_read) {
if (req->type == QEMU_AIO_READ) {
if (ret > 0) {
luring_resubmit_short_read(s, luringcb, ret);
continue;
} else {
/* Pad with zeroes */
qemu_iovec_memset(luringcb->qiov, total_bytes, 0,
luringcb->qiov->size - total_bytes);
ret = 0;
luring_resubmit_short_read(req, ret);
return;
}
/* Pad with zeroes */
qemu_iovec_memset(req->qiov, total_bytes, 0,
req->qiov->size - total_bytes);
ret = 0;
} else {
ret = -ENOSPC;
}
}
end:
luringcb->ret = ret;
qemu_iovec_destroy(&luringcb->resubmit_qiov);
/*
* If the coroutine is already entered it must be in ioq_submit()
* and will notice luringcb->ret has been filled in when it
* eventually runs later. Coroutines cannot be entered recursively
* so avoid doing that!
*/
assert(luringcb->co->ctx == s->aio_context);
if (!qemu_coroutine_entered(luringcb->co)) {
aio_co_wake(luringcb->co);
}
}
qemu_bh_cancel(s->completion_bh);
req->ret = ret;
qemu_iovec_destroy(&req->resubmit_qiov);
defer_call_end();
}
static int ioq_submit(LuringState *s)
{
int ret = 0;
LuringAIOCB *luringcb, *luringcb_next;
while (s->io_q.in_queue > 0) {
/*
* Try to fetch sqes from the ring for requests waiting in
* the overflow queue
*/
QSIMPLEQ_FOREACH_SAFE(luringcb, &s->io_q.submit_queue, next,
luringcb_next) {
struct io_uring_sqe *sqes = io_uring_get_sqe(&s->ring);
if (!sqes) {
break;
}
/* Prep sqe for submission */
*sqes = luringcb->sqeq;
QSIMPLEQ_REMOVE_HEAD(&s->io_q.submit_queue, next);
}
ret = io_uring_submit(&s->ring);
trace_luring_io_uring_submit(s, ret);
/* Prevent infinite loop if submission is refused */
if (ret <= 0) {
if (ret == -EAGAIN || ret == -EINTR) {
continue;
}
break;
}
s->io_q.in_flight += ret;
s->io_q.in_queue -= ret;
}
s->io_q.blocked = (s->io_q.in_queue > 0);
if (s->io_q.in_flight) {
/*
* We can try to complete something just right away if there are
* still requests in-flight.
*/
luring_process_completions(s);
}
return ret;
}
static void luring_process_completions_and_submit(LuringState *s)
{
luring_process_completions(s);
if (s->io_q.in_queue > 0) {
ioq_submit(s);
/*
* If the coroutine is already entered it must be in luring_co_submit() and
* will notice req->ret has been filled in when it eventually runs later.
* Coroutines cannot be entered recursively so avoid doing that!
*/
if (!qemu_coroutine_entered(req->co)) {
aio_co_wake(req->co);
}
}
static void qemu_luring_completion_bh(void *opaque)
int coroutine_fn luring_co_submit(BlockDriverState *bs, int fd,
uint64_t offset, QEMUIOVector *qiov,
int type, BdrvRequestFlags flags)
{
LuringState *s = opaque;
luring_process_completions_and_submit(s);
}
static void qemu_luring_completion_cb(void *opaque)
{
LuringState *s = opaque;
luring_process_completions_and_submit(s);
}
static bool qemu_luring_poll_cb(void *opaque)
{
LuringState *s = opaque;
return io_uring_cq_ready(&s->ring);
}
static void qemu_luring_poll_ready(void *opaque)
{
LuringState *s = opaque;
luring_process_completions_and_submit(s);
}
static void ioq_init(LuringQueue *io_q)
{
QSIMPLEQ_INIT(&io_q->submit_queue);
io_q->in_queue = 0;
io_q->in_flight = 0;
io_q->blocked = false;
}
static void luring_deferred_fn(void *opaque)
{
LuringState *s = opaque;
trace_luring_unplug_fn(s, s->io_q.blocked, s->io_q.in_queue,
s->io_q.in_flight);
if (!s->io_q.blocked && s->io_q.in_queue > 0) {
ioq_submit(s);
}
}
/**
* luring_do_submit:
* @fd: file descriptor for I/O
* @luringcb: AIO control block
* @s: AIO state
* @offset: offset for request
* @type: type of request
*
* Fetches sqes from ring, adds to pending queue and preps them
*
*/
static int luring_do_submit(int fd, LuringAIOCB *luringcb, LuringState *s,
uint64_t offset, int type)
{
int ret;
struct io_uring_sqe *sqes = &luringcb->sqeq;
switch (type) {
case QEMU_AIO_WRITE:
io_uring_prep_writev(sqes, fd, luringcb->qiov->iov,
luringcb->qiov->niov, offset);
break;
case QEMU_AIO_ZONE_APPEND:
io_uring_prep_writev(sqes, fd, luringcb->qiov->iov,
luringcb->qiov->niov, offset);
break;
case QEMU_AIO_READ:
io_uring_prep_readv(sqes, fd, luringcb->qiov->iov,
luringcb->qiov->niov, offset);
break;
case QEMU_AIO_FLUSH:
io_uring_prep_fsync(sqes, fd, IORING_FSYNC_DATASYNC);
break;
default:
fprintf(stderr, "%s: invalid AIO request type, aborting 0x%x.\n",
__func__, type);
abort();
}
io_uring_sqe_set_data(sqes, luringcb);
QSIMPLEQ_INSERT_TAIL(&s->io_q.submit_queue, luringcb, next);
s->io_q.in_queue++;
trace_luring_do_submit(s, s->io_q.blocked, s->io_q.in_queue,
s->io_q.in_flight);
if (!s->io_q.blocked) {
if (s->io_q.in_flight + s->io_q.in_queue >= MAX_ENTRIES) {
ret = ioq_submit(s);
trace_luring_do_submit_done(s, ret);
return ret;
}
defer_call(luring_deferred_fn, s);
}
return 0;
}
int coroutine_fn luring_co_submit(BlockDriverState *bs, int fd, uint64_t offset,
QEMUIOVector *qiov, int type)
{
int ret;
AioContext *ctx = qemu_get_current_aio_context();
LuringState *s = aio_get_linux_io_uring(ctx);
LuringAIOCB luringcb = {
LuringRequest req = {
.co = qemu_coroutine_self(),
.ret = -EINPROGRESS,
.qiov = qiov,
.is_read = (type == QEMU_AIO_READ),
.ret = -EINPROGRESS,
.type = type,
.fd = fd,
.offset = offset,
.flags = flags,
};
trace_luring_co_submit(bs, s, &luringcb, fd, offset, qiov ? qiov->size : 0,
type);
ret = luring_do_submit(fd, &luringcb, s, offset, type);
if (ret < 0) {
return ret;
}
req.cqe_handler.cb = luring_cqe_handler;
if (luringcb.ret == -EINPROGRESS) {
trace_luring_co_submit(bs, &req, fd, offset, qiov ? qiov->size : 0, type);
aio_add_sqe(luring_prep_sqe, &req, &req.cqe_handler);
if (req.ret == -EINPROGRESS) {
qemu_coroutine_yield();
}
return luringcb.ret;
return req.ret;
}
void luring_detach_aio_context(LuringState *s, AioContext *old_context)
bool luring_has_fua(void)
{
aio_set_fd_handler(old_context, s->ring.ring_fd,
NULL, NULL, NULL, NULL, s);
qemu_bh_delete(s->completion_bh);
s->aio_context = NULL;
}
void luring_attach_aio_context(LuringState *s, AioContext *new_context)
{
s->aio_context = new_context;
s->completion_bh = aio_bh_new(new_context, qemu_luring_completion_bh, s);
aio_set_fd_handler(s->aio_context, s->ring.ring_fd,
qemu_luring_completion_cb, NULL,
qemu_luring_poll_cb, qemu_luring_poll_ready, s);
}
LuringState *luring_init(Error **errp)
{
int rc;
LuringState *s = g_new0(LuringState, 1);
struct io_uring *ring = &s->ring;
trace_luring_init_state(s, sizeof(*s));
rc = io_uring_queue_init(MAX_ENTRIES, ring, 0);
if (rc < 0) {
error_setg_errno(errp, -rc, "failed to init linux io_uring ring");
g_free(s);
return NULL;
}
ioq_init(&s->io_q);
return s;
}
void luring_cleanup(LuringState *s)
{
io_uring_queue_exit(&s->ring);
trace_luring_cleanup_state(s);
g_free(s);
#ifdef HAVE_IO_URING_PREP_WRITEV2
return true;
#else
return false;
#endif
}
+32 -45
View File
@@ -28,7 +28,7 @@
#include <poll.h>
#include <math.h>
#include <arpa/inet.h>
#include "sysemu/sysemu.h"
#include "system/system.h"
#include "qemu/config-file.h"
#include "qemu/error-report.h"
#include "qemu/bitops.h"
@@ -41,11 +41,11 @@
#include "qemu/module.h"
#include "qemu/option.h"
#include "qemu/uuid.h"
#include "sysemu/replay.h"
#include "system/replay.h"
#include "qapi/error.h"
#include "qapi/qapi-commands-machine.h"
#include "qapi/qmp/qdict.h"
#include "qapi/qmp/qstring.h"
#include "qobject/qdict.h"
#include "qobject/qstring.h"
#include "crypto/secret.h"
#include "scsi/utils.h"
#include "trace.h"
@@ -107,7 +107,6 @@ typedef struct IscsiLun {
typedef struct IscsiTask {
int status;
int complete;
int retries;
int do_retry;
struct scsi_task *task;
@@ -120,6 +119,7 @@ typedef struct IscsiTask {
typedef struct IscsiAIOCB {
BlockAIOCB common;
AioContext *ctx;
QEMUBH *bh;
IscsiLun *iscsilun;
struct scsi_task *task;
@@ -174,27 +174,16 @@ iscsi_schedule_bh(IscsiAIOCB *acb)
if (acb->bh) {
return;
}
acb->bh = aio_bh_new(acb->iscsilun->aio_context, iscsi_bh_cb, acb);
acb->bh = aio_bh_new(acb->ctx, iscsi_bh_cb, acb);
qemu_bh_schedule(acb->bh);
}
#endif
static void iscsi_co_generic_bh_cb(void *opaque)
{
struct IscsiTask *iTask = opaque;
iTask->complete = 1;
aio_co_wake(iTask->co);
}
static void iscsi_retry_timer_expired(void *opaque)
{
struct IscsiTask *iTask = opaque;
iTask->complete = 1;
if (iTask->co) {
aio_co_wake(iTask->co);
}
aio_co_wake(iTask->co);
}
static inline unsigned exp_random(double mean)
@@ -239,6 +228,8 @@ iscsi_co_generic_cb(struct iscsi_context *iscsi, int status,
{
struct IscsiTask *iTask = opaque;
struct scsi_task *task = command_data;
IscsiLun *iscsilun = iTask->iscsilun;
AioContext *itask_ctx = qemu_coroutine_get_aio_context(iTask->co);
iTask->status = status;
iTask->do_retry = 0;
@@ -263,9 +254,9 @@ iscsi_co_generic_cb(struct iscsi_context *iscsi, int status,
" (retry #%u in %u ms): %s",
iTask->retries, retry_time,
iscsi_get_error(iscsi));
aio_timer_init(iTask->iscsilun->aio_context,
&iTask->retry_timer, QEMU_CLOCK_REALTIME,
SCALE_MS, iscsi_retry_timer_expired, iTask);
aio_timer_init(itask_ctx, &iTask->retry_timer,
QEMU_CLOCK_REALTIME, SCALE_MS,
iscsi_retry_timer_expired, iTask);
timer_mod(&iTask->retry_timer,
qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + retry_time);
iTask->do_retry = 1;
@@ -284,12 +275,17 @@ iscsi_co_generic_cb(struct iscsi_context *iscsi, int status,
}
}
if (iTask->co) {
replay_bh_schedule_oneshot_event(iTask->iscsilun->aio_context,
iscsi_co_generic_bh_cb, iTask);
} else {
iTask->complete = 1;
}
/*
* aio_co_wake() is safe to call: iscsi_service(), which called us, is only
* run from the event_timer and/or the FD handlers, never from the request
* coroutine. The request coroutine in turn will yield unconditionally.
* We must release the lock, though, in case we enter the coroutine
* directly. (Note that if do we enter the coroutine, iTask will probably
* be dangling once aio_co_wake() returns.)
*/
qemu_mutex_unlock(&iscsilun->mutex);
aio_co_wake(iTask->co);
qemu_mutex_lock(&iscsilun->mutex);
}
static void coroutine_fn
@@ -592,12 +588,10 @@ static inline bool iscsi_allocmap_is_valid(IscsiLun *iscsilun,
static void coroutine_fn iscsi_co_wait_for_task(IscsiTask *iTask,
IscsiLun *iscsilun)
{
while (!iTask->complete) {
iscsi_set_events(iscsilun);
qemu_mutex_unlock(&iscsilun->mutex);
qemu_coroutine_yield();
qemu_mutex_lock(&iscsilun->mutex);
}
iscsi_set_events(iscsilun);
qemu_mutex_unlock(&iscsilun->mutex);
qemu_coroutine_yield();
qemu_mutex_lock(&iscsilun->mutex);
}
static int coroutine_fn
@@ -669,7 +663,6 @@ retry:
}
if (iTask.do_retry) {
iTask.complete = 0;
goto retry;
}
@@ -694,9 +687,9 @@ out_unlock:
static int coroutine_fn iscsi_co_block_status(BlockDriverState *bs,
bool want_zero, int64_t offset,
int64_t bytes, int64_t *pnum,
int64_t *map,
unsigned int mode,
int64_t offset, int64_t bytes,
int64_t *pnum, int64_t *map,
BlockDriverState **file)
{
IscsiLun *iscsilun = bs->opaque;
@@ -740,7 +733,6 @@ retry:
scsi_free_scsi_task(iTask.task);
iTask.task = NULL;
}
iTask.complete = 0;
goto retry;
}
@@ -902,7 +894,6 @@ retry:
}
if (iTask.do_retry) {
iTask.complete = 0;
goto retry;
}
@@ -940,7 +931,6 @@ retry:
}
if (iTask.do_retry) {
iTask.complete = 0;
goto retry;
}
@@ -1023,8 +1013,7 @@ static void iscsi_ioctl_handle_emulated(IscsiAIOCB *acb, int req, void *buf)
ret = -EINVAL;
}
assert(!acb->bh);
acb->bh = aio_bh_new(bdrv_get_aio_context(bs),
iscsi_ioctl_bh_completion, acb);
acb->bh = aio_bh_new(acb->ctx, iscsi_ioctl_bh_completion, acb);
acb->ret = ret;
qemu_bh_schedule(acb->bh);
}
@@ -1041,6 +1030,7 @@ static BlockAIOCB *iscsi_aio_ioctl(BlockDriverState *bs,
acb = qemu_aio_get(&iscsi_aiocb_info, bs, cb, opaque);
acb->iscsilun = iscsilun;
acb->ctx = qemu_get_current_aio_context();
acb->bh = NULL;
acb->status = -EINPROGRESS;
acb->ioh = buf;
@@ -1184,7 +1174,6 @@ retry:
}
if (iTask.do_retry) {
iTask.complete = 0;
goto retry;
}
@@ -1301,7 +1290,6 @@ retry:
}
if (iTask.do_retry) {
iTask.complete = 0;
goto retry;
}
@@ -2390,7 +2378,6 @@ retry:
iscsi_co_wait_for_task(&iscsi_task, dst_lun);
if (iscsi_task.do_retry) {
iscsi_task.complete = 0;
goto retry;
}
+24 -5
View File
@@ -16,7 +16,7 @@
#include "qemu/coroutine.h"
#include "qemu/defer-call.h"
#include "qapi/error.h"
#include "sysemu/block-backend.h"
#include "system/block-backend.h"
/* Only used for assertions. */
#include "qemu/coroutine_int.h"
@@ -291,7 +291,7 @@ static void ioq_submit(LinuxAioState *s)
{
int ret, len;
struct qemu_laiocb *aiocb;
struct iocb *iocbs[MAX_EVENTS];
QEMU_UNINITIALIZED struct iocb *iocbs[MAX_EVENTS];
QSIMPLEQ_HEAD(, qemu_laiocb) completed;
do {
@@ -368,7 +368,8 @@ static void laio_deferred_fn(void *opaque)
}
static int laio_do_submit(int fd, struct qemu_laiocb *laiocb, off_t offset,
int type, uint64_t dev_max_batch)
int type, BdrvRequestFlags flags,
uint64_t dev_max_batch)
{
LinuxAioState *s = laiocb->ctx;
struct iocb *iocbs = &laiocb->iocb;
@@ -376,7 +377,15 @@ static int laio_do_submit(int fd, struct qemu_laiocb *laiocb, off_t offset,
switch (type) {
case QEMU_AIO_WRITE:
#ifdef HAVE_IO_PREP_PWRITEV2
{
int laio_flags = (flags & BDRV_REQ_FUA) ? RWF_DSYNC : 0;
io_prep_pwritev2(iocbs, fd, qiov->iov, qiov->niov, offset, laio_flags);
}
#else
assert(flags == 0);
io_prep_pwritev(iocbs, fd, qiov->iov, qiov->niov, offset);
#endif
break;
case QEMU_AIO_ZONE_APPEND:
io_prep_pwritev(iocbs, fd, qiov->iov, qiov->niov, offset);
@@ -409,7 +418,8 @@ static int laio_do_submit(int fd, struct qemu_laiocb *laiocb, off_t offset,
}
int coroutine_fn laio_co_submit(int fd, uint64_t offset, QEMUIOVector *qiov,
int type, uint64_t dev_max_batch)
int type, BdrvRequestFlags flags,
uint64_t dev_max_batch)
{
int ret;
AioContext *ctx = qemu_get_current_aio_context();
@@ -422,7 +432,7 @@ int coroutine_fn laio_co_submit(int fd, uint64_t offset, QEMUIOVector *qiov,
.qiov = qiov,
};
ret = laio_do_submit(fd, &laiocb, offset, type, dev_max_batch);
ret = laio_do_submit(fd, &laiocb, offset, type, flags, dev_max_batch);
if (ret < 0) {
return ret;
}
@@ -505,3 +515,12 @@ bool laio_has_fdsync(int fd)
io_destroy(ctx);
return (ret == -EINVAL) ? false : true;
}
bool laio_has_fua(void)
{
#ifdef HAVE_IO_PREP_PWRITEV2
return true;
#else
return false;
#endif
}
+3 -3
View File
@@ -154,8 +154,8 @@ block_gen_c = custom_target('block-gen.c',
'../include/block/dirty-bitmap.h',
'../include/block/block_int-io.h',
'../include/block/block-global-state.h',
'../include/sysemu/block-backend-global-state.h',
'../include/sysemu/block-backend-io.h',
'../include/system/block-backend-global-state.h',
'../include/system/block-backend-io.h',
'coroutines.h'
),
command: [wrapper_py, '@OUTPUT@', '@INPUT@'])
@@ -163,7 +163,7 @@ block_ss.add(block_gen_c)
block_ss.add(files('stream.c'))
system_ss.add(files('qapi-sysemu.c'))
system_ss.add(files('qapi-system.c'))
subdir('export')
subdir('monitor')
+159 -45
View File
@@ -19,7 +19,7 @@
#include "block/blockjob_int.h"
#include "block/block_int.h"
#include "block/dirty-bitmap.h"
#include "sysemu/block-backend.h"
#include "system/block-backend.h"
#include "qapi/error.h"
#include "qemu/ratelimit.h"
#include "qemu/bitmap.h"
@@ -51,10 +51,10 @@ typedef struct MirrorBlockJob {
BlockDriverState *to_replace;
/* Used to block operations on the drive-mirror-replace target */
Error *replace_blocker;
bool is_none_mode;
MirrorSyncMode sync_mode;
BlockMirrorBackingMode backing_mode;
/* Whether the target image requires explicit zero-initialization */
bool zero_target;
/* Whether the target should be assumed to be already zero initialized */
bool target_is_zero;
/*
* To be accesssed with atomics. Written only under the BQL (required by the
* current implementation of mirror_change()).
@@ -73,6 +73,7 @@ typedef struct MirrorBlockJob {
size_t buf_size;
int64_t bdev_length;
unsigned long *cow_bitmap;
unsigned long *zero_bitmap;
BdrvDirtyBitmap *dirty_bitmap;
BdrvDirtyBitmapIter *dbi;
uint8_t *buf;
@@ -108,9 +109,12 @@ struct MirrorOp {
int64_t offset;
uint64_t bytes;
/* The pointee is set by mirror_co_read(), mirror_co_zero(), and
* mirror_co_discard() before yielding for the first time */
/*
* These pointers are set by mirror_co_read(), mirror_co_zero(), and
* mirror_co_discard() before yielding for the first time
*/
int64_t *bytes_handled;
bool *io_skipped;
bool is_pseudo_op;
bool is_active_write;
@@ -408,15 +412,34 @@ static void coroutine_fn mirror_co_read(void *opaque)
static void coroutine_fn mirror_co_zero(void *opaque)
{
MirrorOp *op = opaque;
int ret;
bool write_needed = true;
int ret = 0;
op->s->in_flight++;
op->s->bytes_in_flight += op->bytes;
*op->bytes_handled = op->bytes;
op->is_in_flight = true;
ret = blk_co_pwrite_zeroes(op->s->target, op->offset, op->bytes,
op->s->unmap ? BDRV_REQ_MAY_UNMAP : 0);
if (op->s->zero_bitmap) {
unsigned long end = DIV_ROUND_UP(op->offset + op->bytes,
op->s->granularity);
assert(QEMU_IS_ALIGNED(op->offset, op->s->granularity));
assert(QEMU_IS_ALIGNED(op->bytes, op->s->granularity) ||
op->offset + op->bytes == op->s->bdev_length);
if (find_next_zero_bit(op->s->zero_bitmap, end,
op->offset / op->s->granularity) == end) {
write_needed = false;
*op->io_skipped = true;
}
}
if (write_needed) {
ret = blk_co_pwrite_zeroes(op->s->target, op->offset, op->bytes,
op->s->unmap ? BDRV_REQ_MAY_UNMAP : 0);
}
if (ret >= 0 && op->s->zero_bitmap) {
bitmap_set(op->s->zero_bitmap, op->offset / op->s->granularity,
DIV_ROUND_UP(op->bytes, op->s->granularity));
}
mirror_write_complete(op, ret);
}
@@ -435,29 +458,43 @@ static void coroutine_fn mirror_co_discard(void *opaque)
}
static unsigned mirror_perform(MirrorBlockJob *s, int64_t offset,
unsigned bytes, MirrorMethod mirror_method)
unsigned bytes, MirrorMethod mirror_method,
bool *io_skipped)
{
MirrorOp *op;
Coroutine *co;
int64_t bytes_handled = -1;
assert(QEMU_IS_ALIGNED(offset, s->granularity));
assert(QEMU_IS_ALIGNED(bytes, s->granularity) ||
offset + bytes == s->bdev_length);
op = g_new(MirrorOp, 1);
*op = (MirrorOp){
.s = s,
.offset = offset,
.bytes = bytes,
.bytes_handled = &bytes_handled,
.io_skipped = io_skipped,
};
qemu_co_queue_init(&op->waiting_requests);
switch (mirror_method) {
case MIRROR_METHOD_COPY:
if (s->zero_bitmap) {
bitmap_clear(s->zero_bitmap, offset / s->granularity,
DIV_ROUND_UP(bytes, s->granularity));
}
co = qemu_coroutine_create(mirror_co_read, op);
break;
case MIRROR_METHOD_ZERO:
/* s->zero_bitmap handled in mirror_co_zero */
co = qemu_coroutine_create(mirror_co_zero, op);
break;
case MIRROR_METHOD_DISCARD:
if (s->zero_bitmap) {
bitmap_clear(s->zero_bitmap, offset / s->granularity,
DIV_ROUND_UP(bytes, s->granularity));
}
co = qemu_coroutine_create(mirror_co_discard, op);
break;
default:
@@ -568,6 +605,7 @@ static void coroutine_fn GRAPH_UNLOCKED mirror_iteration(MirrorBlockJob *s)
int ret = -1;
int64_t io_bytes;
int64_t io_bytes_acct;
bool io_skipped = false;
MirrorMethod mirror_method = MIRROR_METHOD_COPY;
assert(!(offset % s->granularity));
@@ -611,8 +649,10 @@ static void coroutine_fn GRAPH_UNLOCKED mirror_iteration(MirrorBlockJob *s)
}
io_bytes = mirror_clip_bytes(s, offset, io_bytes);
io_bytes = mirror_perform(s, offset, io_bytes, mirror_method);
if (mirror_method != MIRROR_METHOD_COPY && write_zeroes_ok) {
io_bytes = mirror_perform(s, offset, io_bytes, mirror_method,
&io_skipped);
if (io_skipped ||
(mirror_method != MIRROR_METHOD_COPY && write_zeroes_ok)) {
io_bytes_acct = 0;
} else {
io_bytes_acct = io_bytes;
@@ -721,11 +761,16 @@ static int mirror_exit_common(Job *job)
bdrv_graph_rdlock_main_loop();
bdrv_child_refresh_perms(mirror_top_bs, mirror_top_bs->backing,
&error_abort);
bdrv_graph_rdunlock_main_loop();
if (!abort && s->backing_mode == MIRROR_SOURCE_BACKING_CHAIN) {
BlockDriverState *backing = s->is_none_mode ? src : s->base;
BlockDriverState *unfiltered_target = bdrv_skip_filters(target_bs);
BlockDriverState *backing;
BlockDriverState *unfiltered_target;
bdrv_graph_wrlock_drained();
unfiltered_target = bdrv_skip_filters(target_bs);
backing = s->sync_mode == MIRROR_SYNC_MODE_NONE ? src : s->base;
if (bdrv_cow_bs(unfiltered_target) != backing) {
bdrv_set_backing_hd(unfiltered_target, backing, &local_err);
if (local_err) {
@@ -734,16 +779,18 @@ static int mirror_exit_common(Job *job)
ret = -EPERM;
}
}
bdrv_graph_wrunlock();
} else if (!abort && s->backing_mode == MIRROR_OPEN_BACKING_CHAIN) {
bdrv_graph_rdlock_main_loop();
assert(!bdrv_backing_chain_next(target_bs));
ret = bdrv_open_backing_file(bdrv_skip_filters(target_bs), NULL,
"backing", &local_err);
bdrv_graph_rdunlock_main_loop();
if (ret < 0) {
error_report_err(local_err);
local_err = NULL;
}
}
bdrv_graph_rdunlock_main_loop();
if (s->should_complete && !abort) {
BlockDriverState *to_replace = s->to_replace ?: src;
@@ -841,15 +888,54 @@ static int coroutine_fn GRAPH_UNLOCKED mirror_dirty_init(MirrorBlockJob *s)
int64_t offset;
BlockDriverState *bs;
BlockDriverState *target_bs = blk_bs(s->target);
int ret = -1;
int ret = -EIO;
int64_t count;
bool punch_holes =
target_bs->detect_zeroes == BLOCKDEV_DETECT_ZEROES_OPTIONS_UNMAP &&
bdrv_can_write_zeroes_with_unmap(target_bs);
int64_t bitmap_length = DIV_ROUND_UP(s->bdev_length, s->granularity);
/* Determine if the image is already zero, regardless of sync mode. */
s->zero_bitmap = bitmap_new(bitmap_length);
bdrv_graph_co_rdlock();
bs = s->mirror_top_bs->backing->bs;
if (s->target_is_zero) {
ret = 1;
} else {
ret = bdrv_co_is_all_zeroes(target_bs);
}
bdrv_graph_co_rdunlock();
if (s->zero_target) {
if (!bdrv_can_write_zeroes_with_unmap(target_bs)) {
/* Determine if a pre-zeroing pass is necessary. */
if (ret < 0) {
return ret;
} else if (s->sync_mode == MIRROR_SYNC_MODE_TOP) {
/*
* In TOP mode, there is no benefit to a pre-zeroing pass, but
* the zero bitmap can be set if the destination already reads
* as zero and we are not punching holes.
*/
if (ret > 0 && !punch_holes) {
bitmap_set(s->zero_bitmap, 0, bitmap_length);
}
} else if (ret == 0 || punch_holes) {
/*
* Here, we are in FULL mode; our goal is to avoid writing
* zeroes if the destination already reads as zero, except
* when we are trying to punch holes. This is possible if
* zeroing happened externally (ret > 0) or if we have a fast
* way to pre-zero the image (the dirty bitmap will be
* populated later by the non-zero portions, the same as for
* TOP mode). If pre-zeroing is not fast, or we need to visit
* the entire image in order to punch holes even in the
* non-allocated regions of the source, then just mark the
* entire image dirty and leave the zero bitmap clear at this
* point in time. Otherwise, it can be faster to pre-zero the
* image now, even if we re-write the allocated portions of
* the disk later, and the pre-zero pass will populate the
* zero bitmap.
*/
if (!bdrv_can_write_zeroes_with_unmap(target_bs) || punch_holes) {
bdrv_set_dirty_bitmap(s->dirty_bitmap, 0, s->bdev_length);
return 0;
}
@@ -858,6 +944,7 @@ static int coroutine_fn GRAPH_UNLOCKED mirror_dirty_init(MirrorBlockJob *s)
for (offset = 0; offset < s->bdev_length; ) {
int bytes = MIN(s->bdev_length - offset,
QEMU_ALIGN_DOWN(INT_MAX, s->granularity));
bool ignored;
mirror_throttle(s);
@@ -873,12 +960,15 @@ static int coroutine_fn GRAPH_UNLOCKED mirror_dirty_init(MirrorBlockJob *s)
continue;
}
mirror_perform(s, offset, bytes, MIRROR_METHOD_ZERO);
mirror_perform(s, offset, bytes, MIRROR_METHOD_ZERO, &ignored);
offset += bytes;
}
mirror_wait_for_all_io(s);
s->initial_zeroing_ongoing = false;
} else {
/* In FULL mode, and image already reads as zero. */
bitmap_set(s->zero_bitmap, 0, bitmap_length);
}
/* First part, loop on the sectors and initialize the dirty bitmap. */
@@ -1020,7 +1110,7 @@ static int coroutine_fn mirror_run(Job *job, Error **errp)
mirror_free_init(s);
s->last_pause_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME);
if (!s->is_none_mode) {
if (s->sync_mode != MIRROR_SYNC_MODE_NONE) {
ret = mirror_dirty_init(s);
if (ret < 0 || job_is_cancelled(&s->common.job)) {
goto immediate_exit;
@@ -1163,6 +1253,7 @@ immediate_exit:
assert(s->in_flight == 0);
qemu_vfree(s->buf);
g_free(s->cow_bitmap);
g_free(s->zero_bitmap);
g_free(s->in_flight_bitmap);
bdrv_dirty_iter_free(s->dbi);
@@ -1341,7 +1432,8 @@ do_sync_target_write(MirrorBlockJob *job, MirrorMethod method,
{
int ret;
size_t qiov_offset = 0;
int64_t bitmap_offset, bitmap_end;
int64_t dirty_bitmap_offset, dirty_bitmap_end;
int64_t zero_bitmap_offset, zero_bitmap_end;
if (!QEMU_IS_ALIGNED(offset, job->granularity) &&
bdrv_dirty_bitmap_get(job->dirty_bitmap, offset))
@@ -1385,31 +1477,54 @@ do_sync_target_write(MirrorBlockJob *job, MirrorMethod method,
}
/*
* Tails are either clean or shrunk, so for bitmap resetting
* we safely align the range down.
* Tails are either clean or shrunk, so for dirty bitmap resetting
* we safely align the range narrower. But for zero bitmap, round
* range wider for checking or clearing, and narrower for setting.
*/
bitmap_offset = QEMU_ALIGN_UP(offset, job->granularity);
bitmap_end = QEMU_ALIGN_DOWN(offset + bytes, job->granularity);
if (bitmap_offset < bitmap_end) {
bdrv_reset_dirty_bitmap(job->dirty_bitmap, bitmap_offset,
bitmap_end - bitmap_offset);
dirty_bitmap_offset = QEMU_ALIGN_UP(offset, job->granularity);
dirty_bitmap_end = QEMU_ALIGN_DOWN(offset + bytes, job->granularity);
if (dirty_bitmap_offset < dirty_bitmap_end) {
bdrv_reset_dirty_bitmap(job->dirty_bitmap, dirty_bitmap_offset,
dirty_bitmap_end - dirty_bitmap_offset);
}
zero_bitmap_offset = offset / job->granularity;
zero_bitmap_end = DIV_ROUND_UP(offset + bytes, job->granularity);
job_progress_increase_remaining(&job->common.job, bytes);
job->active_write_bytes_in_flight += bytes;
switch (method) {
case MIRROR_METHOD_COPY:
if (job->zero_bitmap) {
bitmap_clear(job->zero_bitmap, zero_bitmap_offset,
zero_bitmap_end - zero_bitmap_offset);
}
ret = blk_co_pwritev_part(job->target, offset, bytes,
qiov, qiov_offset, flags);
break;
case MIRROR_METHOD_ZERO:
if (job->zero_bitmap) {
if (find_next_zero_bit(job->zero_bitmap, zero_bitmap_end,
zero_bitmap_offset) == zero_bitmap_end) {
ret = 0;
break;
}
}
assert(!qiov);
ret = blk_co_pwrite_zeroes(job->target, offset, bytes, flags);
if (job->zero_bitmap && ret >= 0) {
bitmap_set(job->zero_bitmap, dirty_bitmap_offset / job->granularity,
(dirty_bitmap_end - dirty_bitmap_offset) /
job->granularity);
}
break;
case MIRROR_METHOD_DISCARD:
if (job->zero_bitmap) {
bitmap_clear(job->zero_bitmap, zero_bitmap_offset,
zero_bitmap_end - zero_bitmap_offset);
}
assert(!qiov);
ret = blk_co_pdiscard(job->target, offset, bytes);
break;
@@ -1430,10 +1545,10 @@ do_sync_target_write(MirrorBlockJob *job, MirrorMethod method,
* at function start, and they must be still dirty, as we've locked
* the region for in-flight op.
*/
bitmap_offset = QEMU_ALIGN_DOWN(offset, job->granularity);
bitmap_end = QEMU_ALIGN_UP(offset + bytes, job->granularity);
bdrv_set_dirty_bitmap(job->dirty_bitmap, bitmap_offset,
bitmap_end - bitmap_offset);
dirty_bitmap_offset = QEMU_ALIGN_DOWN(offset, job->granularity);
dirty_bitmap_end = QEMU_ALIGN_UP(offset + bytes, job->granularity);
bdrv_set_dirty_bitmap(job->dirty_bitmap, dirty_bitmap_offset,
dirty_bitmap_end - dirty_bitmap_offset);
qatomic_set(&job->actively_synced, false);
action = mirror_error_action(job, false, -ret);
@@ -1711,15 +1826,16 @@ static BlockJob *mirror_start_job(
int creation_flags, BlockDriverState *target,
const char *replaces, int64_t speed,
uint32_t granularity, int64_t buf_size,
MirrorSyncMode sync_mode,
BlockMirrorBackingMode backing_mode,
bool zero_target,
bool target_is_zero,
BlockdevOnError on_source_error,
BlockdevOnError on_target_error,
bool unmap,
BlockCompletionFunc *cb,
void *opaque,
const BlockJobDriver *driver,
bool is_none_mode, BlockDriverState *base,
BlockDriverState *base,
bool auto_complete, const char *filter_node_name,
bool is_mirror, MirrorCopyMode copy_mode,
bool base_ro,
@@ -1878,9 +1994,9 @@ static BlockJob *mirror_start_job(
s->replaces = g_strdup(replaces);
s->on_source_error = on_source_error;
s->on_target_error = on_target_error;
s->is_none_mode = is_none_mode;
s->sync_mode = sync_mode;
s->backing_mode = backing_mode;
s->zero_target = zero_target;
s->target_is_zero = target_is_zero;
qatomic_set(&s->copy_mode, copy_mode);
s->base = base;
s->base_overlay = bdrv_find_overlay(bs, base);
@@ -1904,7 +2020,7 @@ static BlockJob *mirror_start_job(
*/
bdrv_disable_dirty_bitmap(s->dirty_bitmap);
bdrv_graph_wrlock();
bdrv_graph_wrlock_drained();
ret = block_job_add_bdrv(&s->common, "source", bs, 0,
BLK_PERM_WRITE_UNCHANGED | BLK_PERM_WRITE |
BLK_PERM_CONSISTENT_READ,
@@ -2009,13 +2125,12 @@ void mirror_start(const char *job_id, BlockDriverState *bs,
int creation_flags, int64_t speed,
uint32_t granularity, int64_t buf_size,
MirrorSyncMode mode, BlockMirrorBackingMode backing_mode,
bool zero_target,
bool target_is_zero,
BlockdevOnError on_source_error,
BlockdevOnError on_target_error,
bool unmap, const char *filter_node_name,
MirrorCopyMode copy_mode, Error **errp)
{
bool is_none_mode;
BlockDriverState *base;
GLOBAL_STATE_CODE();
@@ -2028,14 +2143,13 @@ void mirror_start(const char *job_id, BlockDriverState *bs,
}
bdrv_graph_rdlock_main_loop();
is_none_mode = mode == MIRROR_SYNC_MODE_NONE;
base = mode == MIRROR_SYNC_MODE_TOP ? bdrv_backing_chain_next(bs) : NULL;
bdrv_graph_rdunlock_main_loop();
mirror_start_job(job_id, bs, creation_flags, target, replaces,
speed, granularity, buf_size, backing_mode, zero_target,
on_source_error, on_target_error, unmap, NULL, NULL,
&mirror_job_driver, is_none_mode, base, false,
speed, granularity, buf_size, mode, backing_mode,
target_is_zero, on_source_error, on_target_error, unmap,
NULL, NULL, &mirror_job_driver, base, false,
filter_node_name, true, copy_mode, false, errp);
}
@@ -2061,9 +2175,9 @@ BlockJob *commit_active_start(const char *job_id, BlockDriverState *bs,
job = mirror_start_job(
job_id, bs, creation_flags, base, NULL, speed, 0, 0,
MIRROR_LEAVE_BACKING_CHAIN, false,
MIRROR_SYNC_MODE_TOP, MIRROR_LEAVE_BACKING_CHAIN, false,
on_error, on_error, true, cb, opaque,
&commit_active_job_driver, false, base, auto_complete,
&commit_active_job_driver, base, auto_complete,
filter_node_name, false, MIRROR_COPY_MODE_BACKGROUND,
base_read_only, errp);
if (!job) {
+42 -35
View File
@@ -37,11 +37,11 @@
#include "qemu/osdep.h"
#include "hw/boards.h"
#include "sysemu/block-backend.h"
#include "sysemu/blockdev.h"
#include "system/block-backend.h"
#include "system/blockdev.h"
#include "qapi/qapi-commands-block.h"
#include "qapi/qapi-commands-block-export.h"
#include "qapi/qmp/qdict.h"
#include "qobject/qdict.h"
#include "qapi/error.h"
#include "qapi/qmp/qerror.h"
#include "qemu/config-file.h"
@@ -49,7 +49,7 @@
#include "qemu/sockets.h"
#include "qemu/cutils.h"
#include "qemu/error-report.h"
#include "sysemu/sysemu.h"
#include "system/system.h"
#include "monitor/monitor.h"
#include "monitor/hmp.h"
#include "block/nbd.h"
@@ -62,7 +62,7 @@ static void hmp_drive_add_node(Monitor *mon, const char *optstr)
{
QemuOpts *opts;
QDict *qdict;
Error *local_err = NULL;
Error *err = NULL;
opts = qemu_opts_parse_noisily(&qemu_drive_opts, optstr, false);
if (!opts) {
@@ -73,19 +73,19 @@ static void hmp_drive_add_node(Monitor *mon, const char *optstr)
if (!qdict_get_try_str(qdict, "node-name")) {
qobject_unref(qdict);
error_report("'node-name' needs to be specified");
error_setg(&err, "'node-name' needs to be specified");
goto out;
}
BlockDriverState *bs = bds_tree_init(qdict, &local_err);
BlockDriverState *bs = bds_tree_init(qdict, &err);
if (!bs) {
error_report_err(local_err);
goto out;
}
bdrv_set_monitor_owned(bs);
out:
qemu_opts_del(opts);
hmp_handle_error(mon, err);
}
void hmp_drive_add(Monitor *mon, const QDict *qdict)
@@ -109,7 +109,6 @@ void hmp_drive_add(Monitor *mon, const QDict *qdict)
mc = MACHINE_GET_CLASS(current_machine);
dinfo = drive_new(opts, mc->block_default_type, &err);
if (err) {
error_report_err(err);
qemu_opts_del(opts);
goto err;
}
@@ -123,7 +122,7 @@ void hmp_drive_add(Monitor *mon, const QDict *qdict)
monitor_printf(mon, "OK\n");
break;
default:
monitor_printf(mon, "Can't hot-add drive to type %d\n", dinfo->type);
error_setg(&err, "Can't hot-add drive to type %d", dinfo->type);
goto err;
}
return;
@@ -134,6 +133,7 @@ err:
monitor_remove_blk(blk);
blk_unref(blk);
}
hmp_handle_error(mon, err);
}
void hmp_drive_del(Monitor *mon, const QDict *qdict)
@@ -141,40 +141,38 @@ void hmp_drive_del(Monitor *mon, const QDict *qdict)
const char *id = qdict_get_str(qdict, "id");
BlockBackend *blk;
BlockDriverState *bs;
Error *local_err = NULL;
Error *err = NULL;
GLOBAL_STATE_CODE();
GRAPH_RDLOCK_GUARD_MAINLOOP();
bdrv_graph_rdlock_main_loop();
bs = bdrv_find_node(id);
if (bs) {
qmp_blockdev_del(id, &local_err);
if (local_err) {
error_report_err(local_err);
}
return;
qmp_blockdev_del(id, &err);
goto unlock;
}
blk = blk_by_name(id);
if (!blk) {
error_report("Device '%s' not found", id);
return;
error_setg(&err, "Device '%s' not found", id);
goto unlock;
}
if (!blk_legacy_dinfo(blk)) {
error_report("Deleting device added with blockdev-add"
" is not supported");
return;
error_setg(&err, "Deleting device added with blockdev-add"
" is not supported");
goto unlock;
}
bs = blk_bs(blk);
if (bs) {
if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_DRIVE_DEL, &local_err)) {
error_report_err(local_err);
return;
if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_DRIVE_DEL, &err)) {
goto unlock;
}
bdrv_graph_rdunlock_main_loop();
blk_remove_bs(blk);
bdrv_graph_rdlock_main_loop();
}
/* Make the BlockBackend and the attached BlockDriverState anonymous */
@@ -191,6 +189,10 @@ void hmp_drive_del(Monitor *mon, const QDict *qdict)
} else {
blk_unref(blk);
}
unlock:
bdrv_graph_rdunlock_main_loop();
hmp_handle_error(mon, err);
}
void hmp_commit(Monitor *mon, const QDict *qdict)
@@ -198,6 +200,7 @@ void hmp_commit(Monitor *mon, const QDict *qdict)
const char *device = qdict_get_str(qdict, "device");
BlockBackend *blk;
int ret;
Error *err = NULL;
GLOBAL_STATE_CODE();
GRAPH_RDLOCK_GUARD_MAINLOOP();
@@ -209,22 +212,25 @@ void hmp_commit(Monitor *mon, const QDict *qdict)
blk = blk_by_name(device);
if (!blk) {
error_report("Device '%s' not found", device);
return;
error_setg(&err, "Device '%s' not found", device);
goto end;
}
bs = bdrv_skip_implicit_filters(blk_bs(blk));
if (!blk_is_available(blk)) {
error_report("Device '%s' has no medium", device);
return;
error_setg(&err, "Device '%s' has no medium", device);
goto end;
}
ret = bdrv_commit(bs);
}
if (ret < 0) {
error_report("'commit' error for '%s': %s", device, strerror(-ret));
error_setg(&err, "'commit' error for '%s': %s", device, strerror(-ret));
}
end:
hmp_handle_error(mon, err);
}
void hmp_drive_mirror(Monitor *mon, const QDict *qdict)
@@ -402,8 +408,8 @@ void hmp_nbd_server_start(Monitor *mon, const QDict *qdict)
goto exit;
}
nbd_server_start(addr, NULL, NULL, NBD_DEFAULT_MAX_CONNECTIONS,
&local_err);
nbd_server_start(addr, NBD_DEFAULT_HANDSHAKE_MAX_SECS, NULL, NULL,
NBD_DEFAULT_MAX_CONNECTIONS, &local_err);
qapi_free_SocketAddress(addr);
if (local_err != NULL) {
goto exit;
@@ -630,11 +636,12 @@ static void print_block_info(Monitor *mon, BlockInfo *info,
}
if (inserted) {
monitor_printf(mon, ": %s (%s%s%s)\n",
monitor_printf(mon, ": %s (%s%s%s%s)\n",
inserted->file,
inserted->drv,
inserted->ro ? ", read-only" : "",
inserted->encrypted ? ", encrypted" : "");
inserted->encrypted ? ", encrypted" : "",
inserted->active ? "" : ", inactive");
} else {
monitor_printf(mon, ": [not inserted]\n");
}
@@ -884,7 +891,7 @@ void hmp_info_snapshots(Monitor *mon, const QDict *qdict)
bs = bdrv_all_find_vmstate_bs(NULL, false, NULL, &err);
if (!bs) {
error_report_err(err);
hmp_handle_error(mon, err);
return;
}
+6 -4
View File
@@ -36,7 +36,7 @@
#include "qemu/main-loop.h"
#include "qapi/qapi-visit-sockets.h"
#include "qapi/qmp/qstring.h"
#include "qobject/qstring.h"
#include "qapi/clone-visitor.h"
#include "block/qdict.h"
@@ -351,7 +351,9 @@ int coroutine_fn nbd_co_do_establish_connection(BlockDriverState *bs,
return ret;
}
qio_channel_set_blocking(s->ioc, false, NULL);
if (!qio_channel_set_blocking(s->ioc, false, errp)) {
return -EINVAL;
}
qio_channel_set_follow_coroutine_ctx(s->ioc, true);
/* successfully connected */
@@ -1397,8 +1399,8 @@ nbd_client_co_pdiscard(BlockDriverState *bs, int64_t offset, int64_t bytes)
}
static int coroutine_fn GRAPH_RDLOCK nbd_client_co_block_status(
BlockDriverState *bs, bool want_zero, int64_t offset, int64_t bytes,
int64_t *pnum, int64_t *map, BlockDriverState **file)
BlockDriverState *bs, unsigned int mode, int64_t offset,
int64_t bytes, int64_t *pnum, int64_t *map, BlockDriverState **file)
{
int ret, request_ret;
NBDExtent64 extent = { 0 };
+19 -28
View File
@@ -39,10 +39,10 @@
#include "qemu/module.h"
#include "qemu/option.h"
#include "qemu/cutils.h"
#include "sysemu/replay.h"
#include "system/replay.h"
#include "qapi/qapi-visit-block-core.h"
#include "qapi/qmp/qdict.h"
#include "qapi/qmp/qstring.h"
#include "qobject/qdict.h"
#include "qobject/qstring.h"
#include "qapi/qobject-input-visitor.h"
#include "qapi/qobject-output-visitor.h"
#include <nfsc/libnfs.h>
@@ -69,7 +69,6 @@ typedef struct NFSClient {
typedef struct NFSRPC {
BlockDriverState *bs;
int ret;
int complete;
QEMUIOVector *iov;
struct stat *st;
Coroutine *co;
@@ -230,14 +229,6 @@ static void coroutine_fn nfs_co_init_task(BlockDriverState *bs, NFSRPC *task)
};
}
static void nfs_co_generic_bh_cb(void *opaque)
{
NFSRPC *task = opaque;
task->complete = 1;
aio_co_wake(task->co);
}
/* Called (via nfs_service) with QemuMutex held. */
static void
nfs_co_generic_cb(int ret, struct nfs_context *nfs, void *data,
@@ -256,8 +247,16 @@ nfs_co_generic_cb(int ret, struct nfs_context *nfs, void *data,
if (task->ret < 0) {
error_report("NFS Error: %s", nfs_get_error(nfs));
}
replay_bh_schedule_oneshot_event(task->client->aio_context,
nfs_co_generic_bh_cb, task);
/*
* Safe to call: nfs_service(), which called us, is only run from the FD
* handlers, never from the request coroutine. The request coroutine in
* turn will yield unconditionally.
* No need to release the lock, even if we directly enter the coroutine, as
* the lock is never re-taken after yielding. (Note: If we do enter the
* coroutine, @task will probably be dangling once aio_co_wake() returns.)
*/
aio_co_wake(task->co);
}
static int coroutine_fn nfs_co_preadv(BlockDriverState *bs, int64_t offset,
@@ -278,9 +277,7 @@ static int coroutine_fn nfs_co_preadv(BlockDriverState *bs, int64_t offset,
nfs_set_events(client);
}
while (!task.complete) {
qemu_coroutine_yield();
}
qemu_coroutine_yield();
if (task.ret < 0) {
return task.ret;
@@ -328,9 +325,7 @@ static int coroutine_fn nfs_co_pwritev(BlockDriverState *bs, int64_t offset,
nfs_set_events(client);
}
while (!task.complete) {
qemu_coroutine_yield();
}
qemu_coroutine_yield();
if (my_buffer) {
g_free(buf);
@@ -358,9 +353,7 @@ static int coroutine_fn nfs_co_flush(BlockDriverState *bs)
nfs_set_events(client);
}
while (!task.complete) {
qemu_coroutine_yield();
}
qemu_coroutine_yield();
return task.ret;
}
@@ -723,8 +716,8 @@ nfs_get_allocated_file_size_cb(int ret, struct nfs_context *nfs, void *data,
if (task->ret < 0) {
error_report("NFS Error: %s", nfs_get_error(nfs));
}
replay_bh_schedule_oneshot_event(task->client->aio_context,
nfs_co_generic_bh_cb, task);
/* Safe to call, see nfs_co_generic_cb() */
aio_co_wake(task->co);
}
static int64_t coroutine_fn nfs_co_get_allocated_file_size(BlockDriverState *bs)
@@ -748,9 +741,7 @@ static int64_t coroutine_fn nfs_co_get_allocated_file_size(BlockDriverState *bs)
nfs_set_events(client);
}
while (!task.complete) {
qemu_coroutine_yield();
}
qemu_coroutine_yield();
return (task.ret < 0 ? task.ret : st.st_blocks * 512);
}
+9 -10
View File
@@ -12,13 +12,13 @@
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "qapi/qmp/qdict.h"
#include "qapi/qmp/qstring.h"
#include "qobject/qdict.h"
#include "qobject/qstring.h"
#include "qemu/module.h"
#include "qemu/option.h"
#include "block/block-io.h"
#include "block/block_int.h"
#include "sysemu/replay.h"
#include "system/replay.h"
#define NULL_OPT_LATENCY "latency-ns"
#define NULL_OPT_ZEROES "read-zeroes"
@@ -173,18 +173,17 @@ static inline BlockAIOCB *null_aio_common(BlockDriverState *bs,
{
NullAIOCB *acb;
BDRVNullState *s = bs->opaque;
AioContext *ctx = qemu_get_current_aio_context();
acb = qemu_aio_get(&null_aiocb_info, bs, cb, opaque);
/* Only emulate latency after vcpu is running. */
if (s->latency_ns) {
aio_timer_init(bdrv_get_aio_context(bs), &acb->timer,
QEMU_CLOCK_REALTIME, SCALE_NS,
aio_timer_init(ctx, &acb->timer, QEMU_CLOCK_REALTIME, SCALE_NS,
null_timer_cb, acb);
timer_mod_ns(&acb->timer,
qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + s->latency_ns);
} else {
replay_bh_schedule_oneshot_event(bdrv_get_aio_context(bs),
null_bh_cb, acb);
replay_bh_schedule_oneshot_event(ctx, null_bh_cb, acb);
}
return &acb->common;
}
@@ -227,9 +226,9 @@ static int null_reopen_prepare(BDRVReopenState *reopen_state,
}
static int coroutine_fn null_co_block_status(BlockDriverState *bs,
bool want_zero, int64_t offset,
int64_t bytes, int64_t *pnum,
int64_t *map,
unsigned int mode,
int64_t offset, int64_t bytes,
int64_t *pnum, int64_t *map,
BlockDriverState **file)
{
BDRVNullState *s = bs->opaque;
+74 -32
View File
@@ -14,10 +14,11 @@
#include "qemu/osdep.h"
#include <linux/vfio.h>
#include "qapi/error.h"
#include "qapi/qmp/qdict.h"
#include "qapi/qmp/qstring.h"
#include "qobject/qdict.h"
#include "qobject/qstring.h"
#include "qemu/defer-call.h"
#include "qemu/error-report.h"
#include "qemu/host-pci-mmio.h"
#include "qemu/main-loop.h"
#include "qemu/module.h"
#include "qemu/cutils.h"
@@ -26,8 +27,8 @@
#include "qemu/vfio-helpers.h"
#include "block/block-io.h"
#include "block/block_int.h"
#include "sysemu/block-backend.h"
#include "sysemu/replay.h"
#include "system/block-backend.h"
#include "system/replay.h"
#include "trace.h"
#include "block/nvme.h"
@@ -60,10 +61,11 @@ typedef struct {
uint8_t *queue;
uint64_t iova;
/* Hardware MMIO register */
volatile uint32_t *doorbell;
uint32_t *doorbell;
} NVMeQueue;
typedef struct {
/* Called from nvme_process_completion() in the BDS's main AioContext */
BlockCompletionFunc *cb;
void *opaque;
int cid;
@@ -83,6 +85,7 @@ typedef struct {
uint8_t *prp_list_pages;
/* Fields protected by @lock */
/* Coroutines in this queue are woken in their own context */
CoQueue free_req_queue;
NVMeQueue sq, cq;
int cq_phase;
@@ -91,7 +94,7 @@ typedef struct {
int need_kick;
int inflight;
/* Thread-safe, no lock necessary */
/* Thread-safe, no lock necessary; runs in the BDS's main context */
QEMUBH *completion_bh;
} NVMeQueuePair;
@@ -100,7 +103,7 @@ struct BDRVNVMeState {
QEMUVFIOState *vfio;
void *bar0_wo_map;
/* Memory mapped registers */
volatile struct {
struct {
uint32_t sq_tail;
uint32_t cq_head;
} *doorbells;
@@ -205,11 +208,13 @@ static void nvme_free_queue_pair(NVMeQueuePair *q)
g_free(q);
}
/* Runs in the BDS's main AioContext */
static void nvme_free_req_queue_cb(void *opaque)
{
NVMeQueuePair *q = opaque;
qemu_mutex_lock(&q->lock);
/* qemu_co_enter_next() wakes the coroutine in its own AioContext */
while (q->free_req_head != -1 &&
qemu_co_enter_next(&q->free_req_queue, &q->lock)) {
/* Retry waiting requests */
@@ -280,7 +285,7 @@ fail:
return NULL;
}
/* With q->lock */
/* With q->lock, must be run in the BDS's main AioContext */
static void nvme_kick(NVMeQueuePair *q)
{
BDRVNVMeState *s = q->s;
@@ -292,7 +297,7 @@ static void nvme_kick(NVMeQueuePair *q)
assert(!(q->sq.tail & 0xFF00));
/* Fence the write to submission queue entry before notifying the device. */
smp_wmb();
*q->sq.doorbell = cpu_to_le32(q->sq.tail);
host_pci_stl_le_p(q->sq.doorbell, q->sq.tail);
q->inflight += q->need_kick;
q->need_kick = 0;
}
@@ -307,7 +312,10 @@ static NVMeRequest *nvme_get_free_req_nofail_locked(NVMeQueuePair *q)
return req;
}
/* Return a free request element if any, otherwise return NULL. */
/*
* Return a free request element if any, otherwise return NULL.
* May be run from any AioContext.
*/
static NVMeRequest *nvme_get_free_req_nowait(NVMeQueuePair *q)
{
QEMU_LOCK_GUARD(&q->lock);
@@ -320,6 +328,7 @@ static NVMeRequest *nvme_get_free_req_nowait(NVMeQueuePair *q)
/*
* Wait for a free request to become available if necessary, then
* return it.
* May be called in any AioContext.
*/
static coroutine_fn NVMeRequest *nvme_get_free_req(NVMeQueuePair *q)
{
@@ -327,20 +336,21 @@ static coroutine_fn NVMeRequest *nvme_get_free_req(NVMeQueuePair *q)
while (q->free_req_head == -1) {
trace_nvme_free_req_queue_wait(q->s, q->index);
/* nvme_free_req_queue_cb() wakes us in our own AioContext */
qemu_co_queue_wait(&q->free_req_queue, &q->lock);
}
return nvme_get_free_req_nofail_locked(q);
}
/* With q->lock */
/* With q->lock, may be called in any AioContext */
static void nvme_put_free_req_locked(NVMeQueuePair *q, NVMeRequest *req)
{
req->free_req_next = q->free_req_head;
q->free_req_head = req - q->reqs;
}
/* With q->lock */
/* With q->lock, may be called in any AioContext */
static void nvme_wake_free_req_locked(NVMeQueuePair *q)
{
if (!qemu_co_queue_empty(&q->free_req_queue)) {
@@ -349,7 +359,7 @@ static void nvme_wake_free_req_locked(NVMeQueuePair *q)
}
}
/* Insert a request in the freelist and wake waiters */
/* Insert a request in the freelist and wake waiters (from any AioContext) */
static void nvme_put_free_req_and_wake(NVMeQueuePair *q, NVMeRequest *req)
{
qemu_mutex_lock(&q->lock);
@@ -380,7 +390,7 @@ static inline int nvme_translate_error(const NvmeCqe *c)
}
}
/* With q->lock */
/* With q->lock, must be run in the BDS's main AioContext */
static bool nvme_process_completion(NVMeQueuePair *q)
{
BDRVNVMeState *s = q->s;
@@ -441,7 +451,7 @@ static bool nvme_process_completion(NVMeQueuePair *q)
if (progress) {
/* Notify the device so it can post more completions. */
smp_mb_release();
*q->cq.doorbell = cpu_to_le32(q->cq.head);
host_pci_stl_le_p(q->cq.doorbell, q->cq.head);
nvme_wake_free_req_locked(q);
}
@@ -450,6 +460,7 @@ static bool nvme_process_completion(NVMeQueuePair *q)
return progress;
}
/* As q->completion_bh, runs in the BDS's main AioContext */
static void nvme_process_completion_bh(void *opaque)
{
NVMeQueuePair *q = opaque;
@@ -460,7 +471,7 @@ static void nvme_process_completion_bh(void *opaque)
* so notify the device that it has space to fill in more completions now.
*/
smp_mb_release();
*q->cq.doorbell = cpu_to_le32(q->cq.head);
host_pci_stl_le_p(q->cq.doorbell, q->cq.head);
nvme_wake_free_req_locked(q);
nvme_process_completion(q);
@@ -480,7 +491,8 @@ static void nvme_trace_command(const NvmeCmd *cmd)
}
}
static void nvme_deferred_fn(void *opaque)
/* Must be run in the BDS's main AioContext */
static void nvme_kick_and_check_completions(void *opaque)
{
NVMeQueuePair *q = opaque;
@@ -489,6 +501,20 @@ static void nvme_deferred_fn(void *opaque)
nvme_process_completion(q);
}
/* Runs in nvme_submit_command()'s AioContext */
static void nvme_deferred_fn(void *opaque)
{
NVMeQueuePair *q = opaque;
if (qemu_get_current_aio_context() == q->s->aio_context) {
nvme_kick_and_check_completions(q);
} else {
aio_bh_schedule_oneshot(q->s->aio_context,
nvme_kick_and_check_completions, q);
}
}
/* May be run in any AioContext */
static void nvme_submit_command(NVMeQueuePair *q, NVMeRequest *req,
NvmeCmd *cmd, BlockCompletionFunc cb,
void *opaque)
@@ -510,6 +536,7 @@ static void nvme_submit_command(NVMeQueuePair *q, NVMeRequest *req,
defer_call(nvme_deferred_fn, q);
}
/* Put into NVMeRequest.cb, so runs in the BDS's main AioContext */
static void nvme_admin_cmd_sync_cb(void *opaque, int ret)
{
int *pret = opaque;
@@ -517,6 +544,7 @@ static void nvme_admin_cmd_sync_cb(void *opaque, int ret)
aio_wait_kick();
}
/* Must be run in the BDS's or qemu's main AioContext */
static int nvme_admin_cmd_sync(BlockDriverState *bs, NvmeCmd *cmd)
{
BDRVNVMeState *s = bs->opaque;
@@ -625,6 +653,7 @@ out:
return ret;
}
/* Must be run in the BDS's main AioContext */
static void nvme_poll_queue(NVMeQueuePair *q)
{
const size_t cqe_offset = q->cq.head * NVME_CQ_ENTRY_BYTES;
@@ -647,6 +676,7 @@ static void nvme_poll_queue(NVMeQueuePair *q)
qemu_mutex_unlock(&q->lock);
}
/* Must be run in the BDS's main AioContext */
static void nvme_poll_queues(BDRVNVMeState *s)
{
int i;
@@ -656,6 +686,7 @@ static void nvme_poll_queues(BDRVNVMeState *s)
}
}
/* Run as an event notifier in the BDS's main AioContext */
static void nvme_handle_event(EventNotifier *n)
{
BDRVNVMeState *s = container_of(n, BDRVNVMeState,
@@ -709,6 +740,7 @@ out_error:
return false;
}
/* Run as an event notifier in the BDS's main AioContext */
static bool nvme_poll_cb(void *opaque)
{
EventNotifier *e = opaque;
@@ -732,6 +764,7 @@ static bool nvme_poll_cb(void *opaque)
return false;
}
/* Run as an event notifier in the BDS's main AioContext */
static void nvme_poll_ready(EventNotifier *e)
{
BDRVNVMeState *s = container_of(e, BDRVNVMeState,
@@ -749,9 +782,10 @@ static int nvme_init(BlockDriverState *bs, const char *device, int namespace,
int ret;
uint64_t cap;
uint32_t ver;
uint32_t cc;
uint64_t timeout_ms;
uint64_t deadline, now;
volatile NvmeBar *regs = NULL;
NvmeBar *regs = NULL;
qemu_co_mutex_init(&s->dma_map_lock);
qemu_co_queue_init(&s->dma_flush_queue);
@@ -779,7 +813,7 @@ static int nvme_init(BlockDriverState *bs, const char *device, int namespace,
/* Perform initialize sequence as described in NVMe spec "7.6.1
* Initialization". */
cap = le64_to_cpu(regs->cap);
cap = host_pci_ldq_le_p(&regs->cap);
trace_nvme_controller_capability_raw(cap);
trace_nvme_controller_capability("Maximum Queue Entries Supported",
1 + NVME_CAP_MQES(cap));
@@ -805,16 +839,17 @@ static int nvme_init(BlockDriverState *bs, const char *device, int namespace,
bs->bl.request_alignment = s->page_size;
timeout_ms = MIN(500 * NVME_CAP_TO(cap), 30000);
ver = le32_to_cpu(regs->vs);
ver = host_pci_ldl_le_p(&regs->vs);
trace_nvme_controller_spec_version(extract32(ver, 16, 16),
extract32(ver, 8, 8),
extract32(ver, 0, 8));
/* Reset device to get a clean state. */
regs->cc = cpu_to_le32(le32_to_cpu(regs->cc) & 0xFE);
cc = host_pci_ldl_le_p(&regs->cc);
host_pci_stl_le_p(&regs->cc, cc & 0xFE);
/* Wait for CSTS.RDY = 0. */
deadline = qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + timeout_ms * SCALE_MS;
while (NVME_CSTS_RDY(le32_to_cpu(regs->csts))) {
while (NVME_CSTS_RDY(host_pci_ldl_le_p(&regs->csts))) {
if (qemu_clock_get_ns(QEMU_CLOCK_REALTIME) > deadline) {
error_setg(errp, "Timeout while waiting for device to reset (%"
PRId64 " ms)",
@@ -843,19 +878,21 @@ static int nvme_init(BlockDriverState *bs, const char *device, int namespace,
s->queues[INDEX_ADMIN] = q;
s->queue_count = 1;
QEMU_BUILD_BUG_ON((NVME_QUEUE_SIZE - 1) & 0xF000);
regs->aqa = cpu_to_le32(((NVME_QUEUE_SIZE - 1) << AQA_ACQS_SHIFT) |
((NVME_QUEUE_SIZE - 1) << AQA_ASQS_SHIFT));
regs->asq = cpu_to_le64(q->sq.iova);
regs->acq = cpu_to_le64(q->cq.iova);
host_pci_stl_le_p(&regs->aqa,
((NVME_QUEUE_SIZE - 1) << AQA_ACQS_SHIFT) |
((NVME_QUEUE_SIZE - 1) << AQA_ASQS_SHIFT));
host_pci_stq_le_p(&regs->asq, q->sq.iova);
host_pci_stq_le_p(&regs->acq, q->cq.iova);
/* After setting up all control registers we can enable device now. */
regs->cc = cpu_to_le32((ctz32(NVME_CQ_ENTRY_BYTES) << CC_IOCQES_SHIFT) |
(ctz32(NVME_SQ_ENTRY_BYTES) << CC_IOSQES_SHIFT) |
CC_EN_MASK);
host_pci_stl_le_p(&regs->cc,
(ctz32(NVME_CQ_ENTRY_BYTES) << CC_IOCQES_SHIFT) |
(ctz32(NVME_SQ_ENTRY_BYTES) << CC_IOSQES_SHIFT) |
CC_EN_MASK);
/* Wait for CSTS.RDY = 1. */
now = qemu_clock_get_ns(QEMU_CLOCK_REALTIME);
deadline = now + timeout_ms * SCALE_MS;
while (!NVME_CSTS_RDY(le32_to_cpu(regs->csts))) {
while (!NVME_CSTS_RDY(host_pci_ldl_le_p(&regs->csts))) {
if (qemu_clock_get_ns(QEMU_CLOCK_REALTIME) > deadline) {
error_setg(errp, "Timeout while waiting for device to start (%"
PRId64 " ms)",
@@ -1033,7 +1070,7 @@ static int nvme_probe_blocksizes(BlockDriverState *bs, BlockSizes *bsz)
return 0;
}
/* Called with s->dma_map_lock */
/* Called with s->dma_map_lock, may be run in any AioContext */
static coroutine_fn int nvme_cmd_unmap_qiov(BlockDriverState *bs,
QEMUIOVector *qiov)
{
@@ -1044,13 +1081,17 @@ static coroutine_fn int nvme_cmd_unmap_qiov(BlockDriverState *bs,
if (!s->dma_map_count && !qemu_co_queue_empty(&s->dma_flush_queue)) {
r = qemu_vfio_dma_reset_temporary(s->vfio);
if (!r) {
/*
* Queue access is protected by the dma_map_lock, and all
* coroutines are woken in their own AioContext
*/
qemu_co_queue_restart_all(&s->dma_flush_queue);
}
}
return r;
}
/* Called with s->dma_map_lock */
/* Called with s->dma_map_lock, may be run in any AioContext */
static coroutine_fn int nvme_cmd_map_qiov(BlockDriverState *bs, NvmeCmd *cmd,
NVMeRequest *req, QEMUIOVector *qiov)
{
@@ -1169,6 +1210,7 @@ static void nvme_rw_cb_bh(void *opaque)
qemu_coroutine_enter(data->co);
}
/* Put into NVMeRequest.cb, so runs in the BDS's main AioContext */
static void nvme_rw_cb(void *opaque, int ret)
{
NVMeCoData *data = opaque;
+10 -6
View File
@@ -33,10 +33,10 @@
#include "qapi/error.h"
#include "block/block_int.h"
#include "block/qdict.h"
#include "sysemu/block-backend.h"
#include "system/block-backend.h"
#include "qemu/module.h"
#include "qemu/option.h"
#include "qapi/qmp/qdict.h"
#include "qobject/qdict.h"
#include "qapi/qobject-input-visitor.h"
#include "qapi/qapi-visit-block-core.h"
#include "qemu/bswap.h"
@@ -416,9 +416,9 @@ parallels_co_flush_to_os(BlockDriverState *bs)
}
static int coroutine_fn GRAPH_RDLOCK
parallels_co_block_status(BlockDriverState *bs, bool want_zero, int64_t offset,
int64_t bytes, int64_t *pnum, int64_t *map,
BlockDriverState **file)
parallels_co_block_status(BlockDriverState *bs, unsigned int mode,
int64_t offset, int64_t bytes, int64_t *pnum,
int64_t *map, BlockDriverState **file)
{
BDRVParallelsState *s = bs->opaque;
int count;
@@ -1117,7 +1117,7 @@ parallels_co_create_opts(BlockDriver *drv, const char *filename,
}
/* Create and open the file (protocol layer) */
ret = bdrv_co_create_file(filename, opts, errp);
ret = bdrv_co_create_file(filename, opts, true, errp);
if (ret < 0) {
goto done;
}
@@ -1298,6 +1298,10 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags,
error_setg(errp, "Catalog too large");
return -EFBIG;
}
if (le64_to_cpu(ph.ext_off) >= (INT64_MAX >> BDRV_SECTOR_BITS)) {
error_setg(errp, "Invalid image: Too big offset");
return -EFBIG;
}
size = bat_entry_off(s->bat_size);
s->header_size = ROUND_UP(size, bdrv_opt_mem_align(bs->file->bs));
+3 -3
View File
@@ -35,9 +35,9 @@
#include "block/block_int.h"
#include "qapi/error.h"
#include "qapi/qapi-commands-block.h"
#include "qapi/qmp/qdict.h"
#include "sysemu/block-backend.h"
#include "sysemu/blockdev.h"
#include "qobject/qdict.h"
#include "system/block-backend.h"
#include "system/blockdev.h"
static BlockBackend *qmp_get_blk(const char *blk_name, const char *qdev_id,
Error **errp)
+79 -12
View File
@@ -33,13 +33,13 @@
#include "qapi/qapi-commands-block-core.h"
#include "qapi/qobject-output-visitor.h"
#include "qapi/qapi-visit-block-core.h"
#include "qapi/qmp/qbool.h"
#include "qapi/qmp/qdict.h"
#include "qapi/qmp/qlist.h"
#include "qapi/qmp/qnum.h"
#include "qapi/qmp/qstring.h"
#include "qobject/qbool.h"
#include "qobject/qdict.h"
#include "qobject/qlist.h"
#include "qobject/qnum.h"
#include "qobject/qstring.h"
#include "qemu/qemu-print.h"
#include "sysemu/block-backend.h"
#include "system/block-backend.h"
BlockDeviceInfo *bdrv_block_device_info(BlockBackend *blk,
BlockDriverState *bs,
@@ -51,6 +51,8 @@ BlockDeviceInfo *bdrv_block_device_info(BlockBackend *blk,
ImageInfo *backing_info;
BlockDriverState *backing;
BlockDeviceInfo *info;
BlockdevChildList **children_list_tail;
BdrvChild *child;
if (!bs->drv) {
error_setg(errp, "Block device %s is ejected", bs->node_name);
@@ -63,6 +65,7 @@ BlockDeviceInfo *bdrv_block_device_info(BlockBackend *blk,
info->file = g_strdup(bs->filename);
info->ro = bdrv_is_read_only(bs);
info->drv = g_strdup(bs->drv->format_name);
info->active = !bdrv_is_inactive(bs);
info->encrypted = bs->encrypted;
info->cache = g_new(BlockdevCacheInfo, 1);
@@ -72,8 +75,14 @@ BlockDeviceInfo *bdrv_block_device_info(BlockBackend *blk,
.no_flush = !!(bs->open_flags & BDRV_O_NO_FLUSH),
};
if (bs->node_name[0]) {
info->node_name = g_strdup(bs->node_name);
info->node_name = g_strdup(bs->node_name);
children_list_tail = &info->children;
QLIST_FOREACH(child, &bs->children, next) {
BlockdevChild *child_ref = g_new0(BlockdevChild, 1);
child_ref->child = g_strdup(child->name);
child_ref->node_name = g_strdup(child->bs->node_name);
QAPI_LIST_APPEND(children_list_tail, child_ref);
}
backing = bdrv_cow_bs(bs);
@@ -226,7 +235,8 @@ int bdrv_query_snapshot_info_list(BlockDriverState *bs,
* in @info, setting @errp on error.
*/
static void GRAPH_RDLOCK
bdrv_do_query_node_info(BlockDriverState *bs, BlockNodeInfo *info, Error **errp)
bdrv_do_query_node_info(BlockDriverState *bs, BlockNodeInfo *info, bool limits,
Error **errp)
{
int64_t size;
const char *backing_filename;
@@ -260,6 +270,33 @@ bdrv_do_query_node_info(BlockDriverState *bs, BlockNodeInfo *info, Error **errp)
info->dirty_flag = bdi.is_dirty;
info->has_dirty_flag = true;
}
if (limits) {
info->limits = g_new(BlockLimitsInfo, 1);
*info->limits = (BlockLimitsInfo) {
.request_alignment = bs->bl.request_alignment,
.has_max_discard = bs->bl.max_pdiscard != 0,
.max_discard = bs->bl.max_pdiscard,
.has_discard_alignment = bs->bl.pdiscard_alignment != 0,
.discard_alignment = bs->bl.pdiscard_alignment,
.has_max_write_zeroes = bs->bl.max_pwrite_zeroes != 0,
.max_write_zeroes = bs->bl.max_pwrite_zeroes,
.has_write_zeroes_alignment = bs->bl.pwrite_zeroes_alignment != 0,
.write_zeroes_alignment = bs->bl.pwrite_zeroes_alignment,
.has_opt_transfer = bs->bl.opt_transfer != 0,
.opt_transfer = bs->bl.opt_transfer,
.has_max_transfer = bs->bl.max_transfer != 0,
.max_transfer = bs->bl.max_transfer,
.has_max_hw_transfer = bs->bl.max_hw_transfer != 0,
.max_hw_transfer = bs->bl.max_hw_transfer,
.max_iov = bs->bl.max_iov,
.has_max_hw_iov = bs->bl.max_hw_iov != 0,
.max_hw_iov = bs->bl.max_hw_iov,
.min_mem_alignment = bs->bl.min_mem_alignment,
.opt_mem_alignment = bs->bl.opt_mem_alignment,
};
}
info->format_specific = bdrv_get_specific_info(bs, &err);
if (err) {
error_propagate(errp, err);
@@ -334,7 +371,7 @@ void bdrv_query_image_info(BlockDriverState *bs,
ImageInfo *info;
info = g_new0(ImageInfo, 1);
bdrv_do_query_node_info(bs, qapi_ImageInfo_base(info), errp);
bdrv_do_query_node_info(bs, qapi_ImageInfo_base(info), true, errp);
if (*errp) {
goto fail;
}
@@ -380,6 +417,7 @@ fail:
*/
void bdrv_query_block_graph_info(BlockDriverState *bs,
BlockGraphInfo **p_info,
bool limits,
Error **errp)
{
ERRP_GUARD();
@@ -388,7 +426,7 @@ void bdrv_query_block_graph_info(BlockDriverState *bs,
BdrvChild *c;
info = g_new0(BlockGraphInfo, 1);
bdrv_do_query_node_info(bs, qapi_BlockGraphInfo_base(info), errp);
bdrv_do_query_node_info(bs, qapi_BlockGraphInfo_base(info), limits, errp);
if (*errp) {
goto fail;
}
@@ -402,7 +440,7 @@ void bdrv_query_block_graph_info(BlockDriverState *bs,
QAPI_LIST_APPEND(children_list_tail, c_info);
c_info->name = g_strdup(c->name);
bdrv_query_block_graph_info(c->bs, &c_info->info, errp);
bdrv_query_block_graph_info(c->bs, &c_info->info, limits, errp);
if (*errp) {
goto fail;
}
@@ -899,6 +937,29 @@ void bdrv_image_info_specific_dump(ImageInfoSpecific *info_spec,
visit_free(v);
}
/**
* Dumps the given BlockLimitsInfo object in a human-readable form,
* prepending an optional prefix if the dump is not empty.
*/
static void bdrv_image_info_limits_dump(BlockLimitsInfo *limits,
const char *prefix,
int indentation)
{
QObject *obj;
Visitor *v = qobject_output_visitor_new(&obj);
visit_type_BlockLimitsInfo(v, NULL, &limits, &error_abort);
visit_complete(v, &obj);
if (!qobject_is_empty_dump(obj)) {
if (prefix) {
qemu_printf("%*s%s", indentation * 4, "", prefix);
}
dump_qobject(indentation + 1, obj);
}
qobject_unref(obj);
visit_free(v);
}
/**
* Print the given @info object in human-readable form. Every field is indented
* using the given @indentation (four spaces per indentation level).
@@ -974,6 +1035,12 @@ void bdrv_node_info_dump(BlockNodeInfo *info, int indentation, bool protocol)
}
}
if (info->limits) {
bdrv_image_info_limits_dump(info->limits,
"Block limits:\n",
indentation);
}
if (info->has_snapshots) {
SnapshotInfoList *elem;
+10 -10
View File
@@ -27,15 +27,15 @@
#include "qemu/error-report.h"
#include "block/block_int.h"
#include "block/qdict.h"
#include "sysemu/block-backend.h"
#include "system/block-backend.h"
#include "qemu/module.h"
#include "qemu/option.h"
#include "qemu/bswap.h"
#include "qemu/cutils.h"
#include "qemu/memalign.h"
#include <zlib.h>
#include "qapi/qmp/qdict.h"
#include "qapi/qmp/qstring.h"
#include "qobject/qdict.h"
#include "qobject/qstring.h"
#include "qapi/qobject-input-visitor.h"
#include "qapi/qapi-visit-block-core.h"
#include "crypto/block.h"
@@ -530,7 +530,7 @@ get_cluster_offset(BlockDriverState *bs, uint64_t offset, int allocate,
}
static int coroutine_fn GRAPH_RDLOCK
qcow_co_block_status(BlockDriverState *bs, bool want_zero,
qcow_co_block_status(BlockDriverState *bs, unsigned int mode,
int64_t offset, int64_t bytes, int64_t *pnum,
int64_t *map, BlockDriverState **file)
{
@@ -978,7 +978,7 @@ qcow_co_create_opts(BlockDriver *drv, const char *filename,
}
/* Create and open the file (protocol layer) */
ret = bdrv_co_create_file(filename, opts, errp);
ret = bdrv_co_create_file(filename, opts, true, errp);
if (ret < 0) {
goto fail;
}
@@ -1184,11 +1184,11 @@ static const char *const qcow_strong_runtime_opts[] = {
};
static BlockDriver bdrv_qcow = {
.format_name = "qcow",
.instance_size = sizeof(BDRVQcowState),
.bdrv_probe = qcow_probe,
.bdrv_open = qcow_open,
.bdrv_close = qcow_close,
.format_name = "qcow",
.instance_size = sizeof(BDRVQcowState),
.bdrv_probe = qcow_probe,
.bdrv_open = qcow_open,
.bdrv_close = qcow_close,
.bdrv_child_perm = bdrv_default_perms,
.bdrv_reopen_prepare = qcow_reopen_prepare,
.bdrv_co_create = qcow_co_create,
+6 -10
View File
@@ -1978,12 +1978,10 @@ discard_in_l2_slice(BlockDriverState *bs, uint64_t offset, uint64_t nb_clusters,
if (!keep_reference) {
/* Then decrease the refcount */
qcow2_free_any_cluster(bs, old_l2_entry, type);
} else if (s->discard_passthrough[type] &&
(cluster_type == QCOW2_CLUSTER_NORMAL ||
cluster_type == QCOW2_CLUSTER_ZERO_ALLOC)) {
} else {
/* If we keep the reference, pass on the discard still */
bdrv_pdiscard(s->data_file, old_l2_entry & L2E_OFFSET_MASK,
s->cluster_size);
qcow2_discard_cluster(bs, old_l2_entry & L2E_OFFSET_MASK,
s->cluster_size, cluster_type, type);
}
}
@@ -2092,12 +2090,10 @@ zero_in_l2_slice(BlockDriverState *bs, uint64_t offset,
if (!keep_reference) {
/* Then decrease the refcount */
qcow2_free_any_cluster(bs, old_l2_entry, QCOW2_DISCARD_REQUEST);
} else if (s->discard_passthrough[QCOW2_DISCARD_REQUEST] &&
(type == QCOW2_CLUSTER_NORMAL ||
type == QCOW2_CLUSTER_ZERO_ALLOC)) {
} else {
/* If we keep the reference, pass on the discard still */
bdrv_pdiscard(s->data_file, old_l2_entry & L2E_OFFSET_MASK,
s->cluster_size);
qcow2_discard_cluster(bs, old_l2_entry & L2E_OFFSET_MASK,
s->cluster_size, type, QCOW2_DISCARD_REQUEST);
}
}
}
+21 -4
View File
@@ -754,8 +754,8 @@ void qcow2_process_discards(BlockDriverState *bs, int ret)
}
}
static void update_refcount_discard(BlockDriverState *bs,
uint64_t offset, uint64_t length)
static void queue_discard(BlockDriverState *bs,
uint64_t offset, uint64_t length)
{
BDRVQcow2State *s = bs->opaque;
Qcow2DiscardRegion *d, *p, *next;
@@ -902,7 +902,7 @@ update_refcount(BlockDriverState *bs, int64_t offset, int64_t length,
}
if (s->discard_passthrough[type]) {
update_refcount_discard(bs, cluster_offset, s->cluster_size);
queue_discard(bs, cluster_offset, s->cluster_size);
}
}
}
@@ -1205,6 +1205,23 @@ void qcow2_free_any_cluster(BlockDriverState *bs, uint64_t l2_entry,
}
}
void qcow2_discard_cluster(BlockDriverState *bs, uint64_t offset,
uint64_t length, QCow2ClusterType ctype,
enum qcow2_discard_type dtype)
{
BDRVQcow2State *s = bs->opaque;
if (s->discard_passthrough[dtype] &&
(ctype == QCOW2_CLUSTER_NORMAL ||
ctype == QCOW2_CLUSTER_ZERO_ALLOC)) {
if (has_data_file(bs)) {
bdrv_pdiscard(s->data_file, offset, length);
} else {
queue_discard(bs, offset, length);
}
}
}
int qcow2_write_caches(BlockDriverState *bs)
{
BDRVQcow2State *s = bs->opaque;
@@ -3619,7 +3636,7 @@ qcow2_discard_refcount_block(BlockDriverState *bs, uint64_t discard_block_offs)
/* discard refblock from the cache if refblock is cached */
qcow2_cache_discard(s->refcount_block_cache, refblock);
}
update_refcount_discard(bs, discard_block_offs, s->cluster_size);
queue_discard(bs, discard_block_offs, s->cluster_size);
return 0;
}
+1 -1
View File
@@ -23,7 +23,7 @@
*/
#include "qemu/osdep.h"
#include "sysemu/block-backend.h"
#include "system/block-backend.h"
#include "qapi/error.h"
#include "qcow2.h"
#include "qemu/bswap.h"
+128 -39
View File
@@ -25,15 +25,15 @@
#include "qemu/osdep.h"
#include "block/qdict.h"
#include "sysemu/block-backend.h"
#include "system/block-backend.h"
#include "qemu/main-loop.h"
#include "qemu/module.h"
#include "qcow2.h"
#include "qemu/error-report.h"
#include "qapi/error.h"
#include "qapi/qapi-events-block-core.h"
#include "qapi/qmp/qdict.h"
#include "qapi/qmp/qstring.h"
#include "qobject/qdict.h"
#include "qobject/qstring.h"
#include "trace.h"
#include "qemu/option_int.h"
#include "qemu/cutils.h"
@@ -835,41 +835,113 @@ static const char *overlap_bool_option_names[QCOW2_OL_MAX_BITNR] = {
[QCOW2_OL_BITMAP_DIRECTORY_BITNR] = QCOW2_OPT_OVERLAP_BITMAP_DIRECTORY,
};
static void cache_clean_timer_cb(void *opaque)
static void coroutine_fn cache_clean_timer(void *opaque)
{
BlockDriverState *bs = opaque;
BDRVQcow2State *s = bs->opaque;
qcow2_cache_clean_unused(s->l2_table_cache);
qcow2_cache_clean_unused(s->refcount_block_cache);
timer_mod(s->cache_clean_timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) +
(int64_t) s->cache_clean_interval * 1000);
BDRVQcow2State *s = opaque;
uint64_t wait_ns;
WITH_QEMU_LOCK_GUARD(&s->lock) {
wait_ns = s->cache_clean_interval * NANOSECONDS_PER_SECOND;
}
while (wait_ns > 0) {
qemu_co_sleep_ns_wakeable(&s->cache_clean_timer_wake,
QEMU_CLOCK_REALTIME, wait_ns);
WITH_QEMU_LOCK_GUARD(&s->lock) {
if (s->cache_clean_interval > 0) {
qcow2_cache_clean_unused(s->l2_table_cache);
qcow2_cache_clean_unused(s->refcount_block_cache);
}
wait_ns = s->cache_clean_interval * NANOSECONDS_PER_SECOND;
}
}
WITH_QEMU_LOCK_GUARD(&s->lock) {
s->cache_clean_timer_co = NULL;
qemu_co_queue_restart_all(&s->cache_clean_timer_exit);
}
}
static void cache_clean_timer_init(BlockDriverState *bs, AioContext *context)
{
BDRVQcow2State *s = bs->opaque;
if (s->cache_clean_interval > 0) {
s->cache_clean_timer =
aio_timer_new_with_attrs(context, QEMU_CLOCK_VIRTUAL,
SCALE_MS, QEMU_TIMER_ATTR_EXTERNAL,
cache_clean_timer_cb, bs);
timer_mod(s->cache_clean_timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) +
(int64_t) s->cache_clean_interval * 1000);
assert(!s->cache_clean_timer_co);
s->cache_clean_timer_co = qemu_coroutine_create(cache_clean_timer, s);
aio_co_enter(context, s->cache_clean_timer_co);
}
}
static void cache_clean_timer_del(BlockDriverState *bs)
/**
* Delete the cache clean timer and await any yet running instance.
* Called holding s->lock.
*/
static void coroutine_fn
cache_clean_timer_co_locked_del_and_wait(BlockDriverState *bs)
{
BDRVQcow2State *s = bs->opaque;
if (s->cache_clean_timer) {
timer_free(s->cache_clean_timer);
s->cache_clean_timer = NULL;
if (s->cache_clean_timer_co) {
s->cache_clean_interval = 0;
qemu_co_sleep_wake(&s->cache_clean_timer_wake);
qemu_co_queue_wait(&s->cache_clean_timer_exit, &s->lock);
}
}
/**
* Same as cache_clean_timer_co_locked_del_and_wait(), but takes s->lock.
*/
static void coroutine_fn
cache_clean_timer_co_del_and_wait(BlockDriverState *bs)
{
BDRVQcow2State *s = bs->opaque;
WITH_QEMU_LOCK_GUARD(&s->lock) {
cache_clean_timer_co_locked_del_and_wait(bs);
}
}
struct CacheCleanTimerDelAndWaitCoParams {
BlockDriverState *bs;
bool done;
};
static void coroutine_fn cache_clean_timer_del_and_wait_co_entry(void *opaque)
{
struct CacheCleanTimerDelAndWaitCoParams *p = opaque;
cache_clean_timer_co_del_and_wait(p->bs);
p->done = true;
aio_wait_kick();
}
/**
* Delete the cache clean timer and await any yet running instance.
* Must be called from the main or BDS AioContext without s->lock held.
*/
static void coroutine_mixed_fn
cache_clean_timer_del_and_wait(BlockDriverState *bs)
{
IO_OR_GS_CODE();
if (qemu_in_coroutine()) {
cache_clean_timer_co_del_and_wait(bs);
} else {
struct CacheCleanTimerDelAndWaitCoParams p = { .bs = bs };
Coroutine *co;
co = qemu_coroutine_create(cache_clean_timer_del_and_wait_co_entry, &p);
qemu_coroutine_enter(co);
BDRV_POLL_WHILE(bs, !p.done);
}
}
static void qcow2_detach_aio_context(BlockDriverState *bs)
{
cache_clean_timer_del(bs);
cache_clean_timer_del_and_wait(bs);
}
static void qcow2_attach_aio_context(BlockDriverState *bs,
@@ -1214,12 +1286,24 @@ fail:
return ret;
}
/* s_locked specifies whether s->lock is held or not */
static void qcow2_update_options_commit(BlockDriverState *bs,
Qcow2ReopenState *r)
Qcow2ReopenState *r,
bool s_locked)
{
BDRVQcow2State *s = bs->opaque;
int i;
/*
* We need to stop the cache-clean-timer before destroying the metadata
* table caches
*/
if (s_locked) {
cache_clean_timer_co_locked_del_and_wait(bs);
} else {
cache_clean_timer_del_and_wait(bs);
}
if (s->l2_table_cache) {
qcow2_cache_destroy(s->l2_table_cache);
}
@@ -1228,6 +1312,7 @@ static void qcow2_update_options_commit(BlockDriverState *bs,
}
s->l2_table_cache = r->l2_table_cache;
s->refcount_block_cache = r->refcount_block_cache;
s->l2_slice_size = r->l2_slice_size;
s->overlap_check = r->overlap_check;
@@ -1239,11 +1324,8 @@ static void qcow2_update_options_commit(BlockDriverState *bs,
s->discard_no_unref = r->discard_no_unref;
if (s->cache_clean_interval != r->cache_clean_interval) {
cache_clean_timer_del(bs);
s->cache_clean_interval = r->cache_clean_interval;
cache_clean_timer_init(bs, bdrv_get_aio_context(bs));
}
s->cache_clean_interval = r->cache_clean_interval;
cache_clean_timer_init(bs, bdrv_get_aio_context(bs));
qapi_free_QCryptoBlockOpenOptions(s->crypto_opts);
s->crypto_opts = r->crypto_opts;
@@ -1261,6 +1343,7 @@ static void qcow2_update_options_abort(BlockDriverState *bs,
qapi_free_QCryptoBlockOpenOptions(r->crypto_opts);
}
/* Called with s->lock held */
static int coroutine_fn GRAPH_RDLOCK
qcow2_update_options(BlockDriverState *bs, QDict *options, int flags,
Error **errp)
@@ -1270,7 +1353,7 @@ qcow2_update_options(BlockDriverState *bs, QDict *options, int flags,
ret = qcow2_update_options_prepare(bs, &r, options, flags, errp);
if (ret >= 0) {
qcow2_update_options_commit(bs, &r);
qcow2_update_options_commit(bs, &r, true);
} else {
qcow2_update_options_abort(bs, &r);
}
@@ -1721,7 +1804,7 @@ qcow2_do_open(BlockDriverState *bs, QDict *options, int flags,
ret = -EINVAL;
goto fail;
}
} else if (!(flags & BDRV_O_NO_IO)) {
} else {
error_setg(errp, "Missing CRYPTO header for crypt method %d",
s->crypt_method_header);
ret = -EINVAL;
@@ -1895,7 +1978,9 @@ qcow2_do_open(BlockDriverState *bs, QDict *options, int flags,
g_free(s->image_data_file);
if (open_data_file && has_data_file(bs)) {
bdrv_graph_co_rdunlock();
bdrv_drain_all_begin();
bdrv_co_unref_child(bs, s->data_file);
bdrv_drain_all_end();
bdrv_graph_co_rdlock();
s->data_file = NULL;
}
@@ -1906,7 +1991,7 @@ qcow2_do_open(BlockDriverState *bs, QDict *options, int flags,
qemu_vfree(s->l1_table);
/* else pre-write overlap checks in cache_destroy may crash */
s->l1_table = NULL;
cache_clean_timer_del(bs);
cache_clean_timer_co_locked_del_and_wait(bs);
if (s->l2_table_cache) {
qcow2_cache_destroy(s->l2_table_cache);
}
@@ -1961,6 +2046,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags,
/* Initialise locks */
qemu_co_mutex_init(&s->lock);
qemu_co_queue_init(&s->cache_clean_timer_exit);
assert(!qemu_in_coroutine());
assert(qemu_get_current_aio_context() == qemu_get_aio_context());
@@ -1976,7 +2062,7 @@ static void qcow2_refresh_limits(BlockDriverState *bs, Error **errp)
{
BDRVQcow2State *s = bs->opaque;
if (bs->encrypted) {
if (s->crypto) {
/* Encryption works on a sector granularity */
bs->bl.request_alignment = qcrypto_block_get_sector_size(s->crypto);
}
@@ -2046,7 +2132,7 @@ static void qcow2_reopen_commit(BDRVReopenState *state)
GRAPH_RDLOCK_GUARD_MAINLOOP();
qcow2_update_options_commit(state->bs, state->opaque);
qcow2_update_options_commit(state->bs, state->opaque, false);
if (!s->data_file) {
/*
* If we don't have an external data file, s->data_file was cleared by
@@ -2141,9 +2227,9 @@ static void qcow2_join_options(QDict *options, QDict *old_options)
}
static int coroutine_fn GRAPH_RDLOCK
qcow2_co_block_status(BlockDriverState *bs, bool want_zero, int64_t offset,
int64_t count, int64_t *pnum, int64_t *map,
BlockDriverState **file)
qcow2_co_block_status(BlockDriverState *bs, unsigned int mode,
int64_t offset, int64_t count, int64_t *pnum,
int64_t *map, BlockDriverState **file)
{
BDRVQcow2State *s = bs->opaque;
uint64_t host_offset;
@@ -2803,7 +2889,7 @@ qcow2_do_close(BlockDriverState *bs, bool close_data_file)
qcow2_inactivate(bs);
}
cache_clean_timer_del(bs);
cache_clean_timer_del_and_wait(bs);
qcow2_cache_destroy(s->l2_table_cache);
qcow2_cache_destroy(s->refcount_block_cache);
@@ -2821,7 +2907,7 @@ qcow2_do_close(BlockDriverState *bs, bool close_data_file)
if (close_data_file && has_data_file(bs)) {
GLOBAL_STATE_CODE();
bdrv_graph_rdunlock_main_loop();
bdrv_graph_wrlock();
bdrv_graph_wrlock_drained();
bdrv_unref_child(bs, s->data_file);
bdrv_graph_wrunlock();
s->data_file = NULL;
@@ -2871,6 +2957,9 @@ qcow2_co_invalidate_cache(BlockDriverState *bs, Error **errp)
data_file = s->data_file;
memset(s, 0, sizeof(BDRVQcow2State));
s->data_file = data_file;
/* Re-initialize objects initialized in qcow2_open() */
qemu_co_mutex_init(&s->lock);
qemu_co_queue_init(&s->cache_clean_timer_exit);
options = qdict_clone_shallow(bs->options);
@@ -3954,7 +4043,7 @@ qcow2_co_create_opts(BlockDriver *drv, const char *filename, QemuOpts *opts,
}
/* Create and open the file (protocol layer) */
ret = bdrv_co_create_file(filename, opts, errp);
ret = bdrv_co_create_file(filename, opts, true, errp);
if (ret < 0) {
goto finish;
}
@@ -3969,7 +4058,7 @@ qcow2_co_create_opts(BlockDriver *drv, const char *filename, QemuOpts *opts,
/* Create and open an external data file (protocol layer) */
val = qdict_get_try_str(qdict, BLOCK_OPT_DATA_FILE);
if (val) {
ret = bdrv_co_create_file(val, opts, errp);
ret = bdrv_co_create_file(val, opts, false, errp);
if (ret < 0) {
goto finish;
}
+8 -1
View File
@@ -345,8 +345,11 @@ typedef struct BDRVQcow2State {
Qcow2Cache *l2_table_cache;
Qcow2Cache *refcount_block_cache;
QEMUTimer *cache_clean_timer;
/* Non-NULL while the timer is running */
Coroutine *cache_clean_timer_co;
unsigned cache_clean_interval;
QemuCoSleep cache_clean_timer_wake;
CoQueue cache_clean_timer_exit;
QLIST_HEAD(, QCowL2Meta) cluster_allocs;
@@ -880,6 +883,10 @@ void GRAPH_RDLOCK qcow2_free_clusters(BlockDriverState *bs,
void GRAPH_RDLOCK
qcow2_free_any_cluster(BlockDriverState *bs, uint64_t l2_entry,
enum qcow2_discard_type type);
void GRAPH_RDLOCK
qcow2_discard_cluster(BlockDriverState *bs, uint64_t offset,
uint64_t length, QCow2ClusterType ctype,
enum qcow2_discard_type dtype);
int GRAPH_RDLOCK
qcow2_update_snapshot_refcount(BlockDriverState *bs, int64_t l1_table_offset,
+7 -6
View File
@@ -23,8 +23,8 @@
#include "qemu/memalign.h"
#include "trace.h"
#include "qed.h"
#include "sysemu/block-backend.h"
#include "qapi/qmp/qdict.h"
#include "system/block-backend.h"
#include "qobject/qdict.h"
#include "qapi/qobject-input-visitor.h"
#include "qapi/qapi-visit-block-core.h"
@@ -353,6 +353,7 @@ static void bdrv_qed_detach_aio_context(BlockDriverState *bs)
qed_cancel_need_check_timer(s);
timer_free(s->need_check_timer);
s->need_check_timer = NULL;
}
static void bdrv_qed_attach_aio_context(BlockDriverState *bs,
@@ -787,7 +788,7 @@ bdrv_qed_co_create_opts(BlockDriver *drv, const char *filename,
}
/* Create and open the file (protocol layer) */
ret = bdrv_co_create_file(filename, opts, errp);
ret = bdrv_co_create_file(filename, opts, true, errp);
if (ret < 0) {
goto fail;
}
@@ -832,9 +833,9 @@ fail:
}
static int coroutine_fn GRAPH_RDLOCK
bdrv_qed_co_block_status(BlockDriverState *bs, bool want_zero, int64_t pos,
int64_t bytes, int64_t *pnum, int64_t *map,
BlockDriverState **file)
bdrv_qed_co_block_status(BlockDriverState *bs, unsigned int mode,
int64_t pos, int64_t bytes, int64_t *pnum,
int64_t *map, BlockDriverState **file)
{
BDRVQEDState *s = bs->opaque;
size_t len = MIN(bytes, SIZE_MAX);
+7 -7
View File
@@ -23,10 +23,10 @@
#include "block/qdict.h"
#include "qapi/error.h"
#include "qapi/qapi-events-block.h"
#include "qapi/qmp/qdict.h"
#include "qobject/qdict.h"
#include "qapi/qmp/qerror.h"
#include "qapi/qmp/qlist.h"
#include "qapi/qmp/qstring.h"
#include "qobject/qlist.h"
#include "qobject/qstring.h"
#include "crypto/hash.h"
#define HASH_LENGTH 32
@@ -1037,7 +1037,7 @@ static int quorum_open(BlockDriverState *bs, QDict *options, int flags,
close_exit:
/* cleanup on error */
bdrv_graph_wrlock();
bdrv_graph_wrlock_drained();
for (i = 0; i < s->num_children; i++) {
if (!opened[i]) {
continue;
@@ -1057,7 +1057,7 @@ static void quorum_close(BlockDriverState *bs)
BDRVQuorumState *s = bs->opaque;
int i;
bdrv_graph_wrlock();
bdrv_graph_wrlock_drained();
for (i = 0; i < s->num_children; i++) {
bdrv_unref_child(bs, s->children[i]);
}
@@ -1226,7 +1226,7 @@ static void quorum_child_perm(BlockDriverState *bs, BdrvChild *c,
* region contains zeroes, and BDRV_BLOCK_DATA otherwise.
*/
static int coroutine_fn GRAPH_RDLOCK
quorum_co_block_status(BlockDriverState *bs, bool want_zero,
quorum_co_block_status(BlockDriverState *bs, unsigned int mode,
int64_t offset, int64_t count,
int64_t *pnum, int64_t *map, BlockDriverState **file)
{
@@ -1238,7 +1238,7 @@ quorum_co_block_status(BlockDriverState *bs, bool want_zero,
for (i = 0; i < s->num_children; i++) {
int64_t bytes;
ret = bdrv_co_common_block_status_above(s->children[i]->bs, NULL, false,
want_zero, offset, count,
mode, offset, count,
&bytes, NULL, NULL, NULL);
if (ret < 0) {
quorum_report_bad(QUORUM_OP_TYPE_READ, offset, count,
+3 -3
View File
@@ -283,8 +283,8 @@ fail:
}
static int coroutine_fn GRAPH_RDLOCK
raw_co_block_status(BlockDriverState *bs, bool want_zero, int64_t offset,
int64_t bytes, int64_t *pnum, int64_t *map,
raw_co_block_status(BlockDriverState *bs, unsigned int mode,
int64_t offset, int64_t bytes, int64_t *pnum, int64_t *map,
BlockDriverState **file)
{
BDRVRawState *s = bs->opaque;
@@ -463,7 +463,7 @@ static int coroutine_fn GRAPH_UNLOCKED
raw_co_create_opts(BlockDriver *drv, const char *filename,
QemuOpts *opts, Error **errp)
{
return bdrv_co_create_file(filename, opts, errp);
return bdrv_co_create_file(filename, opts, true, errp);
}
static int raw_open(BlockDriverState *bs, QDict *options, int flags,
+80 -53
View File
@@ -23,11 +23,11 @@
#include "block/qdict.h"
#include "crypto/secret.h"
#include "qemu/cutils.h"
#include "sysemu/replay.h"
#include "qapi/qmp/qstring.h"
#include "qapi/qmp/qdict.h"
#include "qapi/qmp/qjson.h"
#include "qapi/qmp/qlist.h"
#include "system/replay.h"
#include "qobject/qstring.h"
#include "qobject/qdict.h"
#include "qobject/qjson.h"
#include "qobject/qlist.h"
#include "qapi/qobject-input-visitor.h"
#include "qapi/qapi-visit-block-core.h"
@@ -99,12 +99,18 @@ typedef struct BDRVRBDState {
char *namespace;
uint64_t image_size;
uint64_t object_size;
/*
* If @bs->encrypted is true, this is the encryption format actually loaded
* at the librbd level. If it is false, it is the result of probing.
* RBD_IMAGE_ENCRYPTION_FORMAT__MAX means that encryption is not enabled and
* probing didn't find any known encryption header either.
*/
RbdImageEncryptionFormat encryption_format;
} BDRVRBDState;
typedef struct RBDTask {
BlockDriverState *bs;
Coroutine *co;
bool complete;
int64_t ret;
} RBDTask;
@@ -254,7 +260,6 @@ static void qemu_rbd_parse_filename(const char *filename, QDict *options,
done:
g_free(buf);
qobject_unref(keypairs);
return;
}
static int qemu_rbd_set_auth(rados_t cluster, BlockdevOptionsRbd *opts,
@@ -471,10 +476,12 @@ static int qemu_rbd_encryption_format(rbd_image_t image,
return 0;
}
static int qemu_rbd_encryption_load(rbd_image_t image,
static int qemu_rbd_encryption_load(BlockDriverState *bs,
rbd_image_t image,
RbdEncryptionOptions *encrypt,
Error **errp)
{
BDRVRBDState *s = bs->opaque;
int r = 0;
g_autofree char *passphrase = NULL;
rbd_encryption_luks1_format_options_t luks_opts;
@@ -545,15 +552,19 @@ static int qemu_rbd_encryption_load(rbd_image_t image,
error_setg_errno(errp, -r, "encryption load fail");
return r;
}
bs->encrypted = true;
s->encryption_format = encrypt->format;
return 0;
}
#ifdef LIBRBD_SUPPORTS_ENCRYPTION_LOAD2
static int qemu_rbd_encryption_load2(rbd_image_t image,
static int qemu_rbd_encryption_load2(BlockDriverState *bs,
rbd_image_t image,
RbdEncryptionOptions *encrypt,
Error **errp)
{
BDRVRBDState *s = bs->opaque;
int r = 0;
int encrypt_count = 1;
int i;
@@ -639,6 +650,8 @@ static int qemu_rbd_encryption_load2(rbd_image_t image,
error_setg_errno(errp, -r, "layered encryption load fail");
goto exit;
}
bs->encrypted = true;
s->encryption_format = encrypt->format;
exit:
for (i = 0; i < encrypt_count; ++i) {
@@ -672,6 +685,45 @@ exit:
#endif
#endif
/*
* For an image without encryption enabled on the rbd layer, probe the start of
* the image if it could be opened as an encrypted image so that we can display
* it when the user queries the node (most importantly in qemu-img).
*
* If the guest writes an encryption header to its disk after this probing, this
* won't be reflected when queried, but that's okay. There is no reason why the
* user should want to apply encryption at the rbd level while the image is
* still in use. This is just guest data.
*/
static void qemu_rbd_encryption_probe(BlockDriverState *bs)
{
BDRVRBDState *s = bs->opaque;
char buf[RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN] = {0};
int r;
assert(s->encryption_format == RBD_IMAGE_ENCRYPTION_FORMAT__MAX);
r = rbd_read(s->image, 0,
RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN, buf);
if (r < RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN) {
return;
}
if (memcmp(buf, rbd_luks_header_verification,
RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN) == 0) {
s->encryption_format = RBD_IMAGE_ENCRYPTION_FORMAT_LUKS;
} else if (memcmp(buf, rbd_luks2_header_verification,
RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN) == 0) {
s->encryption_format = RBD_IMAGE_ENCRYPTION_FORMAT_LUKS2;
} else if (memcmp(buf, rbd_layered_luks_header_verification,
RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN) == 0) {
s->encryption_format = RBD_IMAGE_ENCRYPTION_FORMAT_LUKS;
} else if (memcmp(buf, rbd_layered_luks2_header_verification,
RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN) == 0) {
s->encryption_format = RBD_IMAGE_ENCRYPTION_FORMAT_LUKS2;
}
}
/* FIXME Deprecate and remove keypairs or make it available in QMP. */
static int qemu_rbd_do_create(BlockdevCreateOptions *options,
const char *keypairs, const char *password_secret,
@@ -1134,17 +1186,18 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
goto failed_open;
}
s->encryption_format = RBD_IMAGE_ENCRYPTION_FORMAT__MAX;
if (opts->encrypt) {
#ifdef LIBRBD_SUPPORTS_ENCRYPTION
if (opts->encrypt->parent) {
#ifdef LIBRBD_SUPPORTS_ENCRYPTION_LOAD2
r = qemu_rbd_encryption_load2(s->image, opts->encrypt, errp);
r = qemu_rbd_encryption_load2(bs, s->image, opts->encrypt, errp);
#else
r = -ENOTSUP;
error_setg(errp, "RBD library does not support layered encryption");
#endif
} else {
r = qemu_rbd_encryption_load(s->image, opts->encrypt, errp);
r = qemu_rbd_encryption_load(bs, s->image, opts->encrypt, errp);
}
if (r < 0) {
goto failed_post_open;
@@ -1154,6 +1207,8 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
error_setg(errp, "RBD library does not support image encryption");
goto failed_post_open;
#endif
} else {
qemu_rbd_encryption_probe(bs);
}
r = rbd_stat(s->image, &info, sizeof(info));
@@ -1252,7 +1307,6 @@ static int qemu_rbd_resize(BlockDriverState *bs, uint64_t size)
static void qemu_rbd_finish_bh(void *opaque)
{
RBDTask *task = opaque;
task->complete = true;
aio_co_wake(task->co);
}
@@ -1269,7 +1323,7 @@ static void qemu_rbd_completion_cb(rbd_completion_t c, RBDTask *task)
{
task->ret = rbd_aio_get_return_value(c);
rbd_aio_release(c);
aio_bh_schedule_oneshot(bdrv_get_aio_context(task->bs),
aio_bh_schedule_oneshot(qemu_coroutine_get_aio_context(task->co),
qemu_rbd_finish_bh, task);
}
@@ -1281,7 +1335,7 @@ static int coroutine_fn qemu_rbd_start_co(BlockDriverState *bs,
RBDAIOCmd cmd)
{
BDRVRBDState *s = bs->opaque;
RBDTask task = { .bs = bs, .co = qemu_coroutine_self() };
RBDTask task = { .co = qemu_coroutine_self() };
rbd_completion_t c;
int r;
@@ -1344,9 +1398,8 @@ static int coroutine_fn qemu_rbd_start_co(BlockDriverState *bs,
return r;
}
while (!task.complete) {
qemu_coroutine_yield();
}
/* Expect exactly a single wake from qemu_rbd_finish_bh() */
qemu_coroutine_yield();
if (task.ret < 0) {
error_report("rbd request failed: cmd %d offset %" PRIu64 " bytes %"
@@ -1413,17 +1466,6 @@ static ImageInfoSpecific *qemu_rbd_get_specific_info(BlockDriverState *bs,
{
BDRVRBDState *s = bs->opaque;
ImageInfoSpecific *spec_info;
char buf[RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN] = {0};
int r;
if (s->image_size >= RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN) {
r = rbd_read(s->image, 0,
RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN, buf);
if (r < 0) {
error_setg_errno(errp, -r, "cannot read image start for probe");
return NULL;
}
}
spec_info = g_new(ImageInfoSpecific, 1);
*spec_info = (ImageInfoSpecific){
@@ -1431,28 +1473,13 @@ static ImageInfoSpecific *qemu_rbd_get_specific_info(BlockDriverState *bs,
.u.rbd.data = g_new0(ImageInfoSpecificRbd, 1),
};
if (memcmp(buf, rbd_luks_header_verification,
RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN) == 0) {
spec_info->u.rbd.data->encryption_format =
RBD_IMAGE_ENCRYPTION_FORMAT_LUKS;
spec_info->u.rbd.data->has_encryption_format = true;
} else if (memcmp(buf, rbd_luks2_header_verification,
RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN) == 0) {
spec_info->u.rbd.data->encryption_format =
RBD_IMAGE_ENCRYPTION_FORMAT_LUKS2;
spec_info->u.rbd.data->has_encryption_format = true;
} else if (memcmp(buf, rbd_layered_luks_header_verification,
RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN) == 0) {
spec_info->u.rbd.data->encryption_format =
RBD_IMAGE_ENCRYPTION_FORMAT_LUKS;
spec_info->u.rbd.data->has_encryption_format = true;
} else if (memcmp(buf, rbd_layered_luks2_header_verification,
RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN) == 0) {
spec_info->u.rbd.data->encryption_format =
RBD_IMAGE_ENCRYPTION_FORMAT_LUKS2;
spec_info->u.rbd.data->has_encryption_format = true;
if (s->encryption_format == RBD_IMAGE_ENCRYPTION_FORMAT__MAX) {
assert(!bs->encrypted);
} else {
spec_info->u.rbd.data->has_encryption_format = false;
ImageInfoSpecificRbd *rbd_info = spec_info->u.rbd.data;
rbd_info->has_encryption_format = true;
rbd_info->encryption_format = s->encryption_format;
}
return spec_info;
@@ -1504,9 +1531,9 @@ static int qemu_rbd_diff_iterate_cb(uint64_t offs, size_t len,
}
static int coroutine_fn qemu_rbd_co_block_status(BlockDriverState *bs,
bool want_zero, int64_t offset,
int64_t bytes, int64_t *pnum,
int64_t *map,
unsigned int mode,
int64_t offset, int64_t bytes,
int64_t *pnum, int64_t *map,
BlockDriverState **file)
{
BDRVRBDState *s = bs->opaque;
+9 -8
View File
@@ -19,9 +19,9 @@
#include "block/blockjob.h"
#include "block/block_int.h"
#include "block/block_backup.h"
#include "sysemu/block-backend.h"
#include "system/block-backend.h"
#include "qapi/error.h"
#include "qapi/qmp/qdict.h"
#include "qobject/qdict.h"
#include "block/replication.h"
typedef enum {
@@ -176,7 +176,6 @@ static void replication_child_perm(BlockDriverState *bs, BdrvChild *c,
*nshared = BLK_PERM_CONSISTENT_READ
| BLK_PERM_WRITE
| BLK_PERM_WRITE_UNCHANGED;
return;
}
static int64_t coroutine_fn GRAPH_RDLOCK
@@ -365,14 +364,15 @@ static void reopen_backing_file(BlockDriverState *bs, bool writable,
BlockReopenQueue *reopen_queue = NULL;
GLOBAL_STATE_CODE();
GRAPH_RDLOCK_GUARD_MAINLOOP();
bdrv_graph_rdlock_main_loop();
/*
* s->hidden_disk and s->secondary_disk may not be set yet, as they will
* only be set after the children are writable.
*/
hidden_disk = bs->file->bs->backing;
secondary_disk = hidden_disk->bs->backing;
bdrv_graph_rdunlock_main_loop();
if (writable) {
s->orig_hidden_read_only = bdrv_is_read_only(hidden_disk->bs);
@@ -541,7 +541,7 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode,
return;
}
bdrv_graph_wrlock();
bdrv_graph_wrlock_drained();
bdrv_ref(hidden_disk->bs);
s->hidden_disk = bdrv_attach_child(bs, hidden_disk->bs, "hidden disk",
@@ -576,7 +576,6 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode,
return;
}
bdrv_op_block_all(top_bs, s->blocker);
bdrv_op_unblock(top_bs, BLOCK_OP_TYPE_DATAPLANE, s->blocker);
bdrv_graph_wrunlock();
@@ -585,7 +584,9 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode,
0, MIRROR_SYNC_MODE_NONE, NULL, 0, false, false,
NULL, &perf,
BLOCKDEV_ON_ERROR_REPORT,
BLOCKDEV_ON_ERROR_REPORT, JOB_INTERNAL,
BLOCKDEV_ON_ERROR_REPORT,
ON_CBW_ERROR_BREAK_GUEST_WRITE,
JOB_INTERNAL,
backup_job_completed, bs, NULL, &local_err);
if (local_err) {
error_propagate(errp, local_err);
@@ -651,7 +652,7 @@ static void replication_done(void *opaque, int ret)
if (ret == 0) {
s->stage = BLOCK_REPLICATION_DONE;
bdrv_graph_wrlock();
bdrv_graph_wrlock_drained();
bdrv_unref_child(bs, s->secondary_disk);
s->secondary_disk = NULL;
bdrv_unref_child(bs, s->hidden_disk);
+3 -3
View File
@@ -22,7 +22,7 @@
#include "qemu/osdep.h"
#include "sysemu/block-backend.h"
#include "system/block-backend.h"
#include "qemu/cutils.h"
#include "block/block_int.h"
@@ -41,11 +41,11 @@ snapshot_access_co_preadv_part(BlockDriverState *bs,
static int coroutine_fn GRAPH_RDLOCK
snapshot_access_co_block_status(BlockDriverState *bs,
bool want_zero, int64_t offset,
unsigned int mode, int64_t offset,
int64_t bytes, int64_t *pnum,
int64_t *map, BlockDriverState **file)
{
return bdrv_co_snapshot_block_status(bs->file->bs, want_zero, offset,
return bdrv_co_snapshot_block_status(bs->file->bs, mode, offset,
bytes, pnum, map, file);
}
+20 -15
View File
@@ -27,10 +27,10 @@
#include "block/block_int.h"
#include "block/qdict.h"
#include "qapi/error.h"
#include "qapi/qmp/qdict.h"
#include "qapi/qmp/qstring.h"
#include "qobject/qdict.h"
#include "qobject/qstring.h"
#include "qemu/option.h"
#include "sysemu/block-backend.h"
#include "system/block-backend.h"
QemuOptsList internal_snapshot_opts = {
.name = "snapshot",
@@ -291,11 +291,12 @@ int bdrv_snapshot_goto(BlockDriverState *bs,
}
/* .bdrv_open() will re-attach it */
bdrv_graph_wrlock();
bdrv_graph_wrlock_drained();
bdrv_unref_child(bs, fallback);
bdrv_graph_wrunlock();
ret = bdrv_snapshot_goto(fallback_bs, snapshot_id, errp);
memset(bs->opaque, 0, drv->instance_size);
open_ret = drv->bdrv_open(bs, options, bs->open_flags, &local_err);
qobject_unref(options);
if (open_ret < 0) {
@@ -326,7 +327,7 @@ int bdrv_snapshot_goto(BlockDriverState *bs,
/**
* Delete an internal snapshot by @snapshot_id and @name.
* @bs: block device used in the operation
* @bs: block device used in the operation, must be drained
* @snapshot_id: unique snapshot ID, or NULL
* @name: snapshot name, or NULL
* @errp: location to store error
@@ -357,6 +358,8 @@ int bdrv_snapshot_delete(BlockDriverState *bs,
GLOBAL_STATE_CODE();
assert(bs->quiesce_counter > 0);
if (!drv) {
error_setg(errp, "Device '%s' has no medium",
bdrv_get_device_name(bs));
@@ -367,9 +370,6 @@ int bdrv_snapshot_delete(BlockDriverState *bs,
return -EINVAL;
}
/* drain all pending i/o before deleting snapshot */
bdrv_drained_begin(bs);
if (drv->bdrv_snapshot_delete) {
ret = drv->bdrv_snapshot_delete(bs, snapshot_id, name, errp);
} else if (fallback_bs) {
@@ -381,7 +381,6 @@ int bdrv_snapshot_delete(BlockDriverState *bs,
ret = -ENOTSUP;
}
bdrv_drained_end(bs);
return ret;
}
@@ -570,19 +569,22 @@ int bdrv_all_delete_snapshot(const char *name,
ERRP_GUARD();
g_autoptr(GList) bdrvs = NULL;
GList *iterbdrvs;
int ret = 0;
GLOBAL_STATE_CODE();
GRAPH_RDLOCK_GUARD_MAINLOOP();
if (bdrv_all_get_snapshot_devices(has_devices, devices, &bdrvs, errp) < 0) {
return -1;
bdrv_drain_all_begin();
bdrv_graph_rdlock_main_loop();
ret = bdrv_all_get_snapshot_devices(has_devices, devices, &bdrvs, errp);
if (ret < 0) {
goto out;
}
iterbdrvs = bdrvs;
while (iterbdrvs) {
BlockDriverState *bs = iterbdrvs->data;
QEMUSnapshotInfo sn1, *snapshot = &sn1;
int ret = 0;
if ((devices || bdrv_all_snapshots_includes_bs(bs)) &&
bdrv_snapshot_find(bs, snapshot, name) >= 0)
@@ -593,13 +595,16 @@ int bdrv_all_delete_snapshot(const char *name,
if (ret < 0) {
error_prepend(errp, "Could not delete snapshot '%s' on '%s': ",
name, bdrv_get_device_or_node_name(bs));
return -1;
goto out;
}
iterbdrvs = iterbdrvs->next;
}
return 0;
out:
bdrv_graph_rdunlock_main_loop();
bdrv_drain_all_end();
return ret;
}
+13 -13
View File
@@ -39,8 +39,8 @@
#include "qemu/sockets.h"
#include "qapi/qapi-visit-sockets.h"
#include "qapi/qapi-visit-block-core.h"
#include "qapi/qmp/qdict.h"
#include "qapi/qmp/qstring.h"
#include "qobject/qdict.h"
#include "qobject/qstring.h"
#include "qapi/qobject-input-visitor.h"
#include "qapi/qobject-output-visitor.h"
#include "trace.h"
@@ -1010,19 +1010,18 @@ static int ssh_has_zero_init(BlockDriverState *bs)
}
typedef struct BDRVSSHRestart {
BlockDriverState *bs;
BDRVSSHState *s;
Coroutine *co;
} BDRVSSHRestart;
static void restart_coroutine(void *opaque)
{
BDRVSSHRestart *restart = opaque;
BlockDriverState *bs = restart->bs;
BDRVSSHState *s = bs->opaque;
AioContext *ctx = bdrv_get_aio_context(bs);
BDRVSSHState *s = restart->s;
trace_ssh_restart_coroutine(restart->co);
aio_set_fd_handler(ctx, s->sock, NULL, NULL, NULL, NULL, NULL);
aio_set_fd_handler(qemu_get_current_aio_context(), s->sock,
NULL, NULL, NULL, NULL, NULL);
aio_co_wake(restart->co);
}
@@ -1031,12 +1030,13 @@ static void restart_coroutine(void *opaque)
* handlers are set up so that we'll be rescheduled when there is an
* interesting event on the socket.
*/
static coroutine_fn void co_yield(BDRVSSHState *s, BlockDriverState *bs)
static coroutine_fn void co_yield(BDRVSSHState *s)
{
int r;
IOHandler *rd_handler = NULL, *wr_handler = NULL;
AioContext *ctx = qemu_get_current_aio_context();
BDRVSSHRestart restart = {
.bs = bs,
.s = s,
.co = qemu_coroutine_self()
};
@@ -1051,7 +1051,7 @@ static coroutine_fn void co_yield(BDRVSSHState *s, BlockDriverState *bs)
trace_ssh_co_yield(s->sock, rd_handler, wr_handler);
aio_set_fd_handler(bdrv_get_aio_context(bs), s->sock,
aio_set_fd_handler(ctx, s->sock,
rd_handler, wr_handler, NULL, NULL, &restart);
qemu_coroutine_yield();
trace_ssh_co_yield_back(s->sock);
@@ -1093,7 +1093,7 @@ static coroutine_fn int ssh_read(BDRVSSHState *s, BlockDriverState *bs,
trace_ssh_read_return(r, sftp_get_error(s->sftp));
if (r == SSH_AGAIN) {
co_yield(s, bs);
co_yield(s);
goto again;
}
if (r == SSH_EOF || (r == 0 && sftp_get_error(s->sftp) == SSH_FX_EOF)) {
@@ -1168,7 +1168,7 @@ static coroutine_fn int ssh_write(BDRVSSHState *s, BlockDriverState *bs,
trace_ssh_write_return(r, sftp_get_error(s->sftp));
if (r == SSH_AGAIN) {
co_yield(s, bs);
co_yield(s);
goto again;
}
if (r < 0) {
@@ -1233,7 +1233,7 @@ static coroutine_fn int ssh_flush(BDRVSSHState *s, BlockDriverState *bs)
again:
r = sftp_fsync(s->sftp_handle);
if (r == SSH_AGAIN) {
co_yield(s, bs);
co_yield(s);
goto again;
}
if (r < 0) {
+12 -15
View File
@@ -16,9 +16,9 @@
#include "block/block_int.h"
#include "block/blockjob_int.h"
#include "qapi/error.h"
#include "qapi/qmp/qdict.h"
#include "qobject/qdict.h"
#include "qemu/ratelimit.h"
#include "sysemu/block-backend.h"
#include "system/block-backend.h"
#include "block/copy-on-read.h"
enum {
@@ -51,7 +51,7 @@ static int coroutine_fn stream_populate(BlockBackend *blk,
return blk_co_preadv(blk, offset, bytes, NULL, BDRV_REQ_PREFETCH);
}
static int stream_prepare(Job *job)
static int GRAPH_UNLOCKED stream_prepare(Job *job)
{
StreamBlockJob *s = container_of(job, StreamBlockJob, common.job);
BlockDriverState *unfiltered_bs;
@@ -73,18 +73,16 @@ static int stream_prepare(Job *job)
s->cor_filter_bs = NULL;
/*
* bdrv_set_backing_hd() requires that the unfiltered_bs and the COW child
* of unfiltered_bs is drained. Drain already here and use
* bdrv_set_backing_hd_drained() instead because the polling during
* drained_begin() might change the graph, and if we do this only later, we
* may end up working with the wrong base node (or it might even have gone
* away by the time we want to use it).
* bdrv_set_backing_hd() requires that all block nodes are drained. Drain
* already here, because the polling during drained_begin() might change the
* graph, and if we do this only later, we may end up working with the wrong
* base node (or it might even have gone away by the time we want to use
* it).
*/
bdrv_drained_begin(unfiltered_bs);
if (unfiltered_bs_cow) {
bdrv_ref(unfiltered_bs_cow);
bdrv_drained_begin(unfiltered_bs_cow);
}
bdrv_drain_all_begin();
bdrv_graph_rdlock_main_loop();
base = bdrv_filter_or_cow_bs(s->above_base);
@@ -106,7 +104,7 @@ static int stream_prepare(Job *job)
}
bdrv_graph_wrlock();
bdrv_set_backing_hd_drained(unfiltered_bs, base, &local_err);
bdrv_set_backing_hd(unfiltered_bs, base, &local_err);
bdrv_graph_wrunlock();
/*
@@ -123,11 +121,10 @@ static int stream_prepare(Job *job)
}
out:
bdrv_drain_all_end();
if (unfiltered_bs_cow) {
bdrv_drained_end(unfiltered_bs_cow);
bdrv_unref(unfiltered_bs_cow);
}
bdrv_drained_end(unfiltered_bs);
return ret;
}
@@ -373,7 +370,7 @@ void stream_start(const char *job_id, BlockDriverState *bs,
* already have our own plans. Also don't allow resize as the image size is
* queried only at the job start and then cached.
*/
bdrv_graph_wrlock();
bdrv_graph_wrlock_drained();
if (block_job_add_bdrv(&s->common, "active node", bs, 0,
basic_flags | BLK_PERM_WRITE, errp)) {
bdrv_graph_wrunlock();
+5 -5
View File
@@ -23,13 +23,13 @@
*/
#include "qemu/osdep.h"
#include "sysemu/block-backend.h"
#include "system/block-backend.h"
#include "block/throttle-groups.h"
#include "qemu/throttle-options.h"
#include "qemu/main-loop.h"
#include "qemu/queue.h"
#include "qemu/thread.h"
#include "sysemu/qtest.h"
#include "system/qtest.h"
#include "qapi/error.h"
#include "qapi/qapi-visit-block-core.h"
#include "qom/object.h"
@@ -908,7 +908,6 @@ unlock:
qemu_mutex_unlock(&tg->lock);
qapi_free_ThrottleLimits(argp);
error_propagate(errp, local_err);
return;
}
static void throttle_group_get_limits(Object *obj, Visitor *v,
@@ -934,7 +933,8 @@ static bool throttle_group_can_be_deleted(UserCreatable *uc)
return OBJECT(uc)->ref == 1;
}
static void throttle_group_obj_class_init(ObjectClass *klass, void *class_data)
static void throttle_group_obj_class_init(ObjectClass *klass,
const void *class_data)
{
size_t i = 0;
UserCreatableClass *ucc = USER_CREATABLE_CLASS(klass);
@@ -967,7 +967,7 @@ static const TypeInfo throttle_group_info = {
.instance_size = sizeof(ThrottleGroup),
.instance_init = throttle_group_obj_init,
.instance_finalize = throttle_group_obj_finalize,
.interfaces = (InterfaceInfo[]) {
.interfaces = (const InterfaceInfo[]) {
{ TYPE_USER_CREATABLE },
{ }
},
+3 -9
View File
@@ -62,15 +62,9 @@ qmp_block_stream(void *bs) "bs %p"
file_paio_submit(void *acb, void *opaque, int64_t offset, int count, int type) "acb %p opaque %p offset %"PRId64" count %d type %d"
# io_uring.c
luring_init_state(void *s, size_t size) "s %p size %zu"
luring_cleanup_state(void *s) "%p freed"
luring_unplug_fn(void *s, int blocked, int queued, int inflight) "LuringState %p blocked %d queued %d inflight %d"
luring_do_submit(void *s, int blocked, int queued, int inflight) "LuringState %p blocked %d queued %d inflight %d"
luring_do_submit_done(void *s, int ret) "LuringState %p submitted to kernel %d"
luring_co_submit(void *bs, void *s, void *luringcb, int fd, uint64_t offset, size_t nbytes, int type) "bs %p s %p luringcb %p fd %d offset %" PRId64 " nbytes %zd type %d"
luring_process_completion(void *s, void *aiocb, int ret) "LuringState %p luringcb %p ret %d"
luring_io_uring_submit(void *s, int ret) "LuringState %p ret %d"
luring_resubmit_short_read(void *s, void *luringcb, int nread) "LuringState %p luringcb %p nread %d"
luring_cqe_handler(void *req, int ret) "req %p ret %d"
luring_co_submit(void *bs, void *req, int fd, uint64_t offset, size_t nbytes, int type) "bs %p req %p fd %d offset %" PRId64 " nbytes %zd type %d"
luring_resubmit_short_read(void *req, int nread) "req %p nread %d"
# qcow2.c
qcow2_add_task(void *co, void *bs, void *pool, const char *action, int cluster_type, uint64_t host_offset, uint64_t offset, uint64_t bytes, void *qiov, size_t qiov_offset) "co %p bs %p pool %p: %s: cluster_type %d file_cluster_offset %" PRIu64 " offset %" PRIu64 " bytes %" PRIu64 " qiov %p qiov_offset %zu"
+4 -4
View File
@@ -58,7 +58,7 @@
#include "qapi/qapi-visit-block-core.h"
#include "block/block_int.h"
#include "block/qdict.h"
#include "sysemu/block-backend.h"
#include "system/block-backend.h"
#include "qemu/module.h"
#include "qemu/option.h"
#include "qemu/bswap.h"
@@ -523,8 +523,8 @@ static int vdi_reopen_prepare(BDRVReopenState *state,
}
static int coroutine_fn GRAPH_RDLOCK
vdi_co_block_status(BlockDriverState *bs, bool want_zero, int64_t offset,
int64_t bytes, int64_t *pnum, int64_t *map,
vdi_co_block_status(BlockDriverState *bs, unsigned int mode,
int64_t offset, int64_t bytes, int64_t *pnum, int64_t *map,
BlockDriverState **file)
{
BDRVVdiState *s = (BDRVVdiState *)bs->opaque;
@@ -938,7 +938,7 @@ vdi_co_create_opts(BlockDriver *drv, const char *filename,
qdict = qemu_opts_to_qdict_filtered(opts, NULL, &vdi_create_opts, true);
/* Create and open the file (protocol layer) */
ret = bdrv_co_create_file(filename, opts, errp);
ret = bdrv_co_create_file(filename, opts, true, errp);
if (ret < 0) {
goto done;
}
+3 -3
View File
@@ -19,7 +19,7 @@
#include "qapi/error.h"
#include "block/block_int.h"
#include "block/qdict.h"
#include "sysemu/block-backend.h"
#include "system/block-backend.h"
#include "qemu/module.h"
#include "qemu/option.h"
#include "qemu/crc32c.h"
@@ -29,7 +29,7 @@
#include "vhdx.h"
#include "migration/blocker.h"
#include "qemu/uuid.h"
#include "qapi/qmp/qdict.h"
#include "qobject/qdict.h"
#include "qapi/qobject-input-visitor.h"
#include "qapi/qapi-visit-block-core.h"
@@ -2096,7 +2096,7 @@ vhdx_co_create_opts(BlockDriver *drv, const char *filename,
}
/* Create and open the file (protocol layer) */
ret = bdrv_co_create_file(filename, opts, errp);
ret = bdrv_co_create_file(filename, opts, true, errp);
if (ret < 0) {
goto fail;
}
+14 -12
View File
@@ -26,8 +26,8 @@
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "block/block_int.h"
#include "sysemu/block-backend.h"
#include "qapi/qmp/qdict.h"
#include "system/block-backend.h"
#include "qobject/qdict.h"
#include "qemu/error-report.h"
#include "qemu/module.h"
#include "qemu/option.h"
@@ -271,7 +271,7 @@ static void vmdk_free_extents(BlockDriverState *bs)
BDRVVmdkState *s = bs->opaque;
VmdkExtent *e;
bdrv_graph_wrlock();
bdrv_graph_wrlock_drained();
for (i = 0; i < s->num_extents; i++) {
e = &s->extents[i];
g_free(e->l1_table);
@@ -1089,7 +1089,7 @@ vmdk_open_vmdk4(BlockDriverState *bs, BdrvChild *file, int flags,
static int vmdk_parse_description(const char *desc, const char *opt_name,
char *buf, int buf_size)
{
char *opt_pos, *opt_end;
const char *opt_pos, *opt_end;
const char *end = desc + strlen(desc);
opt_pos = strstr(desc, opt_name);
@@ -1229,9 +1229,11 @@ vmdk_parse_extents(const char *desc, BlockDriverState *bs, QDict *options,
extent_role |= BDRV_CHILD_METADATA;
}
bdrv_graph_rdunlock_main_loop();
extent_file = bdrv_open_child(extent_path, options, extent_opt_prefix,
bs, &child_of_bds, extent_role, false,
&local_err);
bdrv_graph_rdlock_main_loop();
g_free(extent_path);
if (!extent_file) {
error_propagate(errp, local_err);
@@ -1247,7 +1249,7 @@ vmdk_parse_extents(const char *desc, BlockDriverState *bs, QDict *options,
0, 0, 0, 0, 0, &extent, errp);
if (ret < 0) {
bdrv_graph_rdunlock_main_loop();
bdrv_graph_wrlock();
bdrv_graph_wrlock_drained();
bdrv_unref_child(bs, extent_file);
bdrv_graph_wrunlock();
bdrv_graph_rdlock_main_loop();
@@ -1266,7 +1268,7 @@ vmdk_parse_extents(const char *desc, BlockDriverState *bs, QDict *options,
g_free(buf);
if (ret) {
bdrv_graph_rdunlock_main_loop();
bdrv_graph_wrlock();
bdrv_graph_wrlock_drained();
bdrv_unref_child(bs, extent_file);
bdrv_graph_wrunlock();
bdrv_graph_rdlock_main_loop();
@@ -1277,7 +1279,7 @@ vmdk_parse_extents(const char *desc, BlockDriverState *bs, QDict *options,
ret = vmdk_open_se_sparse(bs, extent_file, bs->open_flags, errp);
if (ret) {
bdrv_graph_rdunlock_main_loop();
bdrv_graph_wrlock();
bdrv_graph_wrlock_drained();
bdrv_unref_child(bs, extent_file);
bdrv_graph_wrunlock();
bdrv_graph_rdlock_main_loop();
@@ -1287,7 +1289,7 @@ vmdk_parse_extents(const char *desc, BlockDriverState *bs, QDict *options,
} else {
error_setg(errp, "Unsupported extent type '%s'", type);
bdrv_graph_rdunlock_main_loop();
bdrv_graph_wrlock();
bdrv_graph_wrlock_drained();
bdrv_unref_child(bs, extent_file);
bdrv_graph_wrunlock();
bdrv_graph_rdlock_main_loop();
@@ -1352,13 +1354,13 @@ static int vmdk_open(BlockDriverState *bs, QDict *options, int flags,
BDRVVmdkState *s = bs->opaque;
uint32_t magic;
GRAPH_RDLOCK_GUARD_MAINLOOP();
ret = bdrv_open_file_child(NULL, options, "file", bs, errp);
if (ret < 0) {
return ret;
}
GRAPH_RDLOCK_GUARD_MAINLOOP();
buf = vmdk_read_desc(bs->file, 0, errp);
if (!buf) {
return -EINVAL;
@@ -1777,7 +1779,7 @@ static inline uint64_t vmdk_find_offset_in_cluster(VmdkExtent *extent,
}
static int coroutine_fn GRAPH_RDLOCK
vmdk_co_block_status(BlockDriverState *bs, bool want_zero,
vmdk_co_block_status(BlockDriverState *bs, unsigned int mode,
int64_t offset, int64_t bytes, int64_t *pnum,
int64_t *map, BlockDriverState **file)
{
@@ -2332,7 +2334,7 @@ vmdk_create_extent(const char *filename, int64_t filesize, bool flat,
int ret;
BlockBackend *blk = NULL;
ret = bdrv_co_create_file(filename, opts, errp);
ret = bdrv_co_create_file(filename, opts, false, errp);
if (ret < 0) {
goto exit;
}
+39 -34
View File
@@ -27,14 +27,14 @@
#include "qapi/error.h"
#include "block/block_int.h"
#include "block/qdict.h"
#include "sysemu/block-backend.h"
#include "system/block-backend.h"
#include "qemu/module.h"
#include "qemu/option.h"
#include "migration/blocker.h"
#include "qemu/bswap.h"
#include "qemu/uuid.h"
#include "qemu/memalign.h"
#include "qapi/qmp/qdict.h"
#include "qobject/qdict.h"
#include "qapi/qobject-input-visitor.h"
#include "qapi/qapi-visit-block-core.h"
@@ -216,6 +216,39 @@ static void vpc_parse_options(BlockDriverState *bs, QemuOpts *opts,
}
}
/*
* Microsoft Virtual PC and Microsoft Hyper-V produce and read
* VHD image sizes differently. VPC will rely on CHS geometry,
* while Hyper-V and disk2vhd use the size specified in the footer.
*
* We use a couple of approaches to try and determine the correct method:
* look at the Creator App field, and look for images that have CHS
* geometry that is the maximum value.
*
* If the CHS geometry is the maximum CHS geometry, then we assume that
* the size is the footer->current_size to avoid truncation. Otherwise,
* we follow the table based on footer->creator_app:
*
* Known creator apps:
* 'vpc ' : CHS Virtual PC (uses disk geometry)
* 'qemu' : CHS QEMU (uses disk geometry)
* 'qem2' : current_size QEMU (uses current_size)
* 'win ' : current_size Hyper-V
* 'd2v ' : current_size Disk2vhd
* 'tap\0' : current_size XenServer
* 'CTXS' : current_size XenConverter
* 'wa\0\0': current_size Azure
*
* The user can override the table values via drive options, however
* even with an override we will still use current_size for images
* that have CHS geometry of the maximum size.
*/
static bool vpc_ignore_current_size(VHDFooter *footer)
{
return !strncmp(footer->creator_app, "vpc ", 4) ||
!strncmp(footer->creator_app, "qemu", 4);
}
static int vpc_open(BlockDriverState *bs, QDict *options, int flags,
Error **errp)
{
@@ -304,36 +337,8 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags,
bs->total_sectors = (int64_t)
be16_to_cpu(footer->cyls) * footer->heads * footer->secs_per_cyl;
/* Microsoft Virtual PC and Microsoft Hyper-V produce and read
* VHD image sizes differently. VPC will rely on CHS geometry,
* while Hyper-V and disk2vhd use the size specified in the footer.
*
* We use a couple of approaches to try and determine the correct method:
* look at the Creator App field, and look for images that have CHS
* geometry that is the maximum value.
*
* If the CHS geometry is the maximum CHS geometry, then we assume that
* the size is the footer->current_size to avoid truncation. Otherwise,
* we follow the table based on footer->creator_app:
*
* Known creator apps:
* 'vpc ' : CHS Virtual PC (uses disk geometry)
* 'qemu' : CHS QEMU (uses disk geometry)
* 'qem2' : current_size QEMU (uses current_size)
* 'win ' : current_size Hyper-V
* 'd2v ' : current_size Disk2vhd
* 'tap\0' : current_size XenServer
* 'CTXS' : current_size XenConverter
*
* The user can override the table values via drive options, however
* even with an override we will still use current_size for images
* that have CHS geometry of the maximum size.
*/
use_chs = (!!strncmp(footer->creator_app, "win ", 4) &&
!!strncmp(footer->creator_app, "qem2", 4) &&
!!strncmp(footer->creator_app, "d2v ", 4) &&
!!strncmp(footer->creator_app, "CTXS", 4) &&
!!memcmp(footer->creator_app, "tap", 4)) || s->force_use_chs;
/* Use CHS or current_size to determine the image size. */
use_chs = vpc_ignore_current_size(footer) || s->force_use_chs;
if (!use_chs || bs->total_sectors == VHD_MAX_GEOMETRY || s->force_use_sz) {
bs->total_sectors = be64_to_cpu(footer->current_size) /
@@ -721,7 +726,7 @@ fail:
}
static int coroutine_fn GRAPH_RDLOCK
vpc_co_block_status(BlockDriverState *bs, bool want_zero,
vpc_co_block_status(BlockDriverState *bs, unsigned int mode,
int64_t offset, int64_t bytes,
int64_t *pnum, int64_t *map,
BlockDriverState **file)
@@ -1113,7 +1118,7 @@ vpc_co_create_opts(BlockDriver *drv, const char *filename,
}
/* Create and open the file (protocol layer) */
ret = bdrv_co_create_file(filename, opts, errp);
ret = bdrv_co_create_file(filename, opts, true, errp);
if (ret < 0) {
goto fail;
}
+11 -12
View File
@@ -34,8 +34,8 @@
#include "qemu/option.h"
#include "qemu/bswap.h"
#include "migration/blocker.h"
#include "qapi/qmp/qdict.h"
#include "qapi/qmp/qstring.h"
#include "qobject/qdict.h"
#include "qobject/qstring.h"
#include "qemu/ctype.h"
#include "qemu/cutils.h"
#include "qemu/error-report.h"
@@ -403,7 +403,6 @@ static direntry_t *create_long_filename(BDRVVVFATState *s, const char *filename)
{
int number_of_entries, i;
glong length;
direntry_t *entry;
gunichar2 *longname = g_utf8_to_utf16(filename, -1, NULL, &length, NULL);
if (!longname) {
@@ -414,24 +413,24 @@ static direntry_t *create_long_filename(BDRVVVFATState *s, const char *filename)
number_of_entries = DIV_ROUND_UP(length * 2, 26);
for(i=0;i<number_of_entries;i++) {
entry=array_get_next(&(s->directory));
direntry_t *entry=array_get_next(&(s->directory));
entry->attributes=0xf;
entry->reserved[0]=0;
entry->begin=0;
entry->name[0]=(number_of_entries-i)|(i==0?0x40:0);
}
for(i=0;i<26*number_of_entries;i++) {
unsigned char *entry=array_get(&(s->directory),s->directory.next-1-(i/26));
int offset=(i%26);
if(offset<10) offset=1+offset;
else if(offset<22) offset=14+offset-10;
else offset=28+offset-22;
entry=array_get(&(s->directory),s->directory.next-1-(i/26));
if (i >= 2 * length + 2) {
entry->name[offset] = 0xff;
entry[offset] = 0xff;
} else if (i % 2 == 0) {
entry->name[offset] = longname[i / 2] & 0xff;
entry[offset] = longname[i / 2] & 0xff;
} else {
entry->name[offset] = longname[i / 2] >> 8;
entry[offset] = longname[i / 2] >> 8;
}
}
g_free(longname);
@@ -1827,7 +1826,7 @@ cluster_was_modified(BDRVVVFATState *s, uint32_t cluster_num)
static const char* get_basename(const char* path)
{
char* basename = strrchr(path, '/');
const char *basename = strrchr(path, '/');
if (basename == NULL)
return path;
else
@@ -3135,9 +3134,9 @@ vvfat_co_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes,
}
static int coroutine_fn vvfat_co_block_status(BlockDriverState *bs,
bool want_zero, int64_t offset,
int64_t bytes, int64_t *n,
int64_t *map,
unsigned int mode,
int64_t offset, int64_t bytes,
int64_t *n, int64_t *map,
BlockDriverState **file)
{
*n = bytes;
+24 -7
View File
@@ -48,48 +48,62 @@ struct QEMUWin32AIOState {
typedef struct QEMUWin32AIOCB {
BlockAIOCB common;
struct QEMUWin32AIOState *ctx;
AioContext *req_ctx;
int nbytes;
OVERLAPPED ov;
QEMUIOVector *qiov;
void *buf;
bool is_read;
bool is_linear;
int ret;
} QEMUWin32AIOCB;
static void win32_aio_completion_cb_bh(void *opaque)
{
QEMUWin32AIOCB *waiocb = opaque;
waiocb->common.cb(waiocb->common.opaque, waiocb->ret);
aio_context_unref(waiocb->req_ctx);
qemu_aio_unref(waiocb);
}
/*
* Completes an AIO request (calls the callback and frees the ACB).
*/
static void win32_aio_process_completion(QEMUWin32AIOState *s,
QEMUWin32AIOCB *waiocb, DWORD count)
{
int ret;
s->count--;
if (waiocb->ov.Internal != 0) {
ret = -EIO;
waiocb->ret = -EIO;
} else {
ret = 0;
waiocb->ret = 0;
if (count < waiocb->nbytes) {
/* Short reads mean EOF, pad with zeros. */
if (waiocb->is_read) {
qemu_iovec_memset(waiocb->qiov, count, 0,
waiocb->qiov->size - count);
} else {
ret = -EINVAL;
waiocb->ret = -EINVAL;
}
}
}
if (!waiocb->is_linear) {
if (ret == 0 && waiocb->is_read) {
if (waiocb->ret == 0 && waiocb->is_read) {
QEMUIOVector *qiov = waiocb->qiov;
iov_from_buf(qiov->iov, qiov->niov, 0, waiocb->buf, qiov->size);
}
qemu_vfree(waiocb->buf);
}
waiocb->common.cb(waiocb->common.opaque, ret);
qemu_aio_unref(waiocb);
if (waiocb->req_ctx == s->aio_ctx) {
win32_aio_completion_cb_bh(waiocb);
} else {
aio_bh_schedule_oneshot(waiocb->req_ctx, win32_aio_completion_cb_bh,
waiocb);
}
}
static void win32_aio_completion_cb(EventNotifier *e)
@@ -120,10 +134,13 @@ BlockAIOCB *win32_aio_submit(BlockDriverState *bs,
DWORD rc;
waiocb = qemu_aio_get(&win32_aiocb_info, bs, cb, opaque);
waiocb->req_ctx = qemu_get_current_aio_context();
waiocb->nbytes = bytes;
waiocb->qiov = qiov;
waiocb->is_read = (type == QEMU_AIO_READ);
aio_context_ref(waiocb->req_ctx);
if (qiov->niov > 1) {
waiocb->buf = qemu_try_blockalign(bs, qiov->size);
if (waiocb->buf == NULL) {