Compare commits
65 Commits
master
...
stable-2.3
Author | SHA1 | Date |
---|---|---|
Michael Roth | dfa83a6bae | |
Paolo Bonzini | 35a616edef | |
Stefan Hajnoczi | 35c30d3efd | |
Stefan Hajnoczi | f4c861fd68 | |
Stefan Hajnoczi | b7a197c39e | |
Stefan Hajnoczi | 85611098ff | |
Stefan Hajnoczi | ce4f451bbb | |
Stefan Hajnoczi | 6722c126f3 | |
Stefan Hajnoczi | 8dd45dcd83 | |
Aurelien Jarno | e750591c8a | |
Aurelien Jarno | f9c0ae2723 | |
Stefano Stabellini | c8bd74d1d5 | |
Kevin Wolf | d1557697fd | |
Kevin Wolf | 86d6fe4cb0 | |
Kevin Wolf | 9634e45e0b | |
Jeff Cody | 0dc545e977 | |
Jeff Cody | 358f0ee234 | |
Paolo Bonzini | 961c74a841 | |
Alex Williamson | 98fe91ed66 | |
Jason Wang | 46addaa0b5 | |
Alex Williamson | 5a4568717c | |
James Hogan | 87740cecc3 | |
James Hogan | 8df2a9acd2 | |
Fam Zheng | c5c71e87aa | |
马文霜 | 2060efae47 | |
Michael Roth | 8d64975c98 | |
David Gibson | 9b4420ad62 | |
Christian Borntraeger | 99c3468d8f | |
Peter Lieven | 1c17e8c7d3 | |
John Snow | ffd060d51f | |
Fam Zheng | e4fb4bea37 | |
Fam Zheng | edc0a65326 | |
Fam Zheng | c62f6c8f67 | |
Fam Zheng | 3d8b7aed60 | |
Fam Zheng | 27ed14c4d7 | |
Fam Zheng | 6a45a1b8e4 | |
Fam Zheng | 6cacd2651a | |
Cornelia Huck | e8248a5af1 | |
Jason Wang | 81cb0a5657 | |
Laszlo Ersek | 6130c46232 | |
Petr Matousek | 49ef542e41 | |
Gerd Hoffmann | c270245a53 | |
Alberto Garcia | 9272707a1f | |
Fam Zheng | c759f1a078 | |
Fam Zheng | 714b54401c | |
Max Reitz | e7e08380c3 | |
Max Reitz | c631ee6520 | |
Gerd Hoffmann | b153c8d3f3 | |
Gerd Hoffmann | f45048225a | |
Justin Ossevoort | ae0fa48f51 | |
Shannon Zhao | bb3a1da4d4 | |
Fam Zheng | b48a391cff | |
Fam Zheng | cc883fe42d | |
Michael Roth | 4072585ecf | |
Petr Matousek | 959fad0ff1 | |
Peter Maydell | a4bb522ee5 | |
Jason Wang | cf6c213981 | |
Michal Kazior | cf3297868c | |
Kevin Wolf | ad9c167fd2 | |
Fam Zheng | d8e231fce2 | |
Fam Zheng | 53cd79c117 | |
Bogdan Purcareata | 3dd15f3e58 | |
Ján Tomko | 4c59860506 | |
Peter Lieven | b575af0730 | |
Stefan Hajnoczi | d3b59789e8 |
215
block.c
215
block.c
|
@ -99,8 +99,6 @@ static QLIST_HEAD(, BlockDriver) bdrv_drivers =
|
|||
|
||||
static void bdrv_set_dirty(BlockDriverState *bs, int64_t cur_sector,
|
||||
int nr_sectors);
|
||||
static void bdrv_reset_dirty(BlockDriverState *bs, int64_t cur_sector,
|
||||
int nr_sectors);
|
||||
/* If non-zero, use only whitelisted block drivers */
|
||||
static int use_bdrv_whitelist;
|
||||
|
||||
|
@ -1382,7 +1380,7 @@ int bdrv_append_temp_snapshot(BlockDriverState *bs, int flags, Error **errp)
|
|||
QemuOpts *opts = NULL;
|
||||
QDict *snapshot_options;
|
||||
BlockDriverState *bs_snapshot;
|
||||
Error *local_err;
|
||||
Error *local_err = NULL;
|
||||
int ret;
|
||||
|
||||
/* if snapshot, we create a temporary backing file and open it
|
||||
|
@ -3118,19 +3116,6 @@ out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static inline uint64_t bdrv_get_align(BlockDriverState *bs)
|
||||
{
|
||||
/* TODO Lift BDRV_SECTOR_SIZE restriction in BlockDriver interface */
|
||||
return MAX(BDRV_SECTOR_SIZE, bs->request_alignment);
|
||||
}
|
||||
|
||||
static inline bool bdrv_req_is_aligned(BlockDriverState *bs,
|
||||
int64_t offset, size_t bytes)
|
||||
{
|
||||
int64_t align = bdrv_get_align(bs);
|
||||
return !(offset & (align - 1) || (bytes & (align - 1)));
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle a read request in coroutine context
|
||||
*/
|
||||
|
@ -3141,7 +3126,8 @@ static int coroutine_fn bdrv_co_do_preadv(BlockDriverState *bs,
|
|||
BlockDriver *drv = bs->drv;
|
||||
BdrvTrackedRequest req;
|
||||
|
||||
uint64_t align = bdrv_get_align(bs);
|
||||
/* TODO Lift BDRV_SECTOR_SIZE restriction in BlockDriver interface */
|
||||
uint64_t align = MAX(BDRV_SECTOR_SIZE, bs->request_alignment);
|
||||
uint8_t *head_buf = NULL;
|
||||
uint8_t *tail_buf = NULL;
|
||||
QEMUIOVector local_qiov;
|
||||
|
@ -3375,6 +3361,94 @@ static int coroutine_fn bdrv_aligned_pwritev(BlockDriverState *bs,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int coroutine_fn bdrv_co_do_zero_pwritev(BlockDriverState *bs,
|
||||
int64_t offset,
|
||||
unsigned int bytes,
|
||||
BdrvRequestFlags flags,
|
||||
BdrvTrackedRequest *req)
|
||||
{
|
||||
uint8_t *buf = NULL;
|
||||
QEMUIOVector local_qiov;
|
||||
struct iovec iov;
|
||||
uint64_t align = MAX(BDRV_SECTOR_SIZE, bs->request_alignment);
|
||||
unsigned int head_padding_bytes, tail_padding_bytes;
|
||||
int ret = 0;
|
||||
|
||||
head_padding_bytes = offset & (align - 1);
|
||||
tail_padding_bytes = align - ((offset + bytes) & (align - 1));
|
||||
|
||||
|
||||
assert(flags & BDRV_REQ_ZERO_WRITE);
|
||||
if (head_padding_bytes || tail_padding_bytes) {
|
||||
buf = qemu_blockalign(bs, align);
|
||||
iov = (struct iovec) {
|
||||
.iov_base = buf,
|
||||
.iov_len = align,
|
||||
};
|
||||
qemu_iovec_init_external(&local_qiov, &iov, 1);
|
||||
}
|
||||
if (head_padding_bytes) {
|
||||
uint64_t zero_bytes = MIN(bytes, align - head_padding_bytes);
|
||||
|
||||
/* RMW the unaligned part before head. */
|
||||
mark_request_serialising(req, align);
|
||||
wait_serialising_requests(req);
|
||||
BLKDBG_EVENT(bs, BLKDBG_PWRITEV_RMW_HEAD);
|
||||
ret = bdrv_aligned_preadv(bs, req, offset & ~(align - 1), align,
|
||||
align, &local_qiov, 0);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
BLKDBG_EVENT(bs, BLKDBG_PWRITEV_RMW_AFTER_HEAD);
|
||||
|
||||
memset(buf + head_padding_bytes, 0, zero_bytes);
|
||||
ret = bdrv_aligned_pwritev(bs, req, offset & ~(align - 1), align,
|
||||
&local_qiov,
|
||||
flags & ~BDRV_REQ_ZERO_WRITE);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
offset += zero_bytes;
|
||||
bytes -= zero_bytes;
|
||||
}
|
||||
|
||||
assert(!bytes || (offset & (align - 1)) == 0);
|
||||
if (bytes >= align) {
|
||||
/* Write the aligned part in the middle. */
|
||||
uint64_t aligned_bytes = bytes & ~(align - 1);
|
||||
ret = bdrv_aligned_pwritev(bs, req, offset, aligned_bytes,
|
||||
NULL, flags);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
bytes -= aligned_bytes;
|
||||
offset += aligned_bytes;
|
||||
}
|
||||
|
||||
assert(!bytes || (offset & (align - 1)) == 0);
|
||||
if (bytes) {
|
||||
assert(align == tail_padding_bytes + bytes);
|
||||
/* RMW the unaligned part after tail. */
|
||||
mark_request_serialising(req, align);
|
||||
wait_serialising_requests(req);
|
||||
BLKDBG_EVENT(bs, BLKDBG_PWRITEV_RMW_TAIL);
|
||||
ret = bdrv_aligned_preadv(bs, req, offset, align,
|
||||
align, &local_qiov, 0);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
BLKDBG_EVENT(bs, BLKDBG_PWRITEV_RMW_AFTER_TAIL);
|
||||
|
||||
memset(buf, 0, bytes);
|
||||
ret = bdrv_aligned_pwritev(bs, req, offset, align,
|
||||
&local_qiov, flags & ~BDRV_REQ_ZERO_WRITE);
|
||||
}
|
||||
fail:
|
||||
qemu_vfree(buf);
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle a write request in coroutine context
|
||||
*/
|
||||
|
@ -3383,7 +3457,8 @@ static int coroutine_fn bdrv_co_do_pwritev(BlockDriverState *bs,
|
|||
BdrvRequestFlags flags)
|
||||
{
|
||||
BdrvTrackedRequest req;
|
||||
uint64_t align = bdrv_get_align(bs);
|
||||
/* TODO Lift BDRV_SECTOR_SIZE restriction in BlockDriver interface */
|
||||
uint64_t align = MAX(BDRV_SECTOR_SIZE, bs->request_alignment);
|
||||
uint8_t *head_buf = NULL;
|
||||
uint8_t *tail_buf = NULL;
|
||||
QEMUIOVector local_qiov;
|
||||
|
@ -3414,6 +3489,11 @@ static int coroutine_fn bdrv_co_do_pwritev(BlockDriverState *bs,
|
|||
*/
|
||||
tracked_request_begin(&req, bs, offset, bytes, true);
|
||||
|
||||
if (!qiov) {
|
||||
ret = bdrv_co_do_zero_pwritev(bs, offset, bytes, flags, &req);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (offset & (align - 1)) {
|
||||
QEMUIOVector head_qiov;
|
||||
struct iovec head_iov;
|
||||
|
@ -3482,23 +3562,19 @@ static int coroutine_fn bdrv_co_do_pwritev(BlockDriverState *bs,
|
|||
bytes = ROUND_UP(bytes, align);
|
||||
}
|
||||
|
||||
if (use_local_qiov) {
|
||||
/* Local buffer may have non-zero data. */
|
||||
flags &= ~BDRV_REQ_ZERO_WRITE;
|
||||
}
|
||||
ret = bdrv_aligned_pwritev(bs, &req, offset, bytes,
|
||||
use_local_qiov ? &local_qiov : qiov,
|
||||
flags);
|
||||
|
||||
fail:
|
||||
tracked_request_end(&req);
|
||||
|
||||
if (use_local_qiov) {
|
||||
qemu_iovec_destroy(&local_qiov);
|
||||
}
|
||||
qemu_vfree(head_buf);
|
||||
qemu_vfree(tail_buf);
|
||||
|
||||
out:
|
||||
tracked_request_end(&req);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -3526,32 +3602,14 @@ int coroutine_fn bdrv_co_write_zeroes(BlockDriverState *bs,
|
|||
int64_t sector_num, int nb_sectors,
|
||||
BdrvRequestFlags flags)
|
||||
{
|
||||
int ret;
|
||||
|
||||
trace_bdrv_co_write_zeroes(bs, sector_num, nb_sectors, flags);
|
||||
|
||||
if (!(bs->open_flags & BDRV_O_UNMAP)) {
|
||||
flags &= ~BDRV_REQ_MAY_UNMAP;
|
||||
}
|
||||
if (bdrv_req_is_aligned(bs, sector_num << BDRV_SECTOR_BITS,
|
||||
nb_sectors << BDRV_SECTOR_BITS)) {
|
||||
ret = bdrv_co_do_writev(bs, sector_num, nb_sectors, NULL,
|
||||
BDRV_REQ_ZERO_WRITE | flags);
|
||||
} else {
|
||||
uint8_t *buf;
|
||||
QEMUIOVector local_qiov;
|
||||
size_t bytes = nb_sectors << BDRV_SECTOR_BITS;
|
||||
|
||||
buf = qemu_memalign(bdrv_opt_mem_align(bs), bytes);
|
||||
memset(buf, 0, bytes);
|
||||
qemu_iovec_init(&local_qiov, 1);
|
||||
qemu_iovec_add(&local_qiov, buf, bytes);
|
||||
|
||||
ret = bdrv_co_do_writev(bs, sector_num, nb_sectors, &local_qiov,
|
||||
BDRV_REQ_ZERO_WRITE | flags);
|
||||
qemu_vfree(buf);
|
||||
}
|
||||
return ret;
|
||||
return bdrv_co_do_writev(bs, sector_num, nb_sectors, NULL,
|
||||
BDRV_REQ_ZERO_WRITE | flags);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -4142,28 +4200,54 @@ static int64_t coroutine_fn bdrv_co_get_block_status(BlockDriverState *bs,
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* Coroutine wrapper for bdrv_get_block_status() */
|
||||
static void coroutine_fn bdrv_get_block_status_co_entry(void *opaque)
|
||||
static int64_t coroutine_fn bdrv_co_get_block_status_above(BlockDriverState *bs,
|
||||
BlockDriverState *base,
|
||||
int64_t sector_num,
|
||||
int nb_sectors,
|
||||
int *pnum)
|
||||
{
|
||||
BlockDriverState *p;
|
||||
int64_t ret = 0;
|
||||
|
||||
assert(bs != base);
|
||||
for (p = bs; p != base; p = p->backing_hd) {
|
||||
ret = bdrv_co_get_block_status(p, sector_num, nb_sectors, pnum);
|
||||
if (ret < 0 || ret & BDRV_BLOCK_ALLOCATED) {
|
||||
break;
|
||||
}
|
||||
/* [sector_num, pnum] unallocated on this layer, which could be only
|
||||
* the first part of [sector_num, nb_sectors]. */
|
||||
nb_sectors = MIN(nb_sectors, *pnum);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Coroutine wrapper for bdrv_get_block_status_above() */
|
||||
static void coroutine_fn bdrv_get_block_status_above_co_entry(void *opaque)
|
||||
{
|
||||
BdrvCoGetBlockStatusData *data = opaque;
|
||||
BlockDriverState *bs = data->bs;
|
||||
|
||||
data->ret = bdrv_co_get_block_status(bs, data->sector_num, data->nb_sectors,
|
||||
data->pnum);
|
||||
data->ret = bdrv_co_get_block_status_above(data->bs, data->base,
|
||||
data->sector_num,
|
||||
data->nb_sectors,
|
||||
data->pnum);
|
||||
data->done = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Synchronous wrapper around bdrv_co_get_block_status().
|
||||
* Synchronous wrapper around bdrv_co_get_block_status_above().
|
||||
*
|
||||
* See bdrv_co_get_block_status() for details.
|
||||
* See bdrv_co_get_block_status_above() for details.
|
||||
*/
|
||||
int64_t bdrv_get_block_status(BlockDriverState *bs, int64_t sector_num,
|
||||
int nb_sectors, int *pnum)
|
||||
int64_t bdrv_get_block_status_above(BlockDriverState *bs,
|
||||
BlockDriverState *base,
|
||||
int64_t sector_num,
|
||||
int nb_sectors, int *pnum)
|
||||
{
|
||||
Coroutine *co;
|
||||
BdrvCoGetBlockStatusData data = {
|
||||
.bs = bs,
|
||||
.base = base,
|
||||
.sector_num = sector_num,
|
||||
.nb_sectors = nb_sectors,
|
||||
.pnum = pnum,
|
||||
|
@ -4172,11 +4256,11 @@ int64_t bdrv_get_block_status(BlockDriverState *bs, int64_t sector_num,
|
|||
|
||||
if (qemu_in_coroutine()) {
|
||||
/* Fast-path if already in coroutine context */
|
||||
bdrv_get_block_status_co_entry(&data);
|
||||
bdrv_get_block_status_above_co_entry(&data);
|
||||
} else {
|
||||
AioContext *aio_context = bdrv_get_aio_context(bs);
|
||||
|
||||
co = qemu_coroutine_create(bdrv_get_block_status_co_entry);
|
||||
co = qemu_coroutine_create(bdrv_get_block_status_above_co_entry);
|
||||
qemu_coroutine_enter(co, &data);
|
||||
while (!data.done) {
|
||||
aio_poll(aio_context, true);
|
||||
|
@ -4185,6 +4269,14 @@ int64_t bdrv_get_block_status(BlockDriverState *bs, int64_t sector_num,
|
|||
return data.ret;
|
||||
}
|
||||
|
||||
int64_t bdrv_get_block_status(BlockDriverState *bs,
|
||||
int64_t sector_num,
|
||||
int nb_sectors, int *pnum)
|
||||
{
|
||||
return bdrv_get_block_status_above(bs, bs->backing_hd,
|
||||
sector_num, nb_sectors, pnum);
|
||||
}
|
||||
|
||||
int coroutine_fn bdrv_is_allocated(BlockDriverState *bs, int64_t sector_num,
|
||||
int nb_sectors, int *pnum)
|
||||
{
|
||||
|
@ -5205,8 +5297,6 @@ int coroutine_fn bdrv_co_discard(BlockDriverState *bs, int64_t sector_num,
|
|||
return -EROFS;
|
||||
}
|
||||
|
||||
bdrv_reset_dirty(bs, sector_num, nb_sectors);
|
||||
|
||||
/* Do nothing if disabled. */
|
||||
if (!(bs->open_flags & BDRV_O_UNMAP)) {
|
||||
return 0;
|
||||
|
@ -5216,6 +5306,8 @@ int coroutine_fn bdrv_co_discard(BlockDriverState *bs, int64_t sector_num,
|
|||
return 0;
|
||||
}
|
||||
|
||||
bdrv_set_dirty(bs, sector_num, nb_sectors);
|
||||
|
||||
max_discard = MIN_NON_ZERO(bs->bl.max_discard, BDRV_REQUEST_MAX_SECTORS);
|
||||
while (nb_sectors > 0) {
|
||||
int ret;
|
||||
|
@ -5526,15 +5618,6 @@ static void bdrv_set_dirty(BlockDriverState *bs, int64_t cur_sector,
|
|||
}
|
||||
}
|
||||
|
||||
static void bdrv_reset_dirty(BlockDriverState *bs, int64_t cur_sector,
|
||||
int nr_sectors)
|
||||
{
|
||||
BdrvDirtyBitmap *bitmap;
|
||||
QLIST_FOREACH(bitmap, &bs->dirty_bitmaps, list) {
|
||||
hbitmap_reset(bitmap->bitmap, cur_sector, nr_sectors);
|
||||
}
|
||||
}
|
||||
|
||||
int64_t bdrv_get_dirty_count(BlockDriverState *bs, BdrvDirtyBitmap *bitmap)
|
||||
{
|
||||
return hbitmap_count(bitmap->bitmap);
|
||||
|
|
|
@ -1501,6 +1501,9 @@ out:
|
|||
|
||||
if (ret) {
|
||||
if (iscsi != NULL) {
|
||||
if (iscsi_is_logged_in(iscsi)) {
|
||||
iscsi_logout_sync(iscsi);
|
||||
}
|
||||
iscsi_destroy_context(iscsi);
|
||||
}
|
||||
memset(iscsilun, 0, sizeof(IscsiLun));
|
||||
|
@ -1514,6 +1517,9 @@ static void iscsi_close(BlockDriverState *bs)
|
|||
struct iscsi_context *iscsi = iscsilun->iscsi;
|
||||
|
||||
iscsi_detach_aio_context(bs);
|
||||
if (iscsi_is_logged_in(iscsi)) {
|
||||
iscsi_logout_sync(iscsi);
|
||||
}
|
||||
iscsi_destroy_context(iscsi);
|
||||
g_free(iscsilun->zeroblock);
|
||||
g_free(iscsilun->allocationmap);
|
||||
|
|
|
@ -57,6 +57,7 @@ typedef struct MirrorBlockJob {
|
|||
int in_flight;
|
||||
int sectors_in_flight;
|
||||
int ret;
|
||||
bool unmap;
|
||||
} MirrorBlockJob;
|
||||
|
||||
typedef struct MirrorOp {
|
||||
|
@ -167,6 +168,8 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
|
|||
int64_t end, sector_num, next_chunk, next_sector, hbitmap_next_sector;
|
||||
uint64_t delay_ns = 0;
|
||||
MirrorOp *op;
|
||||
int pnum;
|
||||
int64_t ret;
|
||||
|
||||
s->sector_num = hbitmap_iter_next(&s->hbi);
|
||||
if (s->sector_num < 0) {
|
||||
|
@ -295,8 +298,22 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
|
|||
s->in_flight++;
|
||||
s->sectors_in_flight += nb_sectors;
|
||||
trace_mirror_one_iteration(s, sector_num, nb_sectors);
|
||||
bdrv_aio_readv(source, sector_num, &op->qiov, nb_sectors,
|
||||
mirror_read_complete, op);
|
||||
|
||||
ret = bdrv_get_block_status_above(source, NULL, sector_num,
|
||||
nb_sectors, &pnum);
|
||||
if (ret < 0 || pnum < nb_sectors ||
|
||||
(ret & BDRV_BLOCK_DATA && !(ret & BDRV_BLOCK_ZERO))) {
|
||||
bdrv_aio_readv(source, sector_num, &op->qiov, nb_sectors,
|
||||
mirror_read_complete, op);
|
||||
} else if (ret & BDRV_BLOCK_ZERO) {
|
||||
bdrv_aio_write_zeroes(s->target, sector_num, op->nb_sectors,
|
||||
s->unmap ? BDRV_REQ_MAY_UNMAP : 0,
|
||||
mirror_write_complete, op);
|
||||
} else {
|
||||
assert(!(ret & BDRV_BLOCK_DATA));
|
||||
bdrv_aio_discard(s->target, sector_num, op->nb_sectors,
|
||||
mirror_write_complete, op);
|
||||
}
|
||||
return delay_ns;
|
||||
}
|
||||
|
||||
|
@ -660,6 +677,7 @@ static void mirror_start_job(BlockDriverState *bs, BlockDriverState *target,
|
|||
int64_t buf_size,
|
||||
BlockdevOnError on_source_error,
|
||||
BlockdevOnError on_target_error,
|
||||
bool unmap,
|
||||
BlockCompletionFunc *cb,
|
||||
void *opaque, Error **errp,
|
||||
const BlockJobDriver *driver,
|
||||
|
@ -702,6 +720,7 @@ static void mirror_start_job(BlockDriverState *bs, BlockDriverState *target,
|
|||
s->base = base;
|
||||
s->granularity = granularity;
|
||||
s->buf_size = MAX(buf_size, granularity);
|
||||
s->unmap = unmap;
|
||||
|
||||
s->dirty_bitmap = bdrv_create_dirty_bitmap(bs, granularity, errp);
|
||||
if (!s->dirty_bitmap) {
|
||||
|
@ -720,6 +739,7 @@ void mirror_start(BlockDriverState *bs, BlockDriverState *target,
|
|||
int64_t speed, int64_t granularity, int64_t buf_size,
|
||||
MirrorSyncMode mode, BlockdevOnError on_source_error,
|
||||
BlockdevOnError on_target_error,
|
||||
bool unmap,
|
||||
BlockCompletionFunc *cb,
|
||||
void *opaque, Error **errp)
|
||||
{
|
||||
|
@ -730,7 +750,7 @@ void mirror_start(BlockDriverState *bs, BlockDriverState *target,
|
|||
base = mode == MIRROR_SYNC_MODE_TOP ? bs->backing_hd : NULL;
|
||||
mirror_start_job(bs, target, replaces,
|
||||
speed, granularity, buf_size,
|
||||
on_source_error, on_target_error, cb, opaque, errp,
|
||||
on_source_error, on_target_error, unmap, cb, opaque, errp,
|
||||
&mirror_job_driver, is_none_mode, base);
|
||||
}
|
||||
|
||||
|
@ -778,7 +798,7 @@ void commit_active_start(BlockDriverState *bs, BlockDriverState *base,
|
|||
|
||||
bdrv_ref(base);
|
||||
mirror_start_job(bs, base, NULL, speed, 0, 0,
|
||||
on_error, on_error, cb, opaque, &local_err,
|
||||
on_error, on_error, false, cb, opaque, &local_err,
|
||||
&commit_active_job_driver, false, base);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
|
|
|
@ -35,6 +35,8 @@
|
|||
#include "sysemu/sysemu.h"
|
||||
#include <nfsc/libnfs.h>
|
||||
|
||||
#define QEMU_NFS_MAX_READAHEAD_SIZE 1048576
|
||||
|
||||
typedef struct NFSClient {
|
||||
struct nfs_context *context;
|
||||
struct nfsfh *fh;
|
||||
|
@ -327,6 +329,11 @@ static int64_t nfs_client_open(NFSClient *client, const char *filename,
|
|||
nfs_set_tcp_syncnt(client->context, val);
|
||||
#ifdef LIBNFS_FEATURE_READAHEAD
|
||||
} else if (!strcmp(qp->p[i].name, "readahead")) {
|
||||
if (val > QEMU_NFS_MAX_READAHEAD_SIZE) {
|
||||
error_report("NFS Warning: Truncating NFS readahead"
|
||||
" size to %d", QEMU_NFS_MAX_READAHEAD_SIZE);
|
||||
val = QEMU_NFS_MAX_READAHEAD_SIZE;
|
||||
}
|
||||
nfs_set_readahead(client->context, val);
|
||||
#endif
|
||||
} else {
|
||||
|
|
|
@ -833,6 +833,11 @@ static int64_t alloc_clusters_noref(BlockDriverState *bs, uint64_t size)
|
|||
uint64_t i, nb_clusters, refcount;
|
||||
int ret;
|
||||
|
||||
/* We can't allocate clusters if they may still be queued for discard. */
|
||||
if (s->cache_discards) {
|
||||
qcow2_process_discards(bs, 0);
|
||||
}
|
||||
|
||||
nb_clusters = size_to_clusters(s, size);
|
||||
retry:
|
||||
for(i = 0; i < nb_clusters; i++) {
|
||||
|
|
|
@ -62,7 +62,8 @@
|
|||
#define MIN_CLUSTER_BITS 9
|
||||
#define MAX_CLUSTER_BITS 21
|
||||
|
||||
#define MIN_L2_CACHE_SIZE 1 /* cluster */
|
||||
/* Must be at least 2 to cover COW */
|
||||
#define MIN_L2_CACHE_SIZE 2 /* clusters */
|
||||
|
||||
/* Must be at least 4 to cover all cases of refcount table growth */
|
||||
#define MIN_REFCOUNT_CACHE_SIZE 4 /* clusters */
|
||||
|
|
40
block/vmdk.c
40
block/vmdk.c
|
@ -451,7 +451,8 @@ static int vmdk_init_tables(BlockDriverState *bs, VmdkExtent *extent,
|
|||
Error **errp)
|
||||
{
|
||||
int ret;
|
||||
int l1_size, i;
|
||||
size_t l1_size;
|
||||
int i;
|
||||
|
||||
/* read the L1 table */
|
||||
l1_size = extent->l1_size * sizeof(uint32_t);
|
||||
|
@ -1247,6 +1248,17 @@ static VmdkExtent *find_extent(BDRVVmdkState *s,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static inline uint64_t vmdk_find_index_in_cluster(VmdkExtent *extent,
|
||||
int64_t sector_num)
|
||||
{
|
||||
uint64_t index_in_cluster, extent_begin_sector, extent_relative_sector_num;
|
||||
|
||||
extent_begin_sector = extent->end_sector - extent->sectors;
|
||||
extent_relative_sector_num = sector_num - extent_begin_sector;
|
||||
index_in_cluster = extent_relative_sector_num % extent->cluster_sectors;
|
||||
return index_in_cluster;
|
||||
}
|
||||
|
||||
static int64_t coroutine_fn vmdk_co_get_block_status(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors, int *pnum)
|
||||
{
|
||||
|
@ -1284,7 +1296,7 @@ static int64_t coroutine_fn vmdk_co_get_block_status(BlockDriverState *bs,
|
|||
break;
|
||||
}
|
||||
|
||||
index_in_cluster = sector_num % extent->cluster_sectors;
|
||||
index_in_cluster = vmdk_find_index_in_cluster(extent, sector_num);
|
||||
n = extent->cluster_sectors - index_in_cluster;
|
||||
if (n > nb_sectors) {
|
||||
n = nb_sectors;
|
||||
|
@ -1302,6 +1314,8 @@ static int vmdk_write_extent(VmdkExtent *extent, int64_t cluster_offset,
|
|||
uLongf buf_len;
|
||||
const uint8_t *write_buf = buf;
|
||||
int write_len = nb_sectors * 512;
|
||||
int64_t write_offset;
|
||||
int64_t write_end_sector;
|
||||
|
||||
if (extent->compressed) {
|
||||
if (!extent->has_marker) {
|
||||
|
@ -1320,10 +1334,14 @@ static int vmdk_write_extent(VmdkExtent *extent, int64_t cluster_offset,
|
|||
write_buf = (uint8_t *)data;
|
||||
write_len = buf_len + sizeof(VmdkGrainMarker);
|
||||
}
|
||||
ret = bdrv_pwrite(extent->file,
|
||||
cluster_offset + offset_in_cluster,
|
||||
write_buf,
|
||||
write_len);
|
||||
write_offset = cluster_offset + offset_in_cluster,
|
||||
ret = bdrv_pwrite(extent->file, write_offset, write_buf, write_len);
|
||||
|
||||
write_end_sector = DIV_ROUND_UP(write_offset + write_len, BDRV_SECTOR_SIZE);
|
||||
|
||||
extent->next_cluster_sector = MAX(extent->next_cluster_sector,
|
||||
write_end_sector);
|
||||
|
||||
if (ret != write_len) {
|
||||
ret = ret < 0 ? ret : -EIO;
|
||||
goto out;
|
||||
|
@ -1406,7 +1424,6 @@ static int vmdk_read(BlockDriverState *bs, int64_t sector_num,
|
|||
BDRVVmdkState *s = bs->opaque;
|
||||
int ret;
|
||||
uint64_t n, index_in_cluster;
|
||||
uint64_t extent_begin_sector, extent_relative_sector_num;
|
||||
VmdkExtent *extent = NULL;
|
||||
uint64_t cluster_offset;
|
||||
|
||||
|
@ -1418,9 +1435,7 @@ static int vmdk_read(BlockDriverState *bs, int64_t sector_num,
|
|||
ret = get_cluster_offset(bs, extent, NULL,
|
||||
sector_num << 9, false, &cluster_offset,
|
||||
0, 0);
|
||||
extent_begin_sector = extent->end_sector - extent->sectors;
|
||||
extent_relative_sector_num = sector_num - extent_begin_sector;
|
||||
index_in_cluster = extent_relative_sector_num % extent->cluster_sectors;
|
||||
index_in_cluster = vmdk_find_index_in_cluster(extent, sector_num);
|
||||
n = extent->cluster_sectors - index_in_cluster;
|
||||
if (n > nb_sectors) {
|
||||
n = nb_sectors;
|
||||
|
@ -1482,7 +1497,6 @@ static int vmdk_write(BlockDriverState *bs, int64_t sector_num,
|
|||
VmdkExtent *extent = NULL;
|
||||
int ret;
|
||||
int64_t index_in_cluster, n;
|
||||
uint64_t extent_begin_sector, extent_relative_sector_num;
|
||||
uint64_t cluster_offset;
|
||||
VmdkMetaData m_data;
|
||||
|
||||
|
@ -1498,9 +1512,7 @@ static int vmdk_write(BlockDriverState *bs, int64_t sector_num,
|
|||
if (!extent) {
|
||||
return -EIO;
|
||||
}
|
||||
extent_begin_sector = extent->end_sector - extent->sectors;
|
||||
extent_relative_sector_num = sector_num - extent_begin_sector;
|
||||
index_in_cluster = extent_relative_sector_num % extent->cluster_sectors;
|
||||
index_in_cluster = vmdk_find_index_in_cluster(extent, sector_num);
|
||||
n = extent->cluster_sectors - index_in_cluster;
|
||||
if (n > nb_sectors) {
|
||||
n = nb_sectors;
|
||||
|
|
18
block/vpc.c
18
block/vpc.c
|
@ -168,6 +168,7 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags,
|
|||
uint8_t buf[HEADER_SIZE];
|
||||
uint32_t checksum;
|
||||
uint64_t computed_size;
|
||||
uint64_t pagetable_size;
|
||||
int disk_type = VHD_DYNAMIC;
|
||||
int ret;
|
||||
|
||||
|
@ -269,7 +270,17 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags,
|
|||
goto fail;
|
||||
}
|
||||
|
||||
s->pagetable = qemu_try_blockalign(bs->file, s->max_table_entries * 4);
|
||||
if (s->max_table_entries > SIZE_MAX / 4 ||
|
||||
s->max_table_entries > (int) INT_MAX / 4) {
|
||||
error_setg(errp, "Max Table Entries too large (%" PRId32 ")",
|
||||
s->max_table_entries);
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
pagetable_size = (uint64_t) s->max_table_entries * 4;
|
||||
|
||||
s->pagetable = qemu_try_blockalign(bs->file, pagetable_size);
|
||||
if (s->pagetable == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
|
@ -277,14 +288,13 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags,
|
|||
|
||||
s->bat_offset = be64_to_cpu(dyndisk_header->table_offset);
|
||||
|
||||
ret = bdrv_pread(bs->file, s->bat_offset, s->pagetable,
|
||||
s->max_table_entries * 4);
|
||||
ret = bdrv_pread(bs->file, s->bat_offset, s->pagetable, pagetable_size);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
s->free_data_block_offset =
|
||||
(s->bat_offset + (s->max_table_entries * 4) + 511) & ~511;
|
||||
ROUND_UP(s->bat_offset + pagetable_size, 512);
|
||||
|
||||
for (i = 0; i < s->max_table_entries; i++) {
|
||||
be32_to_cpus(&s->pagetable[i]);
|
||||
|
|
|
@ -2461,6 +2461,7 @@ void qmp_drive_mirror(const char *device, const char *target,
|
|||
bool has_buf_size, int64_t buf_size,
|
||||
bool has_on_source_error, BlockdevOnError on_source_error,
|
||||
bool has_on_target_error, BlockdevOnError on_target_error,
|
||||
bool has_unmap, bool unmap,
|
||||
Error **errp)
|
||||
{
|
||||
BlockBackend *blk;
|
||||
|
@ -2492,6 +2493,9 @@ void qmp_drive_mirror(const char *device, const char *target,
|
|||
if (!has_buf_size) {
|
||||
buf_size = DEFAULT_MIRROR_BUF_SIZE;
|
||||
}
|
||||
if (!has_unmap) {
|
||||
unmap = true;
|
||||
}
|
||||
|
||||
if (granularity != 0 && (granularity < 512 || granularity > 1048576 * 64)) {
|
||||
error_set(errp, QERR_INVALID_PARAMETER_VALUE, "granularity",
|
||||
|
@ -2631,6 +2635,7 @@ void qmp_drive_mirror(const char *device, const char *target,
|
|||
has_replaces ? replaces : NULL,
|
||||
speed, granularity, buf_size, sync,
|
||||
on_source_error, on_target_error,
|
||||
unmap,
|
||||
block_job_cb, bs, &local_err);
|
||||
if (local_err != NULL) {
|
||||
bdrv_unref(target_bs);
|
||||
|
|
2
hmp.c
2
hmp.c
|
@ -1034,7 +1034,7 @@ void hmp_drive_mirror(Monitor *mon, const QDict *qdict)
|
|||
false, NULL, false, NULL,
|
||||
full ? MIRROR_SYNC_MODE_FULL : MIRROR_SYNC_MODE_TOP,
|
||||
true, mode, false, 0, false, 0, false, 0,
|
||||
false, 0, false, 0, &err);
|
||||
false, 0, false, 0, false, true, &err);
|
||||
hmp_handle_error(mon, &err);
|
||||
}
|
||||
|
||||
|
|
|
@ -304,6 +304,7 @@ static void aml_free(gpointer data, gpointer user_data)
|
|||
{
|
||||
Aml *var = data;
|
||||
build_free_array(var->buf);
|
||||
g_free(var);
|
||||
}
|
||||
|
||||
Aml *init_aml_allocator(void)
|
||||
|
|
|
@ -1512,7 +1512,7 @@ static uint32_t fdctrl_read_data(FDCtrl *fdctrl)
|
|||
{
|
||||
FDrive *cur_drv;
|
||||
uint32_t retval = 0;
|
||||
int pos;
|
||||
uint32_t pos;
|
||||
|
||||
cur_drv = get_cur_drv(fdctrl);
|
||||
fdctrl->dsr &= ~FD_DSR_PWRDOWN;
|
||||
|
@ -1521,8 +1521,8 @@ static uint32_t fdctrl_read_data(FDCtrl *fdctrl)
|
|||
return 0;
|
||||
}
|
||||
pos = fdctrl->data_pos;
|
||||
pos %= FD_SECTOR_LEN;
|
||||
if (fdctrl->msr & FD_MSR_NONDMA) {
|
||||
pos %= FD_SECTOR_LEN;
|
||||
if (pos == 0) {
|
||||
if (fdctrl->data_pos != 0)
|
||||
if (!fdctrl_seek_to_next_sect(fdctrl, cur_drv)) {
|
||||
|
@ -1867,10 +1867,13 @@ static void fdctrl_handle_option(FDCtrl *fdctrl, int direction)
|
|||
static void fdctrl_handle_drive_specification_command(FDCtrl *fdctrl, int direction)
|
||||
{
|
||||
FDrive *cur_drv = get_cur_drv(fdctrl);
|
||||
uint32_t pos;
|
||||
|
||||
if (fdctrl->fifo[fdctrl->data_pos - 1] & 0x80) {
|
||||
pos = fdctrl->data_pos - 1;
|
||||
pos %= FD_SECTOR_LEN;
|
||||
if (fdctrl->fifo[pos] & 0x80) {
|
||||
/* Command parameters done */
|
||||
if (fdctrl->fifo[fdctrl->data_pos - 1] & 0x40) {
|
||||
if (fdctrl->fifo[pos] & 0x40) {
|
||||
fdctrl->fifo[0] = fdctrl->fifo[1];
|
||||
fdctrl->fifo[2] = 0;
|
||||
fdctrl->fifo[3] = 0;
|
||||
|
@ -1970,7 +1973,7 @@ static uint8_t command_to_handler[256];
|
|||
static void fdctrl_write_data(FDCtrl *fdctrl, uint32_t value)
|
||||
{
|
||||
FDrive *cur_drv;
|
||||
int pos;
|
||||
uint32_t pos;
|
||||
|
||||
/* Reset mode */
|
||||
if (!(fdctrl->dor & FD_DOR_nRESET)) {
|
||||
|
@ -2019,7 +2022,9 @@ static void fdctrl_write_data(FDCtrl *fdctrl, uint32_t value)
|
|||
}
|
||||
|
||||
FLOPPY_DPRINTF("%s: %02x\n", __func__, value);
|
||||
fdctrl->fifo[fdctrl->data_pos++] = value;
|
||||
pos = fdctrl->data_pos++;
|
||||
pos %= FD_SECTOR_LEN;
|
||||
fdctrl->fifo[pos] = value;
|
||||
if (fdctrl->data_pos == fdctrl->data_len) {
|
||||
/* We now have all parameters
|
||||
* and will be able to treat the command
|
||||
|
|
|
@ -707,7 +707,7 @@ static void sdp_service_record_build(struct sdp_service_record_s *record,
|
|||
len += sdp_attr_max_size(&def->attributes[record->attributes ++].data,
|
||||
&record->uuids);
|
||||
}
|
||||
record->uuids = 1 << ffs(record->uuids - 1);
|
||||
record->uuids = pow2ceil(record->uuids);
|
||||
record->attribute_list =
|
||||
g_malloc0(record->attributes * sizeof(*record->attribute_list));
|
||||
record->uuid =
|
||||
|
|
|
@ -228,6 +228,10 @@ VIOsPAPRDevice *vty_lookup(sPAPREnvironment *spapr, target_ulong reg)
|
|||
return spapr_vty_get_default(spapr->vio_bus);
|
||||
}
|
||||
|
||||
if (!object_dynamic_cast(OBJECT(sdev), TYPE_VIO_SPAPR_VTY_DEVICE)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return sdev;
|
||||
}
|
||||
|
||||
|
|
|
@ -281,19 +281,15 @@ static void sysbus_dev_print(Monitor *mon, DeviceState *dev, int indent)
|
|||
static char *sysbus_get_fw_dev_path(DeviceState *dev)
|
||||
{
|
||||
SysBusDevice *s = SYS_BUS_DEVICE(dev);
|
||||
char path[40];
|
||||
int off;
|
||||
|
||||
off = snprintf(path, sizeof(path), "%s", qdev_fw_name(dev));
|
||||
|
||||
if (s->num_mmio) {
|
||||
snprintf(path + off, sizeof(path) - off, "@"TARGET_FMT_plx,
|
||||
s->mmio[0].addr);
|
||||
} else if (s->num_pio) {
|
||||
snprintf(path + off, sizeof(path) - off, "@i%04x", s->pio[0]);
|
||||
return g_strdup_printf("%s@" TARGET_FMT_plx, qdev_fw_name(dev),
|
||||
s->mmio[0].addr);
|
||||
}
|
||||
|
||||
return g_strdup(path);
|
||||
if (s->num_pio) {
|
||||
return g_strdup_printf("%s@i%04x", qdev_fw_name(dev), s->pio[0]);
|
||||
}
|
||||
return g_strdup(qdev_fw_name(dev));
|
||||
}
|
||||
|
||||
void sysbus_add_io(SysBusDevice *dev, hwaddr addr,
|
||||
|
|
|
@ -983,6 +983,7 @@ static void cmd_start_stop_unit(IDEState *s, uint8_t* buf)
|
|||
|
||||
if (pwrcnd) {
|
||||
/* eject/load only happens for power condition == 0 */
|
||||
ide_atapi_cmd_ok(s);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -2021,11 +2021,17 @@ void ide_data_writew(void *opaque, uint32_t addr, uint32_t val)
|
|||
}
|
||||
|
||||
p = s->data_ptr;
|
||||
if (p + 2 > s->data_end) {
|
||||
return;
|
||||
}
|
||||
|
||||
*(uint16_t *)p = le16_to_cpu(val);
|
||||
p += 2;
|
||||
s->data_ptr = p;
|
||||
if (p >= s->data_end)
|
||||
if (p >= s->data_end) {
|
||||
s->status &= ~DRQ_STAT;
|
||||
s->end_transfer_func(s);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t ide_data_readw(void *opaque, uint32_t addr)
|
||||
|
@ -2042,11 +2048,17 @@ uint32_t ide_data_readw(void *opaque, uint32_t addr)
|
|||
}
|
||||
|
||||
p = s->data_ptr;
|
||||
if (p + 2 > s->data_end) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = cpu_to_le16(*(uint16_t *)p);
|
||||
p += 2;
|
||||
s->data_ptr = p;
|
||||
if (p >= s->data_end)
|
||||
if (p >= s->data_end) {
|
||||
s->status &= ~DRQ_STAT;
|
||||
s->end_transfer_func(s);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -2063,11 +2075,17 @@ void ide_data_writel(void *opaque, uint32_t addr, uint32_t val)
|
|||
}
|
||||
|
||||
p = s->data_ptr;
|
||||
if (p + 4 > s->data_end) {
|
||||
return;
|
||||
}
|
||||
|
||||
*(uint32_t *)p = le32_to_cpu(val);
|
||||
p += 4;
|
||||
s->data_ptr = p;
|
||||
if (p >= s->data_end)
|
||||
if (p >= s->data_end) {
|
||||
s->status &= ~DRQ_STAT;
|
||||
s->end_transfer_func(s);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t ide_data_readl(void *opaque, uint32_t addr)
|
||||
|
@ -2084,11 +2102,17 @@ uint32_t ide_data_readl(void *opaque, uint32_t addr)
|
|||
}
|
||||
|
||||
p = s->data_ptr;
|
||||
if (p + 4 > s->data_end) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = cpu_to_le32(*(uint32_t *)p);
|
||||
p += 4;
|
||||
s->data_ptr = p;
|
||||
if (p >= s->data_end)
|
||||
if (p >= s->data_end) {
|
||||
s->status &= ~DRQ_STAT;
|
||||
s->end_transfer_func(s);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
@ -169,6 +169,7 @@ int pci_piix3_xen_ide_unplug(DeviceState *dev)
|
|||
PCIIDEState *pci_ide;
|
||||
DriveInfo *di;
|
||||
int i;
|
||||
IDEDevice *idedev;
|
||||
|
||||
pci_ide = PCI_IDE(dev);
|
||||
|
||||
|
@ -181,6 +182,12 @@ int pci_piix3_xen_ide_unplug(DeviceState *dev)
|
|||
blk_detach_dev(blk, ds);
|
||||
}
|
||||
pci_ide->bus[di->bus].ifs[di->unit].blk = NULL;
|
||||
if (!(i % 2)) {
|
||||
idedev = pci_ide->bus[di->bus].master;
|
||||
} else {
|
||||
idedev = pci_ide->bus[di->bus].slave;
|
||||
}
|
||||
idedev->conf.blk = NULL;
|
||||
blk_unref(blk);
|
||||
}
|
||||
}
|
||||
|
|
395
hw/net/rtl8139.c
395
hw/net/rtl8139.c
|
@ -2150,6 +2150,11 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s)
|
|||
{
|
||||
DPRINTF("+++ C+ mode offloaded task checksum\n");
|
||||
|
||||
/* Large enough for Ethernet and IP headers? */
|
||||
if (saved_size < ETH_HLEN + sizeof(ip_header)) {
|
||||
goto skip_offload;
|
||||
}
|
||||
|
||||
/* ip packet header */
|
||||
ip_header *ip = NULL;
|
||||
int hlen = 0;
|
||||
|
@ -2160,223 +2165,235 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s)
|
|||
size_t eth_payload_len = 0;
|
||||
|
||||
int proto = be16_to_cpu(*(uint16_t *)(saved_buffer + 12));
|
||||
if (proto == ETH_P_IP)
|
||||
if (proto != ETH_P_IP)
|
||||
{
|
||||
DPRINTF("+++ C+ mode has IP packet\n");
|
||||
|
||||
/* not aligned */
|
||||
eth_payload_data = saved_buffer + ETH_HLEN;
|
||||
eth_payload_len = saved_size - ETH_HLEN;
|
||||
|
||||
ip = (ip_header*)eth_payload_data;
|
||||
|
||||
if (IP_HEADER_VERSION(ip) != IP_HEADER_VERSION_4) {
|
||||
DPRINTF("+++ C+ mode packet has bad IP version %d "
|
||||
"expected %d\n", IP_HEADER_VERSION(ip),
|
||||
IP_HEADER_VERSION_4);
|
||||
ip = NULL;
|
||||
} else {
|
||||
hlen = IP_HEADER_LENGTH(ip);
|
||||
ip_protocol = ip->ip_p;
|
||||
ip_data_len = be16_to_cpu(ip->ip_len) - hlen;
|
||||
}
|
||||
goto skip_offload;
|
||||
}
|
||||
|
||||
if (ip)
|
||||
DPRINTF("+++ C+ mode has IP packet\n");
|
||||
|
||||
/* not aligned */
|
||||
eth_payload_data = saved_buffer + ETH_HLEN;
|
||||
eth_payload_len = saved_size - ETH_HLEN;
|
||||
|
||||
ip = (ip_header*)eth_payload_data;
|
||||
|
||||
if (IP_HEADER_VERSION(ip) != IP_HEADER_VERSION_4) {
|
||||
DPRINTF("+++ C+ mode packet has bad IP version %d "
|
||||
"expected %d\n", IP_HEADER_VERSION(ip),
|
||||
IP_HEADER_VERSION_4);
|
||||
goto skip_offload;
|
||||
}
|
||||
|
||||
hlen = IP_HEADER_LENGTH(ip);
|
||||
if (hlen < sizeof(ip_header) || hlen > eth_payload_len) {
|
||||
goto skip_offload;
|
||||
}
|
||||
|
||||
ip_protocol = ip->ip_p;
|
||||
|
||||
ip_data_len = be16_to_cpu(ip->ip_len);
|
||||
if (ip_data_len < hlen || ip_data_len > eth_payload_len) {
|
||||
goto skip_offload;
|
||||
}
|
||||
ip_data_len -= hlen;
|
||||
|
||||
if (txdw0 & CP_TX_IPCS)
|
||||
{
|
||||
if (txdw0 & CP_TX_IPCS)
|
||||
{
|
||||
DPRINTF("+++ C+ mode need IP checksum\n");
|
||||
DPRINTF("+++ C+ mode need IP checksum\n");
|
||||
|
||||
if (hlen<sizeof(ip_header) || hlen>eth_payload_len) {/* min header length */
|
||||
/* bad packet header len */
|
||||
/* or packet too short */
|
||||
}
|
||||
else
|
||||
{
|
||||
ip->ip_sum = 0;
|
||||
ip->ip_sum = ip_checksum(ip, hlen);
|
||||
DPRINTF("+++ C+ mode IP header len=%d checksum=%04x\n",
|
||||
hlen, ip->ip_sum);
|
||||
}
|
||||
ip->ip_sum = 0;
|
||||
ip->ip_sum = ip_checksum(ip, hlen);
|
||||
DPRINTF("+++ C+ mode IP header len=%d checksum=%04x\n",
|
||||
hlen, ip->ip_sum);
|
||||
}
|
||||
|
||||
if ((txdw0 & CP_TX_LGSEN) && ip_protocol == IP_PROTO_TCP)
|
||||
{
|
||||
/* Large enough for the TCP header? */
|
||||
if (ip_data_len < sizeof(tcp_header)) {
|
||||
goto skip_offload;
|
||||
}
|
||||
|
||||
if ((txdw0 & CP_TX_LGSEN) && ip_protocol == IP_PROTO_TCP)
|
||||
int large_send_mss = (txdw0 >> 16) & CP_TC_LGSEN_MSS_MASK;
|
||||
|
||||
DPRINTF("+++ C+ mode offloaded task TSO MTU=%d IP data %d "
|
||||
"frame data %d specified MSS=%d\n", ETH_MTU,
|
||||
ip_data_len, saved_size - ETH_HLEN, large_send_mss);
|
||||
|
||||
int tcp_send_offset = 0;
|
||||
int send_count = 0;
|
||||
|
||||
/* maximum IP header length is 60 bytes */
|
||||
uint8_t saved_ip_header[60];
|
||||
|
||||
/* save IP header template; data area is used in tcp checksum calculation */
|
||||
memcpy(saved_ip_header, eth_payload_data, hlen);
|
||||
|
||||
/* a placeholder for checksum calculation routine in tcp case */
|
||||
uint8_t *data_to_checksum = eth_payload_data + hlen - 12;
|
||||
// size_t data_to_checksum_len = eth_payload_len - hlen + 12;
|
||||
|
||||
/* pointer to TCP header */
|
||||
tcp_header *p_tcp_hdr = (tcp_header*)(eth_payload_data + hlen);
|
||||
|
||||
int tcp_hlen = TCP_HEADER_DATA_OFFSET(p_tcp_hdr);
|
||||
|
||||
/* Invalid TCP data offset? */
|
||||
if (tcp_hlen < sizeof(tcp_header) || tcp_hlen > ip_data_len) {
|
||||
goto skip_offload;
|
||||
}
|
||||
|
||||
/* ETH_MTU = ip header len + tcp header len + payload */
|
||||
int tcp_data_len = ip_data_len - tcp_hlen;
|
||||
int tcp_chunk_size = ETH_MTU - hlen - tcp_hlen;
|
||||
|
||||
DPRINTF("+++ C+ mode TSO IP data len %d TCP hlen %d TCP "
|
||||
"data len %d TCP chunk size %d\n", ip_data_len,
|
||||
tcp_hlen, tcp_data_len, tcp_chunk_size);
|
||||
|
||||
/* note the cycle below overwrites IP header data,
|
||||
but restores it from saved_ip_header before sending packet */
|
||||
|
||||
int is_last_frame = 0;
|
||||
|
||||
for (tcp_send_offset = 0; tcp_send_offset < tcp_data_len; tcp_send_offset += tcp_chunk_size)
|
||||
{
|
||||
int large_send_mss = (txdw0 >> 16) & CP_TC_LGSEN_MSS_MASK;
|
||||
uint16_t chunk_size = tcp_chunk_size;
|
||||
|
||||
DPRINTF("+++ C+ mode offloaded task TSO MTU=%d IP data %d "
|
||||
"frame data %d specified MSS=%d\n", ETH_MTU,
|
||||
ip_data_len, saved_size - ETH_HLEN, large_send_mss);
|
||||
|
||||
int tcp_send_offset = 0;
|
||||
int send_count = 0;
|
||||
|
||||
/* maximum IP header length is 60 bytes */
|
||||
uint8_t saved_ip_header[60];
|
||||
|
||||
/* save IP header template; data area is used in tcp checksum calculation */
|
||||
memcpy(saved_ip_header, eth_payload_data, hlen);
|
||||
|
||||
/* a placeholder for checksum calculation routine in tcp case */
|
||||
uint8_t *data_to_checksum = eth_payload_data + hlen - 12;
|
||||
// size_t data_to_checksum_len = eth_payload_len - hlen + 12;
|
||||
|
||||
/* pointer to TCP header */
|
||||
tcp_header *p_tcp_hdr = (tcp_header*)(eth_payload_data + hlen);
|
||||
|
||||
int tcp_hlen = TCP_HEADER_DATA_OFFSET(p_tcp_hdr);
|
||||
|
||||
/* ETH_MTU = ip header len + tcp header len + payload */
|
||||
int tcp_data_len = ip_data_len - tcp_hlen;
|
||||
int tcp_chunk_size = ETH_MTU - hlen - tcp_hlen;
|
||||
|
||||
DPRINTF("+++ C+ mode TSO IP data len %d TCP hlen %d TCP "
|
||||
"data len %d TCP chunk size %d\n", ip_data_len,
|
||||
tcp_hlen, tcp_data_len, tcp_chunk_size);
|
||||
|
||||
/* note the cycle below overwrites IP header data,
|
||||
but restores it from saved_ip_header before sending packet */
|
||||
|
||||
int is_last_frame = 0;
|
||||
|
||||
for (tcp_send_offset = 0; tcp_send_offset < tcp_data_len; tcp_send_offset += tcp_chunk_size)
|
||||
/* check if this is the last frame */
|
||||
if (tcp_send_offset + tcp_chunk_size >= tcp_data_len)
|
||||
{
|
||||
uint16_t chunk_size = tcp_chunk_size;
|
||||
|
||||
/* check if this is the last frame */
|
||||
if (tcp_send_offset + tcp_chunk_size >= tcp_data_len)
|
||||
{
|
||||
is_last_frame = 1;
|
||||
chunk_size = tcp_data_len - tcp_send_offset;
|
||||
}
|
||||
|
||||
DPRINTF("+++ C+ mode TSO TCP seqno %08x\n",
|
||||
be32_to_cpu(p_tcp_hdr->th_seq));
|
||||
|
||||
/* add 4 TCP pseudoheader fields */
|
||||
/* copy IP source and destination fields */
|
||||
memcpy(data_to_checksum, saved_ip_header + 12, 8);
|
||||
|
||||
DPRINTF("+++ C+ mode TSO calculating TCP checksum for "
|
||||
"packet with %d bytes data\n", tcp_hlen +
|
||||
chunk_size);
|
||||
|
||||
if (tcp_send_offset)
|
||||
{
|
||||
memcpy((uint8_t*)p_tcp_hdr + tcp_hlen, (uint8_t*)p_tcp_hdr + tcp_hlen + tcp_send_offset, chunk_size);
|
||||
}
|
||||
|
||||
/* keep PUSH and FIN flags only for the last frame */
|
||||
if (!is_last_frame)
|
||||
{
|
||||
TCP_HEADER_CLEAR_FLAGS(p_tcp_hdr, TCP_FLAG_PUSH|TCP_FLAG_FIN);
|
||||
}
|
||||
|
||||
/* recalculate TCP checksum */
|
||||
ip_pseudo_header *p_tcpip_hdr = (ip_pseudo_header *)data_to_checksum;
|
||||
p_tcpip_hdr->zeros = 0;
|
||||
p_tcpip_hdr->ip_proto = IP_PROTO_TCP;
|
||||
p_tcpip_hdr->ip_payload = cpu_to_be16(tcp_hlen + chunk_size);
|
||||
|
||||
p_tcp_hdr->th_sum = 0;
|
||||
|
||||
int tcp_checksum = ip_checksum(data_to_checksum, tcp_hlen + chunk_size + 12);
|
||||
DPRINTF("+++ C+ mode TSO TCP checksum %04x\n",
|
||||
tcp_checksum);
|
||||
|
||||
p_tcp_hdr->th_sum = tcp_checksum;
|
||||
|
||||
/* restore IP header */
|
||||
memcpy(eth_payload_data, saved_ip_header, hlen);
|
||||
|
||||
/* set IP data length and recalculate IP checksum */
|
||||
ip->ip_len = cpu_to_be16(hlen + tcp_hlen + chunk_size);
|
||||
|
||||
/* increment IP id for subsequent frames */
|
||||
ip->ip_id = cpu_to_be16(tcp_send_offset/tcp_chunk_size + be16_to_cpu(ip->ip_id));
|
||||
|
||||
ip->ip_sum = 0;
|
||||
ip->ip_sum = ip_checksum(eth_payload_data, hlen);
|
||||
DPRINTF("+++ C+ mode TSO IP header len=%d "
|
||||
"checksum=%04x\n", hlen, ip->ip_sum);
|
||||
|
||||
int tso_send_size = ETH_HLEN + hlen + tcp_hlen + chunk_size;
|
||||
DPRINTF("+++ C+ mode TSO transferring packet size "
|
||||
"%d\n", tso_send_size);
|
||||
rtl8139_transfer_frame(s, saved_buffer, tso_send_size,
|
||||
0, (uint8_t *) dot1q_buffer);
|
||||
|
||||
/* add transferred count to TCP sequence number */
|
||||
p_tcp_hdr->th_seq = cpu_to_be32(chunk_size + be32_to_cpu(p_tcp_hdr->th_seq));
|
||||
++send_count;
|
||||
is_last_frame = 1;
|
||||
chunk_size = tcp_data_len - tcp_send_offset;
|
||||
}
|
||||
|
||||
/* Stop sending this frame */
|
||||
saved_size = 0;
|
||||
}
|
||||
else if (txdw0 & (CP_TX_TCPCS|CP_TX_UDPCS))
|
||||
{
|
||||
DPRINTF("+++ C+ mode need TCP or UDP checksum\n");
|
||||
|
||||
/* maximum IP header length is 60 bytes */
|
||||
uint8_t saved_ip_header[60];
|
||||
memcpy(saved_ip_header, eth_payload_data, hlen);
|
||||
|
||||
uint8_t *data_to_checksum = eth_payload_data + hlen - 12;
|
||||
// size_t data_to_checksum_len = eth_payload_len - hlen + 12;
|
||||
DPRINTF("+++ C+ mode TSO TCP seqno %08x\n",
|
||||
be32_to_cpu(p_tcp_hdr->th_seq));
|
||||
|
||||
/* add 4 TCP pseudoheader fields */
|
||||
/* copy IP source and destination fields */
|
||||
memcpy(data_to_checksum, saved_ip_header + 12, 8);
|
||||
|
||||
if ((txdw0 & CP_TX_TCPCS) && ip_protocol == IP_PROTO_TCP)
|
||||
DPRINTF("+++ C+ mode TSO calculating TCP checksum for "
|
||||
"packet with %d bytes data\n", tcp_hlen +
|
||||
chunk_size);
|
||||
|
||||
if (tcp_send_offset)
|
||||
{
|
||||
DPRINTF("+++ C+ mode calculating TCP checksum for "
|
||||
"packet with %d bytes data\n", ip_data_len);
|
||||
|
||||
ip_pseudo_header *p_tcpip_hdr = (ip_pseudo_header *)data_to_checksum;
|
||||
p_tcpip_hdr->zeros = 0;
|
||||
p_tcpip_hdr->ip_proto = IP_PROTO_TCP;
|
||||
p_tcpip_hdr->ip_payload = cpu_to_be16(ip_data_len);
|
||||
|
||||
tcp_header* p_tcp_hdr = (tcp_header *) (data_to_checksum+12);
|
||||
|
||||
p_tcp_hdr->th_sum = 0;
|
||||
|
||||
int tcp_checksum = ip_checksum(data_to_checksum, ip_data_len + 12);
|
||||
DPRINTF("+++ C+ mode TCP checksum %04x\n",
|
||||
tcp_checksum);
|
||||
|
||||
p_tcp_hdr->th_sum = tcp_checksum;
|
||||
memcpy((uint8_t*)p_tcp_hdr + tcp_hlen, (uint8_t*)p_tcp_hdr + tcp_hlen + tcp_send_offset, chunk_size);
|
||||
}
|
||||
else if ((txdw0 & CP_TX_UDPCS) && ip_protocol == IP_PROTO_UDP)
|
||||
|
||||
/* keep PUSH and FIN flags only for the last frame */
|
||||
if (!is_last_frame)
|
||||
{
|
||||
DPRINTF("+++ C+ mode calculating UDP checksum for "
|
||||
"packet with %d bytes data\n", ip_data_len);
|
||||
|
||||
ip_pseudo_header *p_udpip_hdr = (ip_pseudo_header *)data_to_checksum;
|
||||
p_udpip_hdr->zeros = 0;
|
||||
p_udpip_hdr->ip_proto = IP_PROTO_UDP;
|
||||
p_udpip_hdr->ip_payload = cpu_to_be16(ip_data_len);
|
||||
|
||||
udp_header *p_udp_hdr = (udp_header *) (data_to_checksum+12);
|
||||
|
||||
p_udp_hdr->uh_sum = 0;
|
||||
|
||||
int udp_checksum = ip_checksum(data_to_checksum, ip_data_len + 12);
|
||||
DPRINTF("+++ C+ mode UDP checksum %04x\n",
|
||||
udp_checksum);
|
||||
|
||||
p_udp_hdr->uh_sum = udp_checksum;
|
||||
TCP_HEADER_CLEAR_FLAGS(p_tcp_hdr, TCP_FLAG_PUSH|TCP_FLAG_FIN);
|
||||
}
|
||||
|
||||
/* recalculate TCP checksum */
|
||||
ip_pseudo_header *p_tcpip_hdr = (ip_pseudo_header *)data_to_checksum;
|
||||
p_tcpip_hdr->zeros = 0;
|
||||
p_tcpip_hdr->ip_proto = IP_PROTO_TCP;
|
||||
p_tcpip_hdr->ip_payload = cpu_to_be16(tcp_hlen + chunk_size);
|
||||
|
||||
p_tcp_hdr->th_sum = 0;
|
||||
|
||||
int tcp_checksum = ip_checksum(data_to_checksum, tcp_hlen + chunk_size + 12);
|
||||
DPRINTF("+++ C+ mode TSO TCP checksum %04x\n",
|
||||
tcp_checksum);
|
||||
|
||||
p_tcp_hdr->th_sum = tcp_checksum;
|
||||
|
||||
/* restore IP header */
|
||||
memcpy(eth_payload_data, saved_ip_header, hlen);
|
||||
|
||||
/* set IP data length and recalculate IP checksum */
|
||||
ip->ip_len = cpu_to_be16(hlen + tcp_hlen + chunk_size);
|
||||
|
||||
/* increment IP id for subsequent frames */
|
||||
ip->ip_id = cpu_to_be16(tcp_send_offset/tcp_chunk_size + be16_to_cpu(ip->ip_id));
|
||||
|
||||
ip->ip_sum = 0;
|
||||
ip->ip_sum = ip_checksum(eth_payload_data, hlen);
|
||||
DPRINTF("+++ C+ mode TSO IP header len=%d "
|
||||
"checksum=%04x\n", hlen, ip->ip_sum);
|
||||
|
||||
int tso_send_size = ETH_HLEN + hlen + tcp_hlen + chunk_size;
|
||||
DPRINTF("+++ C+ mode TSO transferring packet size "
|
||||
"%d\n", tso_send_size);
|
||||
rtl8139_transfer_frame(s, saved_buffer, tso_send_size,
|
||||
0, (uint8_t *) dot1q_buffer);
|
||||
|
||||
/* add transferred count to TCP sequence number */
|
||||
p_tcp_hdr->th_seq = cpu_to_be32(chunk_size + be32_to_cpu(p_tcp_hdr->th_seq));
|
||||
++send_count;
|
||||
}
|
||||
|
||||
/* Stop sending this frame */
|
||||
saved_size = 0;
|
||||
}
|
||||
else if (txdw0 & (CP_TX_TCPCS|CP_TX_UDPCS))
|
||||
{
|
||||
DPRINTF("+++ C+ mode need TCP or UDP checksum\n");
|
||||
|
||||
/* maximum IP header length is 60 bytes */
|
||||
uint8_t saved_ip_header[60];
|
||||
memcpy(saved_ip_header, eth_payload_data, hlen);
|
||||
|
||||
uint8_t *data_to_checksum = eth_payload_data + hlen - 12;
|
||||
// size_t data_to_checksum_len = eth_payload_len - hlen + 12;
|
||||
|
||||
/* add 4 TCP pseudoheader fields */
|
||||
/* copy IP source and destination fields */
|
||||
memcpy(data_to_checksum, saved_ip_header + 12, 8);
|
||||
|
||||
if ((txdw0 & CP_TX_TCPCS) && ip_protocol == IP_PROTO_TCP)
|
||||
{
|
||||
DPRINTF("+++ C+ mode calculating TCP checksum for "
|
||||
"packet with %d bytes data\n", ip_data_len);
|
||||
|
||||
ip_pseudo_header *p_tcpip_hdr = (ip_pseudo_header *)data_to_checksum;
|
||||
p_tcpip_hdr->zeros = 0;
|
||||
p_tcpip_hdr->ip_proto = IP_PROTO_TCP;
|
||||
p_tcpip_hdr->ip_payload = cpu_to_be16(ip_data_len);
|
||||
|
||||
tcp_header* p_tcp_hdr = (tcp_header *) (data_to_checksum+12);
|
||||
|
||||
p_tcp_hdr->th_sum = 0;
|
||||
|
||||
int tcp_checksum = ip_checksum(data_to_checksum, ip_data_len + 12);
|
||||
DPRINTF("+++ C+ mode TCP checksum %04x\n",
|
||||
tcp_checksum);
|
||||
|
||||
p_tcp_hdr->th_sum = tcp_checksum;
|
||||
}
|
||||
else if ((txdw0 & CP_TX_UDPCS) && ip_protocol == IP_PROTO_UDP)
|
||||
{
|
||||
DPRINTF("+++ C+ mode calculating UDP checksum for "
|
||||
"packet with %d bytes data\n", ip_data_len);
|
||||
|
||||
ip_pseudo_header *p_udpip_hdr = (ip_pseudo_header *)data_to_checksum;
|
||||
p_udpip_hdr->zeros = 0;
|
||||
p_udpip_hdr->ip_proto = IP_PROTO_UDP;
|
||||
p_udpip_hdr->ip_payload = cpu_to_be16(ip_data_len);
|
||||
|
||||
udp_header *p_udp_hdr = (udp_header *) (data_to_checksum+12);
|
||||
|
||||
p_udp_hdr->uh_sum = 0;
|
||||
|
||||
int udp_checksum = ip_checksum(data_to_checksum, ip_data_len + 12);
|
||||
DPRINTF("+++ C+ mode UDP checksum %04x\n",
|
||||
udp_checksum);
|
||||
|
||||
p_udp_hdr->uh_sum = udp_checksum;
|
||||
}
|
||||
|
||||
/* restore IP header */
|
||||
memcpy(eth_payload_data, saved_ip_header, hlen);
|
||||
}
|
||||
}
|
||||
|
||||
skip_offload:
|
||||
/* update tally counter */
|
||||
++s->tally_counters.TxOk;
|
||||
|
||||
|
|
|
@ -1138,7 +1138,8 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q)
|
|||
ssize_t ret, len;
|
||||
unsigned int out_num = elem.out_num;
|
||||
struct iovec *out_sg = &elem.out_sg[0];
|
||||
struct iovec sg[VIRTQUEUE_MAX_SIZE];
|
||||
struct iovec sg[VIRTQUEUE_MAX_SIZE], sg2[VIRTQUEUE_MAX_SIZE + 1];
|
||||
struct virtio_net_hdr_mrg_rxbuf mhdr;
|
||||
|
||||
if (out_num < 1) {
|
||||
error_report("virtio-net header not in first element");
|
||||
|
@ -1146,13 +1147,25 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q)
|
|||
}
|
||||
|
||||
if (n->has_vnet_hdr) {
|
||||
if (out_sg[0].iov_len < n->guest_hdr_len) {
|
||||
if (iov_to_buf(out_sg, out_num, 0, &mhdr, n->guest_hdr_len) <
|
||||
n->guest_hdr_len) {
|
||||
error_report("virtio-net header incorrect");
|
||||
exit(1);
|
||||
}
|
||||
virtio_net_hdr_swap(vdev, (void *) out_sg[0].iov_base);
|
||||
if (virtio_needs_swap(vdev)) {
|
||||
virtio_net_hdr_swap(vdev, (void *) &mhdr);
|
||||
sg2[0].iov_base = &mhdr;
|
||||
sg2[0].iov_len = n->guest_hdr_len;
|
||||
out_num = iov_copy(&sg2[1], ARRAY_SIZE(sg2) - 1,
|
||||
out_sg, out_num,
|
||||
n->guest_hdr_len, -1);
|
||||
if (out_num == VIRTQUEUE_MAX_SIZE) {
|
||||
goto drop;
|
||||
}
|
||||
out_num += 1;
|
||||
out_sg = sg2;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If host wants to see the guest header as is, we can
|
||||
* pass it on unchanged. Otherwise, copy just the parts
|
||||
|
@ -1182,7 +1195,7 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q)
|
|||
}
|
||||
|
||||
len += ret;
|
||||
|
||||
drop:
|
||||
virtqueue_push(q->tx_vq, &elem, 0);
|
||||
virtio_notify(vdev, q->tx_vq);
|
||||
|
||||
|
@ -1309,7 +1322,7 @@ static void virtio_net_set_multiqueue(VirtIONet *n, int multiqueue)
|
|||
|
||||
n->multiqueue = multiqueue;
|
||||
|
||||
for (i = 2; i <= n->max_queues * 2 + 1; i++) {
|
||||
for (i = 2; i < n->max_queues * 2 + 1; i++) {
|
||||
virtio_del_queue(vdev, i);
|
||||
}
|
||||
|
||||
|
|
|
@ -218,7 +218,7 @@ static Property s390_ipl_properties[] = {
|
|||
* - -1 if no valid boot device was found
|
||||
* - ccw id of the boot device otherwise
|
||||
*/
|
||||
static uint32_t s390_update_iplstate(CPUS390XState *env, S390IPLState *ipl)
|
||||
static uint64_t s390_update_iplstate(CPUS390XState *env, S390IPLState *ipl)
|
||||
{
|
||||
DeviceState *dev_st;
|
||||
|
||||
|
@ -248,7 +248,7 @@ static uint32_t s390_update_iplstate(CPUS390XState *env, S390IPLState *ipl)
|
|||
|
||||
return -1;
|
||||
out:
|
||||
return ipl->cssid << 24 | ipl->ssid << 16 | ipl->devno;
|
||||
return (uint32_t) (ipl->cssid << 24 | ipl->ssid << 16 | ipl->devno);
|
||||
}
|
||||
|
||||
int s390_ipl_update_diag308(IplParameterBlock *iplb)
|
||||
|
|
|
@ -295,6 +295,25 @@ static int virtio_ccw_set_vqs(SubchDev *sch, uint64_t addr, uint32_t align,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void virtio_ccw_reset_virtio(VirtioCcwDevice *dev, VirtIODevice *vdev)
|
||||
{
|
||||
virtio_ccw_stop_ioeventfd(dev);
|
||||
virtio_reset(vdev);
|
||||
if (dev->indicators) {
|
||||
release_indicator(&dev->routes.adapter, dev->indicators);
|
||||
dev->indicators = NULL;
|
||||
}
|
||||
if (dev->indicators2) {
|
||||
release_indicator(&dev->routes.adapter, dev->indicators2);
|
||||
dev->indicators2 = NULL;
|
||||
}
|
||||
if (dev->summary_indicator) {
|
||||
release_indicator(&dev->routes.adapter, dev->summary_indicator);
|
||||
dev->summary_indicator = NULL;
|
||||
}
|
||||
dev->sch->thinint_active = false;
|
||||
}
|
||||
|
||||
static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw)
|
||||
{
|
||||
int ret;
|
||||
|
@ -351,8 +370,7 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw)
|
|||
}
|
||||
break;
|
||||
case CCW_CMD_VDEV_RESET:
|
||||
virtio_ccw_stop_ioeventfd(dev);
|
||||
virtio_reset(vdev);
|
||||
virtio_ccw_reset_virtio(dev, vdev);
|
||||
ret = 0;
|
||||
break;
|
||||
case CCW_CMD_READ_FEAT:
|
||||
|
@ -480,7 +498,7 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw)
|
|||
}
|
||||
virtio_set_status(vdev, status);
|
||||
if (vdev->status == 0) {
|
||||
virtio_reset(vdev);
|
||||
virtio_ccw_reset_virtio(dev, vdev);
|
||||
}
|
||||
if (status & VIRTIO_CONFIG_S_DRIVER_OK) {
|
||||
virtio_ccw_start_ioeventfd(dev);
|
||||
|
@ -1098,21 +1116,8 @@ static void virtio_ccw_reset(DeviceState *d)
|
|||
VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
|
||||
VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
|
||||
|
||||
virtio_ccw_stop_ioeventfd(dev);
|
||||
virtio_reset(vdev);
|
||||
virtio_ccw_reset_virtio(dev, vdev);
|
||||
css_reset_sch(dev->sch);
|
||||
if (dev->indicators) {
|
||||
release_indicator(&dev->routes.adapter, dev->indicators);
|
||||
dev->indicators = NULL;
|
||||
}
|
||||
if (dev->indicators2) {
|
||||
release_indicator(&dev->routes.adapter, dev->indicators2);
|
||||
dev->indicators2 = NULL;
|
||||
}
|
||||
if (dev->summary_indicator) {
|
||||
release_indicator(&dev->routes.adapter, dev->summary_indicator);
|
||||
dev->summary_indicator = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void virtio_ccw_vmstate_change(DeviceState *d, bool running)
|
||||
|
|
|
@ -1239,10 +1239,15 @@ int scsi_cdb_length(uint8_t *buf) {
|
|||
int scsi_req_parse_cdb(SCSIDevice *dev, SCSICommand *cmd, uint8_t *buf)
|
||||
{
|
||||
int rc;
|
||||
int len;
|
||||
|
||||
cmd->lba = -1;
|
||||
cmd->len = scsi_cdb_length(buf);
|
||||
len = scsi_cdb_length(buf);
|
||||
if (len < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
cmd->len = len;
|
||||
switch (dev->type) {
|
||||
case TYPE_TAPE:
|
||||
rc = scsi_req_stream_xfer(cmd, dev, buf);
|
||||
|
|
|
@ -196,6 +196,12 @@ static uint64_t pit_ioport_read(void *opaque, hwaddr addr,
|
|||
PITChannelState *s;
|
||||
|
||||
addr &= 3;
|
||||
|
||||
if (addr == 3) {
|
||||
/* Mode/Command register is write only, read is ignored */
|
||||
return 0;
|
||||
}
|
||||
|
||||
s = &pit->channels[addr];
|
||||
if (s->status_latched) {
|
||||
s->status_latched = 0;
|
||||
|
|
|
@ -1310,6 +1310,10 @@ static int usbnet_can_receive(NetClientState *nc)
|
|||
{
|
||||
USBNetState *s = qemu_get_nic_opaque(nc);
|
||||
|
||||
if (!s->dev.config) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (is_rndis(s) && s->rndis_state != RNDIS_DATA_INITIALIZED) {
|
||||
return 1;
|
||||
}
|
||||
|
|
|
@ -1516,7 +1516,7 @@ static uint64_t vfio_rtl8168_window_quirk_read(void *opaque,
|
|||
memory_region_name(&quirk->mem),
|
||||
vdev->vbasedev.name);
|
||||
|
||||
return quirk->data.address_match ^ 0x10000000U;
|
||||
return quirk->data.address_match ^ 0x80000000U;
|
||||
}
|
||||
break;
|
||||
case 0: /* data */
|
||||
|
@ -1554,7 +1554,7 @@ static void vfio_rtl8168_window_quirk_write(void *opaque, hwaddr addr,
|
|||
switch (addr) {
|
||||
case 4: /* address */
|
||||
if ((data & 0x7fff0000) == 0x10000) {
|
||||
if (data & 0x10000000U &&
|
||||
if (data & 0x80000000U &&
|
||||
vdev->pdev.cap_present & QEMU_PCI_CAP_MSIX) {
|
||||
|
||||
trace_vfio_rtl8168_window_quirk_write_table(
|
||||
|
@ -1562,8 +1562,9 @@ static void vfio_rtl8168_window_quirk_write(void *opaque, hwaddr addr,
|
|||
vdev->vbasedev.name);
|
||||
|
||||
io_mem_write(&vdev->pdev.msix_table_mmio,
|
||||
(hwaddr)(quirk->data.address_match & 0xfff),
|
||||
data, size);
|
||||
(hwaddr)(data & 0xfff),
|
||||
(uint64_t)quirk->data.address_mask,
|
||||
size);
|
||||
}
|
||||
|
||||
quirk->data.flags = 1;
|
||||
|
@ -3555,7 +3556,6 @@ static Property vfio_pci_dev_properties[] = {
|
|||
VFIO_FEATURE_ENABLE_VGA_BIT, false),
|
||||
DEFINE_PROP_BIT("x-req", VFIOPCIDevice, features,
|
||||
VFIO_FEATURE_ENABLE_REQ_BIT, true),
|
||||
DEFINE_PROP_INT32("bootindex", VFIOPCIDevice, bootindex, -1),
|
||||
DEFINE_PROP_BOOL("x-mmap", VFIOPCIDevice, vbasedev.allow_mmap, true),
|
||||
/*
|
||||
* TODO - support passed fds... is this necessary?
|
||||
|
|
|
@ -921,7 +921,7 @@ int vhost_dev_enable_notifiers(struct vhost_dev *hdev, VirtIODevice *vdev)
|
|||
BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev)));
|
||||
VirtioBusState *vbus = VIRTIO_BUS(qbus);
|
||||
VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(vbus);
|
||||
int i, r;
|
||||
int i, r, e;
|
||||
if (!k->set_host_notifier) {
|
||||
fprintf(stderr, "binding does not support host notifiers\n");
|
||||
r = -ENOSYS;
|
||||
|
@ -939,12 +939,12 @@ int vhost_dev_enable_notifiers(struct vhost_dev *hdev, VirtIODevice *vdev)
|
|||
return 0;
|
||||
fail_vq:
|
||||
while (--i >= 0) {
|
||||
r = k->set_host_notifier(qbus->parent, hdev->vq_index + i, false);
|
||||
if (r < 0) {
|
||||
e = k->set_host_notifier(qbus->parent, hdev->vq_index + i, false);
|
||||
if (e < 0) {
|
||||
fprintf(stderr, "vhost VQ %d notifier cleanup error: %d\n", i, -r);
|
||||
fflush(stderr);
|
||||
}
|
||||
assert (r >= 0);
|
||||
assert (e >= 0);
|
||||
}
|
||||
fail:
|
||||
return r;
|
||||
|
|
|
@ -361,6 +361,10 @@ bool bdrv_unallocated_blocks_are_zero(BlockDriverState *bs);
|
|||
bool bdrv_can_write_zeroes_with_unmap(BlockDriverState *bs);
|
||||
int64_t bdrv_get_block_status(BlockDriverState *bs, int64_t sector_num,
|
||||
int nb_sectors, int *pnum);
|
||||
int64_t bdrv_get_block_status_above(BlockDriverState *bs,
|
||||
BlockDriverState *base,
|
||||
int64_t sector_num,
|
||||
int nb_sectors, int *pnum);
|
||||
int bdrv_is_allocated(BlockDriverState *bs, int64_t sector_num, int nb_sectors,
|
||||
int *pnum);
|
||||
int bdrv_is_allocated_above(BlockDriverState *top, BlockDriverState *base,
|
||||
|
|
|
@ -579,6 +579,7 @@ void commit_active_start(BlockDriverState *bs, BlockDriverState *base,
|
|||
* @mode: Whether to collapse all images in the chain to the target.
|
||||
* @on_source_error: The action to take upon error reading from the source.
|
||||
* @on_target_error: The action to take upon error writing to the target.
|
||||
* @unmap: Whether to unmap target where source sectors only contain zeroes.
|
||||
* @cb: Completion function for the job.
|
||||
* @opaque: Opaque pointer value passed to @cb.
|
||||
* @errp: Error object.
|
||||
|
@ -593,6 +594,7 @@ void mirror_start(BlockDriverState *bs, BlockDriverState *target,
|
|||
int64_t speed, int64_t granularity, int64_t buf_size,
|
||||
MirrorSyncMode mode, BlockdevOnError on_source_error,
|
||||
BlockdevOnError on_target_error,
|
||||
bool unmap,
|
||||
BlockCompletionFunc *cb,
|
||||
void *opaque, Error **errp);
|
||||
|
||||
|
|
|
@ -126,6 +126,15 @@ static inline uint64_t virtio_ldq_p(VirtIODevice *vdev, const void *ptr)
|
|||
}
|
||||
}
|
||||
|
||||
static inline bool virtio_needs_swap(VirtIODevice *vdev)
|
||||
{
|
||||
#ifdef HOST_WORDS_BIGENDIAN
|
||||
return virtio_access_is_big_endian(vdev) ? false : true;
|
||||
#else
|
||||
return virtio_access_is_big_endian(vdev) ? true : false;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline uint16_t virtio_tswap16(VirtIODevice *vdev, uint16_t s)
|
||||
{
|
||||
#ifdef HOST_WORDS_BIGENDIAN
|
||||
|
|
17
kvm-all.c
17
kvm-all.c
|
@ -1142,9 +1142,17 @@ static int kvm_irqchip_get_virq(KVMState *s)
|
|||
uint32_t *word = s->used_gsi_bitmap;
|
||||
int max_words = ALIGN(s->gsi_count, 32) / 32;
|
||||
int i, bit;
|
||||
bool retry = true;
|
||||
|
||||
again:
|
||||
/*
|
||||
* PIC and IOAPIC share the first 16 GSI numbers, thus the available
|
||||
* GSI numbers are more than the number of IRQ route. Allocating a GSI
|
||||
* number can succeed even though a new route entry cannot be added.
|
||||
* When this happens, flush dynamic MSI entries to free IRQ route entries.
|
||||
*/
|
||||
if (!s->direct_msi && s->irq_routes->nr == s->gsi_count) {
|
||||
kvm_flush_dynamic_msi_routes(s);
|
||||
}
|
||||
|
||||
/* Return the lowest unused GSI in the bitmap */
|
||||
for (i = 0; i < max_words; i++) {
|
||||
bit = ffs(~word[i]);
|
||||
|
@ -1154,11 +1162,6 @@ again:
|
|||
|
||||
return bit - 1 + i * 32;
|
||||
}
|
||||
if (!s->direct_msi && retry) {
|
||||
retry = false;
|
||||
kvm_flush_dynamic_msi_routes(s);
|
||||
goto again;
|
||||
}
|
||||
return -ENOSPC;
|
||||
|
||||
}
|
||||
|
|
2
nbd.c
2
nbd.c
|
@ -681,7 +681,7 @@ int nbd_init(int fd, int csock, uint32_t flags, off_t size)
|
|||
|
||||
TRACE("Setting size to %zd block(s)", (size_t)(size / BDRV_SECTOR_SIZE));
|
||||
|
||||
if (ioctl(fd, NBD_SET_SIZE_BLOCKS, size / (size_t)BDRV_SECTOR_SIZE) < 0) {
|
||||
if (ioctl(fd, NBD_SET_SIZE_BLOCKS, (size_t)(size / BDRV_SECTOR_SIZE)) < 0) {
|
||||
int serrno = errno;
|
||||
LOG("Failed setting size (in blocks)");
|
||||
return -serrno;
|
||||
|
|
|
@ -2669,6 +2669,7 @@
|
|||
# Since: 1.3.0
|
||||
#
|
||||
# 'unmapped' and 'pause' since 2.0
|
||||
# 'ro' and 'kp_comma' since 2.4
|
||||
##
|
||||
{ 'enum': 'QKeyCode',
|
||||
'data': [ 'unmapped',
|
||||
|
@ -2686,7 +2687,8 @@
|
|||
'kp_9', 'less', 'f11', 'f12', 'print', 'home', 'pgup', 'pgdn', 'end',
|
||||
'left', 'up', 'down', 'right', 'insert', 'delete', 'stop', 'again',
|
||||
'props', 'undo', 'front', 'copy', 'open', 'paste', 'find', 'cut',
|
||||
'lf', 'help', 'meta_l', 'meta_r', 'compose', 'pause' ] }
|
||||
'lf', 'help', 'meta_l', 'meta_r', 'compose', 'pause', 'ro',
|
||||
'kp_comma' ] }
|
||||
|
||||
##
|
||||
# @KeyValue
|
||||
|
|
|
@ -943,6 +943,11 @@
|
|||
# @on-target-error: #optional the action to take on an error on the target,
|
||||
# default 'report' (no limitations, since this applies to
|
||||
# a different block device than @device).
|
||||
# @unmap: #optional Whether to try to unmap target sectors where source has
|
||||
# only zero. If true, and target unallocated sectors will read as zero,
|
||||
# target image sectors will be unmapped; otherwise, zeroes will be
|
||||
# written. Both will result in identical contents.
|
||||
# Default is true. (Since 2.4)
|
||||
#
|
||||
# Returns: nothing on success
|
||||
# If @device is not a valid block device, DeviceNotFound
|
||||
|
@ -955,7 +960,8 @@
|
|||
'sync': 'MirrorSyncMode', '*mode': 'NewImageMode',
|
||||
'*speed': 'int', '*granularity': 'uint32',
|
||||
'*buf-size': 'int', '*on-source-error': 'BlockdevOnError',
|
||||
'*on-target-error': 'BlockdevOnError' } }
|
||||
'*on-target-error': 'BlockdevOnError',
|
||||
'*unmap': 'bool' } }
|
||||
|
||||
##
|
||||
# @block_set_io_throttle:
|
||||
|
|
13
qemu-char.c
13
qemu-char.c
|
@ -2797,7 +2797,10 @@ static ssize_t tcp_chr_recv(CharDriverState *chr, char *buf, size_t len)
|
|||
#ifdef MSG_CMSG_CLOEXEC
|
||||
flags |= MSG_CMSG_CLOEXEC;
|
||||
#endif
|
||||
ret = recvmsg(s->fd, &msg, flags);
|
||||
do {
|
||||
ret = recvmsg(s->fd, &msg, flags);
|
||||
} while (ret == -1 && errno == EINTR);
|
||||
|
||||
if (ret > 0 && s->is_unix) {
|
||||
unix_process_msgfd(chr, &msg);
|
||||
}
|
||||
|
@ -2808,7 +2811,13 @@ static ssize_t tcp_chr_recv(CharDriverState *chr, char *buf, size_t len)
|
|||
static ssize_t tcp_chr_recv(CharDriverState *chr, char *buf, size_t len)
|
||||
{
|
||||
TCPCharDriver *s = chr->opaque;
|
||||
return qemu_recv(s->fd, buf, len, 0);
|
||||
ssize_t ret;
|
||||
|
||||
do {
|
||||
ret = qemu_recv(s->fd, buf, len, 0);
|
||||
} while (ret == -1 && socket_error() == EINTR);
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
@ -1332,11 +1332,7 @@ void qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp)
|
|||
struct FsMount *mount;
|
||||
int fd;
|
||||
Error *local_err = NULL;
|
||||
struct fstrim_range r = {
|
||||
.start = 0,
|
||||
.len = -1,
|
||||
.minlen = has_minimum ? minimum : 0,
|
||||
};
|
||||
struct fstrim_range r;
|
||||
|
||||
slog("guest-fstrim called");
|
||||
|
||||
|
@ -1360,6 +1356,9 @@ void qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp)
|
|||
* error means an unexpected error, so return it in those cases. In
|
||||
* some other cases ENOTTY will be reported (e.g. CD-ROMs).
|
||||
*/
|
||||
r.start = 0;
|
||||
r.len = -1;
|
||||
r.minlen = has_minimum ? minimum : 0;
|
||||
ret = ioctl(fd, FITRIM, &r);
|
||||
if (ret == -1) {
|
||||
if (errno != ENOTTY && errno != EOPNOTSUPP) {
|
||||
|
|
|
@ -1380,6 +1380,7 @@ EQMP
|
|||
.args_type = "sync:s,device:B,target:s,speed:i?,mode:s?,format:s?,"
|
||||
"node-name:s?,replaces:s?,"
|
||||
"on-source-error:s?,on-target-error:s?,"
|
||||
"unmap:b?,"
|
||||
"granularity:i?,buf-size:i?",
|
||||
.mhandler.cmd_new = qmp_marshal_input_drive_mirror,
|
||||
},
|
||||
|
@ -1419,6 +1420,8 @@ Arguments:
|
|||
(BlockdevOnError, default 'report')
|
||||
- "on-target-error": the action to take on an error on the target
|
||||
(BlockdevOnError, default 'report')
|
||||
- "unmap": whether the target sectors should be discarded where source has only
|
||||
zeroes. (json-bool, optional, default true)
|
||||
|
||||
The default value of the granularity is the image cluster size clamped
|
||||
between 4096 and 65536, if the image format defines one. If the format
|
||||
|
|
|
@ -21,6 +21,9 @@ class QMPConnectError(QMPError):
|
|||
class QMPCapabilitiesError(QMPError):
|
||||
pass
|
||||
|
||||
class QMPTimeoutError(QMPError):
|
||||
pass
|
||||
|
||||
class QEMUMonitorProtocol:
|
||||
def __init__(self, address, server=False):
|
||||
"""
|
||||
|
@ -72,6 +75,44 @@ class QEMUMonitorProtocol:
|
|||
|
||||
error = socket.error
|
||||
|
||||
def __get_events(self, wait=False):
|
||||
"""
|
||||
Check for new events in the stream and cache them in __events.
|
||||
|
||||
@param wait (bool): block until an event is available.
|
||||
@param wait (float): If wait is a float, treat it as a timeout value.
|
||||
|
||||
@raise QMPTimeoutError: If a timeout float is provided and the timeout
|
||||
period elapses.
|
||||
@raise QMPConnectError: If wait is True but no events could be retrieved
|
||||
or if some other error occurred.
|
||||
"""
|
||||
|
||||
# Check for new events regardless and pull them into the cache:
|
||||
self.__sock.setblocking(0)
|
||||
try:
|
||||
self.__json_read()
|
||||
except socket.error, err:
|
||||
if err[0] == errno.EAGAIN:
|
||||
# No data available
|
||||
pass
|
||||
self.__sock.setblocking(1)
|
||||
|
||||
# Wait for new events, if needed.
|
||||
# if wait is 0.0, this means "no wait" and is also implicitly false.
|
||||
if not self.__events and wait:
|
||||
if isinstance(wait, float):
|
||||
self.__sock.settimeout(wait)
|
||||
try:
|
||||
ret = self.__json_read(only_event=True)
|
||||
except socket.timeout:
|
||||
raise QMPTimeoutError("Timeout waiting for event")
|
||||
except:
|
||||
raise QMPConnectError("Error while reading from socket")
|
||||
if ret is None:
|
||||
raise QMPConnectError("Error while reading from socket")
|
||||
self.__sock.settimeout(None)
|
||||
|
||||
def connect(self, negotiate=True):
|
||||
"""
|
||||
Connect to the QMP Monitor and perform capabilities negotiation.
|
||||
|
@ -140,43 +181,37 @@ class QEMUMonitorProtocol:
|
|||
"""
|
||||
Get and delete the first available QMP event.
|
||||
|
||||
@param wait: block until an event is available (bool)
|
||||
@param wait (bool): block until an event is available.
|
||||
@param wait (float): If wait is a float, treat it as a timeout value.
|
||||
|
||||
@raise QMPTimeoutError: If a timeout float is provided and the timeout
|
||||
period elapses.
|
||||
@raise QMPConnectError: If wait is True but no events could be retrieved
|
||||
or if some other error occurred.
|
||||
|
||||
@return The first available QMP event, or None.
|
||||
"""
|
||||
self.__sock.setblocking(0)
|
||||
try:
|
||||
self.__json_read()
|
||||
except socket.error, err:
|
||||
if err[0] == errno.EAGAIN:
|
||||
# No data available
|
||||
pass
|
||||
self.__sock.setblocking(1)
|
||||
if not self.__events and wait:
|
||||
self.__json_read(only_event=True)
|
||||
event = self.__events[0]
|
||||
del self.__events[0]
|
||||
return event
|
||||
self.__get_events(wait)
|
||||
|
||||
if self.__events:
|
||||
return self.__events.pop(0)
|
||||
return None
|
||||
|
||||
def get_events(self, wait=False):
|
||||
"""
|
||||
Get a list of available QMP events.
|
||||
|
||||
@param wait: block until an event is available (bool)
|
||||
"""
|
||||
self.__sock.setblocking(0)
|
||||
try:
|
||||
self.__json_read()
|
||||
except socket.error, err:
|
||||
if err[0] == errno.EAGAIN:
|
||||
# No data available
|
||||
pass
|
||||
self.__sock.setblocking(1)
|
||||
if not self.__events and wait:
|
||||
ret = self.__json_read(only_event=True)
|
||||
if ret == None:
|
||||
# We are in blocking mode, if don't get anything, something
|
||||
# went wrong
|
||||
raise QMPConnectError("Error while reading from socket")
|
||||
@param wait (bool): block until an event is available.
|
||||
@param wait (float): If wait is a float, treat it as a timeout value.
|
||||
|
||||
@raise QMPTimeoutError: If a timeout float is provided and the timeout
|
||||
period elapses.
|
||||
@raise QMPConnectError: If wait is True but no events could be retrieved
|
||||
or if some other error occurred.
|
||||
|
||||
@return The list of available QMP events.
|
||||
"""
|
||||
self.__get_events(wait)
|
||||
return self.__events
|
||||
|
||||
def clear_events(self):
|
||||
|
|
|
@ -8423,18 +8423,53 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn)
|
|||
}
|
||||
} else {
|
||||
int address_offset;
|
||||
int load;
|
||||
bool load = insn & (1 << 20);
|
||||
bool doubleword = false;
|
||||
/* Misc load/store */
|
||||
rn = (insn >> 16) & 0xf;
|
||||
rd = (insn >> 12) & 0xf;
|
||||
|
||||
if (!load && (sh & 2)) {
|
||||
/* doubleword */
|
||||
ARCH(5TE);
|
||||
if (rd & 1) {
|
||||
/* UNPREDICTABLE; we choose to UNDEF */
|
||||
goto illegal_op;
|
||||
}
|
||||
load = (sh & 1) == 0;
|
||||
doubleword = true;
|
||||
}
|
||||
|
||||
addr = load_reg(s, rn);
|
||||
if (insn & (1 << 24))
|
||||
gen_add_datah_offset(s, insn, 0, addr);
|
||||
address_offset = 0;
|
||||
if (insn & (1 << 20)) {
|
||||
|
||||
if (doubleword) {
|
||||
if (!load) {
|
||||
/* store */
|
||||
tmp = load_reg(s, rd);
|
||||
gen_aa32_st32(tmp, addr, get_mem_index(s));
|
||||
tcg_temp_free_i32(tmp);
|
||||
tcg_gen_addi_i32(addr, addr, 4);
|
||||
tmp = load_reg(s, rd + 1);
|
||||
gen_aa32_st32(tmp, addr, get_mem_index(s));
|
||||
tcg_temp_free_i32(tmp);
|
||||
} else {
|
||||
/* load */
|
||||
tmp = tcg_temp_new_i32();
|
||||
gen_aa32_ld32u(tmp, addr, get_mem_index(s));
|
||||
store_reg(s, rd, tmp);
|
||||
tcg_gen_addi_i32(addr, addr, 4);
|
||||
tmp = tcg_temp_new_i32();
|
||||
gen_aa32_ld32u(tmp, addr, get_mem_index(s));
|
||||
rd++;
|
||||
}
|
||||
address_offset = -4;
|
||||
} else if (load) {
|
||||
/* load */
|
||||
tmp = tcg_temp_new_i32();
|
||||
switch(sh) {
|
||||
switch (sh) {
|
||||
case 1:
|
||||
gen_aa32_ld16u(tmp, addr, get_mem_index(s));
|
||||
break;
|
||||
|
@ -8446,38 +8481,11 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn)
|
|||
gen_aa32_ld16s(tmp, addr, get_mem_index(s));
|
||||
break;
|
||||
}
|
||||
load = 1;
|
||||
} else if (sh & 2) {
|
||||
ARCH(5TE);
|
||||
/* doubleword */
|
||||
if (sh & 1) {
|
||||
/* store */
|
||||
tmp = load_reg(s, rd);
|
||||
gen_aa32_st32(tmp, addr, get_mem_index(s));
|
||||
tcg_temp_free_i32(tmp);
|
||||
tcg_gen_addi_i32(addr, addr, 4);
|
||||
tmp = load_reg(s, rd + 1);
|
||||
gen_aa32_st32(tmp, addr, get_mem_index(s));
|
||||
tcg_temp_free_i32(tmp);
|
||||
load = 0;
|
||||
} else {
|
||||
/* load */
|
||||
tmp = tcg_temp_new_i32();
|
||||
gen_aa32_ld32u(tmp, addr, get_mem_index(s));
|
||||
store_reg(s, rd, tmp);
|
||||
tcg_gen_addi_i32(addr, addr, 4);
|
||||
tmp = tcg_temp_new_i32();
|
||||
gen_aa32_ld32u(tmp, addr, get_mem_index(s));
|
||||
rd++;
|
||||
load = 1;
|
||||
}
|
||||
address_offset = -4;
|
||||
} else {
|
||||
/* store */
|
||||
tmp = load_reg(s, rd);
|
||||
gen_aa32_st16(tmp, addr, get_mem_index(s));
|
||||
tcg_temp_free_i32(tmp);
|
||||
load = 0;
|
||||
}
|
||||
/* Perform base writeback before the loaded value to
|
||||
ensure correct behavior with overlapping index registers.
|
||||
|
|
|
@ -240,10 +240,9 @@ int kvm_mips_set_ipi_interrupt(MIPSCPU *cpu, int irq, int level)
|
|||
static inline int kvm_mips_put_one_reg(CPUState *cs, uint64_t reg_id,
|
||||
int32_t *addr)
|
||||
{
|
||||
uint64_t val64 = *addr;
|
||||
struct kvm_one_reg cp0reg = {
|
||||
.id = reg_id,
|
||||
.addr = (uintptr_t)&val64
|
||||
.addr = (uintptr_t)addr
|
||||
};
|
||||
|
||||
return kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &cp0reg);
|
||||
|
@ -275,18 +274,12 @@ static inline int kvm_mips_put_one_reg64(CPUState *cs, uint64_t reg_id,
|
|||
static inline int kvm_mips_get_one_reg(CPUState *cs, uint64_t reg_id,
|
||||
int32_t *addr)
|
||||
{
|
||||
int ret;
|
||||
uint64_t val64 = 0;
|
||||
struct kvm_one_reg cp0reg = {
|
||||
.id = reg_id,
|
||||
.addr = (uintptr_t)&val64
|
||||
.addr = (uintptr_t)addr
|
||||
};
|
||||
|
||||
ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &cp0reg);
|
||||
if (ret >= 0) {
|
||||
*addr = val64;
|
||||
}
|
||||
return ret;
|
||||
return kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &cp0reg);
|
||||
}
|
||||
|
||||
static inline int kvm_mips_get_one_ulreg(CPUState *cs, uint64 reg_id,
|
||||
|
@ -640,12 +633,12 @@ int kvm_arch_put_registers(CPUState *cs, int level)
|
|||
|
||||
/* Set the registers based on QEMU's view of things */
|
||||
for (i = 0; i < 32; i++) {
|
||||
regs.gpr[i] = env->active_tc.gpr[i];
|
||||
regs.gpr[i] = (int64_t)(target_long)env->active_tc.gpr[i];
|
||||
}
|
||||
|
||||
regs.hi = env->active_tc.HI[0];
|
||||
regs.lo = env->active_tc.LO[0];
|
||||
regs.pc = env->active_tc.PC;
|
||||
regs.hi = (int64_t)(target_long)env->active_tc.HI[0];
|
||||
regs.lo = (int64_t)(target_long)env->active_tc.LO[0];
|
||||
regs.pc = (int64_t)(target_long)env->active_tc.PC;
|
||||
|
||||
ret = kvm_vcpu_ioctl(cs, KVM_SET_REGS, ®s);
|
||||
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
#include "sysemu/watchdog.h"
|
||||
#include "trace.h"
|
||||
#include "exec/gdbstub.h"
|
||||
#include "sysemu/hostmem.h"
|
||||
|
||||
//#define DEBUG_KVM
|
||||
|
||||
|
@ -302,16 +303,11 @@ static void kvm_get_smmu_info(PowerPCCPU *cpu, struct kvm_ppc_smmu_info *info)
|
|||
kvm_get_fallback_smmu_info(cpu, info);
|
||||
}
|
||||
|
||||
static long getrampagesize(void)
|
||||
static long gethugepagesize(const char *mem_path)
|
||||
{
|
||||
struct statfs fs;
|
||||
int ret;
|
||||
|
||||
if (!mem_path) {
|
||||
/* guest RAM is backed by normal anonymous pages */
|
||||
return getpagesize();
|
||||
}
|
||||
|
||||
do {
|
||||
ret = statfs(mem_path, &fs);
|
||||
} while (ret != 0 && errno == EINTR);
|
||||
|
@ -333,6 +329,55 @@ static long getrampagesize(void)
|
|||
return fs.f_bsize;
|
||||
}
|
||||
|
||||
static int find_max_supported_pagesize(Object *obj, void *opaque)
|
||||
{
|
||||
char *mem_path;
|
||||
long *hpsize_min = opaque;
|
||||
|
||||
if (object_dynamic_cast(obj, TYPE_MEMORY_BACKEND)) {
|
||||
mem_path = object_property_get_str(obj, "mem-path", NULL);
|
||||
if (mem_path) {
|
||||
long hpsize = gethugepagesize(mem_path);
|
||||
if (hpsize < *hpsize_min) {
|
||||
*hpsize_min = hpsize;
|
||||
}
|
||||
} else {
|
||||
*hpsize_min = getpagesize();
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long getrampagesize(void)
|
||||
{
|
||||
long hpsize = LONG_MAX;
|
||||
Object *memdev_root;
|
||||
|
||||
if (mem_path) {
|
||||
return gethugepagesize(mem_path);
|
||||
}
|
||||
|
||||
/* it's possible we have memory-backend objects with
|
||||
* hugepage-backed RAM. these may get mapped into system
|
||||
* address space via -numa parameters or memory hotplug
|
||||
* hooks. we want to take these into account, but we
|
||||
* also want to make sure these supported hugepage
|
||||
* sizes are applicable across the entire range of memory
|
||||
* we may boot from, so we take the min across all
|
||||
* backends, and assume normal pages in cases where a
|
||||
* backend isn't backed by hugepages.
|
||||
*/
|
||||
memdev_root = object_resolve_path("/objects", NULL);
|
||||
if (!memdev_root) {
|
||||
return getpagesize();
|
||||
}
|
||||
|
||||
object_child_foreach(memdev_root, find_max_supported_pagesize, &hpsize);
|
||||
|
||||
return (hpsize == LONG_MAX) ? getpagesize() : hpsize;
|
||||
}
|
||||
|
||||
static bool kvm_valid_page_size(uint32_t flags, long rampgsize, uint32_t shift)
|
||||
{
|
||||
if (!(flags & KVM_PPC_PAGE_SIZES_REAL)) {
|
||||
|
|
|
@ -963,9 +963,11 @@ static void tcg_out_tlb_load(TCGContext *s, TCGReg base, TCGReg addrl,
|
|||
}
|
||||
|
||||
/* Load the tlb comparator. */
|
||||
tcg_out_opc_imm(s, OPC_LW, TCG_TMP0, TCG_REG_A0, cmp_off + LO_OFF);
|
||||
if (TARGET_LONG_BITS == 64) {
|
||||
tcg_out_opc_imm(s, OPC_LW, TCG_TMP0, TCG_REG_A0, cmp_off + LO_OFF);
|
||||
tcg_out_opc_imm(s, OPC_LW, base, TCG_REG_A0, cmp_off + HI_OFF);
|
||||
} else {
|
||||
tcg_out_opc_imm(s, OPC_LW, TCG_TMP0, TCG_REG_A0, cmp_off);
|
||||
}
|
||||
|
||||
/* Mask the page bits, keeping the alignment bits to compare against.
|
||||
|
@ -1267,6 +1269,9 @@ static void tcg_out_addsub2(TCGContext *s, TCGReg rl, TCGReg rh, TCGReg al,
|
|||
if (cbl) {
|
||||
tcg_out_opc_imm(s, OPC_ADDIU, rl, al, bl);
|
||||
tcg_out_opc_imm(s, OPC_SLTIU, TCG_TMP0, rl, bl);
|
||||
} else if (rl == al && rl == bl) {
|
||||
tcg_out_opc_sa(s, OPC_SRL, TCG_TMP0, al, 31);
|
||||
tcg_out_opc_reg(s, OPC_ADDU, rl, al, bl);
|
||||
} else {
|
||||
tcg_out_opc_reg(s, OPC_ADDU, rl, al, bl);
|
||||
tcg_out_opc_reg(s, OPC_SLTU, TCG_TMP0, rl, (rl == bl ? al : bl));
|
||||
|
|
|
@ -78,6 +78,19 @@ for align in 512 4k; do
|
|||
echo
|
||||
echo "== verifying patterns (2) =="
|
||||
do_test $align "read -P 0x0 0x400 0x20000" "$TEST_IMG" | _filter_qemu_io
|
||||
|
||||
echo
|
||||
echo "== rewriting unaligned zeroes =="
|
||||
do_test $align "write -P 0xb 0x0 0x1000" "$TEST_IMG" | _filter_qemu_io
|
||||
do_test $align "write -z 0x200 0x200" "$TEST_IMG" | _filter_qemu_io
|
||||
|
||||
echo
|
||||
echo "== verifying patterns (3) =="
|
||||
do_test $align "read -P 0xb 0x0 0x200" "$TEST_IMG" | _filter_qemu_io
|
||||
do_test $align "read -P 0x0 0x200 0x200" "$TEST_IMG" | _filter_qemu_io
|
||||
do_test $align "read -P 0xb 0x400 0xc00" "$TEST_IMG" | _filter_qemu_io
|
||||
|
||||
echo
|
||||
done
|
||||
|
||||
# success, all done
|
||||
|
|
|
@ -27,6 +27,21 @@ wrote 65536/65536 bytes at offset 65536
|
|||
read 131072/131072 bytes at offset 1024
|
||||
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
|
||||
== rewriting unaligned zeroes ==
|
||||
wrote 4096/4096 bytes at offset 0
|
||||
4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote 512/512 bytes at offset 512
|
||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
|
||||
== verifying patterns (3) ==
|
||||
read 512/512 bytes at offset 0
|
||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
read 512/512 bytes at offset 512
|
||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
read 3072/3072 bytes at offset 1024
|
||||
3 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
|
||||
|
||||
== preparing image ==
|
||||
wrote 1024/1024 bytes at offset 512
|
||||
1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
|
@ -52,4 +67,19 @@ wrote 65536/65536 bytes at offset 65536
|
|||
== verifying patterns (2) ==
|
||||
read 131072/131072 bytes at offset 1024
|
||||
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
|
||||
== rewriting unaligned zeroes ==
|
||||
wrote 4096/4096 bytes at offset 0
|
||||
4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote 512/512 bytes at offset 512
|
||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
|
||||
== verifying patterns (3) ==
|
||||
read 512/512 bytes at offset 0
|
||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
read 512/512 bytes at offset 512
|
||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
read 3072/3072 bytes at offset 1024
|
||||
3 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
|
||||
*** done
|
||||
|
|
|
@ -34,38 +34,8 @@ quorum_img3 = os.path.join(iotests.test_dir, 'quorum3.img')
|
|||
quorum_repair_img = os.path.join(iotests.test_dir, 'quorum_repair.img')
|
||||
quorum_snapshot_file = os.path.join(iotests.test_dir, 'quorum_snapshot.img')
|
||||
|
||||
class ImageMirroringTestCase(iotests.QMPTestCase):
|
||||
'''Abstract base class for image mirroring test cases'''
|
||||
|
||||
def wait_ready(self, drive='drive0'):
|
||||
'''Wait until a block job BLOCK_JOB_READY event'''
|
||||
ready = False
|
||||
while not ready:
|
||||
for event in self.vm.get_qmp_events(wait=True):
|
||||
if event['event'] == 'BLOCK_JOB_READY':
|
||||
self.assert_qmp(event, 'data/type', 'mirror')
|
||||
self.assert_qmp(event, 'data/device', drive)
|
||||
ready = True
|
||||
|
||||
def wait_ready_and_cancel(self, drive='drive0'):
|
||||
self.wait_ready(drive=drive)
|
||||
event = self.cancel_and_wait(drive=drive)
|
||||
self.assertEquals(event['event'], 'BLOCK_JOB_COMPLETED')
|
||||
self.assert_qmp(event, 'data/type', 'mirror')
|
||||
self.assert_qmp(event, 'data/offset', event['data']['len'])
|
||||
|
||||
def complete_and_wait(self, drive='drive0', wait_ready=True):
|
||||
'''Complete a block job and wait for it to finish'''
|
||||
if wait_ready:
|
||||
self.wait_ready(drive=drive)
|
||||
|
||||
result = self.vm.qmp('block-job-complete', device=drive)
|
||||
self.assert_qmp(result, 'return', {})
|
||||
|
||||
event = self.wait_until_completed(drive=drive)
|
||||
self.assert_qmp(event, 'data/type', 'mirror')
|
||||
|
||||
class TestSingleDrive(ImageMirroringTestCase):
|
||||
class TestSingleDrive(iotests.QMPTestCase):
|
||||
image_len = 1 * 1024 * 1024 # MB
|
||||
|
||||
def setUp(self):
|
||||
|
@ -221,17 +191,9 @@ class TestSingleDriveUnalignedLength(TestSingleDrive):
|
|||
test_small_buffer2 = None
|
||||
test_large_cluster = None
|
||||
|
||||
class TestMirrorNoBacking(ImageMirroringTestCase):
|
||||
class TestMirrorNoBacking(iotests.QMPTestCase):
|
||||
image_len = 2 * 1024 * 1024 # MB
|
||||
|
||||
def complete_and_wait(self, drive='drive0', wait_ready=True):
|
||||
iotests.create_image(target_backing_img, TestMirrorNoBacking.image_len)
|
||||
return ImageMirroringTestCase.complete_and_wait(self, drive, wait_ready)
|
||||
|
||||
def compare_images(self, img1, img2):
|
||||
iotests.create_image(target_backing_img, TestMirrorNoBacking.image_len)
|
||||
return iotests.compare_images(img1, img2)
|
||||
|
||||
def setUp(self):
|
||||
iotests.create_image(backing_img, TestMirrorNoBacking.image_len)
|
||||
qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img)
|
||||
|
@ -242,7 +204,10 @@ class TestMirrorNoBacking(ImageMirroringTestCase):
|
|||
self.vm.shutdown()
|
||||
os.remove(test_img)
|
||||
os.remove(backing_img)
|
||||
os.remove(target_backing_img)
|
||||
try:
|
||||
os.remove(target_backing_img)
|
||||
except:
|
||||
pass
|
||||
os.remove(target_img)
|
||||
|
||||
def test_complete(self):
|
||||
|
@ -257,7 +222,7 @@ class TestMirrorNoBacking(ImageMirroringTestCase):
|
|||
result = self.vm.qmp('query-block')
|
||||
self.assert_qmp(result, 'return[0]/inserted/file', target_img)
|
||||
self.vm.shutdown()
|
||||
self.assertTrue(self.compare_images(test_img, target_img),
|
||||
self.assertTrue(iotests.compare_images(test_img, target_img),
|
||||
'target image does not match source after mirroring')
|
||||
|
||||
def test_cancel(self):
|
||||
|
@ -272,7 +237,7 @@ class TestMirrorNoBacking(ImageMirroringTestCase):
|
|||
result = self.vm.qmp('query-block')
|
||||
self.assert_qmp(result, 'return[0]/inserted/file', test_img)
|
||||
self.vm.shutdown()
|
||||
self.assertTrue(self.compare_images(test_img, target_img),
|
||||
self.assertTrue(iotests.compare_images(test_img, target_img),
|
||||
'target image does not match source after mirroring')
|
||||
|
||||
def test_large_cluster(self):
|
||||
|
@ -283,7 +248,6 @@ class TestMirrorNoBacking(ImageMirroringTestCase):
|
|||
%(TestMirrorNoBacking.image_len), target_backing_img)
|
||||
qemu_img('create', '-f', iotests.imgfmt, '-o', 'cluster_size=%d,backing_file=%s'
|
||||
% (TestMirrorNoBacking.image_len, target_backing_img), target_img)
|
||||
os.remove(target_backing_img)
|
||||
|
||||
result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
|
||||
mode='existing', target=target_img)
|
||||
|
@ -293,10 +257,10 @@ class TestMirrorNoBacking(ImageMirroringTestCase):
|
|||
result = self.vm.qmp('query-block')
|
||||
self.assert_qmp(result, 'return[0]/inserted/file', target_img)
|
||||
self.vm.shutdown()
|
||||
self.assertTrue(self.compare_images(test_img, target_img),
|
||||
self.assertTrue(iotests.compare_images(test_img, target_img),
|
||||
'target image does not match source after mirroring')
|
||||
|
||||
class TestMirrorResized(ImageMirroringTestCase):
|
||||
class TestMirrorResized(iotests.QMPTestCase):
|
||||
backing_len = 1 * 1024 * 1024 # MB
|
||||
image_len = 2 * 1024 * 1024 # MB
|
||||
|
||||
|
@ -344,7 +308,7 @@ class TestMirrorResized(ImageMirroringTestCase):
|
|||
self.assertTrue(iotests.compare_images(test_img, target_img),
|
||||
'target image does not match source after mirroring')
|
||||
|
||||
class TestReadErrors(ImageMirroringTestCase):
|
||||
class TestReadErrors(iotests.QMPTestCase):
|
||||
image_len = 2 * 1024 * 1024 # MB
|
||||
|
||||
# this should be a multiple of twice the default granularity
|
||||
|
@ -498,7 +462,7 @@ new_state = "1"
|
|||
self.assert_no_active_block_jobs()
|
||||
self.vm.shutdown()
|
||||
|
||||
class TestWriteErrors(ImageMirroringTestCase):
|
||||
class TestWriteErrors(iotests.QMPTestCase):
|
||||
image_len = 2 * 1024 * 1024 # MB
|
||||
|
||||
# this should be a multiple of twice the default granularity
|
||||
|
@ -624,7 +588,7 @@ new_state = "1"
|
|||
self.assert_no_active_block_jobs()
|
||||
self.vm.shutdown()
|
||||
|
||||
class TestSetSpeed(ImageMirroringTestCase):
|
||||
class TestSetSpeed(iotests.QMPTestCase):
|
||||
image_len = 80 * 1024 * 1024 # MB
|
||||
|
||||
def setUp(self):
|
||||
|
@ -690,7 +654,7 @@ class TestSetSpeed(ImageMirroringTestCase):
|
|||
|
||||
self.wait_ready_and_cancel()
|
||||
|
||||
class TestUnbackedSource(ImageMirroringTestCase):
|
||||
class TestUnbackedSource(iotests.QMPTestCase):
|
||||
image_len = 2 * 1024 * 1024 # MB
|
||||
|
||||
def setUp(self):
|
||||
|
@ -731,7 +695,7 @@ class TestUnbackedSource(ImageMirroringTestCase):
|
|||
self.complete_and_wait()
|
||||
self.assert_no_active_block_jobs()
|
||||
|
||||
class TestRepairQuorum(ImageMirroringTestCase):
|
||||
class TestRepairQuorum(iotests.QMPTestCase):
|
||||
""" This class test quorum file repair using drive-mirror.
|
||||
It's mostly a fork of TestSingleDrive """
|
||||
image_len = 1 * 1024 * 1024 # MB
|
||||
|
|
|
@ -93,6 +93,16 @@ $QEMU_IO -c "open -o l2-cache-size=1M,refcount-cache-size=0.25M $TEST_IMG" \
|
|||
-c 'read -P 42 0 64k' \
|
||||
| _filter_qemu_io
|
||||
|
||||
echo
|
||||
echo '=== Testing minimal L2 cache and COW ==='
|
||||
echo
|
||||
|
||||
$QEMU_IMG snapshot -c foo "$TEST_IMG"
|
||||
# This requires a COW operation, which accesses two L2 tables simultaneously
|
||||
# (COW source and destination), so there must be enough space in the cache to
|
||||
# place both tables there (and qemu should not crash)
|
||||
$QEMU_IO -c "open -o cache-size=0 $TEST_IMG" -c 'write 0 64k' | _filter_qemu_io
|
||||
|
||||
# success, all done
|
||||
echo '*** done'
|
||||
rm -f $seq.full
|
||||
|
|
|
@ -26,4 +26,9 @@ read 65536/65536 bytes at offset 0
|
|||
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
read 65536/65536 bytes at offset 0
|
||||
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
|
||||
=== Testing minimal L2 cache and COW ===
|
||||
|
||||
wrote 65536/65536 bytes at offset 0
|
||||
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
*** done
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# Test mirror with unmap
|
||||
#
|
||||
# Copyright (C) 2015 Red Hat, Inc.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
import time
|
||||
import os
|
||||
import iotests
|
||||
from iotests import qemu_img, qemu_io
|
||||
|
||||
test_img = os.path.join(iotests.test_dir, 'test.img')
|
||||
target_img = os.path.join(iotests.test_dir, 'target.img')
|
||||
|
||||
class TestSingleDrive(iotests.QMPTestCase):
|
||||
image_len = 2 * 1024 * 1024 # MB
|
||||
|
||||
def setUp(self):
|
||||
# Write data to the image so we can compare later
|
||||
qemu_img('create', '-f', iotests.imgfmt, test_img, str(TestSingleDrive.image_len))
|
||||
qemu_io('-f', iotests.imgfmt, '-c', 'write -P0x5d 0 2M', test_img)
|
||||
|
||||
self.vm = iotests.VM().add_drive(test_img, 'discard=unmap')
|
||||
self.vm.launch()
|
||||
|
||||
def tearDown(self):
|
||||
self.vm.shutdown()
|
||||
os.remove(test_img)
|
||||
try:
|
||||
os.remove(target_img)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
def test_mirror_discard(self):
|
||||
result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
|
||||
target=target_img)
|
||||
self.assert_qmp(result, 'return', {})
|
||||
self.vm.hmp_qemu_io('drive0', 'discard 0 64k')
|
||||
self.complete_and_wait('drive0')
|
||||
self.vm.shutdown()
|
||||
self.assertTrue(iotests.compare_images(test_img, target_img),
|
||||
'target image does not match source after mirroring')
|
||||
|
||||
if __name__ == '__main__':
|
||||
iotests.main(supported_fmts=['raw', 'qcow2'])
|
|
@ -0,0 +1,5 @@
|
|||
.
|
||||
----------------------------------------------------------------------
|
||||
Ran 1 tests
|
||||
|
||||
OK
|
|
@ -0,0 +1,54 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# Test VPC open of image with large Max Table Entries value.
|
||||
#
|
||||
# Copyright (C) 2015 Red Hat, Inc.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
# creator
|
||||
owner=jcody@redhat.com
|
||||
|
||||
seq=`basename $0`
|
||||
echo "QA output created by $seq"
|
||||
|
||||
here=`pwd`
|
||||
tmp=/tmp/$$
|
||||
status=1 # failure is the default!
|
||||
|
||||
_cleanup()
|
||||
{
|
||||
_cleanup_test_img
|
||||
}
|
||||
trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
|
||||
# get standard environment, filters and checks
|
||||
. ./common.rc
|
||||
. ./common.filter
|
||||
|
||||
_supported_fmt vpc
|
||||
_supported_proto generic
|
||||
_supported_os Linux
|
||||
|
||||
_use_sample_img afl5.img.bz2
|
||||
|
||||
echo
|
||||
echo "=== Verify image open and failure ===="
|
||||
$QEMU_IMG info "$TEST_IMG" 2>&1| _filter_testdir
|
||||
|
||||
# success, all done
|
||||
echo "*** done"
|
||||
rm -f $seq.full
|
||||
status=0
|
|
@ -0,0 +1,5 @@
|
|||
QA output created by 135
|
||||
|
||||
=== Verify image open and failure ====
|
||||
qemu-img: Could not open 'TEST_DIR/afl5.img': Max Table Entries too large (1073741825)
|
||||
*** done
|
|
@ -125,3 +125,5 @@
|
|||
123 rw auto quick
|
||||
128 rw auto quick
|
||||
130 rw auto quick
|
||||
132 rw auto quick
|
||||
135 rw auto
|
||||
|
|
|
@ -78,6 +78,23 @@ def create_image(name, size):
|
|||
i = i + 512
|
||||
file.close()
|
||||
|
||||
# Test if 'match' is a recursive subset of 'event'
|
||||
def event_match(event, match=None):
|
||||
if match is None:
|
||||
return True
|
||||
|
||||
for key in match:
|
||||
if key in event:
|
||||
if isinstance(event[key], dict):
|
||||
if not event_match(event[key], match[key]):
|
||||
return False
|
||||
elif event[key] != match[key]:
|
||||
return False
|
||||
else:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
class VM(object):
|
||||
'''A QEMU VM'''
|
||||
|
||||
|
@ -92,6 +109,7 @@ class VM(object):
|
|||
'-machine', 'accel=qtest',
|
||||
'-display', 'none', '-vga', 'none']
|
||||
self._num_drives = 0
|
||||
self._events = []
|
||||
|
||||
# This can be used to add an unused monitor instance.
|
||||
def add_monitor_telnet(self, ip, port):
|
||||
|
@ -202,14 +220,34 @@ class VM(object):
|
|||
|
||||
def get_qmp_event(self, wait=False):
|
||||
'''Poll for one queued QMP events and return it'''
|
||||
if len(self._events) > 0:
|
||||
return self._events.pop(0)
|
||||
return self._qmp.pull_event(wait=wait)
|
||||
|
||||
def get_qmp_events(self, wait=False):
|
||||
'''Poll for queued QMP events and return a list of dicts'''
|
||||
events = self._qmp.get_events(wait=wait)
|
||||
events.extend(self._events)
|
||||
del self._events[:]
|
||||
self._qmp.clear_events()
|
||||
return events
|
||||
|
||||
def event_wait(self, name='BLOCK_JOB_COMPLETED', timeout=60.0, match=None):
|
||||
# Search cached events
|
||||
for event in self._events:
|
||||
if (event['event'] == name) and event_match(event, match):
|
||||
self._events.remove(event)
|
||||
return event
|
||||
|
||||
# Poll for new events
|
||||
while True:
|
||||
event = self._qmp.pull_event(wait=timeout)
|
||||
if (event['event'] == name) and event_match(event, match):
|
||||
return event
|
||||
self._events.append(event)
|
||||
|
||||
return None
|
||||
|
||||
index_re = re.compile(r'([^\[]+)\[([^\]]+)\]')
|
||||
|
||||
class QMPTestCase(unittest.TestCase):
|
||||
|
@ -288,6 +326,29 @@ class QMPTestCase(unittest.TestCase):
|
|||
self.assert_no_active_block_jobs()
|
||||
return event
|
||||
|
||||
def wait_ready(self, drive='drive0'):
|
||||
'''Wait until a block job BLOCK_JOB_READY event'''
|
||||
f = {'data': {'type': 'mirror', 'device': drive } }
|
||||
event = self.vm.event_wait(name='BLOCK_JOB_READY', match=f)
|
||||
|
||||
def wait_ready_and_cancel(self, drive='drive0'):
|
||||
self.wait_ready(drive=drive)
|
||||
event = self.cancel_and_wait(drive=drive)
|
||||
self.assertEquals(event['event'], 'BLOCK_JOB_COMPLETED')
|
||||
self.assert_qmp(event, 'data/type', 'mirror')
|
||||
self.assert_qmp(event, 'data/offset', event['data']['len'])
|
||||
|
||||
def complete_and_wait(self, drive='drive0', wait_ready=True):
|
||||
'''Complete a block job and wait for it to finish'''
|
||||
if wait_ready:
|
||||
self.wait_ready(drive=drive)
|
||||
|
||||
result = self.vm.qmp('block-job-complete', device=drive)
|
||||
self.assert_qmp(result, 'return', {})
|
||||
|
||||
event = self.wait_until_completed(drive=drive)
|
||||
self.assert_qmp(event, 'data/type', 'mirror')
|
||||
|
||||
def notrun(reason):
|
||||
'''Skip this test suite'''
|
||||
# Each test in qemu-iotests has a number ("seq")
|
||||
|
|
Binary file not shown.
|
@ -128,6 +128,10 @@ static const int qcode_to_number[] = {
|
|||
|
||||
[Q_KEY_CODE_INSERT] = 0xd2,
|
||||
[Q_KEY_CODE_DELETE] = 0xd3,
|
||||
|
||||
[Q_KEY_CODE_RO] = 0x73,
|
||||
[Q_KEY_CODE_KP_COMMA] = 0x7e,
|
||||
|
||||
[Q_KEY_CODE_MAX] = 0,
|
||||
};
|
||||
|
||||
|
|
|
@ -511,6 +511,10 @@ static void handle_windowevent(SDL_Event *ev)
|
|||
{
|
||||
struct sdl2_console *scon = get_scon_from_window(ev->window.windowID);
|
||||
|
||||
if (!scon) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (ev->window.event) {
|
||||
case SDL_WINDOWEVENT_RESIZED:
|
||||
{
|
||||
|
|
|
@ -199,7 +199,7 @@ static void qemu_spice_create_update(SimpleSpiceDisplay *ssd)
|
|||
static const int blksize = 32;
|
||||
int blocks = (surface_width(ssd->ds) + blksize - 1) / blksize;
|
||||
int dirty_top[blocks];
|
||||
int y, yoff, x, xoff, blk, bw;
|
||||
int y, yoff1, yoff2, x, xoff, blk, bw;
|
||||
int bpp = surface_bytes_per_pixel(ssd->ds);
|
||||
uint8_t *guest, *mirror;
|
||||
|
||||
|
@ -214,13 +214,14 @@ static void qemu_spice_create_update(SimpleSpiceDisplay *ssd)
|
|||
guest = surface_data(ssd->ds);
|
||||
mirror = (void *)pixman_image_get_data(ssd->mirror);
|
||||
for (y = ssd->dirty.top; y < ssd->dirty.bottom; y++) {
|
||||
yoff = y * surface_stride(ssd->ds);
|
||||
yoff1 = y * surface_stride(ssd->ds);
|
||||
yoff2 = y * pixman_image_get_stride(ssd->mirror);
|
||||
for (x = ssd->dirty.left; x < ssd->dirty.right; x += blksize) {
|
||||
xoff = x * bpp;
|
||||
blk = x / blksize;
|
||||
bw = MIN(blksize, ssd->dirty.right - x);
|
||||
if (memcmp(guest + yoff + xoff,
|
||||
mirror + yoff + xoff,
|
||||
if (memcmp(guest + yoff1 + xoff,
|
||||
mirror + yoff2 + xoff,
|
||||
bw * bpp) == 0) {
|
||||
if (dirty_top[blk] != -1) {
|
||||
QXLRect update = {
|
||||
|
|
9
ui/vnc.c
9
ui/vnc.c
|
@ -3482,7 +3482,14 @@ void vnc_display_open(const char *id, Error **errp)
|
|||
|
||||
h = strrchr(vnc, ':');
|
||||
if (h) {
|
||||
char *host = g_strndup(vnc, h - vnc);
|
||||
char *host;
|
||||
size_t hlen = h - vnc;
|
||||
|
||||
if (vnc[0] == '[' && vnc[hlen - 1] == ']') {
|
||||
host = g_strndup(vnc + 1, hlen - 2);
|
||||
} else {
|
||||
host = g_strndup(vnc, hlen);
|
||||
}
|
||||
qemu_opt_set(sopts, "host", host, &error_abort);
|
||||
qemu_opt_set(wsopts, "host", host, &error_abort);
|
||||
qemu_opt_set(sopts, "port", h+1, &error_abort);
|
||||
|
|
|
@ -94,7 +94,7 @@ static const uint8_t x_keycode_to_pc_keycode[115] = {
|
|||
*/
|
||||
|
||||
static const uint8_t evdev_keycode_to_pc_keycode[61] = {
|
||||
0, /* 97 EVDEV - RO ("Internet" Keyboards) */
|
||||
0x73, /* 97 EVDEV - RO ("Internet" Keyboards) */
|
||||
0, /* 98 EVDEV - KATA (Katakana) */
|
||||
0, /* 99 EVDEV - HIRA (Hiragana) */
|
||||
0x79, /* 100 EVDEV - HENK (Henkan) */
|
||||
|
@ -126,7 +126,7 @@ static const uint8_t evdev_keycode_to_pc_keycode[61] = {
|
|||
0, /* 126 EVDEV - I126 ("Internet" Keyboards) */
|
||||
0, /* 127 EVDEV - PAUS */
|
||||
0, /* 128 EVDEV - ???? */
|
||||
0, /* 129 EVDEV - I129 ("Internet" Keyboards) */
|
||||
0x7e, /* 129 EVDEV - KP_COMMA (brazilian) */
|
||||
0xf1, /* 130 EVDEV - HNGL (Korean Hangul Latin toggle) */
|
||||
0xf2, /* 131 EVDEV - HJCV (Korean Hangul Hanja toggle) */
|
||||
0x7d, /* 132 AE13 (Yen)*/
|
||||
|
|
Loading…
Reference in New Issue