mirror of
https://github.com/xemu-project/xemu.git
synced 2026-07-11 01:24:41 +02:00
Merge QEMU v10.2.0
This commit is contained in:
+16
-3
@@ -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
@@ -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
@@ -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
@@ -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"
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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"
|
||||
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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>
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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) {
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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(®s->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(®s->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(®s->cc);
|
||||
host_pci_stl_le_p(®s->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(®s->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(®s->aqa,
|
||||
((NVME_QUEUE_SIZE - 1) << AQA_ACQS_SHIFT) |
|
||||
((NVME_QUEUE_SIZE - 1) << AQA_ASQS_SHIFT));
|
||||
host_pci_stq_le_p(®s->asq, q->sq.iova);
|
||||
host_pci_stq_le_p(®s->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(®s->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(®s->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
@@ -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));
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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();
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user