mirror of https://github.com/xemu-project/xemu.git
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1 iQEcBAABAgAGBQJUIAsHAAoJEJykq7OBq3PIyR4H/ikrs35Boxv08mR8rTyfpSnQ ERQhMnKWIe3cy5pzNG5TKiPliljF0FnkNC3KmBLU5TsoqXeW76WJF//Db5hNnTzG FAIeJu2RUSqhjqoz5K6TNYOJGH2XQ+/EZbMyrIeLBwYFn0gFMvZJOVYgpBWP0QTQ 7sImrlxihUalwwL/6twfE6s5aA12DXN8hlC57u+9nvf+5ocaDyOJ7jUBU2EEhSNr TzDTO3gTCSEmnDriwKi3m3mIW/y7kLTXWyGolprZ0UpRyYRSmjcfgBHu8l0X8NCv lKkrYuE4V3QIzJk4BWbZQSPjoDlLbH9gnq3H+VwMxMZDxDKtAqAJRw/3H4yWhZ8= =JJEF -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/stefanha/tags/block-pull-request' into staging # gpg: Signature made Mon 22 Sep 2014 12:41:59 BST using RSA key ID 81AB73C8 # gpg: Good signature from "Stefan Hajnoczi <stefanha@redhat.com>" # gpg: aka "Stefan Hajnoczi <stefanha@gmail.com>" * remotes/stefanha/tags/block-pull-request: (59 commits) block: Always compile virtio-blk dataplane vring: Better error handling if num is too large virtio: Import virtio_vring.h async: aio_context_new(): Handle event_notifier_init failure block: vhdx - fix reading beyond pointer during image creation block: delete cow block driver block/archipelago: Fix typo in qemu_archipelago_truncate() ahci: Add test_identify case to ahci-test. ahci: Add test_hba_enable to ahci-test. ahci: Add test_hba_spec to ahci-test. ahci: properly shadow the TFD register ahci: add test_pci_enable to ahci-test. ahci: Add test_pci_spec to ahci-test. ahci: MSI capability should be at 0x80, not 0x50. ahci: Adding basic functionality qtest. layout: Add generators for refcount table and blocks fuzz: Add fuzzing functions for entries of refcount table and blocks docs: List all image elements currently supported by the fuzzer qapi/block-core: Add "new" qcow2 options qcow2: Add overlap-check.template option ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
380f649e02
|
@ -283,9 +283,9 @@ bool aio_poll(AioContext *ctx, bool blocking)
|
|||
int count;
|
||||
int timeout;
|
||||
|
||||
if (aio_prepare(ctx)) {
|
||||
have_select_revents = aio_prepare(ctx);
|
||||
if (have_select_revents) {
|
||||
blocking = false;
|
||||
have_select_revents = true;
|
||||
}
|
||||
|
||||
was_dispatching = ctx->dispatching;
|
||||
|
@ -335,6 +335,7 @@ bool aio_poll(AioContext *ctx, bool blocking)
|
|||
event = NULL;
|
||||
if ((DWORD) (ret - WAIT_OBJECT_0) < count) {
|
||||
event = events[ret - WAIT_OBJECT_0];
|
||||
events[ret - WAIT_OBJECT_0] = events[--count];
|
||||
} else if (!have_select_revents) {
|
||||
break;
|
||||
}
|
||||
|
@ -343,9 +344,6 @@ bool aio_poll(AioContext *ctx, bool blocking)
|
|||
blocking = false;
|
||||
|
||||
progress |= aio_dispatch_handlers(ctx, event);
|
||||
|
||||
/* Try again, but only call each handler once. */
|
||||
events[ret - WAIT_OBJECT_0] = events[--count];
|
||||
}
|
||||
|
||||
progress |= timerlistgroup_run_timers(&ctx->tlg);
|
||||
|
|
16
async.c
16
async.c
|
@ -289,18 +289,24 @@ static void aio_rfifolock_cb(void *opaque)
|
|||
aio_notify(opaque);
|
||||
}
|
||||
|
||||
AioContext *aio_context_new(void)
|
||||
AioContext *aio_context_new(Error **errp)
|
||||
{
|
||||
int ret;
|
||||
AioContext *ctx;
|
||||
ctx = (AioContext *) g_source_new(&aio_source_funcs, sizeof(AioContext));
|
||||
ret = event_notifier_init(&ctx->notifier, false);
|
||||
if (ret < 0) {
|
||||
g_source_destroy(&ctx->source);
|
||||
error_setg_errno(errp, -ret, "Failed to initialize event notifier");
|
||||
return NULL;
|
||||
}
|
||||
aio_set_event_notifier(ctx, &ctx->notifier,
|
||||
(EventNotifierHandler *)
|
||||
event_notifier_test_and_clear);
|
||||
ctx->pollfds = g_array_new(FALSE, FALSE, sizeof(GPollFD));
|
||||
ctx->thread_pool = NULL;
|
||||
qemu_mutex_init(&ctx->bh_lock);
|
||||
rfifolock_init(&ctx->lock, aio_rfifolock_cb, ctx);
|
||||
event_notifier_init(&ctx->notifier, false);
|
||||
aio_set_event_notifier(ctx, &ctx->notifier,
|
||||
(EventNotifierHandler *)
|
||||
event_notifier_test_and_clear);
|
||||
timerlistgroup_init(&ctx->tlg, aio_timerlist_notify, ctx);
|
||||
|
||||
return ctx;
|
||||
|
|
72
block.c
72
block.c
|
@ -4640,7 +4640,28 @@ int bdrv_aio_multiwrite(BlockDriverState *bs, BlockRequest *reqs, int num_reqs)
|
|||
|
||||
void bdrv_aio_cancel(BlockDriverAIOCB *acb)
|
||||
{
|
||||
acb->aiocb_info->cancel(acb);
|
||||
qemu_aio_ref(acb);
|
||||
bdrv_aio_cancel_async(acb);
|
||||
while (acb->refcnt > 1) {
|
||||
if (acb->aiocb_info->get_aio_context) {
|
||||
aio_poll(acb->aiocb_info->get_aio_context(acb), true);
|
||||
} else if (acb->bs) {
|
||||
aio_poll(bdrv_get_aio_context(acb->bs), true);
|
||||
} else {
|
||||
abort();
|
||||
}
|
||||
}
|
||||
qemu_aio_unref(acb);
|
||||
}
|
||||
|
||||
/* Async version of aio cancel. The caller is not blocked if the acb implements
|
||||
* cancel_async, otherwise we do nothing and let the request normally complete.
|
||||
* In either case the completion callback must be called. */
|
||||
void bdrv_aio_cancel_async(BlockDriverAIOCB *acb)
|
||||
{
|
||||
if (acb->aiocb_info->cancel_async) {
|
||||
acb->aiocb_info->cancel_async(acb);
|
||||
}
|
||||
}
|
||||
|
||||
/**************************************************************/
|
||||
|
@ -4656,18 +4677,8 @@ typedef struct BlockDriverAIOCBSync {
|
|||
int is_write;
|
||||
} BlockDriverAIOCBSync;
|
||||
|
||||
static void bdrv_aio_cancel_em(BlockDriverAIOCB *blockacb)
|
||||
{
|
||||
BlockDriverAIOCBSync *acb =
|
||||
container_of(blockacb, BlockDriverAIOCBSync, common);
|
||||
qemu_bh_delete(acb->bh);
|
||||
acb->bh = NULL;
|
||||
qemu_aio_release(acb);
|
||||
}
|
||||
|
||||
static const AIOCBInfo bdrv_em_aiocb_info = {
|
||||
.aiocb_size = sizeof(BlockDriverAIOCBSync),
|
||||
.cancel = bdrv_aio_cancel_em,
|
||||
};
|
||||
|
||||
static void bdrv_aio_bh_cb(void *opaque)
|
||||
|
@ -4681,7 +4692,7 @@ static void bdrv_aio_bh_cb(void *opaque)
|
|||
acb->common.cb(acb->common.opaque, acb->ret);
|
||||
qemu_bh_delete(acb->bh);
|
||||
acb->bh = NULL;
|
||||
qemu_aio_release(acb);
|
||||
qemu_aio_unref(acb);
|
||||
}
|
||||
|
||||
static BlockDriverAIOCB *bdrv_aio_rw_vector(BlockDriverState *bs,
|
||||
|
@ -4738,22 +4749,8 @@ typedef struct BlockDriverAIOCBCoroutine {
|
|||
QEMUBH* bh;
|
||||
} BlockDriverAIOCBCoroutine;
|
||||
|
||||
static void bdrv_aio_co_cancel_em(BlockDriverAIOCB *blockacb)
|
||||
{
|
||||
AioContext *aio_context = bdrv_get_aio_context(blockacb->bs);
|
||||
BlockDriverAIOCBCoroutine *acb =
|
||||
container_of(blockacb, BlockDriverAIOCBCoroutine, common);
|
||||
bool done = false;
|
||||
|
||||
acb->done = &done;
|
||||
while (!done) {
|
||||
aio_poll(aio_context, true);
|
||||
}
|
||||
}
|
||||
|
||||
static const AIOCBInfo bdrv_em_co_aiocb_info = {
|
||||
.aiocb_size = sizeof(BlockDriverAIOCBCoroutine),
|
||||
.cancel = bdrv_aio_co_cancel_em,
|
||||
};
|
||||
|
||||
static void bdrv_co_em_bh(void *opaque)
|
||||
|
@ -4762,12 +4759,8 @@ static void bdrv_co_em_bh(void *opaque)
|
|||
|
||||
acb->common.cb(acb->common.opaque, acb->req.error);
|
||||
|
||||
if (acb->done) {
|
||||
*acb->done = true;
|
||||
}
|
||||
|
||||
qemu_bh_delete(acb->bh);
|
||||
qemu_aio_release(acb);
|
||||
qemu_aio_unref(acb);
|
||||
}
|
||||
|
||||
/* Invoke bdrv_co_do_readv/bdrv_co_do_writev */
|
||||
|
@ -4806,7 +4799,6 @@ static BlockDriverAIOCB *bdrv_co_aio_rw_vector(BlockDriverState *bs,
|
|||
acb->req.qiov = qiov;
|
||||
acb->req.flags = flags;
|
||||
acb->is_write = is_write;
|
||||
acb->done = NULL;
|
||||
|
||||
co = qemu_coroutine_create(bdrv_co_do_rw);
|
||||
qemu_coroutine_enter(co, acb);
|
||||
|
@ -4833,7 +4825,6 @@ BlockDriverAIOCB *bdrv_aio_flush(BlockDriverState *bs,
|
|||
BlockDriverAIOCBCoroutine *acb;
|
||||
|
||||
acb = qemu_aio_get(&bdrv_em_co_aiocb_info, bs, cb, opaque);
|
||||
acb->done = NULL;
|
||||
|
||||
co = qemu_coroutine_create(bdrv_aio_flush_co_entry);
|
||||
qemu_coroutine_enter(co, acb);
|
||||
|
@ -4863,7 +4854,6 @@ BlockDriverAIOCB *bdrv_aio_discard(BlockDriverState *bs,
|
|||
acb = qemu_aio_get(&bdrv_em_co_aiocb_info, bs, cb, opaque);
|
||||
acb->req.sector = sector_num;
|
||||
acb->req.nb_sectors = nb_sectors;
|
||||
acb->done = NULL;
|
||||
co = qemu_coroutine_create(bdrv_aio_discard_co_entry);
|
||||
qemu_coroutine_enter(co, acb);
|
||||
|
||||
|
@ -4891,13 +4881,23 @@ void *qemu_aio_get(const AIOCBInfo *aiocb_info, BlockDriverState *bs,
|
|||
acb->bs = bs;
|
||||
acb->cb = cb;
|
||||
acb->opaque = opaque;
|
||||
acb->refcnt = 1;
|
||||
return acb;
|
||||
}
|
||||
|
||||
void qemu_aio_release(void *p)
|
||||
void qemu_aio_ref(void *p)
|
||||
{
|
||||
BlockDriverAIOCB *acb = p;
|
||||
g_slice_free1(acb->aiocb_info->aiocb_size, acb);
|
||||
acb->refcnt++;
|
||||
}
|
||||
|
||||
void qemu_aio_unref(void *p)
|
||||
{
|
||||
BlockDriverAIOCB *acb = p;
|
||||
assert(acb->refcnt > 0);
|
||||
if (--acb->refcnt == 0) {
|
||||
g_slice_free1(acb->aiocb_info->aiocb_size, acb);
|
||||
}
|
||||
}
|
||||
|
||||
/**************************************************************/
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
block-obj-y += raw_bsd.o cow.o qcow.o vdi.o vmdk.o cloop.o dmg.o bochs.o vpc.o vvfat.o
|
||||
block-obj-y += raw_bsd.o qcow.o vdi.o vmdk.o cloop.o dmg.o bochs.o vpc.o vvfat.o
|
||||
block-obj-y += qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot.o qcow2-cache.o
|
||||
block-obj-y += qed.o qed-gencb.o qed-l2-cache.o qed-table.o qed-cluster.o
|
||||
block-obj-y += qed-check.o
|
||||
|
@ -9,6 +9,7 @@ block-obj-y += snapshot.o qapi.o
|
|||
block-obj-$(CONFIG_WIN32) += raw-win32.o win32-aio.o
|
||||
block-obj-$(CONFIG_POSIX) += raw-posix.o
|
||||
block-obj-$(CONFIG_LINUX_AIO) += linux-aio.o
|
||||
block-obj-y += null.o
|
||||
|
||||
block-obj-y += nbd.o nbd-client.o sheepdog.o
|
||||
block-obj-$(CONFIG_LIBISCSI) += iscsi.o
|
||||
|
|
|
@ -91,7 +91,6 @@ typedef struct ArchipelagoAIOCB {
|
|||
struct BDRVArchipelagoState *s;
|
||||
QEMUIOVector *qiov;
|
||||
ARCHIPCmd cmd;
|
||||
bool cancelled;
|
||||
int status;
|
||||
int64_t size;
|
||||
int64_t ret;
|
||||
|
@ -318,9 +317,7 @@ static void qemu_archipelago_complete_aio(void *opaque)
|
|||
aio_cb->common.cb(aio_cb->common.opaque, aio_cb->ret);
|
||||
aio_cb->status = 0;
|
||||
|
||||
if (!aio_cb->cancelled) {
|
||||
qemu_aio_release(aio_cb);
|
||||
}
|
||||
qemu_aio_unref(aio_cb);
|
||||
g_free(reqdata);
|
||||
}
|
||||
|
||||
|
@ -725,19 +722,8 @@ static int qemu_archipelago_create(const char *filename,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void qemu_archipelago_aio_cancel(BlockDriverAIOCB *blockacb)
|
||||
{
|
||||
ArchipelagoAIOCB *aio_cb = (ArchipelagoAIOCB *) blockacb;
|
||||
aio_cb->cancelled = true;
|
||||
while (aio_cb->status == -EINPROGRESS) {
|
||||
aio_poll(bdrv_get_aio_context(aio_cb->common.bs), true);
|
||||
}
|
||||
qemu_aio_release(aio_cb);
|
||||
}
|
||||
|
||||
static const AIOCBInfo archipelago_aiocb_info = {
|
||||
.aiocb_size = sizeof(ArchipelagoAIOCB),
|
||||
.cancel = qemu_archipelago_aio_cancel,
|
||||
};
|
||||
|
||||
static int archipelago_submit_request(BDRVArchipelagoState *s,
|
||||
|
@ -889,7 +875,6 @@ static BlockDriverAIOCB *qemu_archipelago_aio_rw(BlockDriverState *bs,
|
|||
|
||||
aio_cb->ret = 0;
|
||||
aio_cb->s = s;
|
||||
aio_cb->cancelled = false;
|
||||
aio_cb->status = -EINPROGRESS;
|
||||
|
||||
off = sector_num * BDRV_SECTOR_SIZE;
|
||||
|
@ -905,7 +890,7 @@ static BlockDriverAIOCB *qemu_archipelago_aio_rw(BlockDriverState *bs,
|
|||
|
||||
err_exit:
|
||||
error_report("qemu_archipelago_aio_rw(): I/O Error\n");
|
||||
qemu_aio_release(aio_cb);
|
||||
qemu_aio_unref(aio_cb);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -1008,7 +993,7 @@ static int qemu_archipelago_truncate(BlockDriverState *bs, int64_t offset)
|
|||
req = xseg_get_request(s->xseg, s->srcport, s->mportno, X_ALLOC);
|
||||
if (!req) {
|
||||
archipelagolog("Cannot get XSEG request\n");
|
||||
return err_exit2;
|
||||
goto err_exit2;
|
||||
}
|
||||
|
||||
ret = xseg_prep_request(s->xseg, req, targetlen, 0);
|
||||
|
|
|
@ -52,11 +52,8 @@ typedef struct BlkdebugSuspendedReq {
|
|||
QLIST_ENTRY(BlkdebugSuspendedReq) next;
|
||||
} BlkdebugSuspendedReq;
|
||||
|
||||
static void blkdebug_aio_cancel(BlockDriverAIOCB *blockacb);
|
||||
|
||||
static const AIOCBInfo blkdebug_aiocb_info = {
|
||||
.aiocb_size = sizeof(BlkdebugAIOCB),
|
||||
.cancel = blkdebug_aio_cancel,
|
||||
.aiocb_size = sizeof(BlkdebugAIOCB),
|
||||
};
|
||||
|
||||
enum {
|
||||
|
@ -447,17 +444,7 @@ static void error_callback_bh(void *opaque)
|
|||
struct BlkdebugAIOCB *acb = opaque;
|
||||
qemu_bh_delete(acb->bh);
|
||||
acb->common.cb(acb->common.opaque, acb->ret);
|
||||
qemu_aio_release(acb);
|
||||
}
|
||||
|
||||
static void blkdebug_aio_cancel(BlockDriverAIOCB *blockacb)
|
||||
{
|
||||
BlkdebugAIOCB *acb = container_of(blockacb, BlkdebugAIOCB, common);
|
||||
if (acb->bh) {
|
||||
qemu_bh_delete(acb->bh);
|
||||
acb->bh = NULL;
|
||||
}
|
||||
qemu_aio_release(acb);
|
||||
qemu_aio_unref(acb);
|
||||
}
|
||||
|
||||
static BlockDriverAIOCB *inject_error(BlockDriverState *bs,
|
||||
|
|
|
@ -29,7 +29,6 @@ struct BlkverifyAIOCB {
|
|||
|
||||
int ret; /* first completed request's result */
|
||||
unsigned int done; /* completion counter */
|
||||
bool *finished; /* completion signal for cancel */
|
||||
|
||||
QEMUIOVector *qiov; /* user I/O vector */
|
||||
QEMUIOVector raw_qiov; /* cloned I/O vector for raw file */
|
||||
|
@ -38,22 +37,8 @@ struct BlkverifyAIOCB {
|
|||
void (*verify)(BlkverifyAIOCB *acb);
|
||||
};
|
||||
|
||||
static void blkverify_aio_cancel(BlockDriverAIOCB *blockacb)
|
||||
{
|
||||
BlkverifyAIOCB *acb = (BlkverifyAIOCB *)blockacb;
|
||||
AioContext *aio_context = bdrv_get_aio_context(blockacb->bs);
|
||||
bool finished = false;
|
||||
|
||||
/* Wait until request completes, invokes its callback, and frees itself */
|
||||
acb->finished = &finished;
|
||||
while (!finished) {
|
||||
aio_poll(aio_context, true);
|
||||
}
|
||||
}
|
||||
|
||||
static const AIOCBInfo blkverify_aiocb_info = {
|
||||
.aiocb_size = sizeof(BlkverifyAIOCB),
|
||||
.cancel = blkverify_aio_cancel,
|
||||
};
|
||||
|
||||
static void GCC_FMT_ATTR(2, 3) blkverify_err(BlkverifyAIOCB *acb,
|
||||
|
@ -194,7 +179,6 @@ static BlkverifyAIOCB *blkverify_aio_get(BlockDriverState *bs, bool is_write,
|
|||
acb->qiov = qiov;
|
||||
acb->buf = NULL;
|
||||
acb->verify = NULL;
|
||||
acb->finished = NULL;
|
||||
return acb;
|
||||
}
|
||||
|
||||
|
@ -208,10 +192,7 @@ static void blkverify_aio_bh(void *opaque)
|
|||
qemu_vfree(acb->buf);
|
||||
}
|
||||
acb->common.cb(acb->common.opaque, acb->ret);
|
||||
if (acb->finished) {
|
||||
*acb->finished = true;
|
||||
}
|
||||
qemu_aio_release(acb);
|
||||
qemu_aio_unref(acb);
|
||||
}
|
||||
|
||||
static void blkverify_aio_cb(void *opaque, int ret)
|
||||
|
|
433
block/cow.c
433
block/cow.c
|
@ -1,433 +0,0 @@
|
|||
/*
|
||||
* Block driver for the COW format
|
||||
*
|
||||
* Copyright (c) 2004 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "qemu-common.h"
|
||||
#include "block/block_int.h"
|
||||
#include "qemu/module.h"
|
||||
|
||||
/**************************************************************/
|
||||
/* COW block driver using file system holes */
|
||||
|
||||
/* user mode linux compatible COW file */
|
||||
#define COW_MAGIC 0x4f4f4f4d /* MOOO */
|
||||
#define COW_VERSION 2
|
||||
|
||||
struct cow_header_v2 {
|
||||
uint32_t magic;
|
||||
uint32_t version;
|
||||
char backing_file[1024];
|
||||
int32_t mtime;
|
||||
uint64_t size;
|
||||
uint32_t sectorsize;
|
||||
};
|
||||
|
||||
typedef struct BDRVCowState {
|
||||
CoMutex lock;
|
||||
int64_t cow_sectors_offset;
|
||||
} BDRVCowState;
|
||||
|
||||
static int cow_probe(const uint8_t *buf, int buf_size, const char *filename)
|
||||
{
|
||||
const struct cow_header_v2 *cow_header = (const void *)buf;
|
||||
|
||||
if (buf_size >= sizeof(struct cow_header_v2) &&
|
||||
be32_to_cpu(cow_header->magic) == COW_MAGIC &&
|
||||
be32_to_cpu(cow_header->version) == COW_VERSION)
|
||||
return 100;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cow_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
Error **errp)
|
||||
{
|
||||
BDRVCowState *s = bs->opaque;
|
||||
struct cow_header_v2 cow_header;
|
||||
int bitmap_size;
|
||||
int64_t size;
|
||||
int ret;
|
||||
|
||||
/* see if it is a cow image */
|
||||
ret = bdrv_pread(bs->file, 0, &cow_header, sizeof(cow_header));
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (be32_to_cpu(cow_header.magic) != COW_MAGIC) {
|
||||
error_setg(errp, "Image not in COW format");
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (be32_to_cpu(cow_header.version) != COW_VERSION) {
|
||||
char version[64];
|
||||
snprintf(version, sizeof(version),
|
||||
"COW version %" PRIu32, cow_header.version);
|
||||
error_set(errp, QERR_UNKNOWN_BLOCK_FORMAT_FEATURE,
|
||||
bs->device_name, "cow", version);
|
||||
ret = -ENOTSUP;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* cow image found */
|
||||
size = be64_to_cpu(cow_header.size);
|
||||
bs->total_sectors = size / 512;
|
||||
|
||||
pstrcpy(bs->backing_file, sizeof(bs->backing_file),
|
||||
cow_header.backing_file);
|
||||
|
||||
bitmap_size = ((bs->total_sectors + 7) >> 3) + sizeof(cow_header);
|
||||
s->cow_sectors_offset = (bitmap_size + 511) & ~511;
|
||||
qemu_co_mutex_init(&s->lock);
|
||||
return 0;
|
||||
fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline void cow_set_bits(uint8_t *bitmap, int start, int64_t nb_sectors)
|
||||
{
|
||||
int64_t bitnum = start, last = start + nb_sectors;
|
||||
while (bitnum < last) {
|
||||
if ((bitnum & 7) == 0 && bitnum + 8 <= last) {
|
||||
bitmap[bitnum / 8] = 0xFF;
|
||||
bitnum += 8;
|
||||
continue;
|
||||
}
|
||||
bitmap[bitnum/8] |= (1 << (bitnum % 8));
|
||||
bitnum++;
|
||||
}
|
||||
}
|
||||
|
||||
#define BITS_PER_BITMAP_SECTOR (512 * 8)
|
||||
|
||||
/* Cannot use bitmap.c on big-endian machines. */
|
||||
static int cow_test_bit(int64_t bitnum, const uint8_t *bitmap)
|
||||
{
|
||||
return (bitmap[bitnum / 8] & (1 << (bitnum & 7))) != 0;
|
||||
}
|
||||
|
||||
static int cow_find_streak(const uint8_t *bitmap, int value, int start, int nb_sectors)
|
||||
{
|
||||
int streak_value = value ? 0xFF : 0;
|
||||
int last = MIN(start + nb_sectors, BITS_PER_BITMAP_SECTOR);
|
||||
int bitnum = start;
|
||||
while (bitnum < last) {
|
||||
if ((bitnum & 7) == 0 && bitmap[bitnum / 8] == streak_value) {
|
||||
bitnum += 8;
|
||||
continue;
|
||||
}
|
||||
if (cow_test_bit(bitnum, bitmap) == value) {
|
||||
bitnum++;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return MIN(bitnum, last) - start;
|
||||
}
|
||||
|
||||
/* Return true if first block has been changed (ie. current version is
|
||||
* in COW file). Set the number of continuous blocks for which that
|
||||
* is true. */
|
||||
static int coroutine_fn cow_co_is_allocated(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors, int *num_same)
|
||||
{
|
||||
int64_t bitnum = sector_num + sizeof(struct cow_header_v2) * 8;
|
||||
uint64_t offset = (bitnum / 8) & -BDRV_SECTOR_SIZE;
|
||||
bool first = true;
|
||||
int changed = 0, same = 0;
|
||||
|
||||
do {
|
||||
int ret;
|
||||
uint8_t bitmap[BDRV_SECTOR_SIZE];
|
||||
|
||||
bitnum &= BITS_PER_BITMAP_SECTOR - 1;
|
||||
int sector_bits = MIN(nb_sectors, BITS_PER_BITMAP_SECTOR - bitnum);
|
||||
|
||||
ret = bdrv_pread(bs->file, offset, &bitmap, sizeof(bitmap));
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (first) {
|
||||
changed = cow_test_bit(bitnum, bitmap);
|
||||
first = false;
|
||||
}
|
||||
|
||||
same += cow_find_streak(bitmap, changed, bitnum, nb_sectors);
|
||||
|
||||
bitnum += sector_bits;
|
||||
nb_sectors -= sector_bits;
|
||||
offset += BDRV_SECTOR_SIZE;
|
||||
} while (nb_sectors);
|
||||
|
||||
*num_same = same;
|
||||
return changed;
|
||||
}
|
||||
|
||||
static int64_t coroutine_fn cow_co_get_block_status(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors, int *num_same)
|
||||
{
|
||||
BDRVCowState *s = bs->opaque;
|
||||
int ret = cow_co_is_allocated(bs, sector_num, nb_sectors, num_same);
|
||||
int64_t offset = s->cow_sectors_offset + (sector_num << BDRV_SECTOR_BITS);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
return (ret ? BDRV_BLOCK_DATA : 0) | offset | BDRV_BLOCK_OFFSET_VALID;
|
||||
}
|
||||
|
||||
static int cow_update_bitmap(BlockDriverState *bs, int64_t sector_num,
|
||||
int nb_sectors)
|
||||
{
|
||||
int64_t bitnum = sector_num + sizeof(struct cow_header_v2) * 8;
|
||||
uint64_t offset = (bitnum / 8) & -BDRV_SECTOR_SIZE;
|
||||
bool first = true;
|
||||
int sector_bits;
|
||||
|
||||
for ( ; nb_sectors;
|
||||
bitnum += sector_bits,
|
||||
nb_sectors -= sector_bits,
|
||||
offset += BDRV_SECTOR_SIZE) {
|
||||
int ret, set;
|
||||
uint8_t bitmap[BDRV_SECTOR_SIZE];
|
||||
|
||||
bitnum &= BITS_PER_BITMAP_SECTOR - 1;
|
||||
sector_bits = MIN(nb_sectors, BITS_PER_BITMAP_SECTOR - bitnum);
|
||||
|
||||
ret = bdrv_pread(bs->file, offset, &bitmap, sizeof(bitmap));
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Skip over any already set bits */
|
||||
set = cow_find_streak(bitmap, 1, bitnum, sector_bits);
|
||||
bitnum += set;
|
||||
sector_bits -= set;
|
||||
nb_sectors -= set;
|
||||
if (!sector_bits) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (first) {
|
||||
ret = bdrv_flush(bs->file);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
first = false;
|
||||
}
|
||||
|
||||
cow_set_bits(bitmap, bitnum, sector_bits);
|
||||
|
||||
ret = bdrv_pwrite(bs->file, offset, &bitmap, sizeof(bitmap));
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int coroutine_fn cow_read(BlockDriverState *bs, int64_t sector_num,
|
||||
uint8_t *buf, int nb_sectors)
|
||||
{
|
||||
BDRVCowState *s = bs->opaque;
|
||||
int ret, n;
|
||||
|
||||
while (nb_sectors > 0) {
|
||||
ret = cow_co_is_allocated(bs, sector_num, nb_sectors, &n);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
if (ret) {
|
||||
ret = bdrv_pread(bs->file,
|
||||
s->cow_sectors_offset + sector_num * 512,
|
||||
buf, n * 512);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
if (bs->backing_hd) {
|
||||
/* read from the base image */
|
||||
ret = bdrv_read(bs->backing_hd, sector_num, buf, n);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
memset(buf, 0, n * 512);
|
||||
}
|
||||
}
|
||||
nb_sectors -= n;
|
||||
sector_num += n;
|
||||
buf += n * 512;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static coroutine_fn int cow_co_read(BlockDriverState *bs, int64_t sector_num,
|
||||
uint8_t *buf, int nb_sectors)
|
||||
{
|
||||
int ret;
|
||||
BDRVCowState *s = bs->opaque;
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
ret = cow_read(bs, sector_num, buf, nb_sectors);
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cow_write(BlockDriverState *bs, int64_t sector_num,
|
||||
const uint8_t *buf, int nb_sectors)
|
||||
{
|
||||
BDRVCowState *s = bs->opaque;
|
||||
int ret;
|
||||
|
||||
ret = bdrv_pwrite(bs->file, s->cow_sectors_offset + sector_num * 512,
|
||||
buf, nb_sectors * 512);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return cow_update_bitmap(bs, sector_num, nb_sectors);
|
||||
}
|
||||
|
||||
static coroutine_fn int cow_co_write(BlockDriverState *bs, int64_t sector_num,
|
||||
const uint8_t *buf, int nb_sectors)
|
||||
{
|
||||
int ret;
|
||||
BDRVCowState *s = bs->opaque;
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
ret = cow_write(bs, sector_num, buf, nb_sectors);
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void cow_close(BlockDriverState *bs)
|
||||
{
|
||||
}
|
||||
|
||||
static int cow_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
{
|
||||
struct cow_header_v2 cow_header;
|
||||
struct stat st;
|
||||
int64_t image_sectors = 0;
|
||||
char *image_filename = NULL;
|
||||
Error *local_err = NULL;
|
||||
int ret;
|
||||
BlockDriverState *cow_bs = NULL;
|
||||
|
||||
/* Read out options */
|
||||
image_sectors = DIV_ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
|
||||
BDRV_SECTOR_SIZE);
|
||||
image_filename = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FILE);
|
||||
|
||||
ret = bdrv_create_file(filename, opts, &local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ret = bdrv_open(&cow_bs, filename, NULL, NULL,
|
||||
BDRV_O_RDWR | BDRV_O_PROTOCOL, NULL, &local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
memset(&cow_header, 0, sizeof(cow_header));
|
||||
cow_header.magic = cpu_to_be32(COW_MAGIC);
|
||||
cow_header.version = cpu_to_be32(COW_VERSION);
|
||||
if (image_filename) {
|
||||
/* Note: if no file, we put a dummy mtime */
|
||||
cow_header.mtime = cpu_to_be32(0);
|
||||
|
||||
if (stat(image_filename, &st) != 0) {
|
||||
goto mtime_fail;
|
||||
}
|
||||
cow_header.mtime = cpu_to_be32(st.st_mtime);
|
||||
mtime_fail:
|
||||
pstrcpy(cow_header.backing_file, sizeof(cow_header.backing_file),
|
||||
image_filename);
|
||||
}
|
||||
cow_header.sectorsize = cpu_to_be32(512);
|
||||
cow_header.size = cpu_to_be64(image_sectors * 512);
|
||||
ret = bdrv_pwrite(cow_bs, 0, &cow_header, sizeof(cow_header));
|
||||
if (ret < 0) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* resize to include at least all the bitmap */
|
||||
ret = bdrv_truncate(cow_bs,
|
||||
sizeof(cow_header) + ((image_sectors + 7) >> 3));
|
||||
if (ret < 0) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
exit:
|
||||
g_free(image_filename);
|
||||
if (cow_bs) {
|
||||
bdrv_unref(cow_bs);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static QemuOptsList cow_create_opts = {
|
||||
.name = "cow-create-opts",
|
||||
.head = QTAILQ_HEAD_INITIALIZER(cow_create_opts.head),
|
||||
.desc = {
|
||||
{
|
||||
.name = BLOCK_OPT_SIZE,
|
||||
.type = QEMU_OPT_SIZE,
|
||||
.help = "Virtual disk size"
|
||||
},
|
||||
{
|
||||
.name = BLOCK_OPT_BACKING_FILE,
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "File name of a base image"
|
||||
},
|
||||
{ /* end of list */ }
|
||||
}
|
||||
};
|
||||
|
||||
static BlockDriver bdrv_cow = {
|
||||
.format_name = "cow",
|
||||
.instance_size = sizeof(BDRVCowState),
|
||||
|
||||
.bdrv_probe = cow_probe,
|
||||
.bdrv_open = cow_open,
|
||||
.bdrv_close = cow_close,
|
||||
.bdrv_create = cow_create,
|
||||
.bdrv_has_zero_init = bdrv_has_zero_init_1,
|
||||
.supports_backing = true,
|
||||
|
||||
.bdrv_read = cow_co_read,
|
||||
.bdrv_write = cow_co_write,
|
||||
.bdrv_co_get_block_status = cow_co_get_block_status,
|
||||
|
||||
.create_opts = &cow_create_opts,
|
||||
};
|
||||
|
||||
static void bdrv_cow_init(void)
|
||||
{
|
||||
bdrv_register(&bdrv_cow);
|
||||
}
|
||||
|
||||
block_init(bdrv_cow_init);
|
16
block/curl.c
16
block/curl.c
|
@ -212,7 +212,7 @@ static size_t curl_read_cb(void *ptr, size_t size, size_t nmemb, void *opaque)
|
|||
qemu_iovec_from_buf(acb->qiov, 0, s->orig_buf + acb->start,
|
||||
acb->end - acb->start);
|
||||
acb->common.cb(acb->common.opaque, 0);
|
||||
qemu_aio_release(acb);
|
||||
qemu_aio_unref(acb);
|
||||
s->acb[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
@ -304,7 +304,7 @@ static void curl_multi_check_completion(BDRVCURLState *s)
|
|||
}
|
||||
|
||||
acb->common.cb(acb->common.opaque, -EIO);
|
||||
qemu_aio_release(acb);
|
||||
qemu_aio_unref(acb);
|
||||
state->acb[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
@ -613,14 +613,8 @@ out_noclean:
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
static void curl_aio_cancel(BlockDriverAIOCB *blockacb)
|
||||
{
|
||||
// Do we have to implement canceling? Seems to work without...
|
||||
}
|
||||
|
||||
static const AIOCBInfo curl_aiocb_info = {
|
||||
.aiocb_size = sizeof(CURLAIOCB),
|
||||
.cancel = curl_aio_cancel,
|
||||
};
|
||||
|
||||
|
||||
|
@ -642,7 +636,7 @@ static void curl_readv_bh_cb(void *p)
|
|||
// we can just call the callback and be done.
|
||||
switch (curl_find_buf(s, start, acb->nb_sectors * SECTOR_SIZE, acb)) {
|
||||
case FIND_RET_OK:
|
||||
qemu_aio_release(acb);
|
||||
qemu_aio_unref(acb);
|
||||
// fall through
|
||||
case FIND_RET_WAIT:
|
||||
return;
|
||||
|
@ -654,7 +648,7 @@ static void curl_readv_bh_cb(void *p)
|
|||
state = curl_init_state(acb->common.bs, s);
|
||||
if (!state) {
|
||||
acb->common.cb(acb->common.opaque, -EIO);
|
||||
qemu_aio_release(acb);
|
||||
qemu_aio_unref(acb);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -670,7 +664,7 @@ static void curl_readv_bh_cb(void *p)
|
|||
if (state->buf_len && state->orig_buf == NULL) {
|
||||
curl_clean_state(state);
|
||||
acb->common.cb(acb->common.opaque, -ENOMEM);
|
||||
qemu_aio_release(acb);
|
||||
qemu_aio_unref(acb);
|
||||
return;
|
||||
}
|
||||
state->acb[0] = acb;
|
||||
|
|
|
@ -88,7 +88,6 @@ typedef struct IscsiAIOCB {
|
|||
struct scsi_task *task;
|
||||
uint8_t *buf;
|
||||
int status;
|
||||
int canceled;
|
||||
int64_t sector_num;
|
||||
int nb_sectors;
|
||||
#ifdef __linux__
|
||||
|
@ -120,16 +119,14 @@ iscsi_bh_cb(void *p)
|
|||
g_free(acb->buf);
|
||||
acb->buf = NULL;
|
||||
|
||||
if (acb->canceled == 0) {
|
||||
acb->common.cb(acb->common.opaque, acb->status);
|
||||
}
|
||||
acb->common.cb(acb->common.opaque, acb->status);
|
||||
|
||||
if (acb->task != NULL) {
|
||||
scsi_free_scsi_task(acb->task);
|
||||
acb->task = NULL;
|
||||
}
|
||||
|
||||
qemu_aio_release(acb);
|
||||
qemu_aio_unref(acb);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -240,20 +237,15 @@ iscsi_aio_cancel(BlockDriverAIOCB *blockacb)
|
|||
return;
|
||||
}
|
||||
|
||||
acb->canceled = 1;
|
||||
|
||||
/* send a task mgmt call to the target to cancel the task on the target */
|
||||
iscsi_task_mgmt_abort_task_async(iscsilun->iscsi, acb->task,
|
||||
iscsi_abort_task_cb, acb);
|
||||
|
||||
while (acb->status == -EINPROGRESS) {
|
||||
aio_poll(iscsilun->aio_context, true);
|
||||
}
|
||||
}
|
||||
|
||||
static const AIOCBInfo iscsi_aiocb_info = {
|
||||
.aiocb_size = sizeof(IscsiAIOCB),
|
||||
.cancel = iscsi_aio_cancel,
|
||||
.cancel_async = iscsi_aio_cancel,
|
||||
};
|
||||
|
||||
|
||||
|
@ -638,10 +630,6 @@ iscsi_aio_ioctl_cb(struct iscsi_context *iscsi, int status,
|
|||
g_free(acb->buf);
|
||||
acb->buf = NULL;
|
||||
|
||||
if (acb->canceled != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
acb->status = 0;
|
||||
if (status < 0) {
|
||||
error_report("Failed to ioctl(SG_IO) to iSCSI lun. %s",
|
||||
|
@ -683,7 +671,6 @@ static BlockDriverAIOCB *iscsi_aio_ioctl(BlockDriverState *bs,
|
|||
acb = qemu_aio_get(&iscsi_aiocb_info, bs, cb, opaque);
|
||||
|
||||
acb->iscsilun = iscsilun;
|
||||
acb->canceled = 0;
|
||||
acb->bh = NULL;
|
||||
acb->status = -EINPROGRESS;
|
||||
acb->buf = NULL;
|
||||
|
@ -693,7 +680,7 @@ static BlockDriverAIOCB *iscsi_aio_ioctl(BlockDriverState *bs,
|
|||
if (acb->task == NULL) {
|
||||
error_report("iSCSI: Failed to allocate task for scsi command. %s",
|
||||
iscsi_get_error(iscsi));
|
||||
qemu_aio_release(acb);
|
||||
qemu_aio_unref(acb);
|
||||
return NULL;
|
||||
}
|
||||
memset(acb->task, 0, sizeof(struct scsi_task));
|
||||
|
@ -731,7 +718,7 @@ static BlockDriverAIOCB *iscsi_aio_ioctl(BlockDriverState *bs,
|
|||
(data.size > 0) ? &data : NULL,
|
||||
acb) != 0) {
|
||||
scsi_free_scsi_task(acb->task);
|
||||
qemu_aio_release(acb);
|
||||
qemu_aio_unref(acb);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
|
@ -85,11 +85,10 @@ static void qemu_laio_process_completion(struct qemu_laio_state *s,
|
|||
ret = -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
laiocb->common.cb(laiocb->common.opaque, ret);
|
||||
}
|
||||
laiocb->common.cb(laiocb->common.opaque, ret);
|
||||
|
||||
qemu_aio_release(laiocb);
|
||||
qemu_aio_unref(laiocb);
|
||||
}
|
||||
|
||||
/* The completion BH fetches completed I/O requests and invokes their
|
||||
|
@ -153,35 +152,22 @@ static void laio_cancel(BlockDriverAIOCB *blockacb)
|
|||
struct io_event event;
|
||||
int ret;
|
||||
|
||||
if (laiocb->ret != -EINPROGRESS)
|
||||
if (laiocb->ret != -EINPROGRESS) {
|
||||
return;
|
||||
|
||||
/*
|
||||
* Note that as of Linux 2.6.31 neither the block device code nor any
|
||||
* filesystem implements cancellation of AIO request.
|
||||
* Thus the polling loop below is the normal code path.
|
||||
*/
|
||||
}
|
||||
ret = io_cancel(laiocb->ctx->ctx, &laiocb->iocb, &event);
|
||||
if (ret == 0) {
|
||||
laiocb->ret = -ECANCELED;
|
||||
laiocb->ret = -ECANCELED;
|
||||
if (ret != 0) {
|
||||
/* iocb is not cancelled, cb will be called by the event loop later */
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* We have to wait for the iocb to finish.
|
||||
*
|
||||
* The only way to get the iocb status update is by polling the io context.
|
||||
* We might be able to do this slightly more optimal by removing the
|
||||
* O_NONBLOCK flag.
|
||||
*/
|
||||
while (laiocb->ret == -EINPROGRESS) {
|
||||
qemu_laio_completion_cb(&laiocb->ctx->e);
|
||||
}
|
||||
laiocb->common.cb(laiocb->common.opaque, laiocb->ret);
|
||||
}
|
||||
|
||||
static const AIOCBInfo laio_aiocb_info = {
|
||||
.aiocb_size = sizeof(struct qemu_laiocb),
|
||||
.cancel = laio_cancel,
|
||||
.cancel_async = laio_cancel,
|
||||
};
|
||||
|
||||
static void ioq_init(LaioQueue *io_q)
|
||||
|
@ -300,7 +286,7 @@ BlockDriverAIOCB *laio_submit(BlockDriverState *bs, void *aio_ctx, int fd,
|
|||
return &laiocb->common;
|
||||
|
||||
out_free_aiocb:
|
||||
qemu_aio_release(laiocb);
|
||||
qemu_aio_unref(laiocb);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,168 @@
|
|||
/*
|
||||
* Null block driver
|
||||
*
|
||||
* Authors:
|
||||
* Fam Zheng <famz@redhat.com>
|
||||
*
|
||||
* Copyright (C) 2014 Red Hat, Inc.
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include "block/block_int.h"
|
||||
|
||||
typedef struct {
|
||||
int64_t length;
|
||||
} BDRVNullState;
|
||||
|
||||
static QemuOptsList runtime_opts = {
|
||||
.name = "null",
|
||||
.head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
|
||||
.desc = {
|
||||
{
|
||||
.name = "filename",
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "",
|
||||
},
|
||||
{
|
||||
.name = BLOCK_OPT_SIZE,
|
||||
.type = QEMU_OPT_SIZE,
|
||||
.help = "size of the null block",
|
||||
},
|
||||
{ /* end of list */ }
|
||||
},
|
||||
};
|
||||
|
||||
static int null_file_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
Error **errp)
|
||||
{
|
||||
QemuOpts *opts;
|
||||
BDRVNullState *s = bs->opaque;
|
||||
|
||||
opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
|
||||
qemu_opts_absorb_qdict(opts, options, &error_abort);
|
||||
s->length =
|
||||
qemu_opt_get_size(opts, BLOCK_OPT_SIZE, 1 << 30);
|
||||
qemu_opts_del(opts);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void null_close(BlockDriverState *bs)
|
||||
{
|
||||
}
|
||||
|
||||
static int64_t null_getlength(BlockDriverState *bs)
|
||||
{
|
||||
BDRVNullState *s = bs->opaque;
|
||||
return s->length;
|
||||
}
|
||||
|
||||
static coroutine_fn int null_co_readv(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors,
|
||||
QEMUIOVector *qiov)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static coroutine_fn int null_co_writev(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors,
|
||||
QEMUIOVector *qiov)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static coroutine_fn int null_co_flush(BlockDriverState *bs)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
BlockDriverAIOCB common;
|
||||
QEMUBH *bh;
|
||||
} NullAIOCB;
|
||||
|
||||
static const AIOCBInfo null_aiocb_info = {
|
||||
.aiocb_size = sizeof(NullAIOCB),
|
||||
};
|
||||
|
||||
static void null_bh_cb(void *opaque)
|
||||
{
|
||||
NullAIOCB *acb = opaque;
|
||||
acb->common.cb(acb->common.opaque, 0);
|
||||
qemu_bh_delete(acb->bh);
|
||||
qemu_aio_unref(acb);
|
||||
}
|
||||
|
||||
static inline BlockDriverAIOCB *null_aio_common(BlockDriverState *bs,
|
||||
BlockDriverCompletionFunc *cb,
|
||||
void *opaque)
|
||||
{
|
||||
NullAIOCB *acb;
|
||||
|
||||
acb = qemu_aio_get(&null_aiocb_info, bs, cb, opaque);
|
||||
acb->bh = aio_bh_new(bdrv_get_aio_context(bs), null_bh_cb, acb);
|
||||
qemu_bh_schedule(acb->bh);
|
||||
return &acb->common;
|
||||
}
|
||||
|
||||
static BlockDriverAIOCB *null_aio_readv(BlockDriverState *bs,
|
||||
int64_t sector_num, QEMUIOVector *qiov,
|
||||
int nb_sectors,
|
||||
BlockDriverCompletionFunc *cb,
|
||||
void *opaque)
|
||||
{
|
||||
return null_aio_common(bs, cb, opaque);
|
||||
}
|
||||
|
||||
static BlockDriverAIOCB *null_aio_writev(BlockDriverState *bs,
|
||||
int64_t sector_num, QEMUIOVector *qiov,
|
||||
int nb_sectors,
|
||||
BlockDriverCompletionFunc *cb,
|
||||
void *opaque)
|
||||
{
|
||||
return null_aio_common(bs, cb, opaque);
|
||||
}
|
||||
|
||||
static BlockDriverAIOCB *null_aio_flush(BlockDriverState *bs,
|
||||
BlockDriverCompletionFunc *cb,
|
||||
void *opaque)
|
||||
{
|
||||
return null_aio_common(bs, cb, opaque);
|
||||
}
|
||||
|
||||
static BlockDriver bdrv_null_co = {
|
||||
.format_name = "null-co",
|
||||
.protocol_name = "null-co",
|
||||
.instance_size = sizeof(BDRVNullState),
|
||||
|
||||
.bdrv_file_open = null_file_open,
|
||||
.bdrv_close = null_close,
|
||||
.bdrv_getlength = null_getlength,
|
||||
|
||||
.bdrv_co_readv = null_co_readv,
|
||||
.bdrv_co_writev = null_co_writev,
|
||||
.bdrv_co_flush_to_disk = null_co_flush,
|
||||
};
|
||||
|
||||
static BlockDriver bdrv_null_aio = {
|
||||
.format_name = "null-aio",
|
||||
.protocol_name = "null-aio",
|
||||
.instance_size = sizeof(BDRVNullState),
|
||||
|
||||
.bdrv_file_open = null_file_open,
|
||||
.bdrv_close = null_close,
|
||||
.bdrv_getlength = null_getlength,
|
||||
|
||||
.bdrv_aio_readv = null_aio_readv,
|
||||
.bdrv_aio_writev = null_aio_writev,
|
||||
.bdrv_aio_flush = null_aio_flush,
|
||||
};
|
||||
|
||||
static void bdrv_null_init(void)
|
||||
{
|
||||
bdrv_register(&bdrv_null_co);
|
||||
bdrv_register(&bdrv_null_aio);
|
||||
}
|
||||
|
||||
block_init(bdrv_null_init);
|
|
@ -486,6 +486,13 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
|
|||
goto out;
|
||||
}
|
||||
|
||||
if (offset_into_cluster(s, l2_offset)) {
|
||||
qcow2_signal_corruption(bs, true, -1, -1, "L2 table offset %#" PRIx64
|
||||
" unaligned (L1 index: %#" PRIx64 ")",
|
||||
l2_offset, l1_index);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* load the l2 table in memory */
|
||||
|
||||
ret = l2_load(bs, l2_offset, &l2_table);
|
||||
|
@ -508,8 +515,11 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
|
|||
break;
|
||||
case QCOW2_CLUSTER_ZERO:
|
||||
if (s->qcow_version < 3) {
|
||||
qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table);
|
||||
return -EIO;
|
||||
qcow2_signal_corruption(bs, true, -1, -1, "Zero cluster entry found"
|
||||
" in pre-v3 image (L2 offset: %#" PRIx64
|
||||
", L2 index: %#x)", l2_offset, l2_index);
|
||||
ret = -EIO;
|
||||
goto fail;
|
||||
}
|
||||
c = count_contiguous_clusters(nb_clusters, s->cluster_size,
|
||||
&l2_table[l2_index], QCOW_OFLAG_ZERO);
|
||||
|
@ -525,6 +535,14 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
|
|||
c = count_contiguous_clusters(nb_clusters, s->cluster_size,
|
||||
&l2_table[l2_index], QCOW_OFLAG_ZERO);
|
||||
*cluster_offset &= L2E_OFFSET_MASK;
|
||||
if (offset_into_cluster(s, *cluster_offset)) {
|
||||
qcow2_signal_corruption(bs, true, -1, -1, "Data cluster offset %#"
|
||||
PRIx64 " unaligned (L2 offset: %#" PRIx64
|
||||
", L2 index: %#x)", *cluster_offset,
|
||||
l2_offset, l2_index);
|
||||
ret = -EIO;
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
|
@ -541,6 +559,10 @@ out:
|
|||
*num = nb_available - index_in_cluster;
|
||||
|
||||
return ret;
|
||||
|
||||
fail:
|
||||
qcow2_cache_put(bs, s->l2_table_cache, (void **)&l2_table);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -576,6 +598,12 @@ static int get_cluster_table(BlockDriverState *bs, uint64_t offset,
|
|||
|
||||
assert(l1_index < s->l1_size);
|
||||
l2_offset = s->l1_table[l1_index] & L1E_OFFSET_MASK;
|
||||
if (offset_into_cluster(s, l2_offset)) {
|
||||
qcow2_signal_corruption(bs, true, -1, -1, "L2 table offset %#" PRIx64
|
||||
" unaligned (L1 index: %#" PRIx64 ")",
|
||||
l2_offset, l1_index);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* seek the l2 table of the given l2 offset */
|
||||
|
||||
|
@ -948,6 +976,15 @@ static int handle_copied(BlockDriverState *bs, uint64_t guest_offset,
|
|||
bool offset_matches =
|
||||
(cluster_offset & L2E_OFFSET_MASK) == *host_offset;
|
||||
|
||||
if (offset_into_cluster(s, cluster_offset & L2E_OFFSET_MASK)) {
|
||||
qcow2_signal_corruption(bs, true, -1, -1, "Data cluster offset "
|
||||
"%#llx unaligned (guest offset: %#" PRIx64
|
||||
")", cluster_offset & L2E_OFFSET_MASK,
|
||||
guest_offset);
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (*host_offset != 0 && !offset_matches) {
|
||||
*bytes = 0;
|
||||
ret = 0;
|
||||
|
@ -979,7 +1016,7 @@ out:
|
|||
|
||||
/* Only return a host offset if we actually made progress. Otherwise we
|
||||
* would make requirements for handle_alloc() that it can't fulfill */
|
||||
if (ret) {
|
||||
if (ret > 0) {
|
||||
*host_offset = (cluster_offset & L2E_OFFSET_MASK)
|
||||
+ offset_into_cluster(s, guest_offset);
|
||||
}
|
||||
|
|
|
@ -26,8 +26,6 @@
|
|||
#include "block/block_int.h"
|
||||
#include "block/qcow2.h"
|
||||
#include "qemu/range.h"
|
||||
#include "qapi/qmp/types.h"
|
||||
#include "qapi-event.h"
|
||||
|
||||
static int64_t alloc_clusters_noref(BlockDriverState *bs, uint64_t size);
|
||||
static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs,
|
||||
|
@ -110,6 +108,13 @@ static int get_refcount(BlockDriverState *bs, int64_t cluster_index)
|
|||
if (!refcount_block_offset)
|
||||
return 0;
|
||||
|
||||
if (offset_into_cluster(s, refcount_block_offset)) {
|
||||
qcow2_signal_corruption(bs, true, -1, -1, "Refblock offset %#" PRIx64
|
||||
" unaligned (reftable index: %#" PRIx64 ")",
|
||||
refcount_block_offset, refcount_table_index);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
ret = qcow2_cache_get(bs, s->refcount_block_cache, refcount_block_offset,
|
||||
(void**) &refcount_block);
|
||||
if (ret < 0) {
|
||||
|
@ -183,6 +188,14 @@ static int alloc_refcount_block(BlockDriverState *bs,
|
|||
|
||||
/* If it's already there, we're done */
|
||||
if (refcount_block_offset) {
|
||||
if (offset_into_cluster(s, refcount_block_offset)) {
|
||||
qcow2_signal_corruption(bs, true, -1, -1, "Refblock offset %#"
|
||||
PRIx64 " unaligned (reftable index: "
|
||||
"%#x)", refcount_block_offset,
|
||||
refcount_table_index);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return load_refcount_block(bs, refcount_block_offset,
|
||||
(void**) refcount_block);
|
||||
}
|
||||
|
@ -838,8 +851,14 @@ void qcow2_free_any_clusters(BlockDriverState *bs, uint64_t l2_entry,
|
|||
case QCOW2_CLUSTER_NORMAL:
|
||||
case QCOW2_CLUSTER_ZERO:
|
||||
if (l2_entry & L2E_OFFSET_MASK) {
|
||||
qcow2_free_clusters(bs, l2_entry & L2E_OFFSET_MASK,
|
||||
nb_clusters << s->cluster_bits, type);
|
||||
if (offset_into_cluster(s, l2_entry & L2E_OFFSET_MASK)) {
|
||||
qcow2_signal_corruption(bs, false, -1, -1,
|
||||
"Cannot free unaligned cluster %#llx",
|
||||
l2_entry & L2E_OFFSET_MASK);
|
||||
} else {
|
||||
qcow2_free_clusters(bs, l2_entry & L2E_OFFSET_MASK,
|
||||
nb_clusters << s->cluster_bits, type);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case QCOW2_CLUSTER_UNALLOCATED:
|
||||
|
@ -903,6 +922,14 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
|
|||
old_l2_offset = l2_offset;
|
||||
l2_offset &= L1E_OFFSET_MASK;
|
||||
|
||||
if (offset_into_cluster(s, l2_offset)) {
|
||||
qcow2_signal_corruption(bs, true, -1, -1, "L2 table offset %#"
|
||||
PRIx64 " unaligned (L1 index: %#x)",
|
||||
l2_offset, i);
|
||||
ret = -EIO;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = qcow2_cache_get(bs, s->l2_table_cache, l2_offset,
|
||||
(void**) &l2_table);
|
||||
if (ret < 0) {
|
||||
|
@ -935,6 +962,17 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
|
|||
|
||||
case QCOW2_CLUSTER_NORMAL:
|
||||
case QCOW2_CLUSTER_ZERO:
|
||||
if (offset_into_cluster(s, offset & L2E_OFFSET_MASK)) {
|
||||
qcow2_signal_corruption(bs, true, -1, -1, "Data "
|
||||
"cluster offset %#llx "
|
||||
"unaligned (L2 offset: %#"
|
||||
PRIx64 ", L2 index: %#x)",
|
||||
offset & L2E_OFFSET_MASK,
|
||||
l2_offset, j);
|
||||
ret = -EIO;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
cluster_index = (offset & L2E_OFFSET_MASK) >> s->cluster_bits;
|
||||
if (!cluster_index) {
|
||||
/* unallocated */
|
||||
|
@ -1838,26 +1876,11 @@ int qcow2_pre_write_overlap_check(BlockDriverState *bs, int ign, int64_t offset,
|
|||
return ret;
|
||||
} else if (ret > 0) {
|
||||
int metadata_ol_bitnr = ffs(ret) - 1;
|
||||
char *message;
|
||||
|
||||
assert(metadata_ol_bitnr < QCOW2_OL_MAX_BITNR);
|
||||
|
||||
fprintf(stderr, "qcow2: Preventing invalid write on metadata (overlaps "
|
||||
"with %s); image marked as corrupt.\n",
|
||||
metadata_ol_names[metadata_ol_bitnr]);
|
||||
message = g_strdup_printf("Prevented %s overwrite",
|
||||
metadata_ol_names[metadata_ol_bitnr]);
|
||||
qapi_event_send_block_image_corrupted(bdrv_get_device_name(bs),
|
||||
message,
|
||||
true,
|
||||
offset,
|
||||
true,
|
||||
size,
|
||||
&error_abort);
|
||||
g_free(message);
|
||||
|
||||
qcow2_mark_corrupt(bs);
|
||||
bs->drv = NULL; /* make BDS unusable */
|
||||
qcow2_signal_corruption(bs, true, offset, size, "Preventing invalid "
|
||||
"write on metadata (overlaps with %s)",
|
||||
metadata_ol_names[metadata_ol_bitnr]);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
|
|
|
@ -31,6 +31,8 @@
|
|||
#include "qapi/qmp/qerror.h"
|
||||
#include "qapi/qmp/qbool.h"
|
||||
#include "qapi/util.h"
|
||||
#include "qapi/qmp/types.h"
|
||||
#include "qapi-event.h"
|
||||
#include "trace.h"
|
||||
#include "qemu/option_int.h"
|
||||
|
||||
|
@ -403,6 +405,12 @@ static QemuOptsList qcow2_runtime_opts = {
|
|||
.help = "Selects which overlap checks to perform from a range of "
|
||||
"templates (none, constant, cached, all)",
|
||||
},
|
||||
{
|
||||
.name = QCOW2_OPT_OVERLAP_TEMPLATE,
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "Selects which overlap checks to perform from a range of "
|
||||
"templates (none, constant, cached, all)",
|
||||
},
|
||||
{
|
||||
.name = QCOW2_OPT_OVERLAP_MAIN_HEADER,
|
||||
.type = QEMU_OPT_BOOL,
|
||||
|
@ -536,11 +544,11 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags,
|
|||
unsigned int len, i;
|
||||
int ret = 0;
|
||||
QCowHeader header;
|
||||
QemuOpts *opts;
|
||||
QemuOpts *opts = NULL;
|
||||
Error *local_err = NULL;
|
||||
uint64_t ext_end;
|
||||
uint64_t l1_vm_state_index;
|
||||
const char *opt_overlap_check;
|
||||
const char *opt_overlap_check, *opt_overlap_check_template;
|
||||
int overlap_check_template = 0;
|
||||
uint64_t l2_cache_size, refcount_cache_size;
|
||||
|
||||
|
@ -920,7 +928,21 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags,
|
|||
s->discard_passthrough[QCOW2_DISCARD_OTHER] =
|
||||
qemu_opt_get_bool(opts, QCOW2_OPT_DISCARD_OTHER, false);
|
||||
|
||||
opt_overlap_check = qemu_opt_get(opts, "overlap-check") ?: "cached";
|
||||
opt_overlap_check = qemu_opt_get(opts, QCOW2_OPT_OVERLAP);
|
||||
opt_overlap_check_template = qemu_opt_get(opts, QCOW2_OPT_OVERLAP_TEMPLATE);
|
||||
if (opt_overlap_check_template && opt_overlap_check &&
|
||||
strcmp(opt_overlap_check_template, opt_overlap_check))
|
||||
{
|
||||
error_setg(errp, "Conflicting values for qcow2 options '"
|
||||
QCOW2_OPT_OVERLAP "' ('%s') and '" QCOW2_OPT_OVERLAP_TEMPLATE
|
||||
"' ('%s')", opt_overlap_check, opt_overlap_check_template);
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
if (!opt_overlap_check) {
|
||||
opt_overlap_check = opt_overlap_check_template ?: "cached";
|
||||
}
|
||||
|
||||
if (!strcmp(opt_overlap_check, "none")) {
|
||||
overlap_check_template = 0;
|
||||
} else if (!strcmp(opt_overlap_check, "constant")) {
|
||||
|
@ -933,7 +955,6 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags,
|
|||
error_setg(errp, "Unsupported value '%s' for qcow2 option "
|
||||
"'overlap-check'. Allowed are either of the following: "
|
||||
"none, constant, cached, all", opt_overlap_check);
|
||||
qemu_opts_del(opts);
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
@ -948,6 +969,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags,
|
|||
}
|
||||
|
||||
qemu_opts_del(opts);
|
||||
opts = NULL;
|
||||
|
||||
if (s->use_lazy_refcounts && s->qcow_version < 3) {
|
||||
error_setg(errp, "Lazy refcounts require a qcow2 image with at least "
|
||||
|
@ -965,6 +987,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags,
|
|||
return ret;
|
||||
|
||||
fail:
|
||||
qemu_opts_del(opts);
|
||||
g_free(s->unknown_header_fields);
|
||||
cleanup_unknown_header_ext(bs);
|
||||
qcow2_free_snapshots(bs);
|
||||
|
@ -2529,6 +2552,52 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* If offset or size are negative, respectively, they will not be included in
|
||||
* the BLOCK_IMAGE_CORRUPTED event emitted.
|
||||
* fatal will be ignored for read-only BDS; corruptions found there will always
|
||||
* be considered non-fatal.
|
||||
*/
|
||||
void qcow2_signal_corruption(BlockDriverState *bs, bool fatal, int64_t offset,
|
||||
int64_t size, const char *message_format, ...)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
char *message;
|
||||
va_list ap;
|
||||
|
||||
fatal = fatal && !bs->read_only;
|
||||
|
||||
if (s->signaled_corruption &&
|
||||
(!fatal || (s->incompatible_features & QCOW2_INCOMPAT_CORRUPT)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
va_start(ap, message_format);
|
||||
message = g_strdup_vprintf(message_format, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (fatal) {
|
||||
fprintf(stderr, "qcow2: Marking image as corrupt: %s; further "
|
||||
"corruption events will be suppressed\n", message);
|
||||
} else {
|
||||
fprintf(stderr, "qcow2: Image is corrupt: %s; further non-fatal "
|
||||
"corruption events will be suppressed\n", message);
|
||||
}
|
||||
|
||||
qapi_event_send_block_image_corrupted(bdrv_get_device_name(bs), message,
|
||||
offset >= 0, offset, size >= 0, size,
|
||||
fatal, &error_abort);
|
||||
g_free(message);
|
||||
|
||||
if (fatal) {
|
||||
qcow2_mark_corrupt(bs);
|
||||
bs->drv = NULL; /* make BDS unusable */
|
||||
}
|
||||
|
||||
s->signaled_corruption = true;
|
||||
}
|
||||
|
||||
static QemuOptsList qcow2_create_opts = {
|
||||
.name = "qcow2-create-opts",
|
||||
.head = QTAILQ_HEAD_INITIALIZER(qcow2_create_opts.head),
|
||||
|
|
|
@ -83,6 +83,7 @@
|
|||
#define QCOW2_OPT_DISCARD_SNAPSHOT "pass-discard-snapshot"
|
||||
#define QCOW2_OPT_DISCARD_OTHER "pass-discard-other"
|
||||
#define QCOW2_OPT_OVERLAP "overlap-check"
|
||||
#define QCOW2_OPT_OVERLAP_TEMPLATE "overlap-check.template"
|
||||
#define QCOW2_OPT_OVERLAP_MAIN_HEADER "overlap-check.main-header"
|
||||
#define QCOW2_OPT_OVERLAP_ACTIVE_L1 "overlap-check.active-l1"
|
||||
#define QCOW2_OPT_OVERLAP_ACTIVE_L2 "overlap-check.active-l2"
|
||||
|
@ -261,6 +262,7 @@ typedef struct BDRVQcowState {
|
|||
bool discard_passthrough[QCOW2_DISCARD_MAX];
|
||||
|
||||
int overlap_check; /* bitmask of Qcow2MetadataOverlap values */
|
||||
bool signaled_corruption;
|
||||
|
||||
uint64_t incompatible_features;
|
||||
uint64_t compatible_features;
|
||||
|
@ -477,6 +479,10 @@ int qcow2_mark_corrupt(BlockDriverState *bs);
|
|||
int qcow2_mark_consistent(BlockDriverState *bs);
|
||||
int qcow2_update_header(BlockDriverState *bs);
|
||||
|
||||
void qcow2_signal_corruption(BlockDriverState *bs, bool fatal, int64_t offset,
|
||||
int64_t size, const char *message_format, ...)
|
||||
GCC_FMT_ATTR(5, 6);
|
||||
|
||||
/* qcow2-refcount.c functions */
|
||||
int qcow2_refcount_init(BlockDriverState *bs);
|
||||
void qcow2_refcount_close(BlockDriverState *bs);
|
||||
|
|
23
block/qed.c
23
block/qed.c
|
@ -18,22 +18,8 @@
|
|||
#include "qapi/qmp/qerror.h"
|
||||
#include "migration/migration.h"
|
||||
|
||||
static void qed_aio_cancel(BlockDriverAIOCB *blockacb)
|
||||
{
|
||||
QEDAIOCB *acb = (QEDAIOCB *)blockacb;
|
||||
AioContext *aio_context = bdrv_get_aio_context(blockacb->bs);
|
||||
bool finished = false;
|
||||
|
||||
/* Wait for the request to finish */
|
||||
acb->finished = &finished;
|
||||
while (!finished) {
|
||||
aio_poll(aio_context, true);
|
||||
}
|
||||
}
|
||||
|
||||
static const AIOCBInfo qed_aiocb_info = {
|
||||
.aiocb_size = sizeof(QEDAIOCB),
|
||||
.cancel = qed_aio_cancel,
|
||||
};
|
||||
|
||||
static int bdrv_qed_probe(const uint8_t *buf, int buf_size,
|
||||
|
@ -919,18 +905,12 @@ static void qed_aio_complete_bh(void *opaque)
|
|||
BlockDriverCompletionFunc *cb = acb->common.cb;
|
||||
void *user_opaque = acb->common.opaque;
|
||||
int ret = acb->bh_ret;
|
||||
bool *finished = acb->finished;
|
||||
|
||||
qemu_bh_delete(acb->bh);
|
||||
qemu_aio_release(acb);
|
||||
qemu_aio_unref(acb);
|
||||
|
||||
/* Invoke callback */
|
||||
cb(user_opaque, ret);
|
||||
|
||||
/* Signal cancel completion */
|
||||
if (finished) {
|
||||
*finished = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void qed_aio_complete(QEDAIOCB *acb, int ret)
|
||||
|
@ -1397,7 +1377,6 @@ static BlockDriverAIOCB *qed_aio_setup(BlockDriverState *bs,
|
|||
opaque, flags);
|
||||
|
||||
acb->flags = flags;
|
||||
acb->finished = NULL;
|
||||
acb->qiov = qiov;
|
||||
acb->qiov_offset = 0;
|
||||
acb->cur_pos = (uint64_t)sector_num * BDRV_SECTOR_SIZE;
|
||||
|
|
|
@ -138,16 +138,15 @@ static void quorum_aio_cancel(BlockDriverAIOCB *blockacb)
|
|||
|
||||
/* cancel all callbacks */
|
||||
for (i = 0; i < s->num_children; i++) {
|
||||
bdrv_aio_cancel(acb->qcrs[i].aiocb);
|
||||
if (acb->qcrs[i].aiocb) {
|
||||
bdrv_aio_cancel_async(acb->qcrs[i].aiocb);
|
||||
}
|
||||
}
|
||||
|
||||
g_free(acb->qcrs);
|
||||
qemu_aio_release(acb);
|
||||
}
|
||||
|
||||
static AIOCBInfo quorum_aiocb_info = {
|
||||
.aiocb_size = sizeof(QuorumAIOCB),
|
||||
.cancel = quorum_aio_cancel,
|
||||
.cancel_async = quorum_aio_cancel,
|
||||
};
|
||||
|
||||
static void quorum_aio_finalize(QuorumAIOCB *acb)
|
||||
|
@ -169,7 +168,7 @@ static void quorum_aio_finalize(QuorumAIOCB *acb)
|
|||
}
|
||||
|
||||
g_free(acb->qcrs);
|
||||
qemu_aio_release(acb);
|
||||
qemu_aio_unref(acb);
|
||||
}
|
||||
|
||||
static bool quorum_sha256_compare(QuorumVoteValue *a, QuorumVoteValue *b)
|
||||
|
|
25
block/rbd.c
25
block/rbd.c
|
@ -77,7 +77,6 @@ typedef struct RBDAIOCB {
|
|||
int64_t sector_num;
|
||||
int error;
|
||||
struct BDRVRBDState *s;
|
||||
int cancelled;
|
||||
int status;
|
||||
} RBDAIOCB;
|
||||
|
||||
|
@ -408,9 +407,7 @@ static void qemu_rbd_complete_aio(RADOSCB *rcb)
|
|||
acb->common.cb(acb->common.opaque, (acb->ret > 0 ? 0 : acb->ret));
|
||||
acb->status = 0;
|
||||
|
||||
if (!acb->cancelled) {
|
||||
qemu_aio_release(acb);
|
||||
}
|
||||
qemu_aio_unref(acb);
|
||||
}
|
||||
|
||||
/* TODO Convert to fine grained options */
|
||||
|
@ -539,25 +536,8 @@ static void qemu_rbd_close(BlockDriverState *bs)
|
|||
rados_shutdown(s->cluster);
|
||||
}
|
||||
|
||||
/*
|
||||
* Cancel aio. Since we don't reference acb in a non qemu threads,
|
||||
* it is safe to access it here.
|
||||
*/
|
||||
static void qemu_rbd_aio_cancel(BlockDriverAIOCB *blockacb)
|
||||
{
|
||||
RBDAIOCB *acb = (RBDAIOCB *) blockacb;
|
||||
acb->cancelled = 1;
|
||||
|
||||
while (acb->status == -EINPROGRESS) {
|
||||
aio_poll(bdrv_get_aio_context(acb->common.bs), true);
|
||||
}
|
||||
|
||||
qemu_aio_release(acb);
|
||||
}
|
||||
|
||||
static const AIOCBInfo rbd_aiocb_info = {
|
||||
.aiocb_size = sizeof(RBDAIOCB),
|
||||
.cancel = qemu_rbd_aio_cancel,
|
||||
};
|
||||
|
||||
static void rbd_finish_bh(void *opaque)
|
||||
|
@ -640,7 +620,6 @@ static BlockDriverAIOCB *rbd_start_aio(BlockDriverState *bs,
|
|||
acb->ret = 0;
|
||||
acb->error = 0;
|
||||
acb->s = s;
|
||||
acb->cancelled = 0;
|
||||
acb->bh = NULL;
|
||||
acb->status = -EINPROGRESS;
|
||||
|
||||
|
@ -692,7 +671,7 @@ failed_completion:
|
|||
failed:
|
||||
g_free(rcb);
|
||||
qemu_vfree(acb->bounce);
|
||||
qemu_aio_release(acb);
|
||||
qemu_aio_unref(acb);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
|
@ -315,7 +315,6 @@ struct SheepdogAIOCB {
|
|||
void (*aio_done_func)(SheepdogAIOCB *);
|
||||
|
||||
bool cancelable;
|
||||
bool *finished;
|
||||
int nr_pending;
|
||||
};
|
||||
|
||||
|
@ -446,10 +445,7 @@ static inline void free_aio_req(BDRVSheepdogState *s, AIOReq *aio_req)
|
|||
static void coroutine_fn sd_finish_aiocb(SheepdogAIOCB *acb)
|
||||
{
|
||||
qemu_coroutine_enter(acb->coroutine, NULL);
|
||||
if (acb->finished) {
|
||||
*acb->finished = true;
|
||||
}
|
||||
qemu_aio_release(acb);
|
||||
qemu_aio_unref(acb);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -482,36 +478,33 @@ static void sd_aio_cancel(BlockDriverAIOCB *blockacb)
|
|||
SheepdogAIOCB *acb = (SheepdogAIOCB *)blockacb;
|
||||
BDRVSheepdogState *s = acb->common.bs->opaque;
|
||||
AIOReq *aioreq, *next;
|
||||
bool finished = false;
|
||||
|
||||
acb->finished = &finished;
|
||||
while (!finished) {
|
||||
if (sd_acb_cancelable(acb)) {
|
||||
/* Remove outstanding requests from pending and failed queues. */
|
||||
QLIST_FOREACH_SAFE(aioreq, &s->pending_aio_head, aio_siblings,
|
||||
next) {
|
||||
if (aioreq->aiocb == acb) {
|
||||
free_aio_req(s, aioreq);
|
||||
}
|
||||
if (sd_acb_cancelable(acb)) {
|
||||
/* Remove outstanding requests from pending and failed queues. */
|
||||
QLIST_FOREACH_SAFE(aioreq, &s->pending_aio_head, aio_siblings,
|
||||
next) {
|
||||
if (aioreq->aiocb == acb) {
|
||||
free_aio_req(s, aioreq);
|
||||
}
|
||||
QLIST_FOREACH_SAFE(aioreq, &s->failed_aio_head, aio_siblings,
|
||||
next) {
|
||||
if (aioreq->aiocb == acb) {
|
||||
free_aio_req(s, aioreq);
|
||||
}
|
||||
}
|
||||
|
||||
assert(acb->nr_pending == 0);
|
||||
sd_finish_aiocb(acb);
|
||||
return;
|
||||
}
|
||||
aio_poll(s->aio_context, true);
|
||||
QLIST_FOREACH_SAFE(aioreq, &s->failed_aio_head, aio_siblings,
|
||||
next) {
|
||||
if (aioreq->aiocb == acb) {
|
||||
free_aio_req(s, aioreq);
|
||||
}
|
||||
}
|
||||
|
||||
assert(acb->nr_pending == 0);
|
||||
if (acb->common.cb) {
|
||||
acb->common.cb(acb->common.opaque, -ECANCELED);
|
||||
}
|
||||
sd_finish_aiocb(acb);
|
||||
}
|
||||
}
|
||||
|
||||
static const AIOCBInfo sd_aiocb_info = {
|
||||
.aiocb_size = sizeof(SheepdogAIOCB),
|
||||
.cancel = sd_aio_cancel,
|
||||
.aiocb_size = sizeof(SheepdogAIOCB),
|
||||
.cancel_async = sd_aio_cancel,
|
||||
};
|
||||
|
||||
static SheepdogAIOCB *sd_aio_setup(BlockDriverState *bs, QEMUIOVector *qiov,
|
||||
|
@ -528,7 +521,6 @@ static SheepdogAIOCB *sd_aio_setup(BlockDriverState *bs, QEMUIOVector *qiov,
|
|||
|
||||
acb->aio_done_func = NULL;
|
||||
acb->cancelable = true;
|
||||
acb->finished = NULL;
|
||||
acb->coroutine = qemu_coroutine_self();
|
||||
acb->ret = 0;
|
||||
acb->nr_pending = 0;
|
||||
|
@ -2138,7 +2130,7 @@ static coroutine_fn int sd_co_writev(BlockDriverState *bs, int64_t sector_num,
|
|||
|
||||
ret = sd_co_rw_vector(acb);
|
||||
if (ret <= 0) {
|
||||
qemu_aio_release(acb);
|
||||
qemu_aio_unref(acb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -2159,7 +2151,7 @@ static coroutine_fn int sd_co_readv(BlockDriverState *bs, int64_t sector_num,
|
|||
|
||||
ret = sd_co_rw_vector(acb);
|
||||
if (ret <= 0) {
|
||||
qemu_aio_release(acb);
|
||||
qemu_aio_unref(acb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -2518,7 +2510,7 @@ static coroutine_fn int sd_co_discard(BlockDriverState *bs, int64_t sector_num,
|
|||
|
||||
ret = sd_co_rw_vector(acb);
|
||||
if (ret <= 0) {
|
||||
qemu_aio_release(acb);
|
||||
qemu_aio_unref(acb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
19
block/vhdx.c
19
block/vhdx.c
|
@ -99,7 +99,8 @@ static const MSGUID logical_sector_guid = { .data1 = 0x8141bf1d,
|
|||
/* Each parent type must have a valid GUID; this is for parent images
|
||||
* of type 'VHDX'. If we were to allow e.g. a QCOW2 parent, we would
|
||||
* need to make up our own QCOW2 GUID type */
|
||||
static const MSGUID parent_vhdx_guid = { .data1 = 0xb04aefb7,
|
||||
static const MSGUID parent_vhdx_guid __attribute__((unused))
|
||||
= { .data1 = 0xb04aefb7,
|
||||
.data2 = 0xd19e,
|
||||
.data3 = 0x4a81,
|
||||
.data4 = { 0xb7, 0x89, 0x25, 0xb8,
|
||||
|
@ -1407,6 +1408,12 @@ exit:
|
|||
return ret;
|
||||
}
|
||||
|
||||
#define VHDX_METADATA_ENTRY_BUFFER_SIZE \
|
||||
(sizeof(VHDXFileParameters) +\
|
||||
sizeof(VHDXVirtualDiskSize) +\
|
||||
sizeof(VHDXPage83Data) +\
|
||||
sizeof(VHDXVirtualDiskLogicalSectorSize) +\
|
||||
sizeof(VHDXVirtualDiskPhysicalSectorSize))
|
||||
|
||||
/*
|
||||
* Create the Metadata entries.
|
||||
|
@ -1445,11 +1452,7 @@ static int vhdx_create_new_metadata(BlockDriverState *bs,
|
|||
VHDXVirtualDiskLogicalSectorSize *mt_log_sector_size;
|
||||
VHDXVirtualDiskPhysicalSectorSize *mt_phys_sector_size;
|
||||
|
||||
entry_buffer = g_malloc0(sizeof(VHDXFileParameters) +
|
||||
sizeof(VHDXVirtualDiskSize) +
|
||||
sizeof(VHDXPage83Data) +
|
||||
sizeof(VHDXVirtualDiskLogicalSectorSize) +
|
||||
sizeof(VHDXVirtualDiskPhysicalSectorSize));
|
||||
entry_buffer = g_malloc0(VHDX_METADATA_ENTRY_BUFFER_SIZE);
|
||||
|
||||
mt_file_params = entry_buffer;
|
||||
offset += sizeof(VHDXFileParameters);
|
||||
|
@ -1530,7 +1533,7 @@ static int vhdx_create_new_metadata(BlockDriverState *bs,
|
|||
}
|
||||
|
||||
ret = bdrv_pwrite(bs, metadata_offset + (64 * KiB), entry_buffer,
|
||||
VHDX_HEADER_BLOCK_SIZE);
|
||||
VHDX_METADATA_ENTRY_BUFFER_SIZE);
|
||||
if (ret < 0) {
|
||||
goto exit;
|
||||
}
|
||||
|
@ -1725,7 +1728,6 @@ static int vhdx_create_new_region_table(BlockDriverState *bs,
|
|||
goto exit;
|
||||
}
|
||||
|
||||
|
||||
exit:
|
||||
g_free(s);
|
||||
g_free(buffer);
|
||||
|
@ -1876,7 +1878,6 @@ static int vhdx_create(const char *filename, QemuOpts *opts, Error **errp)
|
|||
}
|
||||
|
||||
|
||||
|
||||
delete_and_exit:
|
||||
bdrv_unref(bs);
|
||||
exit:
|
||||
|
|
|
@ -88,7 +88,7 @@ static void win32_aio_process_completion(QEMUWin32AIOState *s,
|
|||
|
||||
|
||||
waiocb->common.cb(waiocb->common.opaque, ret);
|
||||
qemu_aio_release(waiocb);
|
||||
qemu_aio_unref(waiocb);
|
||||
}
|
||||
|
||||
static void win32_aio_completion_cb(EventNotifier *e)
|
||||
|
@ -106,22 +106,8 @@ static void win32_aio_completion_cb(EventNotifier *e)
|
|||
}
|
||||
}
|
||||
|
||||
static void win32_aio_cancel(BlockDriverAIOCB *blockacb)
|
||||
{
|
||||
QEMUWin32AIOCB *waiocb = (QEMUWin32AIOCB *)blockacb;
|
||||
|
||||
/*
|
||||
* CancelIoEx is only supported in Vista and newer. For now, just
|
||||
* wait for completion.
|
||||
*/
|
||||
while (!HasOverlappedIoCompleted(&waiocb->ov)) {
|
||||
aio_poll(bdrv_get_aio_context(blockacb->bs), true);
|
||||
}
|
||||
}
|
||||
|
||||
static const AIOCBInfo win32_aiocb_info = {
|
||||
.aiocb_size = sizeof(QEMUWin32AIOCB),
|
||||
.cancel = win32_aio_cancel,
|
||||
};
|
||||
|
||||
BlockDriverAIOCB *win32_aio_submit(BlockDriverState *bs,
|
||||
|
@ -172,7 +158,7 @@ BlockDriverAIOCB *win32_aio_submit(BlockDriverState *bs,
|
|||
out_dec_count:
|
||||
aio->count--;
|
||||
out:
|
||||
qemu_aio_release(waiocb);
|
||||
qemu_aio_unref(waiocb);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
|
@ -327,7 +327,6 @@ glusterfs=""
|
|||
glusterfs_discard="no"
|
||||
glusterfs_zerofill="no"
|
||||
archipelago=""
|
||||
virtio_blk_data_plane=""
|
||||
gtk=""
|
||||
gtkabi=""
|
||||
vte=""
|
||||
|
@ -1093,9 +1092,8 @@ for opt do
|
|||
;;
|
||||
--enable-archipelago) archipelago="yes"
|
||||
;;
|
||||
--disable-virtio-blk-data-plane) virtio_blk_data_plane="no"
|
||||
;;
|
||||
--enable-virtio-blk-data-plane) virtio_blk_data_plane="yes"
|
||||
--disable-virtio-blk-data-plane|--enable-virtio-blk-data-plane)
|
||||
echo "$0: $opt is obsolete, virtio-blk data-plane is always on" >&2
|
||||
;;
|
||||
--disable-gtk) gtk="no"
|
||||
;;
|
||||
|
@ -2943,16 +2941,6 @@ else
|
|||
tpm_passthrough=no
|
||||
fi
|
||||
|
||||
##########################################
|
||||
# adjust virtio-blk-data-plane based on linux-aio
|
||||
|
||||
if test "$virtio_blk_data_plane" = "yes" -a \
|
||||
"$linux_aio" != "yes" ; then
|
||||
error_exit "virtio-blk-data-plane requires Linux AIO, please try --enable-linux-aio"
|
||||
elif test -z "$virtio_blk_data_plane" ; then
|
||||
virtio_blk_data_plane=$linux_aio
|
||||
fi
|
||||
|
||||
##########################################
|
||||
# attr probe
|
||||
|
||||
|
@ -4327,7 +4315,6 @@ echo "coroutine backend $coroutine"
|
|||
echo "coroutine pool $coroutine_pool"
|
||||
echo "GlusterFS support $glusterfs"
|
||||
echo "Archipelago support $archipelago"
|
||||
echo "virtio-blk-data-plane $virtio_blk_data_plane"
|
||||
echo "gcov $gcov_tool"
|
||||
echo "gcov enabled $gcov"
|
||||
echo "TPM support $tpm"
|
||||
|
@ -4789,10 +4776,6 @@ if test "$quorum" = "yes" ; then
|
|||
echo "CONFIG_QUORUM=y" >> $config_host_mak
|
||||
fi
|
||||
|
||||
if test "$virtio_blk_data_plane" = "yes" ; then
|
||||
echo 'CONFIG_VIRTIO_BLK_DATA_PLANE=$(CONFIG_VIRTIO)' >> $config_host_mak
|
||||
fi
|
||||
|
||||
if test "$vhdx" = "yes" ; then
|
||||
echo "CONFIG_VHDX=y" >> $config_host_mak
|
||||
fi
|
||||
|
|
|
@ -73,7 +73,6 @@ typedef struct {
|
|||
QEMUSGList *sg;
|
||||
uint64_t sector_num;
|
||||
DMADirection dir;
|
||||
bool in_cancel;
|
||||
int sg_cur_index;
|
||||
dma_addr_t sg_cur_byte;
|
||||
QEMUIOVector iov;
|
||||
|
@ -125,12 +124,7 @@ static void dma_complete(DMAAIOCB *dbs, int ret)
|
|||
qemu_bh_delete(dbs->bh);
|
||||
dbs->bh = NULL;
|
||||
}
|
||||
if (!dbs->in_cancel) {
|
||||
/* Requests may complete while dma_aio_cancel is in progress. In
|
||||
* this case, the AIOCB should not be released because it is still
|
||||
* referenced by dma_aio_cancel. */
|
||||
qemu_aio_release(dbs);
|
||||
}
|
||||
qemu_aio_unref(dbs);
|
||||
}
|
||||
|
||||
static void dma_bdrv_cb(void *opaque, int ret)
|
||||
|
@ -186,19 +180,14 @@ static void dma_aio_cancel(BlockDriverAIOCB *acb)
|
|||
trace_dma_aio_cancel(dbs);
|
||||
|
||||
if (dbs->acb) {
|
||||
BlockDriverAIOCB *acb = dbs->acb;
|
||||
dbs->acb = NULL;
|
||||
dbs->in_cancel = true;
|
||||
bdrv_aio_cancel(acb);
|
||||
dbs->in_cancel = false;
|
||||
bdrv_aio_cancel_async(dbs->acb);
|
||||
}
|
||||
dbs->common.cb = NULL;
|
||||
dma_complete(dbs, 0);
|
||||
}
|
||||
|
||||
|
||||
static const AIOCBInfo dma_aiocb_info = {
|
||||
.aiocb_size = sizeof(DMAAIOCB),
|
||||
.cancel = dma_aio_cancel,
|
||||
.cancel_async = dma_aio_cancel,
|
||||
};
|
||||
|
||||
BlockDriverAIOCB *dma_bdrv_io(
|
||||
|
@ -217,7 +206,6 @@ BlockDriverAIOCB *dma_bdrv_io(
|
|||
dbs->sg_cur_index = 0;
|
||||
dbs->sg_cur_byte = 0;
|
||||
dbs->dir = dir;
|
||||
dbs->in_cancel = false;
|
||||
dbs->io_func = io_func;
|
||||
dbs->bh = NULL;
|
||||
qemu_iovec_init(&dbs->iov, sg->nsg);
|
||||
|
|
|
@ -125,7 +125,8 @@ If a fuzzer configuration is specified, then it has the next interpretation:
|
|||
will be always fuzzed for every test. This case is useful for regression
|
||||
testing.
|
||||
|
||||
For now only header fields, header extensions and L1/L2 tables are generated.
|
||||
The generator can create header fields, header extensions, L1/L2 tables and
|
||||
refcount table and blocks.
|
||||
|
||||
Module interfaces
|
||||
-----------------
|
||||
|
|
2
hmp.c
2
hmp.c
|
@ -679,6 +679,8 @@ void hmp_info_block_jobs(Monitor *mon, const QDict *qdict)
|
|||
}
|
||||
list = list->next;
|
||||
}
|
||||
|
||||
qapi_free_BlockJobInfoList(list);
|
||||
}
|
||||
|
||||
void hmp_info_tpm(Monitor *mon, const QDict *qdict)
|
||||
|
|
|
@ -12,4 +12,4 @@ common-obj-$(CONFIG_NVME_PCI) += nvme.o
|
|||
obj-$(CONFIG_SH4) += tc58128.o
|
||||
|
||||
obj-$(CONFIG_VIRTIO) += virtio-blk.o
|
||||
obj-$(CONFIG_VIRTIO_BLK_DATA_PLANE) += dataplane/
|
||||
obj-$(CONFIG_VIRTIO) += dataplane/
|
||||
|
|
|
@ -18,10 +18,8 @@
|
|||
#include "hw/block/block.h"
|
||||
#include "sysemu/blockdev.h"
|
||||
#include "hw/virtio/virtio-blk.h"
|
||||
#ifdef CONFIG_VIRTIO_BLK_DATA_PLANE
|
||||
# include "dataplane/virtio-blk.h"
|
||||
# include "migration/migration.h"
|
||||
#endif
|
||||
#include "dataplane/virtio-blk.h"
|
||||
#include "migration/migration.h"
|
||||
#include "block/scsi.h"
|
||||
#ifdef __linux__
|
||||
# include <scsi/sg.h>
|
||||
|
@ -435,7 +433,6 @@ static void virtio_blk_handle_output(VirtIODevice *vdev, VirtQueue *vq)
|
|||
.num_writes = 0,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_VIRTIO_BLK_DATA_PLANE
|
||||
/* Some guests kick before setting VIRTIO_CONFIG_S_DRIVER_OK so start
|
||||
* dataplane here instead of waiting for .set_status().
|
||||
*/
|
||||
|
@ -443,7 +440,6 @@ static void virtio_blk_handle_output(VirtIODevice *vdev, VirtQueue *vq)
|
|||
virtio_blk_data_plane_start(s->dataplane);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
while ((req = virtio_blk_get_request(s))) {
|
||||
virtio_blk_handle_request(req, &mrb);
|
||||
|
@ -500,11 +496,9 @@ static void virtio_blk_reset(VirtIODevice *vdev)
|
|||
{
|
||||
VirtIOBlock *s = VIRTIO_BLK(vdev);
|
||||
|
||||
#ifdef CONFIG_VIRTIO_BLK_DATA_PLANE
|
||||
if (s->dataplane) {
|
||||
virtio_blk_data_plane_stop(s->dataplane);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This should cancel pending requests, but can't do nicely until there
|
||||
|
@ -594,12 +588,10 @@ static void virtio_blk_set_status(VirtIODevice *vdev, uint8_t status)
|
|||
VirtIOBlock *s = VIRTIO_BLK(vdev);
|
||||
uint32_t features;
|
||||
|
||||
#ifdef CONFIG_VIRTIO_BLK_DATA_PLANE
|
||||
if (s->dataplane && !(status & (VIRTIO_CONFIG_S_DRIVER |
|
||||
VIRTIO_CONFIG_S_DRIVER_OK))) {
|
||||
virtio_blk_data_plane_stop(s->dataplane);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!(status & VIRTIO_CONFIG_S_DRIVER_OK)) {
|
||||
return;
|
||||
|
@ -694,7 +686,6 @@ static const BlockDevOps virtio_block_ops = {
|
|||
.resize_cb = virtio_blk_resize,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_VIRTIO_BLK_DATA_PLANE
|
||||
/* Disable dataplane thread during live migration since it does not
|
||||
* update the dirty memory bitmap yet.
|
||||
*/
|
||||
|
@ -725,7 +716,6 @@ static void virtio_blk_migration_state_changed(Notifier *notifier, void *data)
|
|||
}
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_VIRTIO_BLK_DATA_PLANE */
|
||||
|
||||
static void virtio_blk_device_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
|
@ -762,7 +752,6 @@ static void virtio_blk_device_realize(DeviceState *dev, Error **errp)
|
|||
|
||||
s->vq = virtio_add_queue(vdev, 128, virtio_blk_handle_output);
|
||||
s->complete_request = virtio_blk_complete_request;
|
||||
#ifdef CONFIG_VIRTIO_BLK_DATA_PLANE
|
||||
virtio_blk_data_plane_create(vdev, blk, &s->dataplane, &err);
|
||||
if (err != NULL) {
|
||||
error_propagate(errp, err);
|
||||
|
@ -771,7 +760,6 @@ static void virtio_blk_device_realize(DeviceState *dev, Error **errp)
|
|||
}
|
||||
s->migration_state_notifier.notify = virtio_blk_migration_state_changed;
|
||||
add_migration_state_change_notifier(&s->migration_state_notifier);
|
||||
#endif
|
||||
|
||||
s->change = qemu_add_vm_change_state_handler(virtio_blk_dma_restart_cb, s);
|
||||
register_savevm(dev, "virtio-blk", virtio_blk_id++, 2,
|
||||
|
@ -789,11 +777,9 @@ static void virtio_blk_device_unrealize(DeviceState *dev, Error **errp)
|
|||
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
|
||||
VirtIOBlock *s = VIRTIO_BLK(dev);
|
||||
|
||||
#ifdef CONFIG_VIRTIO_BLK_DATA_PLANE
|
||||
remove_migration_state_change_notifier(&s->migration_state_notifier);
|
||||
virtio_blk_data_plane_destroy(s->dataplane);
|
||||
s->dataplane = NULL;
|
||||
#endif
|
||||
qemu_del_vm_change_state_handler(s->change);
|
||||
unregister_savevm(dev, "virtio-blk", s);
|
||||
blockdev_mark_auto_del(s->bs);
|
||||
|
@ -818,9 +804,7 @@ static Property virtio_blk_properties[] = {
|
|||
#ifdef __linux__
|
||||
DEFINE_PROP_BIT("scsi", VirtIOBlock, blk.scsi, 0, true),
|
||||
#endif
|
||||
#ifdef CONFIG_VIRTIO_BLK_DATA_PLANE
|
||||
DEFINE_PROP_BIT("x-data-plane", VirtIOBlock, blk.data_plane, 0, false),
|
||||
#endif
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
|
|
|
@ -78,8 +78,7 @@ static uint32_t ahci_port_read(AHCIState *s, int port, int offset)
|
|||
val = pr->cmd;
|
||||
break;
|
||||
case PORT_TFDATA:
|
||||
val = ((uint16_t)s->dev[port].port.ifs[0].error << 8) |
|
||||
s->dev[port].port.ifs[0].status;
|
||||
val = pr->tfdata;
|
||||
break;
|
||||
case PORT_SIG:
|
||||
val = pr->sig;
|
||||
|
@ -251,14 +250,13 @@ static void ahci_port_write(AHCIState *s, int port, int offset, uint32_t val)
|
|||
check_cmd(s, port);
|
||||
break;
|
||||
case PORT_TFDATA:
|
||||
s->dev[port].port.ifs[0].error = (val >> 8) & 0xff;
|
||||
s->dev[port].port.ifs[0].status = val & 0xff;
|
||||
/* Read Only. */
|
||||
break;
|
||||
case PORT_SIG:
|
||||
pr->sig = val;
|
||||
/* Read Only */
|
||||
break;
|
||||
case PORT_SCR_STAT:
|
||||
pr->scr_stat = val;
|
||||
/* Read Only */
|
||||
break;
|
||||
case PORT_SCR_CTL:
|
||||
if (((pr->scr_ctl & AHCI_SCR_SCTL_DET) == 1) &&
|
||||
|
@ -497,6 +495,8 @@ static void ahci_reset_port(AHCIState *s, int port)
|
|||
pr->scr_stat = 0;
|
||||
pr->scr_err = 0;
|
||||
pr->scr_act = 0;
|
||||
pr->tfdata = 0x7F;
|
||||
pr->sig = 0xFFFFFFFF;
|
||||
d->busy_slot = -1;
|
||||
d->init_d2h_sent = false;
|
||||
|
||||
|
@ -528,16 +528,16 @@ static void ahci_reset_port(AHCIState *s, int port)
|
|||
|
||||
s->dev[port].port_state = STATE_RUN;
|
||||
if (!ide_state->bs) {
|
||||
s->dev[port].port_regs.sig = 0;
|
||||
pr->sig = 0;
|
||||
ide_state->status = SEEK_STAT | WRERR_STAT;
|
||||
} else if (ide_state->drive_kind == IDE_CD) {
|
||||
s->dev[port].port_regs.sig = SATA_SIGNATURE_CDROM;
|
||||
pr->sig = SATA_SIGNATURE_CDROM;
|
||||
ide_state->lcyl = 0x14;
|
||||
ide_state->hcyl = 0xeb;
|
||||
DPRINTF(port, "set lcyl = %d\n", ide_state->lcyl);
|
||||
ide_state->status = SEEK_STAT | WRERR_STAT | READY_STAT;
|
||||
} else {
|
||||
s->dev[port].port_regs.sig = SATA_SIGNATURE_DISK;
|
||||
pr->sig = SATA_SIGNATURE_DISK;
|
||||
ide_state->status = SEEK_STAT | WRERR_STAT;
|
||||
}
|
||||
|
||||
|
@ -563,7 +563,8 @@ static void debug_print_fis(uint8_t *fis, int cmd_len)
|
|||
|
||||
static void ahci_write_fis_sdb(AHCIState *s, int port, uint32_t finished)
|
||||
{
|
||||
AHCIPortRegs *pr = &s->dev[port].port_regs;
|
||||
AHCIDevice *ad = &s->dev[port];
|
||||
AHCIPortRegs *pr = &ad->port_regs;
|
||||
IDEState *ide_state;
|
||||
uint8_t *sdb_fis;
|
||||
|
||||
|
@ -572,8 +573,8 @@ static void ahci_write_fis_sdb(AHCIState *s, int port, uint32_t finished)
|
|||
return;
|
||||
}
|
||||
|
||||
sdb_fis = &s->dev[port].res_fis[RES_FIS_SDBFIS];
|
||||
ide_state = &s->dev[port].port.ifs[0];
|
||||
sdb_fis = &ad->res_fis[RES_FIS_SDBFIS];
|
||||
ide_state = &ad->port.ifs[0];
|
||||
|
||||
/* clear memory */
|
||||
*(uint32_t*)sdb_fis = 0;
|
||||
|
@ -582,9 +583,14 @@ static void ahci_write_fis_sdb(AHCIState *s, int port, uint32_t finished)
|
|||
sdb_fis[0] = ide_state->error;
|
||||
sdb_fis[2] = ide_state->status & 0x77;
|
||||
s->dev[port].finished |= finished;
|
||||
*(uint32_t*)(sdb_fis + 4) = cpu_to_le32(s->dev[port].finished);
|
||||
*(uint32_t*)(sdb_fis + 4) = cpu_to_le32(ad->finished);
|
||||
|
||||
ahci_trigger_irq(s, &s->dev[port], PORT_IRQ_SDB_FIS);
|
||||
/* Update shadow registers (except BSY 0x80 and DRQ 0x08) */
|
||||
pr->tfdata = (ad->port.ifs[0].error << 8) |
|
||||
(ad->port.ifs[0].status & 0x77) |
|
||||
(pr->tfdata & 0x88);
|
||||
|
||||
ahci_trigger_irq(s, ad, PORT_IRQ_SDB_FIS);
|
||||
}
|
||||
|
||||
static void ahci_write_fis_pio(AHCIDevice *ad, uint16_t len)
|
||||
|
@ -642,6 +648,10 @@ static void ahci_write_fis_pio(AHCIDevice *ad, uint16_t len)
|
|||
pio_fis[18] = 0;
|
||||
pio_fis[19] = 0;
|
||||
|
||||
/* Update shadow registers: */
|
||||
pr->tfdata = (ad->port.ifs[0].error << 8) |
|
||||
ad->port.ifs[0].status;
|
||||
|
||||
if (pio_fis[2] & ERR_STAT) {
|
||||
ahci_trigger_irq(ad->hba, ad, PORT_IRQ_TF_ERR);
|
||||
}
|
||||
|
@ -693,6 +703,10 @@ static void ahci_write_fis_d2h(AHCIDevice *ad, uint8_t *cmd_fis)
|
|||
d2h_fis[i] = 0;
|
||||
}
|
||||
|
||||
/* Update shadow registers: */
|
||||
pr->tfdata = (ad->port.ifs[0].error << 8) |
|
||||
ad->port.ifs[0].status;
|
||||
|
||||
if (d2h_fis[2] & ERR_STAT) {
|
||||
ahci_trigger_irq(ad->hba, ad, PORT_IRQ_TF_ERR);
|
||||
}
|
||||
|
@ -791,6 +805,9 @@ static void ncq_cb(void *opaque, int ret)
|
|||
NCQTransferState *ncq_tfs = (NCQTransferState *)opaque;
|
||||
IDEState *ide_state = &ncq_tfs->drive->port.ifs[0];
|
||||
|
||||
if (ret == -ECANCELED) {
|
||||
return;
|
||||
}
|
||||
/* Clear bit for this tag in SActive */
|
||||
ncq_tfs->drive->port_regs.scr_act &= ~(1 << ncq_tfs->tag);
|
||||
|
||||
|
|
|
@ -136,6 +136,7 @@ void ide_atapi_cmd_ok(IDEState *s)
|
|||
s->error = 0;
|
||||
s->status = READY_STAT | SEEK_STAT;
|
||||
s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO | ATAPI_INT_REASON_CD;
|
||||
ide_transfer_stop(s);
|
||||
ide_set_irq(s->bus);
|
||||
}
|
||||
|
||||
|
@ -149,6 +150,7 @@ void ide_atapi_cmd_error(IDEState *s, int sense_key, int asc)
|
|||
s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO | ATAPI_INT_REASON_CD;
|
||||
s->sense_key = sense_key;
|
||||
s->asc = asc;
|
||||
ide_transfer_stop(s);
|
||||
ide_set_irq(s->bus);
|
||||
}
|
||||
|
||||
|
@ -176,9 +178,7 @@ void ide_atapi_cmd_reply_end(IDEState *s)
|
|||
#endif
|
||||
if (s->packet_transfer_size <= 0) {
|
||||
/* end of transfer */
|
||||
s->status = READY_STAT | SEEK_STAT;
|
||||
s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO | ATAPI_INT_REASON_CD;
|
||||
ide_transfer_stop(s);
|
||||
ide_atapi_cmd_ok(s);
|
||||
ide_set_irq(s->bus);
|
||||
#ifdef DEBUG_IDE_ATAPI
|
||||
printf("status=0x%x\n", s->status);
|
||||
|
@ -188,7 +188,6 @@ void ide_atapi_cmd_reply_end(IDEState *s)
|
|||
if (s->lba != -1 && s->io_buffer_index >= s->cd_sector_size) {
|
||||
ret = cd_read_sector(s, s->lba, s->io_buffer, s->cd_sector_size);
|
||||
if (ret < 0) {
|
||||
ide_transfer_stop(s);
|
||||
ide_atapi_io_error(s, ret);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -372,23 +372,21 @@ static void trim_aio_cancel(BlockDriverAIOCB *acb)
|
|||
{
|
||||
TrimAIOCB *iocb = container_of(acb, TrimAIOCB, common);
|
||||
|
||||
/* Exit the loop in case bdrv_aio_cancel calls ide_issue_trim_cb again. */
|
||||
/* Exit the loop so ide_issue_trim_cb will not continue */
|
||||
iocb->j = iocb->qiov->niov - 1;
|
||||
iocb->i = (iocb->qiov->iov[iocb->j].iov_len / 8) - 1;
|
||||
|
||||
/* Tell ide_issue_trim_cb not to trigger the completion, too. */
|
||||
qemu_bh_delete(iocb->bh);
|
||||
iocb->bh = NULL;
|
||||
iocb->ret = -ECANCELED;
|
||||
|
||||
if (iocb->aiocb) {
|
||||
bdrv_aio_cancel(iocb->aiocb);
|
||||
bdrv_aio_cancel_async(iocb->aiocb);
|
||||
iocb->aiocb = NULL;
|
||||
}
|
||||
qemu_aio_release(iocb);
|
||||
}
|
||||
|
||||
static const AIOCBInfo trim_aiocb_info = {
|
||||
.aiocb_size = sizeof(TrimAIOCB),
|
||||
.cancel = trim_aio_cancel,
|
||||
.cancel_async = trim_aio_cancel,
|
||||
};
|
||||
|
||||
static void ide_trim_bh_cb(void *opaque)
|
||||
|
@ -399,7 +397,7 @@ static void ide_trim_bh_cb(void *opaque)
|
|||
|
||||
qemu_bh_delete(iocb->bh);
|
||||
iocb->bh = NULL;
|
||||
qemu_aio_release(iocb);
|
||||
qemu_aio_unref(iocb);
|
||||
}
|
||||
|
||||
static void ide_issue_trim_cb(void *opaque, int ret)
|
||||
|
@ -568,6 +566,9 @@ static void ide_sector_read_cb(void *opaque, int ret)
|
|||
s->pio_aiocb = NULL;
|
||||
s->status &= ~BUSY_STAT;
|
||||
|
||||
if (ret == -ECANCELED) {
|
||||
return;
|
||||
}
|
||||
block_acct_done(bdrv_get_stats(s->bs), &s->acct);
|
||||
if (ret != 0) {
|
||||
if (ide_handle_rw_error(s, -ret, IDE_RETRY_PIO |
|
||||
|
@ -678,6 +679,9 @@ void ide_dma_cb(void *opaque, int ret)
|
|||
int64_t sector_num;
|
||||
bool stay_active = false;
|
||||
|
||||
if (ret == -ECANCELED) {
|
||||
return;
|
||||
}
|
||||
if (ret < 0) {
|
||||
int op = IDE_RETRY_DMA;
|
||||
|
||||
|
@ -803,6 +807,9 @@ static void ide_sector_write_cb(void *opaque, int ret)
|
|||
IDEState *s = opaque;
|
||||
int n;
|
||||
|
||||
if (ret == -ECANCELED) {
|
||||
return;
|
||||
}
|
||||
block_acct_done(bdrv_get_stats(s->bs), &s->acct);
|
||||
|
||||
s->pio_aiocb = NULL;
|
||||
|
@ -882,6 +889,9 @@ static void ide_flush_cb(void *opaque, int ret)
|
|||
|
||||
s->pio_aiocb = NULL;
|
||||
|
||||
if (ret == -ECANCELED) {
|
||||
return;
|
||||
}
|
||||
if (ret < 0) {
|
||||
/* XXX: What sector number to set here? */
|
||||
if (ide_handle_rw_error(s, -ret, IDE_RETRY_FLUSH)) {
|
||||
|
|
|
@ -71,6 +71,7 @@
|
|||
#include <hw/ide/pci.h>
|
||||
#include <hw/ide/ahci.h>
|
||||
|
||||
#define ICH9_MSI_CAP_OFFSET 0x80
|
||||
#define ICH9_SATA_CAP_OFFSET 0xA8
|
||||
|
||||
#define ICH9_IDP_BAR 4
|
||||
|
@ -115,7 +116,6 @@ static int pci_ich9_ahci_init(PCIDevice *dev)
|
|||
/* XXX Software should program this register */
|
||||
dev->config[0x90] = 1 << 6; /* Address Map Register - AHCI mode */
|
||||
|
||||
msi_init(dev, 0x50, 1, true, false);
|
||||
d->ahci.irq = pci_allocate_irq(dev);
|
||||
|
||||
pci_register_bar(dev, ICH9_IDP_BAR, PCI_BASE_ADDRESS_SPACE_IO,
|
||||
|
@ -135,6 +135,11 @@ static int pci_ich9_ahci_init(PCIDevice *dev)
|
|||
(ICH9_IDP_BAR + 0x4) | (ICH9_IDP_INDEX_LOG2 << 4));
|
||||
d->ahci.idp_offset = ICH9_IDP_INDEX;
|
||||
|
||||
/* Although the AHCI 1.3 specification states that the first capability
|
||||
* should be PMCAP, the Intel ICH9 data sheet specifies that the ICH9
|
||||
* AHCI device puts the MSI capability first, pointing to 0x80. */
|
||||
msi_init(dev, ICH9_MSI_CAP_OFFSET, 1, true, false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -28,7 +28,6 @@
|
|||
#include <sys/socket.h>
|
||||
#include <linux/kvm.h>
|
||||
#include <fcntl.h>
|
||||
#include <linux/virtio_ring.h>
|
||||
#include <netpacket/packet.h>
|
||||
#include <net/ethernet.h>
|
||||
#include <net/if.h>
|
||||
|
@ -36,6 +35,7 @@
|
|||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "hw/virtio/virtio_ring.h"
|
||||
#include "hw/virtio/vhost.h"
|
||||
#include "hw/virtio/virtio-bus.h"
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ common-obj-y += virtio-rng.o
|
|||
common-obj-$(CONFIG_VIRTIO_PCI) += virtio-pci.o
|
||||
common-obj-y += virtio-bus.o
|
||||
common-obj-y += virtio-mmio.o
|
||||
common-obj-$(CONFIG_VIRTIO_BLK_DATA_PLANE) += dataplane/
|
||||
common-obj-$(CONFIG_VIRTIO) += dataplane/
|
||||
|
||||
obj-y += virtio.o virtio-balloon.o
|
||||
obj-$(CONFIG_LINUX) += vhost.o vhost-backend.o vhost-user.o
|
||||
|
|
|
@ -181,7 +181,8 @@ static int get_desc(Vring *vring, VirtQueueElement *elem,
|
|||
|
||||
/* Stop for now if there are not enough iovecs available. */
|
||||
if (*num >= VIRTQUEUE_MAX_SIZE) {
|
||||
return -ENOBUFS;
|
||||
error_report("Invalid SG num: %u", *num);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
/* TODO handle non-contiguous memory across region boundaries */
|
||||
|
|
|
@ -26,7 +26,8 @@ typedef struct BlockDriverAIOCB BlockDriverAIOCB;
|
|||
typedef void BlockDriverCompletionFunc(void *opaque, int ret);
|
||||
|
||||
typedef struct AIOCBInfo {
|
||||
void (*cancel)(BlockDriverAIOCB *acb);
|
||||
void (*cancel_async)(BlockDriverAIOCB *acb);
|
||||
AioContext *(*get_aio_context)(BlockDriverAIOCB *acb);
|
||||
size_t aiocb_size;
|
||||
} AIOCBInfo;
|
||||
|
||||
|
@ -35,11 +36,13 @@ struct BlockDriverAIOCB {
|
|||
BlockDriverState *bs;
|
||||
BlockDriverCompletionFunc *cb;
|
||||
void *opaque;
|
||||
int refcnt;
|
||||
};
|
||||
|
||||
void *qemu_aio_get(const AIOCBInfo *aiocb_info, BlockDriverState *bs,
|
||||
BlockDriverCompletionFunc *cb, void *opaque);
|
||||
void qemu_aio_release(void *p);
|
||||
void qemu_aio_unref(void *p);
|
||||
void qemu_aio_ref(void *p);
|
||||
|
||||
typedef struct AioHandler AioHandler;
|
||||
typedef void QEMUBHFunc(void *opaque);
|
||||
|
@ -99,7 +102,7 @@ void aio_set_dispatching(AioContext *ctx, bool dispatching);
|
|||
* They also provide bottom halves, a service to execute a piece of code
|
||||
* as soon as possible.
|
||||
*/
|
||||
AioContext *aio_context_new(void);
|
||||
AioContext *aio_context_new(Error **errp);
|
||||
|
||||
/**
|
||||
* aio_context_ref:
|
||||
|
|
|
@ -337,6 +337,7 @@ BlockDriverAIOCB *bdrv_aio_discard(BlockDriverState *bs,
|
|||
int64_t sector_num, int nb_sectors,
|
||||
BlockDriverCompletionFunc *cb, void *opaque);
|
||||
void bdrv_aio_cancel(BlockDriverAIOCB *acb);
|
||||
void bdrv_aio_cancel_async(BlockDriverAIOCB *acb);
|
||||
|
||||
typedef struct BlockRequest {
|
||||
/* Fields to be filled by multiwrite caller */
|
||||
|
|
|
@ -17,8 +17,8 @@
|
|||
#ifndef VRING_H
|
||||
#define VRING_H
|
||||
|
||||
#include <linux/virtio_ring.h>
|
||||
#include "qemu-common.h"
|
||||
#include "hw/virtio/virtio_ring.h"
|
||||
#include "hw/virtio/virtio.h"
|
||||
|
||||
typedef struct {
|
||||
|
|
|
@ -132,10 +132,8 @@ typedef struct VirtIOBlock {
|
|||
VMChangeStateEntry *change;
|
||||
/* Function to push to vq and notify guest */
|
||||
void (*complete_request)(struct VirtIOBlockReq *req, unsigned char status);
|
||||
#ifdef CONFIG_VIRTIO_BLK_DATA_PLANE
|
||||
Notifier migration_state_notifier;
|
||||
struct VirtIOBlockDataPlane *dataplane;
|
||||
#endif
|
||||
} VirtIOBlock;
|
||||
|
||||
typedef struct MultiReqBuffer {
|
||||
|
|
|
@ -0,0 +1,167 @@
|
|||
#ifndef _LINUX_VIRTIO_RING_H
|
||||
#define _LINUX_VIRTIO_RING_H
|
||||
/*
|
||||
* This file is copied from /usr/include/linux while converting __uNN types
|
||||
* to uXX_t, __inline__ to inline, and tab to spaces.
|
||||
* */
|
||||
|
||||
/* An interface for efficient virtio implementation, currently for use by KVM
|
||||
* and lguest, but hopefully others soon. Do NOT change this since it will
|
||||
* break existing servers and clients.
|
||||
*
|
||||
* This header is BSD licensed so anyone can use the definitions to implement
|
||||
* compatible drivers/servers.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of IBM nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL IBM OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* Copyright Rusty Russell IBM Corporation 2007. */
|
||||
|
||||
/* This marks a buffer as continuing via the next field. */
|
||||
#define VRING_DESC_F_NEXT 1
|
||||
/* This marks a buffer as write-only (otherwise read-only). */
|
||||
#define VRING_DESC_F_WRITE 2
|
||||
/* This means the buffer contains a list of buffer descriptors. */
|
||||
#define VRING_DESC_F_INDIRECT 4
|
||||
|
||||
/* The Host uses this in used->flags to advise the Guest: don't kick me when
|
||||
* you add a buffer. It's unreliable, so it's simply an optimization. Guest
|
||||
* will still kick if it's out of buffers. */
|
||||
#define VRING_USED_F_NO_NOTIFY 1
|
||||
/* The Guest uses this in avail->flags to advise the Host: don't interrupt me
|
||||
* when you consume a buffer. It's unreliable, so it's simply an
|
||||
* optimization. */
|
||||
#define VRING_AVAIL_F_NO_INTERRUPT 1
|
||||
|
||||
/* We support indirect buffer descriptors */
|
||||
#define VIRTIO_RING_F_INDIRECT_DESC 28
|
||||
|
||||
/* The Guest publishes the used index for which it expects an interrupt
|
||||
* at the end of the avail ring. Host should ignore the avail->flags field. */
|
||||
/* The Host publishes the avail index for which it expects a kick
|
||||
* at the end of the used ring. Guest should ignore the used->flags field. */
|
||||
#define VIRTIO_RING_F_EVENT_IDX 29
|
||||
|
||||
/* Virtio ring descriptors: 16 bytes. These can chain together via "next". */
|
||||
struct vring_desc {
|
||||
/* Address (guest-physical). */
|
||||
uint64_t addr;
|
||||
/* Length. */
|
||||
uint32_t len;
|
||||
/* The flags as indicated above. */
|
||||
uint16_t flags;
|
||||
/* We chain unused descriptors via this, too */
|
||||
uint16_t next;
|
||||
};
|
||||
|
||||
struct vring_avail {
|
||||
uint16_t flags;
|
||||
uint16_t idx;
|
||||
uint16_t ring[];
|
||||
};
|
||||
|
||||
/* u32 is used here for ids for padding reasons. */
|
||||
struct vring_used_elem {
|
||||
/* Index of start of used descriptor chain. */
|
||||
uint32_t id;
|
||||
/* Total length of the descriptor chain which was used (written to) */
|
||||
uint32_t len;
|
||||
};
|
||||
|
||||
struct vring_used {
|
||||
uint16_t flags;
|
||||
uint16_t idx;
|
||||
struct vring_used_elem ring[];
|
||||
};
|
||||
|
||||
struct vring {
|
||||
unsigned int num;
|
||||
|
||||
struct vring_desc *desc;
|
||||
|
||||
struct vring_avail *avail;
|
||||
|
||||
struct vring_used *used;
|
||||
};
|
||||
|
||||
/* The standard layout for the ring is a continuous chunk of memory which looks
|
||||
* like this. We assume num is a power of 2.
|
||||
*
|
||||
* struct vring
|
||||
* {
|
||||
* // The actual descriptors (16 bytes each)
|
||||
* struct vring_desc desc[num];
|
||||
*
|
||||
* // A ring of available descriptor heads with free-running index.
|
||||
* uint16_t avail_flags;
|
||||
* uint16_t avail_idx;
|
||||
* uint16_t available[num];
|
||||
* uint16_t used_event_idx;
|
||||
*
|
||||
* // Padding to the next align boundary.
|
||||
* char pad[];
|
||||
*
|
||||
* // A ring of used descriptor heads with free-running index.
|
||||
* uint16_t used_flags;
|
||||
* uint16_t used_idx;
|
||||
* struct vring_used_elem used[num];
|
||||
* uint16_t avail_event_idx;
|
||||
* };
|
||||
*/
|
||||
/* We publish the used event index at the end of the available ring, and vice
|
||||
* versa. They are at the end for backwards compatibility. */
|
||||
#define vring_used_event(vr) ((vr)->avail->ring[(vr)->num])
|
||||
#define vring_avail_event(vr) (*(uint16_t *)&(vr)->used->ring[(vr)->num])
|
||||
|
||||
static inline void vring_init(struct vring *vr, unsigned int num, void *p,
|
||||
unsigned long align)
|
||||
{
|
||||
vr->num = num;
|
||||
vr->desc = p;
|
||||
vr->avail = p + num*sizeof(struct vring_desc);
|
||||
vr->used = (void *)(((unsigned long)&vr->avail->ring[num] + sizeof(uint16_t)
|
||||
+ align-1) & ~(align - 1));
|
||||
}
|
||||
|
||||
static inline unsigned vring_size(unsigned int num, unsigned long align)
|
||||
{
|
||||
return ((sizeof(struct vring_desc) * num + sizeof(uint16_t) * (3 + num)
|
||||
+ align - 1) & ~(align - 1))
|
||||
+ sizeof(uint16_t) * 3 + sizeof(struct vring_used_elem) * num;
|
||||
}
|
||||
|
||||
/* The following is used with USED_EVENT_IDX and AVAIL_EVENT_IDX */
|
||||
/* Assuming a given event_idx value from the other size, if
|
||||
* we have just incremented index from old to new_idx,
|
||||
* should we trigger an event? */
|
||||
static inline int vring_need_event(uint16_t event_idx, uint16_t new_idx, uint16_t old)
|
||||
{
|
||||
/* Note: Xen has similar logic for notification hold-off
|
||||
* in include/xen/interface/io/ring.h with req_event and req_prod
|
||||
* corresponding to event_idx + 1 and new_idx respectively.
|
||||
* Note also that req_event and req_prod in Xen start at 1,
|
||||
* event indexes in virtio start at 0. */
|
||||
return (uint16_t)(new_idx - event_idx - 1) < (uint16_t)(new_idx - old);
|
||||
}
|
||||
|
||||
#endif /* _LINUX_VIRTIO_RING_H */
|
|
@ -42,7 +42,7 @@
|
|||
*
|
||||
* In the case of QEMU tools, this will also start/initialize timers.
|
||||
*/
|
||||
int qemu_init_main_loop(void);
|
||||
int qemu_init_main_loop(Error **errp);
|
||||
|
||||
/**
|
||||
* main_loop_wait: Run one iteration of the main loop.
|
||||
|
|
11
iothread.c
11
iothread.c
|
@ -17,6 +17,7 @@
|
|||
#include "block/aio.h"
|
||||
#include "sysemu/iothread.h"
|
||||
#include "qmp-commands.h"
|
||||
#include "qemu/error-report.h"
|
||||
|
||||
#define IOTHREADS_PATH "/objects"
|
||||
|
||||
|
@ -53,6 +54,9 @@ static void iothread_instance_finalize(Object *obj)
|
|||
{
|
||||
IOThread *iothread = IOTHREAD(obj);
|
||||
|
||||
if (!iothread->ctx) {
|
||||
return;
|
||||
}
|
||||
iothread->stopping = true;
|
||||
aio_notify(iothread->ctx);
|
||||
qemu_thread_join(&iothread->thread);
|
||||
|
@ -63,11 +67,16 @@ static void iothread_instance_finalize(Object *obj)
|
|||
|
||||
static void iothread_complete(UserCreatable *obj, Error **errp)
|
||||
{
|
||||
Error *local_error = NULL;
|
||||
IOThread *iothread = IOTHREAD(obj);
|
||||
|
||||
iothread->stopping = false;
|
||||
iothread->ctx = aio_context_new();
|
||||
iothread->thread_id = -1;
|
||||
iothread->ctx = aio_context_new(&local_error);
|
||||
if (!iothread->ctx) {
|
||||
error_propagate(errp, local_error);
|
||||
return;
|
||||
}
|
||||
|
||||
qemu_mutex_init(&iothread->init_done_lock);
|
||||
qemu_cond_init(&iothread->init_done_cond);
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
#include <linux/ioctl.h>
|
||||
#include <linux/virtio_config.h>
|
||||
#include <linux/virtio_ring.h>
|
||||
#include "hw/virtio/virtio_ring.h"
|
||||
|
||||
struct vhost_vring_state {
|
||||
unsigned int index;
|
||||
|
|
|
@ -126,10 +126,11 @@ void qemu_notify_event(void)
|
|||
|
||||
static GArray *gpollfds;
|
||||
|
||||
int qemu_init_main_loop(void)
|
||||
int qemu_init_main_loop(Error **errp)
|
||||
{
|
||||
int ret;
|
||||
GSource *src;
|
||||
Error *local_error = NULL;
|
||||
|
||||
init_clocks();
|
||||
|
||||
|
@ -138,8 +139,12 @@ int qemu_init_main_loop(void)
|
|||
return ret;
|
||||
}
|
||||
|
||||
qemu_aio_context = aio_context_new(&local_error);
|
||||
if (!qemu_aio_context) {
|
||||
error_propagate(errp, local_error);
|
||||
return -EMFILE;
|
||||
}
|
||||
gpollfds = g_array_new(FALSE, FALSE, sizeof(GPollFD));
|
||||
qemu_aio_context = aio_context_new();
|
||||
src = aio_get_g_source(qemu_aio_context);
|
||||
g_source_attach(src, NULL);
|
||||
g_source_unref(src);
|
||||
|
|
|
@ -197,7 +197,7 @@
|
|||
# 'file', 'file', 'ftp', 'ftps', 'host_cdrom', 'host_device',
|
||||
# 'host_floppy', 'http', 'https', 'nbd', 'parallels', 'qcow',
|
||||
# 'qcow2', 'raw', 'tftp', 'vdi', 'vmdk', 'vpc', 'vvfat'
|
||||
# 2.2: 'archipelago'
|
||||
# 2.2: 'archipelago' added, 'cow' dropped
|
||||
#
|
||||
# @backing_file: #optional the name of the backing file (for copy-on-write)
|
||||
#
|
||||
|
@ -1148,10 +1148,11 @@
|
|||
# Since: 2.0
|
||||
##
|
||||
{ 'enum': 'BlockdevDriver',
|
||||
'data': [ 'archipelago', 'file', 'host_device', 'host_cdrom', 'host_floppy',
|
||||
'http', 'https', 'ftp', 'ftps', 'tftp', 'vvfat', 'blkdebug',
|
||||
'blkverify', 'bochs', 'cloop', 'cow', 'dmg', 'parallels', 'qcow',
|
||||
'qcow2', 'qed', 'raw', 'vdi', 'vhdx', 'vmdk', 'vpc', 'quorum' ] }
|
||||
'data': [ 'archipelago', 'blkdebug', 'blkverify', 'bochs', 'cloop',
|
||||
'dmg', 'file', 'ftp', 'ftps', 'host_cdrom', 'host_device',
|
||||
'host_floppy', 'http', 'https', 'null-aio', 'null-co', 'parallels',
|
||||
'qcow', 'qcow2', 'qed', 'quorum', 'raw', 'tftp', 'vdi', 'vhdx',
|
||||
'vmdk', 'vpc', 'vvfat' ] }
|
||||
|
||||
##
|
||||
# @BlockdevOptionsBase
|
||||
|
@ -1203,6 +1204,18 @@
|
|||
{ 'type': 'BlockdevOptionsFile',
|
||||
'data': { 'filename': 'str' } }
|
||||
|
||||
##
|
||||
# @BlockdevOptionsNull
|
||||
#
|
||||
# Driver specific block device options for the null backend.
|
||||
#
|
||||
# @size: #optional size of the device in bytes.
|
||||
#
|
||||
# Since: 2.2
|
||||
##
|
||||
{ 'type': 'BlockdevOptionsNull',
|
||||
'data': { '*size': 'int' } }
|
||||
|
||||
##
|
||||
# @BlockdevOptionsVVFAT
|
||||
#
|
||||
|
@ -1250,6 +1263,67 @@
|
|||
'base': 'BlockdevOptionsGenericFormat',
|
||||
'data': { '*backing': 'BlockdevRef' } }
|
||||
|
||||
##
|
||||
# @Qcow2OverlapCheckMode
|
||||
#
|
||||
# General overlap check modes.
|
||||
#
|
||||
# @none: Do not perform any checks
|
||||
#
|
||||
# @constant: Perform only checks which can be done in constant time and
|
||||
# without reading anything from disk
|
||||
#
|
||||
# @cached: Perform only checks which can be done without reading anything
|
||||
# from disk
|
||||
#
|
||||
# @all: Perform all available overlap checks
|
||||
#
|
||||
# Since: 2.2
|
||||
##
|
||||
{ 'enum': 'Qcow2OverlapCheckMode',
|
||||
'data': [ 'none', 'constant', 'cached', 'all' ] }
|
||||
|
||||
##
|
||||
# @Qcow2OverlapCheckFlags
|
||||
#
|
||||
# Structure of flags for each metadata structure. Setting a field to 'true'
|
||||
# makes qemu guard that structure against unintended overwriting. The default
|
||||
# value is chosen according to the template given.
|
||||
#
|
||||
# @template: Specifies a template mode which can be adjusted using the other
|
||||
# flags, defaults to 'cached'
|
||||
#
|
||||
# Since: 2.2
|
||||
##
|
||||
{ 'type': 'Qcow2OverlapCheckFlags',
|
||||
'data': { '*template': 'Qcow2OverlapCheckMode',
|
||||
'*main-header': 'bool',
|
||||
'*active-l1': 'bool',
|
||||
'*active-l2': 'bool',
|
||||
'*refcount-table': 'bool',
|
||||
'*refcount-block': 'bool',
|
||||
'*snapshot-table': 'bool',
|
||||
'*inactive-l1': 'bool',
|
||||
'*inactive-l2': 'bool' } }
|
||||
|
||||
##
|
||||
# @Qcow2OverlapChecks
|
||||
#
|
||||
# Specifies which metadata structures should be guarded against unintended
|
||||
# overwriting.
|
||||
#
|
||||
# @flags: set of flags for separate specification of each metadata structure
|
||||
# type
|
||||
#
|
||||
# @mode: named mode which chooses a specific set of flags
|
||||
#
|
||||
# Since: 2.2
|
||||
##
|
||||
{ 'union': 'Qcow2OverlapChecks',
|
||||
'discriminator': {},
|
||||
'data': { 'flags': 'Qcow2OverlapCheckFlags',
|
||||
'mode': 'Qcow2OverlapCheckMode' } }
|
||||
|
||||
##
|
||||
# @BlockdevOptionsQcow2
|
||||
#
|
||||
|
@ -1269,6 +1343,18 @@
|
|||
# should be issued on other occasions where a cluster
|
||||
# gets freed
|
||||
#
|
||||
# @overlap-check: #optional which overlap checks to perform for writes
|
||||
# to the image, defaults to 'cached' (since 2.2)
|
||||
#
|
||||
# @cache-size: #optional the maximum total size of the L2 table and
|
||||
# refcount block caches in bytes (since 2.2)
|
||||
#
|
||||
# @l2-cache-size: #optional the maximum size of the L2 table cache in
|
||||
# bytes (since 2.2)
|
||||
#
|
||||
# @refcount-cache-size: #optional the maximum size of the refcount block cache
|
||||
# in bytes (since 2.2)
|
||||
#
|
||||
# Since: 1.7
|
||||
##
|
||||
{ 'type': 'BlockdevOptionsQcow2',
|
||||
|
@ -1276,7 +1362,11 @@
|
|||
'data': { '*lazy-refcounts': 'bool',
|
||||
'*pass-discard-request': 'bool',
|
||||
'*pass-discard-snapshot': 'bool',
|
||||
'*pass-discard-other': 'bool' } }
|
||||
'*pass-discard-other': 'bool',
|
||||
'*overlap-check': 'Qcow2OverlapChecks',
|
||||
'*cache-size': 'int',
|
||||
'*l2-cache-size': 'int',
|
||||
'*refcount-cache-size': 'int' } }
|
||||
|
||||
|
||||
##
|
||||
|
@ -1471,39 +1561,40 @@
|
|||
'discriminator': 'driver',
|
||||
'data': {
|
||||
'archipelago':'BlockdevOptionsArchipelago',
|
||||
'file': 'BlockdevOptionsFile',
|
||||
'host_device':'BlockdevOptionsFile',
|
||||
'host_cdrom': 'BlockdevOptionsFile',
|
||||
'host_floppy':'BlockdevOptionsFile',
|
||||
'http': 'BlockdevOptionsFile',
|
||||
'https': 'BlockdevOptionsFile',
|
||||
'ftp': 'BlockdevOptionsFile',
|
||||
'ftps': 'BlockdevOptionsFile',
|
||||
'tftp': 'BlockdevOptionsFile',
|
||||
# TODO gluster: Wait for structured options
|
||||
# TODO iscsi: Wait for structured options
|
||||
# TODO nbd: Should take InetSocketAddress for 'host'?
|
||||
# TODO nfs: Wait for structured options
|
||||
# TODO rbd: Wait for structured options
|
||||
# TODO sheepdog: Wait for structured options
|
||||
# TODO ssh: Should take InetSocketAddress for 'host'?
|
||||
'vvfat': 'BlockdevOptionsVVFAT',
|
||||
'blkdebug': 'BlockdevOptionsBlkdebug',
|
||||
'blkverify': 'BlockdevOptionsBlkverify',
|
||||
'bochs': 'BlockdevOptionsGenericFormat',
|
||||
'cloop': 'BlockdevOptionsGenericFormat',
|
||||
'cow': 'BlockdevOptionsGenericCOWFormat',
|
||||
'dmg': 'BlockdevOptionsGenericFormat',
|
||||
'file': 'BlockdevOptionsFile',
|
||||
'ftp': 'BlockdevOptionsFile',
|
||||
'ftps': 'BlockdevOptionsFile',
|
||||
# TODO gluster: Wait for structured options
|
||||
'host_cdrom': 'BlockdevOptionsFile',
|
||||
'host_device':'BlockdevOptionsFile',
|
||||
'host_floppy':'BlockdevOptionsFile',
|
||||
'http': 'BlockdevOptionsFile',
|
||||
'https': 'BlockdevOptionsFile',
|
||||
# TODO iscsi: Wait for structured options
|
||||
# TODO nbd: Should take InetSocketAddress for 'host'?
|
||||
# TODO nfs: Wait for structured options
|
||||
'null-aio': 'BlockdevOptionsNull',
|
||||
'null-co': 'BlockdevOptionsNull',
|
||||
'parallels': 'BlockdevOptionsGenericFormat',
|
||||
'qcow': 'BlockdevOptionsGenericCOWFormat',
|
||||
'qcow2': 'BlockdevOptionsQcow2',
|
||||
'qcow': 'BlockdevOptionsGenericCOWFormat',
|
||||
'qed': 'BlockdevOptionsGenericCOWFormat',
|
||||
'quorum': 'BlockdevOptionsQuorum',
|
||||
'raw': 'BlockdevOptionsGenericFormat',
|
||||
# TODO rbd: Wait for structured options
|
||||
# TODO sheepdog: Wait for structured options
|
||||
# TODO ssh: Should take InetSocketAddress for 'host'?
|
||||
'tftp': 'BlockdevOptionsFile',
|
||||
'vdi': 'BlockdevOptionsGenericFormat',
|
||||
'vhdx': 'BlockdevOptionsGenericFormat',
|
||||
'vmdk': 'BlockdevOptionsGenericCOWFormat',
|
||||
'vpc': 'BlockdevOptionsGenericFormat',
|
||||
'quorum': 'BlockdevOptionsQuorum'
|
||||
'vvfat': 'BlockdevOptionsVVFAT'
|
||||
} }
|
||||
|
||||
##
|
||||
|
@ -1555,7 +1646,7 @@
|
|||
##
|
||||
# @BLOCK_IMAGE_CORRUPTED
|
||||
#
|
||||
# Emitted when a disk image is being marked corrupt
|
||||
# Emitted when a corruption has been detected in a disk image
|
||||
#
|
||||
# @device: device name
|
||||
#
|
||||
|
@ -1569,13 +1660,18 @@
|
|||
# @size: #optional, if the corruption resulted from an image access, this is
|
||||
# the access size
|
||||
#
|
||||
# fatal: if set, the image is marked corrupt and therefore unusable after this
|
||||
# event and must be repaired (Since 2.2; before, every
|
||||
# BLOCK_IMAGE_CORRUPTED event was fatal)
|
||||
#
|
||||
# Since: 1.7
|
||||
##
|
||||
{ 'event': 'BLOCK_IMAGE_CORRUPTED',
|
||||
'data': { 'device' : 'str',
|
||||
'msg' : 'str',
|
||||
'*offset': 'int',
|
||||
'*size' : 'int' } }
|
||||
'*size' : 'int',
|
||||
'fatal' : 'bool' } }
|
||||
|
||||
##
|
||||
# @BLOCK_IO_ERROR
|
||||
|
|
|
@ -206,7 +206,7 @@ int qdev_device_help(QemuOpts *opts)
|
|||
}
|
||||
|
||||
prop_list = qmp_device_list_properties(driver, &local_err);
|
||||
if (!prop_list) {
|
||||
if (local_err) {
|
||||
error_printf("%s\n", error_get_pretty(local_err));
|
||||
error_free(local_err);
|
||||
return 1;
|
||||
|
|
|
@ -654,15 +654,6 @@ File name of a base image (see @option{create} subcommand)
|
|||
If this option is set to @code{on}, the image is encrypted.
|
||||
@end table
|
||||
|
||||
@item cow
|
||||
User Mode Linux Copy On Write image format. It is supported only for
|
||||
compatibility with previous versions.
|
||||
Supported options:
|
||||
@table @code
|
||||
@item backing_file
|
||||
File name of a base image (see @option{create} subcommand)
|
||||
@end table
|
||||
|
||||
@item vdi
|
||||
VirtualBox 1.1 compatible image format.
|
||||
Supported options:
|
||||
|
|
|
@ -2879,6 +2879,7 @@ int main(int argc, char **argv)
|
|||
{
|
||||
const img_cmd_t *cmd;
|
||||
const char *cmdname;
|
||||
Error *local_error = NULL;
|
||||
int c;
|
||||
static const struct option long_options[] = {
|
||||
{"help", no_argument, 0, 'h'},
|
||||
|
@ -2893,7 +2894,12 @@ int main(int argc, char **argv)
|
|||
error_set_progname(argv[0]);
|
||||
qemu_init_exec_dir(argv[0]);
|
||||
|
||||
qemu_init_main_loop();
|
||||
if (qemu_init_main_loop(&local_error)) {
|
||||
error_report("%s", error_get_pretty(local_error));
|
||||
error_free(local_error);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
bdrv_init();
|
||||
if (argc < 2) {
|
||||
error_exit("Not enough arguments");
|
||||
|
|
|
@ -228,8 +228,8 @@ compression is read-only. It means that if a compressed sector is
|
|||
rewritten, then it is rewritten as uncompressed data.
|
||||
|
||||
Image conversion is also useful to get smaller image when using a
|
||||
growable format such as @code{qcow} or @code{cow}: the empty sectors
|
||||
are detected and suppressed from the destination image.
|
||||
growable format such as @code{qcow}: the empty sectors are detected and
|
||||
suppressed from the destination image.
|
||||
|
||||
@var{sparse_size} indicates the consecutive number of bytes (defaults to 4k)
|
||||
that must contain only zeros for qemu-img to create a sparse image during
|
||||
|
|
|
@ -379,6 +379,7 @@ int main(int argc, char **argv)
|
|||
int c;
|
||||
int opt_index = 0;
|
||||
int flags = BDRV_O_UNMAP;
|
||||
Error *local_error = NULL;
|
||||
|
||||
#ifdef CONFIG_POSIX
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
|
@ -444,7 +445,11 @@ int main(int argc, char **argv)
|
|||
exit(1);
|
||||
}
|
||||
|
||||
qemu_init_main_loop();
|
||||
if (qemu_init_main_loop(&local_error)) {
|
||||
error_report("%s", error_get_pretty(local_error));
|
||||
error_free(local_error);
|
||||
exit(1);
|
||||
}
|
||||
bdrv_init();
|
||||
|
||||
/* initialize commands */
|
||||
|
|
|
@ -674,7 +674,11 @@ int main(int argc, char **argv)
|
|||
snprintf(sockpath, 128, SOCKET_PATH, basename(device));
|
||||
}
|
||||
|
||||
qemu_init_main_loop();
|
||||
if (qemu_init_main_loop(&local_err)) {
|
||||
error_report("%s", error_get_pretty(local_err));
|
||||
error_free(local_err);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
bdrv_init();
|
||||
atexit(bdrv_close_all);
|
||||
|
||||
|
|
|
@ -2081,7 +2081,7 @@ Each json-object contain the following:
|
|||
- "file": device file name (json-string)
|
||||
- "ro": true if read-only, false otherwise (json-bool)
|
||||
- "drv": driver format name (json-string)
|
||||
- Possible values: "blkdebug", "bochs", "cloop", "cow", "dmg",
|
||||
- Possible values: "blkdebug", "bochs", "cloop", "dmg",
|
||||
"file", "file", "ftp", "ftps", "host_cdrom",
|
||||
"host_device", "host_floppy", "http", "https",
|
||||
"nbd", "parallels", "qcow", "qcow2", "raw",
|
||||
|
|
|
@ -177,6 +177,8 @@ const int %(name)s_qtypes[QTYPE_MAX] = {
|
|||
qtype = "QTYPE_QDICT"
|
||||
elif find_union(qapi_type):
|
||||
qtype = "QTYPE_QDICT"
|
||||
elif find_enum(qapi_type):
|
||||
qtype = "QTYPE_QSTRING"
|
||||
else:
|
||||
assert False, "Invalid anonymous union member"
|
||||
|
||||
|
|
|
@ -263,7 +263,8 @@ void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **e
|
|||
for key in members:
|
||||
assert (members[key] in builtin_types
|
||||
or find_struct(members[key])
|
||||
or find_union(members[key])), "Invalid anonymous union member"
|
||||
or find_union(members[key])
|
||||
or find_enum(members[key])), "Invalid anonymous union member"
|
||||
|
||||
enum_full_value = generate_enum_full_value(disc_type, key)
|
||||
ret += mcgen('''
|
||||
|
|
|
@ -132,6 +132,7 @@ check-qtest-i386-y = tests/endianness-test$(EXESUF)
|
|||
check-qtest-i386-y += tests/fdc-test$(EXESUF)
|
||||
gcov-files-i386-y = hw/block/fdc.c
|
||||
check-qtest-i386-y += tests/ide-test$(EXESUF)
|
||||
check-qtest-i386-y += tests/ahci-test$(EXESUF)
|
||||
check-qtest-i386-y += tests/hd-geo-test$(EXESUF)
|
||||
gcov-files-i386-y += hw/block/hd-geometry.c
|
||||
check-qtest-i386-y += tests/boot-order-test$(EXESUF)
|
||||
|
@ -307,6 +308,7 @@ tests/endianness-test$(EXESUF): tests/endianness-test.o
|
|||
tests/spapr-phb-test$(EXESUF): tests/spapr-phb-test.o $(libqos-obj-y)
|
||||
tests/fdc-test$(EXESUF): tests/fdc-test.o
|
||||
tests/ide-test$(EXESUF): tests/ide-test.o $(libqos-pc-obj-y)
|
||||
tests/ahci-test$(EXESUF): tests/ahci-test.o $(libqos-pc-obj-y)
|
||||
tests/hd-geo-test$(EXESUF): tests/hd-geo-test.o
|
||||
tests/boot-order-test$(EXESUF): tests/boot-order-test.o $(libqos-obj-y)
|
||||
tests/bios-tables-test$(EXESUF): tests/bios-tables-test.o $(libqos-obj-y)
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -18,8 +18,8 @@
|
|||
|
||||
import random
|
||||
|
||||
|
||||
UINT8 = 0xff
|
||||
UINT16 = 0xffff
|
||||
UINT32 = 0xffffffff
|
||||
UINT64 = 0xffffffffffffffff
|
||||
# Most significant bit orders
|
||||
|
@ -28,6 +28,8 @@ UINT64_M = 63
|
|||
# Fuzz vectors
|
||||
UINT8_V = [0, 0x10, UINT8/4, UINT8/2 - 1, UINT8/2, UINT8/2 + 1, UINT8 - 1,
|
||||
UINT8]
|
||||
UINT16_V = [0, 0x100, 0x1000, UINT16/4, UINT16/2 - 1, UINT16/2, UINT16/2 + 1,
|
||||
UINT16 - 1, UINT16]
|
||||
UINT32_V = [0, 0x100, 0x1000, 0x10000, 0x100000, UINT32/4, UINT32/2 - 1,
|
||||
UINT32/2, UINT32/2 + 1, UINT32 - 1, UINT32]
|
||||
UINT64_V = UINT32_V + [0x1000000, 0x10000000, 0x100000000, UINT64/4,
|
||||
|
@ -332,9 +334,8 @@ def l1_entry(current):
|
|||
constraints = UINT64_V
|
||||
# Reserved bits are ignored
|
||||
# Added a possibility when only flags are fuzzed
|
||||
offset = 0x7fffffffffffffff & random.choice([selector(current,
|
||||
constraints),
|
||||
current])
|
||||
offset = 0x7fffffffffffffff & \
|
||||
random.choice([selector(current, constraints), current])
|
||||
is_cow = random.randint(0, 1)
|
||||
return offset + (is_cow << UINT64_M)
|
||||
|
||||
|
@ -344,12 +345,23 @@ def l2_entry(current):
|
|||
constraints = UINT64_V
|
||||
# Reserved bits are ignored
|
||||
# Add a possibility when only flags are fuzzed
|
||||
offset = 0x3ffffffffffffffe & random.choice([selector(current,
|
||||
constraints),
|
||||
current])
|
||||
offset = 0x3ffffffffffffffe & \
|
||||
random.choice([selector(current, constraints), current])
|
||||
is_compressed = random.randint(0, 1)
|
||||
is_cow = random.randint(0, 1)
|
||||
is_zero = random.randint(0, 1)
|
||||
value = offset + (is_cow << UINT64_M) + \
|
||||
(is_compressed << UINT64_M - 1) + is_zero
|
||||
return value
|
||||
|
||||
|
||||
def refcount_table_entry(current):
|
||||
"""Fuzz an entry of the refcount table."""
|
||||
constraints = UINT64_V
|
||||
return selector(current, constraints)
|
||||
|
||||
|
||||
def refcount_block_entry(current):
|
||||
"""Fuzz an entry of a refcount block."""
|
||||
constraints = UINT16_V
|
||||
return selector(current, constraints)
|
||||
|
|
|
@ -102,6 +102,8 @@ class Image(object):
|
|||
self.end_of_extension_area = FieldsList()
|
||||
self.l2_tables = FieldsList()
|
||||
self.l1_table = FieldsList()
|
||||
self.refcount_table = FieldsList()
|
||||
self.refcount_blocks = FieldsList()
|
||||
self.ext_offset = 0
|
||||
self.create_header(cluster_bits, backing_file_name)
|
||||
self.set_backing_file_name(backing_file_name)
|
||||
|
@ -113,7 +115,8 @@ class Image(object):
|
|||
def __iter__(self):
|
||||
return chain(self.header, self.backing_file_format,
|
||||
self.feature_name_table, self.end_of_extension_area,
|
||||
self.backing_file_name, self.l1_table, self.l2_tables)
|
||||
self.backing_file_name, self.l1_table, self.l2_tables,
|
||||
self.refcount_table, self.refcount_blocks)
|
||||
|
||||
def create_header(self, cluster_bits, backing_file_name=None):
|
||||
"""Generate a random valid header."""
|
||||
|
@ -330,6 +333,138 @@ class Image(object):
|
|||
float(self.cluster_size**2)))
|
||||
self.header['l1_table_offset'][0].value = l1_offset
|
||||
|
||||
def create_refcount_structures(self):
|
||||
"""Generate random refcount blocks and refcount table."""
|
||||
def allocate_rfc_blocks(data, size):
|
||||
"""Return indices of clusters allocated for refcount blocks."""
|
||||
cluster_ids = set()
|
||||
diff = block_ids = set([x / size for x in data])
|
||||
while len(diff) != 0:
|
||||
# Allocate all yet not allocated clusters
|
||||
new = self._get_available_clusters(data | cluster_ids,
|
||||
len(diff))
|
||||
# Indices of new refcount blocks necessary to cover clusters
|
||||
# in 'new'
|
||||
diff = set([x / size for x in new]) - block_ids
|
||||
cluster_ids |= new
|
||||
block_ids |= diff
|
||||
return cluster_ids, block_ids
|
||||
|
||||
def allocate_rfc_table(data, init_blocks, block_size):
|
||||
"""Return indices of clusters allocated for the refcount table
|
||||
and updated indices of clusters allocated for blocks and indices
|
||||
of blocks.
|
||||
"""
|
||||
blocks = set(init_blocks)
|
||||
clusters = set()
|
||||
# Number of entries in one cluster of the refcount table
|
||||
size = self.cluster_size / UINT64_S
|
||||
# Number of clusters necessary for the refcount table based on
|
||||
# the current number of refcount blocks
|
||||
table_size = int(ceil((max(blocks) + 1) / float(size)))
|
||||
# Index of the first cluster of the refcount table
|
||||
table_start = self._get_adjacent_clusters(data, table_size + 1)
|
||||
# Clusters allocated for the current length of the refcount table
|
||||
table_clusters = set(range(table_start, table_start + table_size))
|
||||
# Clusters allocated for the refcount table including
|
||||
# last optional one for potential l1 growth
|
||||
table_clusters_allocated = set(range(table_start, table_start +
|
||||
table_size + 1))
|
||||
# New refcount blocks necessary for clusters occupied by the
|
||||
# refcount table
|
||||
diff = set([c / block_size for c in table_clusters]) - blocks
|
||||
blocks |= diff
|
||||
while len(diff) != 0:
|
||||
# Allocate clusters for new refcount blocks
|
||||
new = self._get_available_clusters((data | clusters) |
|
||||
table_clusters_allocated,
|
||||
len(diff))
|
||||
# Indices of new refcount blocks necessary to cover
|
||||
# clusters in 'new'
|
||||
diff = set([x / block_size for x in new]) - blocks
|
||||
clusters |= new
|
||||
blocks |= diff
|
||||
# Check if the refcount table needs one more cluster
|
||||
if int(ceil((max(blocks) + 1) / float(size))) > table_size:
|
||||
new_block_id = (table_start + table_size) / block_size
|
||||
# Check if the additional table cluster needs
|
||||
# one more refcount block
|
||||
if new_block_id not in blocks:
|
||||
diff.add(new_block_id)
|
||||
table_clusters.add(table_start + table_size)
|
||||
table_size += 1
|
||||
return table_clusters, blocks, clusters
|
||||
|
||||
def create_table_entry(table_offset, block_cluster, block_size,
|
||||
cluster):
|
||||
"""Generate a refcount table entry."""
|
||||
offset = table_offset + UINT64_S * (cluster / block_size)
|
||||
return ['>Q', offset, block_cluster * self.cluster_size,
|
||||
'refcount_table_entry']
|
||||
|
||||
def create_block_entry(block_cluster, block_size, cluster):
|
||||
"""Generate a list of entries for the current block."""
|
||||
entry_size = self.cluster_size / block_size
|
||||
offset = block_cluster * self.cluster_size
|
||||
entry_offset = offset + entry_size * (cluster % block_size)
|
||||
# While snapshots are not supported all refcounts are set to 1
|
||||
return ['>H', entry_offset, 1, 'refcount_block_entry']
|
||||
# Size of a block entry in bits
|
||||
refcount_bits = 1 << self.header['refcount_order'][0].value
|
||||
# Number of refcount entries per refcount block
|
||||
# Convert self.cluster_size from bytes to bits to have the same
|
||||
# base for the numerator and denominator
|
||||
block_size = self.cluster_size * 8 / refcount_bits
|
||||
meta_data = self._get_metadata()
|
||||
if len(self.data_clusters) == 0:
|
||||
# All metadata for an empty guest image needs 4 clusters:
|
||||
# header, rfc table, rfc block, L1 table.
|
||||
# Header takes cluster #0, other clusters ##1-3 can be used
|
||||
block_clusters = set([random.choice(list(set(range(1, 4)) -
|
||||
meta_data))])
|
||||
block_ids = set([0])
|
||||
table_clusters = set([random.choice(list(set(range(1, 4)) -
|
||||
meta_data -
|
||||
block_clusters))])
|
||||
else:
|
||||
block_clusters, block_ids = \
|
||||
allocate_rfc_blocks(self.data_clusters |
|
||||
meta_data, block_size)
|
||||
table_clusters, block_ids, new_clusters = \
|
||||
allocate_rfc_table(self.data_clusters |
|
||||
meta_data |
|
||||
block_clusters,
|
||||
block_ids,
|
||||
block_size)
|
||||
block_clusters |= new_clusters
|
||||
|
||||
meta_data |= block_clusters | table_clusters
|
||||
table_offset = min(table_clusters) * self.cluster_size
|
||||
block_id = None
|
||||
# Clusters allocated for refcount blocks
|
||||
block_clusters = list(block_clusters)
|
||||
# Indices of refcount blocks
|
||||
block_ids = list(block_ids)
|
||||
# Refcount table entries
|
||||
rfc_table = []
|
||||
# Refcount entries
|
||||
rfc_blocks = []
|
||||
|
||||
for cluster in sorted(self.data_clusters | meta_data):
|
||||
if cluster / block_size != block_id:
|
||||
block_id = cluster / block_size
|
||||
block_cluster = block_clusters[block_ids.index(block_id)]
|
||||
rfc_table.append(create_table_entry(table_offset,
|
||||
block_cluster,
|
||||
block_size, cluster))
|
||||
rfc_blocks.append(create_block_entry(block_cluster, block_size,
|
||||
cluster))
|
||||
self.refcount_table = FieldsList(rfc_table)
|
||||
self.refcount_blocks = FieldsList(rfc_blocks)
|
||||
|
||||
self.header['refcount_table_offset'][0].value = table_offset
|
||||
self.header['refcount_table_clusters'][0].value = len(table_clusters)
|
||||
|
||||
def fuzz(self, fields_to_fuzz=None):
|
||||
"""Fuzz an image by corrupting values of a random subset of its fields.
|
||||
|
||||
|
@ -471,6 +606,7 @@ def create_image(test_img_path, backing_file_name=None, backing_file_fmt=None,
|
|||
image.create_feature_name_table()
|
||||
image.set_end_of_extension_area()
|
||||
image.create_l_structures()
|
||||
image.create_refcount_structures()
|
||||
image.fuzz(fields_to_fuzz)
|
||||
image.write(test_img_path)
|
||||
return image.image_size
|
||||
|
|
|
@ -70,7 +70,7 @@ def run_app(fd, q_args):
|
|||
"""Exception for signal.alarm events."""
|
||||
pass
|
||||
|
||||
def handler(*arg):
|
||||
def handler(*args):
|
||||
"""Notify that an alarm event occurred."""
|
||||
raise Alarm
|
||||
|
||||
|
@ -134,8 +134,8 @@ class TestEnv(object):
|
|||
self.init_path = os.getcwd()
|
||||
self.work_dir = work_dir
|
||||
self.current_dir = os.path.join(work_dir, 'test-' + test_id)
|
||||
self.qemu_img = os.environ.get('QEMU_IMG', 'qemu-img')\
|
||||
.strip().split(' ')
|
||||
self.qemu_img = \
|
||||
os.environ.get('QEMU_IMG', 'qemu-img').strip().split(' ')
|
||||
self.qemu_io = os.environ.get('QEMU_IO', 'qemu-io').strip().split(' ')
|
||||
self.commands = [['qemu-img', 'check', '-f', 'qcow2', '$test_img'],
|
||||
['qemu-img', 'info', '-f', 'qcow2', '$test_img'],
|
||||
|
@ -150,8 +150,7 @@ class TestEnv(object):
|
|||
'discard $off $len'],
|
||||
['qemu-io', '$test_img', '-c',
|
||||
'truncate $off']]
|
||||
for fmt in ['raw', 'vmdk', 'vdi', 'cow', 'qcow2', 'file',
|
||||
'qed', 'vpc']:
|
||||
for fmt in ['raw', 'vmdk', 'vdi', 'qcow2', 'file', 'qed', 'vpc']:
|
||||
self.commands.append(
|
||||
['qemu-img', 'convert', '-f', 'qcow2', '-O', fmt,
|
||||
'$test_img', 'converted_image.' + fmt])
|
||||
|
@ -178,7 +177,7 @@ class TestEnv(object):
|
|||
by 'qemu-img create'.
|
||||
"""
|
||||
# All formats supported by the 'qemu-img create' command.
|
||||
backing_file_fmt = random.choice(['raw', 'vmdk', 'vdi', 'cow', 'qcow2',
|
||||
backing_file_fmt = random.choice(['raw', 'vmdk', 'vdi', 'qcow2',
|
||||
'file', 'qed', 'vpc'])
|
||||
backing_file_name = 'backing_img.' + backing_file_fmt
|
||||
backing_file_size = random.randint(MIN_BACKING_FILE_SIZE,
|
||||
|
@ -212,10 +211,8 @@ class TestEnv(object):
|
|||
|
||||
os.chdir(self.current_dir)
|
||||
backing_file_name, backing_file_fmt = self._create_backing_file()
|
||||
img_size = image_generator.create_image('test.img',
|
||||
backing_file_name,
|
||||
backing_file_fmt,
|
||||
fuzz_config)
|
||||
img_size = image_generator.create_image(
|
||||
'test.img', backing_file_name, backing_file_fmt, fuzz_config)
|
||||
for item in commands:
|
||||
shutil.copy('test.img', 'copy.img')
|
||||
# 'off' and 'len' are multiple of the sector size
|
||||
|
@ -228,7 +225,7 @@ class TestEnv(object):
|
|||
elif item[0] == 'qemu-io':
|
||||
current_cmd = list(self.qemu_io)
|
||||
else:
|
||||
multilog("Warning: test command '%s' is not defined.\n" \
|
||||
multilog("Warning: test command '%s' is not defined.\n"
|
||||
% item[0], sys.stderr, self.log, self.parent_log)
|
||||
continue
|
||||
# Replace all placeholders with their real values
|
||||
|
@ -244,29 +241,28 @@ class TestEnv(object):
|
|||
"Backing file: %s\n" \
|
||||
% (self.seed, " ".join(current_cmd),
|
||||
self.current_dir, backing_file_name)
|
||||
|
||||
temp_log = StringIO.StringIO()
|
||||
try:
|
||||
retcode = run_app(temp_log, current_cmd)
|
||||
except OSError, e:
|
||||
multilog(test_summary + "Error: Start of '%s' failed. " \
|
||||
"Reason: %s\n\n" % (os.path.basename(
|
||||
current_cmd[0]), e[1]),
|
||||
multilog("%sError: Start of '%s' failed. Reason: %s\n\n"
|
||||
% (test_summary, os.path.basename(current_cmd[0]),
|
||||
e[1]),
|
||||
sys.stderr, self.log, self.parent_log)
|
||||
raise TestException
|
||||
|
||||
if retcode < 0:
|
||||
self.log.write(temp_log.getvalue())
|
||||
multilog(test_summary + "FAIL: Test terminated by signal " +
|
||||
"%s\n\n" % str_signal(-retcode), sys.stderr, self.log,
|
||||
self.parent_log)
|
||||
multilog("%sFAIL: Test terminated by signal %s\n\n"
|
||||
% (test_summary, str_signal(-retcode)),
|
||||
sys.stderr, self.log, self.parent_log)
|
||||
self.failed = True
|
||||
else:
|
||||
if self.log_all:
|
||||
self.log.write(temp_log.getvalue())
|
||||
multilog(test_summary + "PASS: Application exited with" +
|
||||
" the code '%d'\n\n" % retcode, sys.stdout,
|
||||
self.log, self.parent_log)
|
||||
multilog("%sPASS: Application exited with the code " \
|
||||
"'%d'\n\n" % (test_summary, retcode),
|
||||
sys.stdout, self.log, self.parent_log)
|
||||
temp_log.close()
|
||||
os.remove('copy.img')
|
||||
|
||||
|
@ -286,8 +282,9 @@ if __name__ == '__main__':
|
|||
|
||||
Set up test environment in TEST_DIR and run a test in it. A module for
|
||||
test image generation should be specified via IMG_GENERATOR.
|
||||
|
||||
Example:
|
||||
runner.py -c '[["qemu-img", "info", "$test_img"]]' /tmp/test qcow2
|
||||
runner.py -c '[["qemu-img", "info", "$test_img"]]' /tmp/test qcow2
|
||||
|
||||
Optional arguments:
|
||||
-h, --help display this help and exit
|
||||
|
@ -305,20 +302,22 @@ if __name__ == '__main__':
|
|||
|
||||
'--command' accepts a JSON array of commands. Each command presents
|
||||
an application under test with all its paramaters as a list of strings,
|
||||
e.g.
|
||||
["qemu-io", "$test_img", "-c", "write $off $len"]
|
||||
e.g. ["qemu-io", "$test_img", "-c", "write $off $len"].
|
||||
|
||||
Supported application aliases: 'qemu-img' and 'qemu-io'.
|
||||
|
||||
Supported argument aliases: $test_img for the fuzzed image, $off
|
||||
for an offset, $len for length.
|
||||
|
||||
Values for $off and $len will be generated based on the virtual disk
|
||||
size of the fuzzed image
|
||||
size of the fuzzed image.
|
||||
|
||||
Paths to 'qemu-img' and 'qemu-io' are retrevied from 'QEMU_IMG' and
|
||||
'QEMU_IO' environment variables
|
||||
'QEMU_IO' environment variables.
|
||||
|
||||
'--config' accepts a JSON array of fields to be fuzzed, e.g.
|
||||
'[["header"], ["header", "version"]]'
|
||||
'[["header"], ["header", "version"]]'.
|
||||
|
||||
Each of the list elements can consist of a complex image element only
|
||||
as ["header"] or ["feature_name_table"] or an exact field as
|
||||
["header", "version"]. In the first case random portion of the element
|
||||
|
@ -368,7 +367,6 @@ if __name__ == '__main__':
|
|||
seed = None
|
||||
config = None
|
||||
duration = None
|
||||
|
||||
for opt, arg in opts:
|
||||
if opt in ('-h', '--help'):
|
||||
usage()
|
||||
|
|
|
@ -71,6 +71,12 @@ void qpci_device_enable(QPCIDevice *dev)
|
|||
cmd = qpci_config_readw(dev, PCI_COMMAND);
|
||||
cmd |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER;
|
||||
qpci_config_writew(dev, PCI_COMMAND, cmd);
|
||||
|
||||
/* Verify the bits are now set. */
|
||||
cmd = qpci_config_readw(dev, PCI_COMMAND);
|
||||
g_assert_cmphex(cmd & PCI_COMMAND_IO, ==, PCI_COMMAND_IO);
|
||||
g_assert_cmphex(cmd & PCI_COMMAND_MEMORY, ==, PCI_COMMAND_MEMORY);
|
||||
g_assert_cmphex(cmd & PCI_COMMAND_MASTER, ==, PCI_COMMAND_MASTER);
|
||||
}
|
||||
|
||||
uint8_t qpci_find_capability(QPCIDevice *dev, uint8_t id)
|
||||
|
|
|
@ -169,9 +169,61 @@ echo "=== Testing unallocated image header ==="
|
|||
echo
|
||||
_make_test_img 64M
|
||||
# Create L1/L2
|
||||
$QEMU_IO -c "$OPEN_RW" -c "write 0 64k" | _filter_qemu_io
|
||||
$QEMU_IO -c "write 0 64k" "$TEST_IMG" | _filter_qemu_io
|
||||
poke_file "$TEST_IMG" "$rb_offset" "\x00\x00"
|
||||
$QEMU_IO -c "$OPEN_RW" -c "write 64k 64k" | _filter_qemu_io
|
||||
$QEMU_IO -c "write 64k 64k" "$TEST_IMG" | _filter_qemu_io
|
||||
|
||||
echo
|
||||
echo "=== Testing unaligned L1 entry ==="
|
||||
echo
|
||||
_make_test_img 64M
|
||||
$QEMU_IO -c "write 0 64k" "$TEST_IMG" | _filter_qemu_io
|
||||
# This will be masked with ~(512 - 1) = ~0x1ff, so whether the lower 9 bits are
|
||||
# aligned or not does not matter
|
||||
poke_file "$TEST_IMG" "$l1_offset" "\x80\x00\x00\x00\x00\x04\x2a\x00"
|
||||
$QEMU_IO -c "read 0 64k" "$TEST_IMG" | _filter_qemu_io
|
||||
|
||||
echo
|
||||
echo "=== Testing unaligned L2 entry ==="
|
||||
echo
|
||||
_make_test_img 64M
|
||||
$QEMU_IO -c "write 0 64k" "$TEST_IMG" | _filter_qemu_io
|
||||
poke_file "$TEST_IMG" "$l2_offset" "\x80\x00\x00\x00\x00\x05\x2a\x00"
|
||||
$QEMU_IO -c "read 0 64k" "$TEST_IMG" | _filter_qemu_io
|
||||
|
||||
echo
|
||||
echo "=== Testing unaligned reftable entry ==="
|
||||
echo
|
||||
_make_test_img 64M
|
||||
poke_file "$TEST_IMG" "$rt_offset" "\x00\x00\x00\x00\x00\x02\x2a\x00"
|
||||
$QEMU_IO -c "write 0 64k" "$TEST_IMG" | _filter_qemu_io
|
||||
|
||||
echo
|
||||
echo "=== Testing non-fatal corruption on freeing ==="
|
||||
echo
|
||||
_make_test_img 64M
|
||||
$QEMU_IO -c "write 0 64k" "$TEST_IMG" | _filter_qemu_io
|
||||
poke_file "$TEST_IMG" "$l2_offset" "\x80\x00\x00\x00\x00\x05\x2a\x00"
|
||||
$QEMU_IO -c "discard 0 64k" "$TEST_IMG" | _filter_qemu_io
|
||||
|
||||
echo
|
||||
echo "=== Testing read-only corruption report ==="
|
||||
echo
|
||||
_make_test_img 64M
|
||||
$QEMU_IO -c "write 0 64k" "$TEST_IMG" | _filter_qemu_io
|
||||
poke_file "$TEST_IMG" "$l2_offset" "\x80\x00\x00\x00\x00\x05\x2a\x00"
|
||||
# Should only emit a single error message
|
||||
$QEMU_IO -c "$OPEN_RO" -c "read 0 64k" -c "read 0 64k" | _filter_qemu_io
|
||||
|
||||
echo
|
||||
echo "=== Testing non-fatal and then fatal corruption report ==="
|
||||
echo
|
||||
_make_test_img 64M
|
||||
$QEMU_IO -c "write 0 128k" "$TEST_IMG" | _filter_qemu_io
|
||||
poke_file "$TEST_IMG" "$l2_offset" "\x80\x00\x00\x00\x00\x05\x2a\x00"
|
||||
poke_file "$TEST_IMG" "$(($l2_offset+8))" "\x80\x00\x00\x00\x00\x06\x2a\x00"
|
||||
# Should emit two error messages
|
||||
$QEMU_IO -c "discard 0 64k" -c "read 64k 64k" "$TEST_IMG" | _filter_qemu_io
|
||||
|
||||
# success, all done
|
||||
echo "*** done"
|
||||
|
|
|
@ -8,7 +8,7 @@ ERROR cluster 3 refcount=1 reference=3
|
|||
1 errors were found on the image.
|
||||
Data may be corrupted, or further writes to the image may corrupt it.
|
||||
incompatible_features 0x0
|
||||
qcow2: Preventing invalid write on metadata (overlaps with active L1 table); image marked as corrupt.
|
||||
qcow2: Marking image as corrupt: Preventing invalid write on metadata (overlaps with active L1 table); further corruption events will be suppressed
|
||||
write failed: Input/output error
|
||||
incompatible_features 0x2
|
||||
qemu-io: can't open device TEST_DIR/t.IMGFMT: IMGFMT: Image is corrupt; cannot be opened read/write
|
||||
|
@ -24,7 +24,7 @@ ERROR cluster 2 refcount=1 reference=2
|
|||
2 errors were found on the image.
|
||||
Data may be corrupted, or further writes to the image may corrupt it.
|
||||
incompatible_features 0x0
|
||||
qcow2: Preventing invalid write on metadata (overlaps with refcount block); image marked as corrupt.
|
||||
qcow2: Marking image as corrupt: Preventing invalid write on metadata (overlaps with refcount block); further corruption events will be suppressed
|
||||
write failed: Input/output error
|
||||
incompatible_features 0x2
|
||||
Repairing refcount block 0 refcount=2
|
||||
|
@ -56,7 +56,7 @@ Data may be corrupted, or further writes to the image may corrupt it.
|
|||
1 leaked clusters were found on the image.
|
||||
This means waste of disk space, but no harm to data.
|
||||
incompatible_features 0x0
|
||||
qcow2: Preventing invalid write on metadata (overlaps with inactive L2 table); image marked as corrupt.
|
||||
qcow2: Marking image as corrupt: Preventing invalid write on metadata (overlaps with inactive L2 table); further corruption events will be suppressed
|
||||
write failed: Input/output error
|
||||
incompatible_features 0x2
|
||||
Repairing cluster 4 refcount=1 reference=2
|
||||
|
@ -88,7 +88,7 @@ wrote 65536/65536 bytes at offset 536870912
|
|||
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
discard 65536/65536 bytes at offset 0
|
||||
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qcow2: Preventing invalid write on metadata (overlaps with active L2 table); image marked as corrupt.
|
||||
qcow2: Marking image as corrupt: Preventing invalid write on metadata (overlaps with active L2 table); further corruption events will be suppressed
|
||||
blkdebug: Suspended request '0'
|
||||
write failed: Input/output error
|
||||
blkdebug: Resuming request '0'
|
||||
|
@ -99,6 +99,57 @@ aio_write failed: No medium found
|
|||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
|
||||
wrote 65536/65536 bytes at offset 0
|
||||
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qcow2: Preventing invalid write on metadata (overlaps with qcow2_header); image marked as corrupt.
|
||||
qcow2: Marking image as corrupt: Preventing invalid write on metadata (overlaps with qcow2_header); further corruption events will be suppressed
|
||||
write failed: Input/output error
|
||||
|
||||
=== Testing unaligned L1 entry ===
|
||||
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
|
||||
wrote 65536/65536 bytes at offset 0
|
||||
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qcow2: Marking image as corrupt: L2 table offset 0x42a00 unaligned (L1 index: 0); further corruption events will be suppressed
|
||||
read failed: Input/output error
|
||||
|
||||
=== Testing unaligned L2 entry ===
|
||||
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
|
||||
wrote 65536/65536 bytes at offset 0
|
||||
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qcow2: Marking image as corrupt: Data cluster offset 0x52a00 unaligned (L2 offset: 0x40000, L2 index: 0); further corruption events will be suppressed
|
||||
read failed: Input/output error
|
||||
|
||||
=== Testing unaligned reftable entry ===
|
||||
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
|
||||
qcow2: Marking image as corrupt: Refblock offset 0x22a00 unaligned (reftable index: 0); further corruption events will be suppressed
|
||||
write failed: Input/output error
|
||||
|
||||
=== Testing non-fatal corruption on freeing ===
|
||||
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
|
||||
wrote 65536/65536 bytes at offset 0
|
||||
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qcow2: Image is corrupt: Cannot free unaligned cluster 0x52a00; further non-fatal corruption events will be suppressed
|
||||
discard 65536/65536 bytes at offset 0
|
||||
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
|
||||
=== Testing read-only corruption report ===
|
||||
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
|
||||
wrote 65536/65536 bytes at offset 0
|
||||
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qcow2: Image is corrupt: Data cluster offset 0x52a00 unaligned (L2 offset: 0x40000, L2 index: 0); further non-fatal corruption events will be suppressed
|
||||
read failed: Input/output error
|
||||
read failed: Input/output error
|
||||
|
||||
=== Testing non-fatal and then fatal corruption report ===
|
||||
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
|
||||
wrote 131072/131072 bytes at offset 0
|
||||
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qcow2: Image is corrupt: Cannot free unaligned cluster 0x52a00; further non-fatal corruption events will be suppressed
|
||||
qcow2: Marking image as corrupt: Data cluster offset 0x62a00 unaligned (L2 offset: 0x40000, L2 index: 0x1); further corruption events will be suppressed
|
||||
discard 65536/65536 bytes at offset 0
|
||||
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
read failed: Input/output error
|
||||
*** done
|
||||
|
|
|
@ -38,7 +38,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
|||
. ./common.rc
|
||||
. ./common.filter
|
||||
|
||||
_supported_fmt cow qed qcow qcow2 vmdk
|
||||
_supported_fmt qed qcow qcow2 vmdk
|
||||
_supported_proto file
|
||||
_supported_os Linux
|
||||
_unsupported_imgopts "subformat=monolithicFlat" "subformat=twoGbMaxExtentFlat"
|
||||
|
|
|
@ -38,7 +38,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
|||
. ./common.rc
|
||||
. ./common.filter
|
||||
|
||||
_supported_fmt vpc vmdk vhdx vdi qed qcow2 qcow cow
|
||||
_supported_fmt vpc vmdk vhdx vdi qed qcow2 qcow
|
||||
_supported_proto file
|
||||
_supported_os Linux
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
|||
|
||||
# Basically all formats, but "raw" has issues with _filter_imgfmt regarding the
|
||||
# raw comparison image for blkverify; also, all images have to support creation
|
||||
_supported_fmt cow qcow qcow2 qed vdi vhdx vmdk vpc
|
||||
_supported_fmt qcow qcow2 qed vdi vhdx vmdk vpc
|
||||
_supported_proto file
|
||||
_supported_os Linux
|
||||
|
||||
|
|
|
@ -136,7 +136,6 @@ common options
|
|||
check options
|
||||
-raw test raw (default)
|
||||
-bochs test bochs
|
||||
-cow test cow
|
||||
-cloop test cloop
|
||||
-parallels test parallels
|
||||
-qcow test qcow
|
||||
|
@ -182,11 +181,6 @@ testlist options
|
|||
xpand=false
|
||||
;;
|
||||
|
||||
-cow)
|
||||
IMGFMT=cow
|
||||
xpand=false
|
||||
;;
|
||||
|
||||
-cloop)
|
||||
IMGFMT=cloop
|
||||
IMGFMT_GENERIC=false
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include "block/aio.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "qemu/sockets.h"
|
||||
#include "qemu/error-report.h"
|
||||
|
||||
static AioContext *ctx;
|
||||
|
||||
|
@ -810,11 +811,18 @@ static void test_source_timer_schedule(void)
|
|||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
Error *local_error = NULL;
|
||||
GSource *src;
|
||||
|
||||
init_clocks();
|
||||
|
||||
ctx = aio_context_new();
|
||||
ctx = aio_context_new(&local_error);
|
||||
if (!ctx) {
|
||||
error_report("Failed to create AIO Context: '%s'",
|
||||
error_get_pretty(local_error));
|
||||
error_free(local_error);
|
||||
exit(1);
|
||||
}
|
||||
src = aio_get_g_source(ctx);
|
||||
g_source_attach(src, NULL);
|
||||
g_source_unref(src);
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "block/thread-pool.h"
|
||||
#include "block/block.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "qemu/error-report.h"
|
||||
|
||||
static AioContext *ctx;
|
||||
static ThreadPool *pool;
|
||||
|
@ -33,7 +34,7 @@ static int long_cb(void *opaque)
|
|||
static void done_cb(void *opaque, int ret)
|
||||
{
|
||||
WorkerTestData *data = opaque;
|
||||
g_assert_cmpint(data->ret, ==, -EINPROGRESS);
|
||||
g_assert(data->ret == -EINPROGRESS || data->ret == -ECANCELED);
|
||||
data->ret = ret;
|
||||
data->aiocb = NULL;
|
||||
|
||||
|
@ -132,7 +133,7 @@ static void test_submit_many(void)
|
|||
}
|
||||
}
|
||||
|
||||
static void test_cancel(void)
|
||||
static void do_test_cancel(bool sync)
|
||||
{
|
||||
WorkerTestData data[100];
|
||||
int num_canceled;
|
||||
|
@ -170,18 +171,25 @@ static void test_cancel(void)
|
|||
for (i = 0; i < 100; i++) {
|
||||
if (atomic_cmpxchg(&data[i].n, 0, 3) == 0) {
|
||||
data[i].ret = -ECANCELED;
|
||||
bdrv_aio_cancel(data[i].aiocb);
|
||||
active--;
|
||||
if (sync) {
|
||||
bdrv_aio_cancel(data[i].aiocb);
|
||||
} else {
|
||||
bdrv_aio_cancel_async(data[i].aiocb);
|
||||
}
|
||||
num_canceled++;
|
||||
}
|
||||
}
|
||||
g_assert_cmpint(active, >, 0);
|
||||
g_assert_cmpint(num_canceled, <, 100);
|
||||
|
||||
/* Canceling the others will be a blocking operation. */
|
||||
for (i = 0; i < 100; i++) {
|
||||
if (data[i].aiocb && data[i].n != 3) {
|
||||
bdrv_aio_cancel(data[i].aiocb);
|
||||
if (sync) {
|
||||
/* Canceling the others will be a blocking operation. */
|
||||
bdrv_aio_cancel(data[i].aiocb);
|
||||
} else {
|
||||
bdrv_aio_cancel_async(data[i].aiocb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -193,22 +201,39 @@ static void test_cancel(void)
|
|||
for (i = 0; i < 100; i++) {
|
||||
if (data[i].n == 3) {
|
||||
g_assert_cmpint(data[i].ret, ==, -ECANCELED);
|
||||
g_assert(data[i].aiocb != NULL);
|
||||
g_assert(data[i].aiocb == NULL);
|
||||
} else {
|
||||
g_assert_cmpint(data[i].n, ==, 2);
|
||||
g_assert_cmpint(data[i].ret, ==, 0);
|
||||
g_assert(data[i].ret == 0 || data[i].ret == -ECANCELED);
|
||||
g_assert(data[i].aiocb == NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void test_cancel(void)
|
||||
{
|
||||
do_test_cancel(true);
|
||||
}
|
||||
|
||||
static void test_cancel_async(void)
|
||||
{
|
||||
do_test_cancel(false);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int ret;
|
||||
Error *local_error = NULL;
|
||||
|
||||
init_clocks();
|
||||
|
||||
ctx = aio_context_new();
|
||||
ctx = aio_context_new(&local_error);
|
||||
if (!ctx) {
|
||||
error_report("Failed to create AIO Context: '%s'",
|
||||
error_get_pretty(local_error));
|
||||
error_free(local_error);
|
||||
exit(1);
|
||||
}
|
||||
pool = aio_get_thread_pool(ctx);
|
||||
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
|
@ -217,6 +242,7 @@ int main(int argc, char **argv)
|
|||
g_test_add_func("/thread-pool/submit-co", test_submit_co);
|
||||
g_test_add_func("/thread-pool/submit-many", test_submit_many);
|
||||
g_test_add_func("/thread-pool/cancel", test_cancel);
|
||||
g_test_add_func("/thread-pool/cancel-async", test_cancel_async);
|
||||
|
||||
ret = g_test_run();
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <math.h>
|
||||
#include "block/aio.h"
|
||||
#include "qemu/throttle.h"
|
||||
#include "qemu/error-report.h"
|
||||
|
||||
static AioContext *ctx;
|
||||
static LeakyBucket bkt;
|
||||
|
@ -492,10 +493,17 @@ static void test_accounting(void)
|
|||
int main(int argc, char **argv)
|
||||
{
|
||||
GSource *src;
|
||||
Error *local_error = NULL;
|
||||
|
||||
init_clocks();
|
||||
|
||||
ctx = aio_context_new();
|
||||
ctx = aio_context_new(&local_error);
|
||||
if (!ctx) {
|
||||
error_report("Failed to create AIO Context: '%s'",
|
||||
error_get_pretty(local_error));
|
||||
error_free(local_error);
|
||||
exit(1);
|
||||
}
|
||||
src = aio_get_g_source(ctx);
|
||||
g_source_attach(src, NULL);
|
||||
g_source_unref(src);
|
||||
|
|
|
@ -31,7 +31,6 @@ enum ThreadState {
|
|||
THREAD_QUEUED,
|
||||
THREAD_ACTIVE,
|
||||
THREAD_DONE,
|
||||
THREAD_CANCELED,
|
||||
};
|
||||
|
||||
struct ThreadPoolElement {
|
||||
|
@ -58,7 +57,6 @@ struct ThreadPool {
|
|||
AioContext *ctx;
|
||||
QEMUBH *completion_bh;
|
||||
QemuMutex lock;
|
||||
QemuCond check_cancel;
|
||||
QemuCond worker_stopped;
|
||||
QemuSemaphore sem;
|
||||
int max_threads;
|
||||
|
@ -73,7 +71,6 @@ struct ThreadPool {
|
|||
int idle_threads;
|
||||
int new_threads; /* backlog of threads we need to create */
|
||||
int pending_threads; /* threads created but not running yet */
|
||||
int pending_cancellations; /* whether we need a cond_broadcast */
|
||||
bool stopping;
|
||||
};
|
||||
|
||||
|
@ -113,9 +110,6 @@ static void *worker_thread(void *opaque)
|
|||
req->state = THREAD_DONE;
|
||||
|
||||
qemu_mutex_lock(&pool->lock);
|
||||
if (pool->pending_cancellations) {
|
||||
qemu_cond_broadcast(&pool->check_cancel);
|
||||
}
|
||||
|
||||
qemu_bh_schedule(pool->completion_bh);
|
||||
}
|
||||
|
@ -173,7 +167,7 @@ static void thread_pool_completion_bh(void *opaque)
|
|||
|
||||
restart:
|
||||
QLIST_FOREACH_SAFE(elem, &pool->head, all, next) {
|
||||
if (elem->state != THREAD_CANCELED && elem->state != THREAD_DONE) {
|
||||
if (elem->state != THREAD_DONE) {
|
||||
continue;
|
||||
}
|
||||
if (elem->state == THREAD_DONE) {
|
||||
|
@ -191,12 +185,12 @@ restart:
|
|||
qemu_bh_schedule(pool->completion_bh);
|
||||
|
||||
elem->common.cb(elem->common.opaque, elem->ret);
|
||||
qemu_aio_release(elem);
|
||||
qemu_aio_unref(elem);
|
||||
goto restart;
|
||||
} else {
|
||||
/* remove the request */
|
||||
QLIST_REMOVE(elem, all);
|
||||
qemu_aio_release(elem);
|
||||
qemu_aio_unref(elem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -217,22 +211,26 @@ static void thread_pool_cancel(BlockDriverAIOCB *acb)
|
|||
*/
|
||||
qemu_sem_timedwait(&pool->sem, 0) == 0) {
|
||||
QTAILQ_REMOVE(&pool->request_list, elem, reqs);
|
||||
elem->state = THREAD_CANCELED;
|
||||
qemu_bh_schedule(pool->completion_bh);
|
||||
} else {
|
||||
pool->pending_cancellations++;
|
||||
while (elem->state != THREAD_CANCELED && elem->state != THREAD_DONE) {
|
||||
qemu_cond_wait(&pool->check_cancel, &pool->lock);
|
||||
}
|
||||
pool->pending_cancellations--;
|
||||
|
||||
elem->state = THREAD_DONE;
|
||||
elem->ret = -ECANCELED;
|
||||
}
|
||||
|
||||
qemu_mutex_unlock(&pool->lock);
|
||||
thread_pool_completion_bh(pool);
|
||||
}
|
||||
|
||||
static AioContext *thread_pool_get_aio_context(BlockDriverAIOCB *acb)
|
||||
{
|
||||
ThreadPoolElement *elem = (ThreadPoolElement *)acb;
|
||||
ThreadPool *pool = elem->pool;
|
||||
return pool->ctx;
|
||||
}
|
||||
|
||||
static const AIOCBInfo thread_pool_aiocb_info = {
|
||||
.aiocb_size = sizeof(ThreadPoolElement),
|
||||
.cancel = thread_pool_cancel,
|
||||
.cancel_async = thread_pool_cancel,
|
||||
.get_aio_context = thread_pool_get_aio_context,
|
||||
};
|
||||
|
||||
BlockDriverAIOCB *thread_pool_submit_aio(ThreadPool *pool,
|
||||
|
@ -299,7 +297,6 @@ static void thread_pool_init_one(ThreadPool *pool, AioContext *ctx)
|
|||
pool->ctx = ctx;
|
||||
pool->completion_bh = aio_bh_new(ctx, thread_pool_completion_bh, pool);
|
||||
qemu_mutex_init(&pool->lock);
|
||||
qemu_cond_init(&pool->check_cancel);
|
||||
qemu_cond_init(&pool->worker_stopped);
|
||||
qemu_sem_init(&pool->sem, 0);
|
||||
pool->max_threads = 64;
|
||||
|
@ -342,7 +339,6 @@ void thread_pool_free(ThreadPool *pool)
|
|||
|
||||
qemu_bh_delete(pool->completion_bh);
|
||||
qemu_sem_destroy(&pool->sem);
|
||||
qemu_cond_destroy(&pool->check_cancel);
|
||||
qemu_cond_destroy(&pool->worker_stopped);
|
||||
qemu_mutex_destroy(&pool->lock);
|
||||
g_free(pool);
|
||||
|
|
|
@ -857,7 +857,7 @@ QemuCocoaView *cocoaView;
|
|||
[op setPrompt:@"Boot image"];
|
||||
[op setMessage:@"Select the disk image you want to boot.\n\nHit the \"Cancel\" button to quit"];
|
||||
NSArray *filetypes = [NSArray arrayWithObjects:@"img", @"iso", @"dmg",
|
||||
@"qcow", @"qcow2", @"cow", @"cloop", @"vmdk", nil];
|
||||
@"qcow", @"qcow2", @"cloop", @"vmdk", nil];
|
||||
#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6)
|
||||
[op setAllowedFileTypes:filetypes];
|
||||
[op beginSheetModalForWindow:normalWindow
|
||||
|
|
5
vl.c
5
vl.c
|
@ -2968,6 +2968,7 @@ int main(int argc, char **argv, char **envp)
|
|||
ram_addr_t maxram_size = default_ram_size;
|
||||
uint64_t ram_slots = 0;
|
||||
FILE *vmstate_dump_file = NULL;
|
||||
Error *main_loop_err = NULL;
|
||||
|
||||
atexit(qemu_run_exit_notifiers);
|
||||
error_set_progname(argv[0]);
|
||||
|
@ -3998,8 +3999,8 @@ int main(int argc, char **argv, char **envp)
|
|||
|
||||
os_daemonize();
|
||||
|
||||
if (qemu_init_main_loop()) {
|
||||
fprintf(stderr, "qemu_init_main_loop failed\n");
|
||||
if (qemu_init_main_loop(&main_loop_err)) {
|
||||
error_report("%s", error_get_pretty(main_loop_err));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue