-----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:
Peter Maydell 2014-09-23 12:08:55 +01:00
commit 380f649e02
72 changed files with 2810 additions and 950 deletions

View File

@ -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
View File

@ -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
View File

@ -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);
}
}
/**************************************************************/

View File

@ -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

View File

@ -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);

View File

@ -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,

View File

@ -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)

View File

@ -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);

View File

@ -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;

View File

@ -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;
}

View File

@ -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;
}

168
block/null.c Normal file
View File

@ -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);

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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),

View File

@ -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);

View File

@ -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;

View File

@ -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)

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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:

View File

@ -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;
}

21
configure vendored
View File

@ -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

View File

@ -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);

View File

@ -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
View File

@ -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)

View File

@ -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/

View File

@ -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(),
};

View File

@ -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);

View File

@ -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;
}

View File

@ -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)) {

View File

@ -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;
}

View File

@ -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"

View File

@ -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

View File

@ -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 */

View File

@ -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:

View File

@ -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 */

View File

@ -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 {

View File

@ -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 {

View File

@ -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 */

View File

@ -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.

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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

View File

@ -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;

View File

@ -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:

View File

@ -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");

View File

@ -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

View File

@ -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 */

View File

@ -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);

View File

@ -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",

View File

@ -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"

View File

@ -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('''

View File

@ -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)

1561
tests/ahci-test.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -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)

View File

@ -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

View File

@ -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()

View File

@ -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)

View File

@ -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"

View File

@ -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

View File

@ -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"

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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();

View File

@ -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);

View File

@ -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);

View File

@ -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
View File

@ -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);
}