summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnthony Liguori <anthony@codemonkey.ws>2013-10-31 17:02:26 +0100
committerAnthony Liguori <anthony@codemonkey.ws>2013-10-31 17:02:26 +0100
commita126050a103c924b03388a9a64ce9af8c96b0969 (patch)
treec93b9c5cdb8b2a0256845e596e198f1c65c4fcde
parentef5cfe5bbd8bb05a51afaf7ab313769eb9ef44b6 (diff)
parentf4c129a38a5430b7342a7a23f53a22831154612f (diff)
downloadqemu-a126050a103c924b03388a9a64ce9af8c96b0969.zip
Merge remote-tracking branch 'kwolf/tags/for-anthony' into staging
Block patches for 1.7.0-rc0 (v2) # gpg: Signature made Thu 31 Oct 2013 04:44:39 PM CET using RSA key ID C88F2FD6 # gpg: Can't check signature: public key not found * kwolf/tags/for-anthony: (30 commits) vmdk: Implment bdrv_get_specific_info qapi: Add optional field 'compressed' to ImageInfo qemu-iotests: prefill some data to test image sheepdog: check simultaneous create in resend_aioreq sheepdog: cancel aio requests if possible sheepdog: make add_aio_request and send_aioreq void functions sheepdog: try to reconnect to sheepdog after network error coroutine: add co_aio_sleep_ns() to allow sleep in block drivers sheepdog: reload inode outside of resend_aioreq sheepdog: handle vdi objects in resend_aio_req sheepdog: check return values of qemu_co_recv/send correctly qemu-iotests: Test case for backing file deletion qemu-iotests: drop duplicated "create_image" qemu-iotests: Fix 051 reference output block: Avoid unecessary drv->bdrv_getlength() calls block: Disable BDRV_O_COPY_ON_READ for the backing file ahci: fix win7 hang on boot sheepdog: pass copy_policy in the request sheepdog: explicitly set copies as type uint8_t block: Don't copy backing file name on error ... Message-id: 1383064269-27720-1-git-send-email-kwolf@redhat.com Signed-off-by: Anthony Liguori <anthony@codemonkey.ws>
-rw-r--r--block.c14
-rw-r--r--block/qcow2.c19
-rw-r--r--block/raw-posix.c9
-rw-r--r--block/raw-win32.c4
-rw-r--r--block/raw_bsd.c1
-rw-r--r--block/sheepdog.c352
-rw-r--r--block/vmdk.c68
-rw-r--r--block/vpc.c7
-rw-r--r--exec.c4
-rw-r--r--hw/ide/ahci.c3
-rw-r--r--include/block/block_int.h3
-rw-r--r--include/block/coroutine.h9
-rw-r--r--qapi-schema.json28
-rw-r--r--qemu-coroutine-sleep.c14
-rw-r--r--qemu-img.c2
-rw-r--r--tests/ide-test.c26
-rw-r--r--tests/multiboot/Makefile18
-rw-r--r--tests/multiboot/libc.c139
-rw-r--r--tests/multiboot/libc.h61
-rw-r--r--tests/multiboot/link.ld19
-rw-r--r--tests/multiboot/mmap.c56
-rw-r--r--tests/multiboot/mmap.out93
-rw-r--r--tests/multiboot/multiboot.h66
-rwxr-xr-xtests/multiboot/run_test.sh81
-rw-r--r--tests/multiboot/start.S51
-rwxr-xr-xtests/qemu-iotests/0304
-rwxr-xr-xtests/qemu-iotests/04014
-rw-r--r--tests/qemu-iotests/051.out2
-rwxr-xr-xtests/qemu-iotests/0592
-rw-r--r--tests/qemu-iotests/059.out5
-rwxr-xr-xtests/qemu-iotests/06865
-rw-r--r--tests/qemu-iotests/068.out11
-rwxr-xr-xtests/qemu-iotests/06959
-rw-r--r--tests/qemu-iotests/069.out8
-rw-r--r--tests/qemu-iotests/group2
35 files changed, 1156 insertions, 163 deletions
diff --git a/block.c b/block.c
index fd05a8008a..58efb5b4e4 100644
--- a/block.c
+++ b/block.c
@@ -999,13 +999,12 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *options, Error **errp)
}
/* backing files always opened read-only */
- back_flags = bs->open_flags & ~(BDRV_O_RDWR | BDRV_O_SNAPSHOT);
+ back_flags = bs->open_flags & ~(BDRV_O_RDWR | BDRV_O_SNAPSHOT |
+ BDRV_O_COPY_ON_READ);
ret = bdrv_open(bs->backing_hd,
*backing_filename ? backing_filename : NULL, options,
back_flags, back_drv, &local_err);
- pstrcpy(bs->backing_file, sizeof(bs->backing_file),
- bs->backing_hd->file->filename);
if (ret < 0) {
bdrv_unref(bs->backing_hd);
bs->backing_hd = NULL;
@@ -1013,6 +1012,8 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *options, Error **errp)
error_propagate(errp, local_err);
return ret;
}
+ pstrcpy(bs->backing_file, sizeof(bs->backing_file),
+ bs->backing_hd->file->filename);
return 0;
}
@@ -2868,9 +2869,10 @@ int64_t bdrv_getlength(BlockDriverState *bs)
if (!drv)
return -ENOMEDIUM;
- if (bdrv_dev_has_removable_media(bs)) {
- if (drv->bdrv_getlength) {
- return drv->bdrv_getlength(bs);
+ if (drv->has_variable_length) {
+ int ret = refresh_total_sectors(bs, bs->total_sectors);
+ if (ret < 0) {
+ return ret;
}
}
return bs->total_sectors * BDRV_SECTOR_SIZE;
diff --git a/block/qcow2.c b/block/qcow2.c
index c1abaffa19..6e5d98dc48 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -1584,6 +1584,16 @@ static int qcow2_create2(const char *filename, int64_t total_size,
}
}
+ bdrv_close(bs);
+
+ /* Reopen the image without BDRV_O_NO_FLUSH to flush it before returning */
+ ret = bdrv_open(bs, filename, NULL,
+ BDRV_O_RDWR | BDRV_O_CACHE_WB, drv, &local_err);
+ if (error_is_set(&local_err)) {
+ error_propagate(errp, local_err);
+ goto out;
+ }
+
ret = 0;
out:
bdrv_unref(bs);
@@ -1939,13 +1949,22 @@ static int qcow2_save_vmstate(BlockDriverState *bs, QEMUIOVector *qiov,
int64_t pos)
{
BDRVQcowState *s = bs->opaque;
+ int64_t total_sectors = bs->total_sectors;
int growable = bs->growable;
+ bool zero_beyond_eof = bs->zero_beyond_eof;
int ret;
BLKDBG_EVENT(bs->file, BLKDBG_VMSTATE_SAVE);
bs->growable = 1;
+ bs->zero_beyond_eof = false;
ret = bdrv_pwritev(bs, qcow2_vm_state_offset(s) + pos, qiov);
bs->growable = growable;
+ bs->zero_beyond_eof = zero_beyond_eof;
+
+ /* bdrv_co_do_writev will have increased the total_sectors value to include
+ * the VM state - the VM state is however not an actual part of the block
+ * device, therefore, we need to restore the old value. */
+ bs->total_sectors = total_sectors;
return ret;
}
diff --git a/block/raw-posix.c b/block/raw-posix.c
index 6f03fbf793..f6d48bbdb2 100644
--- a/block/raw-posix.c
+++ b/block/raw-posix.c
@@ -1715,7 +1715,8 @@ static BlockDriver bdrv_host_floppy = {
.bdrv_aio_flush = raw_aio_flush,
.bdrv_truncate = raw_truncate,
- .bdrv_getlength = raw_getlength,
+ .bdrv_getlength = raw_getlength,
+ .has_variable_length = true,
.bdrv_get_allocated_file_size
= raw_get_allocated_file_size,
@@ -1824,7 +1825,8 @@ static BlockDriver bdrv_host_cdrom = {
.bdrv_aio_flush = raw_aio_flush,
.bdrv_truncate = raw_truncate,
- .bdrv_getlength = raw_getlength,
+ .bdrv_getlength = raw_getlength,
+ .has_variable_length = true,
.bdrv_get_allocated_file_size
= raw_get_allocated_file_size,
@@ -1951,7 +1953,8 @@ static BlockDriver bdrv_host_cdrom = {
.bdrv_aio_flush = raw_aio_flush,
.bdrv_truncate = raw_truncate,
- .bdrv_getlength = raw_getlength,
+ .bdrv_getlength = raw_getlength,
+ .has_variable_length = true,
.bdrv_get_allocated_file_size
= raw_get_allocated_file_size,
diff --git a/block/raw-win32.c b/block/raw-win32.c
index 676b5701db..2741e4de10 100644
--- a/block/raw-win32.c
+++ b/block/raw-win32.c
@@ -616,7 +616,9 @@ static BlockDriver bdrv_host_device = {
.bdrv_aio_writev = raw_aio_writev,
.bdrv_aio_flush = raw_aio_flush,
- .bdrv_getlength = raw_getlength,
+ .bdrv_getlength = raw_getlength,
+ .has_variable_length = true,
+
.bdrv_get_allocated_file_size
= raw_get_allocated_file_size,
};
diff --git a/block/raw_bsd.c b/block/raw_bsd.c
index 0078c1baeb..2265dcc03f 100644
--- a/block/raw_bsd.c
+++ b/block/raw_bsd.c
@@ -178,6 +178,7 @@ static BlockDriver bdrv_raw = {
.bdrv_co_get_block_status = &raw_co_get_block_status,
.bdrv_truncate = &raw_truncate,
.bdrv_getlength = &raw_getlength,
+ .has_variable_length = true,
.bdrv_get_info = &raw_get_info,
.bdrv_is_inserted = &raw_is_inserted,
.bdrv_media_changed = &raw_media_changed,
diff --git a/block/sheepdog.c b/block/sheepdog.c
index 5f81c93ee3..ef387de71f 100644
--- a/block/sheepdog.c
+++ b/block/sheepdog.c
@@ -125,8 +125,9 @@ typedef struct SheepdogObjReq {
uint32_t data_length;
uint64_t oid;
uint64_t cow_oid;
- uint32_t copies;
- uint32_t rsvd;
+ uint8_t copies;
+ uint8_t copy_policy;
+ uint8_t reserved[6];
uint64_t offset;
} SheepdogObjReq;
@@ -138,7 +139,9 @@ typedef struct SheepdogObjRsp {
uint32_t id;
uint32_t data_length;
uint32_t result;
- uint32_t copies;
+ uint8_t copies;
+ uint8_t copy_policy;
+ uint8_t reserved[2];
uint32_t pad[6];
} SheepdogObjRsp;
@@ -151,7 +154,9 @@ typedef struct SheepdogVdiReq {
uint32_t data_length;
uint64_t vdi_size;
uint32_t vdi_id;
- uint32_t copies;
+ uint8_t copies;
+ uint8_t copy_policy;
+ uint8_t reserved[2];
uint32_t snapid;
uint32_t pad[3];
} SheepdogVdiReq;
@@ -222,6 +227,11 @@ static inline uint64_t data_oid_to_idx(uint64_t oid)
return oid & (MAX_DATA_OBJS - 1);
}
+static inline uint32_t oid_to_vid(uint64_t oid)
+{
+ return (oid & ~VDI_BIT) >> VDI_SPACE_SHIFT;
+}
+
static inline uint64_t vid_to_vdi_oid(uint32_t vid)
{
return VDI_BIT | ((uint64_t)vid << VDI_SPACE_SHIFT);
@@ -289,11 +299,14 @@ struct SheepdogAIOCB {
Coroutine *coroutine;
void (*aio_done_func)(SheepdogAIOCB *);
- bool canceled;
+ bool cancelable;
+ bool *finished;
int nr_pending;
};
typedef struct BDRVSheepdogState {
+ BlockDriverState *bs;
+
SheepdogInode inode;
uint32_t min_dirty_data_idx;
@@ -313,8 +326,11 @@ typedef struct BDRVSheepdogState {
Coroutine *co_recv;
uint32_t aioreq_seq_num;
+
+ /* Every aio request must be linked to either of these queues. */
QLIST_HEAD(inflight_aio_head, AIOReq) inflight_aio_head;
QLIST_HEAD(pending_aio_head, AIOReq) pending_aio_head;
+ QLIST_HEAD(failed_aio_head, AIOReq) failed_aio_head;
} BDRVSheepdogState;
static const char * sd_strerror(int err)
@@ -403,6 +419,7 @@ static inline void free_aio_req(BDRVSheepdogState *s, AIOReq *aio_req)
{
SheepdogAIOCB *acb = aio_req->aiocb;
+ acb->cancelable = false;
QLIST_REMOVE(aio_req, aio_siblings);
g_free(aio_req);
@@ -411,23 +428,68 @@ static inline void free_aio_req(BDRVSheepdogState *s, AIOReq *aio_req)
static void coroutine_fn sd_finish_aiocb(SheepdogAIOCB *acb)
{
- if (!acb->canceled) {
- qemu_coroutine_enter(acb->coroutine, NULL);
+ qemu_coroutine_enter(acb->coroutine, NULL);
+ if (acb->finished) {
+ *acb->finished = true;
}
qemu_aio_release(acb);
}
+/*
+ * Check whether the specified acb can be canceled
+ *
+ * We can cancel aio when any request belonging to the acb is:
+ * - Not processed by the sheepdog server.
+ * - Not linked to the inflight queue.
+ */
+static bool sd_acb_cancelable(const SheepdogAIOCB *acb)
+{
+ BDRVSheepdogState *s = acb->common.bs->opaque;
+ AIOReq *aioreq;
+
+ if (!acb->cancelable) {
+ return false;
+ }
+
+ QLIST_FOREACH(aioreq, &s->inflight_aio_head, aio_siblings) {
+ if (aioreq->aiocb == acb) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
static void sd_aio_cancel(BlockDriverAIOCB *blockacb)
{
SheepdogAIOCB *acb = (SheepdogAIOCB *)blockacb;
+ BDRVSheepdogState *s = acb->common.bs->opaque;
+ AIOReq *aioreq, *next;
+ bool finished = false;
+
+ acb->finished = &finished;
+ while (!finished) {
+ if (sd_acb_cancelable(acb)) {
+ /* Remove outstanding requests from pending and failed queues. */
+ QLIST_FOREACH_SAFE(aioreq, &s->pending_aio_head, aio_siblings,
+ next) {
+ if (aioreq->aiocb == acb) {
+ free_aio_req(s, aioreq);
+ }
+ }
+ QLIST_FOREACH_SAFE(aioreq, &s->failed_aio_head, aio_siblings,
+ next) {
+ if (aioreq->aiocb == acb) {
+ free_aio_req(s, aioreq);
+ }
+ }
- /*
- * Sheepdog cannot cancel the requests which are already sent to
- * the servers, so we just complete the request with -EIO here.
- */
- acb->ret = -EIO;
- qemu_coroutine_enter(acb->coroutine, NULL);
- acb->canceled = true;
+ assert(acb->nr_pending == 0);
+ sd_finish_aiocb(acb);
+ return;
+ }
+ qemu_aio_wait();
+ }
}
static const AIOCBInfo sd_aiocb_info = {
@@ -448,7 +510,8 @@ static SheepdogAIOCB *sd_aio_setup(BlockDriverState *bs, QEMUIOVector *qiov,
acb->nb_sectors = nb_sectors;
acb->aio_done_func = NULL;
- acb->canceled = false;
+ acb->cancelable = true;
+ acb->finished = NULL;
acb->coroutine = qemu_coroutine_self();
acb->ret = 0;
acb->nr_pending = 0;
@@ -489,13 +552,13 @@ static coroutine_fn int send_co_req(int sockfd, SheepdogReq *hdr, void *data,
int ret;
ret = qemu_co_send(sockfd, hdr, sizeof(*hdr));
- if (ret < sizeof(*hdr)) {
+ if (ret != sizeof(*hdr)) {
error_report("failed to send a req, %s", strerror(errno));
return ret;
}
ret = qemu_co_send(sockfd, data, *wlen);
- if (ret < *wlen) {
+ if (ret != *wlen) {
error_report("failed to send a req, %s", strerror(errno));
}
@@ -541,7 +604,7 @@ static coroutine_fn void do_co_req(void *opaque)
qemu_aio_set_fd_handler(sockfd, restart_co_req, NULL, co);
ret = qemu_co_recv(sockfd, hdr, sizeof(*hdr));
- if (ret < sizeof(*hdr)) {
+ if (ret != sizeof(*hdr)) {
error_report("failed to get a rsp, %s", strerror(errno));
ret = -errno;
goto out;
@@ -553,7 +616,7 @@ static coroutine_fn void do_co_req(void *opaque)
if (*rlen) {
ret = qemu_co_recv(sockfd, data, *rlen);
- if (ret < *rlen) {
+ if (ret != *rlen) {
error_report("failed to get the data, %s", strerror(errno));
ret = -errno;
goto out;
@@ -596,11 +659,13 @@ static int do_req(int sockfd, SheepdogReq *hdr, void *data,
return srco.ret;
}
-static int coroutine_fn add_aio_request(BDRVSheepdogState *s, AIOReq *aio_req,
+static void coroutine_fn add_aio_request(BDRVSheepdogState *s, AIOReq *aio_req,
struct iovec *iov, int niov, bool create,
enum AIOCBState aiocb_type);
-static int coroutine_fn resend_aioreq(BDRVSheepdogState *s, AIOReq *aio_req);
-
+static void coroutine_fn resend_aioreq(BDRVSheepdogState *s, AIOReq *aio_req);
+static int reload_inode(BDRVSheepdogState *s, uint32_t snapid, const char *tag);
+static int get_sheep_fd(BDRVSheepdogState *s);
+static void co_write_request(void *opaque);
static AIOReq *find_pending_req(BDRVSheepdogState *s, uint64_t oid)
{
@@ -623,22 +688,59 @@ static void coroutine_fn send_pending_req(BDRVSheepdogState *s, uint64_t oid)
{
AIOReq *aio_req;
SheepdogAIOCB *acb;
- int ret;
while ((aio_req = find_pending_req(s, oid)) != NULL) {
acb = aio_req->aiocb;
/* move aio_req from pending list to inflight one */
QLIST_REMOVE(aio_req, aio_siblings);
QLIST_INSERT_HEAD(&s->inflight_aio_head, aio_req, aio_siblings);
- ret = add_aio_request(s, aio_req, acb->qiov->iov,
- acb->qiov->niov, false, acb->aiocb_type);
- if (ret < 0) {
- error_report("add_aio_request is failed");
- free_aio_req(s, aio_req);
- if (!acb->nr_pending) {
- sd_finish_aiocb(acb);
- }
+ add_aio_request(s, aio_req, acb->qiov->iov, acb->qiov->niov, false,
+ acb->aiocb_type);
+ }
+}
+
+static coroutine_fn void reconnect_to_sdog(void *opaque)
+{
+ BDRVSheepdogState *s = opaque;
+ AIOReq *aio_req, *next;
+
+ qemu_aio_set_fd_handler(s->fd, NULL, NULL, NULL);
+ close(s->fd);
+ s->fd = -1;
+
+ /* Wait for outstanding write requests to be completed. */
+ while (s->co_send != NULL) {
+ co_write_request(opaque);
+ }
+
+ /* Try to reconnect the sheepdog server every one second. */
+ while (s->fd < 0) {
+ s->fd = get_sheep_fd(s);
+ if (s->fd < 0) {
+ DPRINTF("Wait for connection to be established\n");
+ co_aio_sleep_ns(bdrv_get_aio_context(s->bs), QEMU_CLOCK_REALTIME,
+ 1000000000ULL);
}
+ };
+
+ /*
+ * Now we have to resend all the request in the inflight queue. However,
+ * resend_aioreq() can yield and newly created requests can be added to the
+ * inflight queue before the coroutine is resumed. To avoid mixing them, we
+ * have to move all the inflight requests to the failed queue before
+ * resend_aioreq() is called.
+ */
+ QLIST_FOREACH_SAFE(aio_req, &s->inflight_aio_head, aio_siblings, next) {
+ QLIST_REMOVE(aio_req, aio_siblings);
+ QLIST_INSERT_HEAD(&s->failed_aio_head, aio_req, aio_siblings);
+ }
+
+ /* Resend all the failed aio requests. */
+ while (!QLIST_EMPTY(&s->failed_aio_head)) {
+ aio_req = QLIST_FIRST(&s->failed_aio_head);
+ QLIST_REMOVE(aio_req, aio_siblings);
+ QLIST_INSERT_HEAD(&s->inflight_aio_head, aio_req, aio_siblings);
+ resend_aioreq(s, aio_req);
}
}
@@ -658,15 +760,11 @@ static void coroutine_fn aio_read_response(void *opaque)
SheepdogAIOCB *acb;
uint64_t idx;
- if (QLIST_EMPTY(&s->inflight_aio_head)) {
- goto out;
- }
-
/* read a header */
ret = qemu_co_recv(fd, &rsp, sizeof(rsp));
- if (ret < 0) {
+ if (ret != sizeof(rsp)) {
error_report("failed to get the header, %s", strerror(errno));
- goto out;
+ goto err;
}
/* find the right aio_req from the inflight aio list */
@@ -677,7 +775,7 @@ static void coroutine_fn aio_read_response(void *opaque)
}
if (!aio_req) {
error_report("cannot find aio_req %x", rsp.id);
- goto out;
+ goto err;
}
acb = aio_req->aiocb;
@@ -715,9 +813,9 @@ static void coroutine_fn aio_read_response(void *opaque)
case AIOCB_READ_UDATA:
ret = qemu_co_recvv(fd, acb->qiov->iov, acb->qiov->niov,
aio_req->iov_offset, rsp.data_length);
- if (ret < 0) {
+ if (ret != rsp.data_length) {
error_report("failed to get the data, %s", strerror(errno));
- goto out;
+ goto err;
}
break;
case AIOCB_FLUSH_CACHE:
@@ -748,11 +846,20 @@ static void coroutine_fn aio_read_response(void *opaque)
case SD_RES_SUCCESS:
break;
case SD_RES_READONLY:
- ret = resend_aioreq(s, aio_req);
- if (ret == SD_RES_SUCCESS) {
- goto out;
+ if (s->inode.vdi_id == oid_to_vid(aio_req->oid)) {
+ ret = reload_inode(s, 0, "");
+ if (ret < 0) {
+ goto err;
+ }
}
- /* fall through */
+ if (is_data_obj(aio_req->oid)) {
+ aio_req->oid = vid_to_data_oid(s->inode.vdi_id,
+ data_oid_to_idx(aio_req->oid));
+ } else {
+ aio_req->oid = vid_to_vdi_oid(s->inode.vdi_id);
+ }
+ resend_aioreq(s, aio_req);
+ goto out;
default:
acb->ret = -EIO;
error_report("%s", sd_strerror(rsp.result));
@@ -769,6 +876,10 @@ static void coroutine_fn aio_read_response(void *opaque)
}
out:
s->co_recv = NULL;
+ return;
+err:
+ s->co_recv = NULL;
+ reconnect_to_sdog(opaque);
}
static void co_read_response(void *opaque)
@@ -997,7 +1108,7 @@ out:
return ret;
}
-static int coroutine_fn add_aio_request(BDRVSheepdogState *s, AIOReq *aio_req,
+static void coroutine_fn add_aio_request(BDRVSheepdogState *s, AIOReq *aio_req,
struct iovec *iov, int niov, bool create,
enum AIOCBState aiocb_type)
{
@@ -1059,29 +1170,25 @@ static int coroutine_fn add_aio_request(BDRVSheepdogState *s, AIOReq *aio_req,
/* send a header */
ret = qemu_co_send(s->fd, &hdr, sizeof(hdr));
- if (ret < 0) {
- qemu_co_mutex_unlock(&s->lock);
+ if (ret != sizeof(hdr)) {
error_report("failed to send a req, %s", strerror(errno));
- return -errno;
+ goto out;
}
if (wlen) {
ret = qemu_co_sendv(s->fd, iov, niov, aio_req->iov_offset, wlen);
- if (ret < 0) {
- qemu_co_mutex_unlock(&s->lock);
+ if (ret != wlen) {
error_report("failed to send a data, %s", strerror(errno));
- return -errno;
}
}
-
+out:
socket_set_cork(s->fd, 0);
qemu_aio_set_fd_handler(s->fd, co_read_response, NULL, s);
+ s->co_send = NULL;
qemu_co_mutex_unlock(&s->lock);
-
- return 0;
}
-static int read_write_object(int fd, char *buf, uint64_t oid, int copies,
+static int read_write_object(int fd, char *buf, uint64_t oid, uint8_t copies,
unsigned int datalen, uint64_t offset,
bool write, bool create, uint32_t cache_flags)
{
@@ -1129,7 +1236,7 @@ static int read_write_object(int fd, char *buf, uint64_t oid, int copies,
}
}
-static int read_object(int fd, char *buf, uint64_t oid, int copies,
+static int read_object(int fd, char *buf, uint64_t oid, uint8_t copies,
unsigned int datalen, uint64_t offset,
uint32_t cache_flags)
{
@@ -1137,7 +1244,7 @@ static int read_object(int fd, char *buf, uint64_t oid, int copies,
false, cache_flags);
}
-static int write_object(int fd, char *buf, uint64_t oid, int copies,
+static int write_object(int fd, char *buf, uint64_t oid, uint8_t copies,
unsigned int datalen, uint64_t offset, bool create,
uint32_t cache_flags)
{
@@ -1181,51 +1288,62 @@ out:
return ret;
}
-static int coroutine_fn resend_aioreq(BDRVSheepdogState *s, AIOReq *aio_req)
+/* Return true if the specified request is linked to the pending list. */
+static bool check_simultaneous_create(BDRVSheepdogState *s, AIOReq *aio_req)
{
- SheepdogAIOCB *acb = aio_req->aiocb;
- bool create = false;
- int ret;
-
- ret = reload_inode(s, 0, "");
- if (ret < 0) {
- return ret;
+ AIOReq *areq;
+ QLIST_FOREACH(areq, &s->inflight_aio_head, aio_siblings) {
+ if (areq != aio_req && areq->oid == aio_req->oid) {
+ /*
+ * Sheepdog cannot handle simultaneous create requests to the same
+ * object, so we cannot send the request until the previous request
+ * finishes.
+ */
+ DPRINTF("simultaneous create to %" PRIx64 "\n", aio_req->oid);
+ aio_req->flags = 0;
+ aio_req->base_oid = 0;
+ QLIST_REMOVE(aio_req, aio_siblings);
+ QLIST_INSERT_HEAD(&s->pending_aio_head, aio_req, aio_siblings);
+ return true;
+ }
}
- aio_req->oid = vid_to_data_oid(s->inode.vdi_id,
- data_oid_to_idx(aio_req->oid));
+ return false;
+}
+
+static void coroutine_fn resend_aioreq(BDRVSheepdogState *s, AIOReq *aio_req)
+{
+ SheepdogAIOCB *acb = aio_req->aiocb;
+ bool create = false;
/* check whether this request becomes a CoW one */
- if (acb->aiocb_type == AIOCB_WRITE_UDATA) {
+ if (acb->aiocb_type == AIOCB_WRITE_UDATA && is_data_obj(aio_req->oid)) {
int idx = data_oid_to_idx(aio_req->oid);
- AIOReq *areq;
- if (s->inode.data_vdi_id[idx] == 0) {
- create = true;
- goto out;
- }
if (is_data_obj_writable(&s->inode, idx)) {
goto out;
}
- /* link to the pending list if there is another CoW request to
- * the same object */
- QLIST_FOREACH(areq, &s->inflight_aio_head, aio_siblings) {
- if (areq != aio_req && areq->oid == aio_req->oid) {
- DPRINTF("simultaneous CoW to %" PRIx64 "\n", aio_req->oid);
- QLIST_REMOVE(aio_req, aio_siblings);
- QLIST_INSERT_HEAD(&s->pending_aio_head, aio_req, aio_siblings);
- return SD_RES_SUCCESS;
- }
+ if (check_simultaneous_create(s, aio_req)) {
+ return;
}
- aio_req->base_oid = vid_to_data_oid(s->inode.data_vdi_id[idx], idx);
- aio_req->flags |= SD_FLAG_CMD_COW;
+ if (s->inode.data_vdi_id[idx]) {
+ aio_req->base_oid = vid_to_data_oid(s->inode.data_vdi_id[idx], idx);
+ aio_req->flags |= SD_FLAG_CMD_COW;
+ }
create = true;
}
out:
- return add_aio_request(s, aio_req, acb->qiov->iov, acb->qiov->niov,
- create, acb->aiocb_type);
+ if (is_data_obj(aio_req->oid)) {
+ add_aio_request(s, aio_req, acb->qiov->iov, acb->qiov->niov, create,
+ acb->aiocb_type);
+ } else {
+ struct iovec iov;
+ iov.iov_base = &s->inode;
+ iov.iov_len = sizeof(s->inode);
+ add_aio_request(s, aio_req, &iov, 1, false, AIOCB_WRITE_UDATA);
+ }
}
/* TODO Convert to fine grained options */
@@ -1255,6 +1373,8 @@ static int sd_open(BlockDriverState *bs, QDict *options, int flags,
Error *local_err = NULL;
const char *filename;
+ s->bs = bs;
+
opts = qemu_opts_create_nofail(&runtime_opts);
qemu_opts_absorb_qdict(opts, options, &local_err);
if (error_is_set(&local_err)) {
@@ -1268,6 +1388,7 @@ static int sd_open(BlockDriverState *bs, QDict *options, int flags,
QLIST_INIT(&s->inflight_aio_head);
QLIST_INIT(&s->pending_aio_head);
+ QLIST_INIT(&s->failed_aio_head);
s->fd = -1;
memset(vdi, 0, sizeof(vdi));
@@ -1344,7 +1465,8 @@ out:
}
static int do_sd_create(BDRVSheepdogState *s, char *filename, int64_t vdi_size,
- uint32_t base_vid, uint32_t *vdi_id, int snapshot)
+ uint32_t base_vid, uint32_t *vdi_id, int snapshot,
+ uint8_t copy_policy)
{
SheepdogVdiReq hdr;
SheepdogVdiRsp *rsp = (SheepdogVdiRsp *)&hdr;
@@ -1374,6 +1496,7 @@ static int do_sd_create(BDRVSheepdogState *s, char *filename, int64_t vdi_size,
hdr.data_length = wlen;
hdr.vdi_size = vdi_size;
+ hdr.copy_policy = copy_policy;
ret = do_req(fd, (SheepdogReq *)&hdr, buf, &wlen, &rlen);
@@ -1526,7 +1649,8 @@ static int sd_create(const char *filename, QEMUOptionParameter *options,
bdrv_unref(bs);
}
- ret = do_sd_create(s, vdi, vdi_size, base_vid, &vid, 0);
+ /* TODO: allow users to specify copy number */
+ ret = do_sd_create(s, vdi, vdi_size, base_vid, &vid, 0, 0);
if (!prealloc || ret) {
goto out;
}
@@ -1621,7 +1745,6 @@ static int sd_truncate(BlockDriverState *bs, int64_t offset)
*/
static void coroutine_fn sd_write_done(SheepdogAIOCB *acb)
{
- int ret;
BDRVSheepdogState *s = acb->common.bs->opaque;
struct iovec iov;
AIOReq *aio_req;
@@ -1643,18 +1766,13 @@ static void coroutine_fn sd_write_done(SheepdogAIOCB *acb)
aio_req = alloc_aio_req(s, acb, vid_to_vdi_oid(s->inode.vdi_id),
data_len, offset, 0, 0, offset);
QLIST_INSERT_HEAD(&s->inflight_aio_head, aio_req, aio_siblings);
- ret = add_aio_request(s, aio_req, &iov, 1, false, AIOCB_WRITE_UDATA);
- if (ret) {
- free_aio_req(s, aio_req);
- acb->ret = -EIO;
- goto out;
- }
+ add_aio_request(s, aio_req, &iov, 1, false, AIOCB_WRITE_UDATA);
acb->aio_done_func = sd_finish_aiocb;
acb->aiocb_type = AIOCB_WRITE_UDATA;
return;
}
-out:
+
sd_finish_aiocb(acb);
}
@@ -1716,7 +1834,7 @@ static int sd_create_branch(BDRVSheepdogState *s)
*/
deleted = sd_delete(s);
ret = do_sd_create(s, s->name, s->inode.vdi_size, s->inode.vdi_id, &vid,
- !deleted);
+ !deleted, s->inode.copy_policy);
if (ret) {
goto out;
}
@@ -1840,35 +1958,16 @@ static int coroutine_fn sd_co_rw_vector(void *p)
}
aio_req = alloc_aio_req(s, acb, oid, len, offset, flags, old_oid, done);
+ QLIST_INSERT_HEAD(&s->inflight_aio_head, aio_req, aio_siblings);
if (create) {
- AIOReq *areq;
- QLIST_FOREACH(areq, &s->inflight_aio_head, aio_siblings) {
- if (areq->oid == oid) {
- /*
- * Sheepdog cannot handle simultaneous create
- * requests to the same object. So we cannot send
- * the request until the previous request
- * finishes.
- */
- aio_req->flags = 0;
- aio_req->base_oid = 0;
- QLIST_INSERT_HEAD(&s->pending_aio_head, aio_req,
- aio_siblings);
- goto done;
- }
+ if (check_simultaneous_create(s, aio_req)) {
+ goto done;
}
}
- QLIST_INSERT_HEAD(&s->inflight_aio_head, aio_req, aio_siblings);
- ret = add_aio_request(s, aio_req, acb->qiov->iov, acb->qiov->niov,
- create, acb->aiocb_type);
- if (ret < 0) {
- error_report("add_aio_request is failed");
- free_aio_req(s, aio_req);
- acb->ret = -EIO;
- goto out;
- }
+ add_aio_request(s, aio_req, acb->qiov->iov, acb->qiov->niov, create,
+ acb->aiocb_type);
done:
offset = 0;
idx++;
@@ -1936,7 +2035,6 @@ static int coroutine_fn sd_co_flush_to_disk(BlockDriverState *bs)
BDRVSheepdogState *s = bs->opaque;
SheepdogAIOCB *acb;
AIOReq *aio_req;
- int ret;
if (s->cache_flags != SD_FLAG_CMD_CACHE) {
return 0;
@@ -1949,13 +2047,7 @@ static int coroutine_fn sd_co_flush_to_disk(BlockDriverState *bs)
aio_req = alloc_aio_req(s, acb, vid_to_vdi_oid(s->inode.vdi_id),
0, 0, 0, 0, 0);
QLIST_INSERT_HEAD(&s->inflight_aio_head, aio_req, aio_siblings);
- ret = add_aio_request(s, aio_req, NULL, 0, false, acb->aiocb_type);
- if (ret < 0) {
- error_report("add_aio_request is failed");
- free_aio_req(s, aio_req);
- qemu_aio_release(acb);
- return ret;
- }
+ add_aio_request(s, aio_req, NULL, 0, false, acb->aiocb_type);
qemu_coroutine_yield();
return acb->ret;
@@ -2006,7 +2098,7 @@ static int sd_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
}
ret = do_sd_create(s, s->name, s->inode.vdi_size, s->inode.vdi_id, &new_vid,
- 1);
+ 1, s->inode.copy_policy);
if (ret < 0) {
error_report("failed to create inode for snapshot. %s",
strerror(errno));
diff --git a/block/vmdk.c b/block/vmdk.c
index 32ec8b7766..a7ebd0f125 100644
--- a/block/vmdk.c
+++ b/block/vmdk.c
@@ -106,6 +106,7 @@ typedef struct VmdkExtent {
uint32_t l2_cache_counts[L2_CACHE_SIZE];
int64_t cluster_sectors;
+ char *type;
} VmdkExtent;
typedef struct BDRVVmdkState {
@@ -113,11 +114,13 @@ typedef struct BDRVVmdkState {
uint64_t desc_offset;
bool cid_updated;
bool cid_checked;
+ uint32_t cid;
uint32_t parent_cid;
int num_extents;
/* Extent array with num_extents entries, ascend ordered by address */
VmdkExtent *extents;
Error *migration_blocker;
+ char *create_type;
} BDRVVmdkState;
typedef struct VmdkMetaData {
@@ -214,6 +217,7 @@ static void vmdk_free_extents(BlockDriverState *bs)
g_free(e->l1_table);
g_free(e->l2_cache);
g_free(e->l1_backup_table);
+ g_free(e->type);
if (e->file != bs->file) {
bdrv_unref(e->file);
}
@@ -534,6 +538,7 @@ static int vmdk_open_vmdk4(BlockDriverState *bs,
uint32_t l1_size, l1_entry_sectors;
VMDK4Header header;
VmdkExtent *extent;
+ BDRVVmdkState *s = bs->opaque;
int64_t l1_backup_offset = 0;
ret = bdrv_pread(file, sizeof(magic), &header, sizeof(header));
@@ -549,6 +554,10 @@ static int vmdk_open_vmdk4(BlockDriverState *bs,
}
}
+ if (!s->create_type) {
+ s->create_type = g_strdup("monolithicSparse");
+ }
+
if (le64_to_cpu(header.gd_offset) == VMDK4_GD_AT_END) {
/*
* The footer takes precedence over the header, so read it in. The
@@ -709,6 +718,8 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
int64_t flat_offset;
char extent_path[PATH_MAX];
BlockDriverState *extent_file;
+ BDRVVmdkState *s = bs->opaque;
+ VmdkExtent *extent;
while (*p) {
/* parse extent line:
@@ -751,7 +762,6 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
/* save to extents array */
if (!strcmp(type, "FLAT") || !strcmp(type, "VMFS")) {
/* FLAT extent */
- VmdkExtent *extent;
ret = vmdk_add_extent(bs, extent_file, true, sectors,
0, 0, 0, 0, 0, &extent, errp);
@@ -766,10 +776,12 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
bdrv_unref(extent_file);
return ret;
}
+ extent = &s->extents[s->num_extents - 1];
} else {
error_setg(errp, "Unsupported extent type '%s'", type);
return -ENOTSUP;
}
+ extent->type = g_strdup(type);
next_line:
/* move to next line */
while (*p) {
@@ -817,6 +829,7 @@ static int vmdk_open_desc_file(BlockDriverState *bs, int flags,
ret = -ENOTSUP;
goto exit;
}
+ s->create_type = g_strdup(ct);
s->desc_offset = 0;
ret = vmdk_parse_extents(buf, bs, bs->file->filename, errp);
exit:
@@ -843,6 +856,7 @@ static int vmdk_open(BlockDriverState *bs, QDict *options, int flags,
if (ret) {
goto fail;
}
+ s->cid = vmdk_read_cid(bs, 0);
s->parent_cid = vmdk_read_cid(bs, 1);
qemu_co_mutex_init(&s->lock);
@@ -855,6 +869,8 @@ static int vmdk_open(BlockDriverState *bs, QDict *options, int flags,
return 0;
fail:
+ g_free(s->create_type);
+ s->create_type = NULL;
vmdk_free_extents(bs);
return ret;
}
@@ -1766,6 +1782,7 @@ static void vmdk_close(BlockDriverState *bs)
BDRVVmdkState *s = bs->opaque;
vmdk_free_extents(bs);
+ g_free(s->create_type);
migrate_del_blocker(s->migration_blocker);
error_free(s->migration_blocker);
@@ -1827,6 +1844,54 @@ static int vmdk_has_zero_init(BlockDriverState *bs)
return 1;
}
+static ImageInfo *vmdk_get_extent_info(VmdkExtent *extent)
+{
+ ImageInfo *info = g_new0(ImageInfo, 1);
+
+ *info = (ImageInfo){
+ .filename = g_strdup(extent->file->filename),
+ .format = g_strdup(extent->type),
+ .virtual_size = extent->sectors * BDRV_SECTOR_SIZE,
+ .compressed = extent->compressed,
+ .has_compressed = extent->compressed,
+ .cluster_size = extent->cluster_sectors * BDRV_SECTOR_SIZE,
+ .has_cluster_size = !extent->flat,
+ };
+
+ return info;
+}
+
+static ImageInfoSpecific *vmdk_get_specific_info(BlockDriverState *bs)
+{
+ int i;
+ BDRVVmdkState *s = bs->opaque;
+ ImageInfoSpecific *spec_info = g_new0(ImageInfoSpecific, 1);
+ ImageInfoList **next;
+
+ *spec_info = (ImageInfoSpecific){
+ .kind = IMAGE_INFO_SPECIFIC_KIND_VMDK,
+ {
+ .vmdk = g_new0(ImageInfoSpecificVmdk, 1),
+ },
+ };
+
+ *spec_info->vmdk = (ImageInfoSpecificVmdk) {
+ .create_type = g_strdup(s->create_type),
+ .cid = s->cid,
+ .parent_cid = s->parent_cid,
+ };
+
+ next = &spec_info->vmdk->extents;
+ for (i = 0; i < s->num_extents; i++) {
+ *next = g_new0(ImageInfoList, 1);
+ (*next)->value = vmdk_get_extent_info(&s->extents[i]);
+ (*next)->next = NULL;
+ next = &(*next)->next;
+ }
+
+ return spec_info;
+}
+
static QEMUOptionParameter vmdk_create_options[] = {
{
.name = BLOCK_OPT_SIZE,
@@ -1879,6 +1944,7 @@ static BlockDriver bdrv_vmdk = {
.bdrv_co_get_block_status = vmdk_co_get_block_status,
.bdrv_get_allocated_file_size = vmdk_get_allocated_file_size,
.bdrv_has_zero_init = vmdk_has_zero_init,
+ .bdrv_get_specific_info = vmdk_get_specific_info,
.create_options = vmdk_create_options,
};
diff --git a/block/vpc.c b/block/vpc.c
index b5dca3961e..627d11cb9b 100644
--- a/block/vpc.c
+++ b/block/vpc.c
@@ -260,6 +260,13 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags,
}
}
+ if (s->free_data_block_offset > bdrv_getlength(bs->file)) {
+ error_setg(errp, "block-vpc: free_data_block_offset points after "
+ "the end of file. The image has been truncated.");
+ ret = -EINVAL;
+ goto fail;
+ }
+
s->last_bitmap_offset = (int64_t) -1;
#ifdef CACHE
diff --git a/exec.c b/exec.c
index 2e31ffcb2c..b453713bdb 100644
--- a/exec.c
+++ b/exec.c
@@ -2099,7 +2099,9 @@ void *address_space_map(AddressSpace *as,
if (bounce.buffer) {
return NULL;
}
- bounce.buffer = qemu_memalign(TARGET_PAGE_SIZE, TARGET_PAGE_SIZE);
+ /* Avoid unbounded allocations */
+ l = MIN(l, TARGET_PAGE_SIZE);
+ bounce.buffer = qemu_memalign(TARGET_PAGE_SIZE, l);
bounce.addr = addr;
bounce.len = l;
diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c
index a8be62cf99..fbea9e8886 100644
--- a/hw/ide/ahci.c
+++ b/hw/ide/ahci.c
@@ -961,7 +961,8 @@ static int handle_cmd(AHCIState *s, int port, int slot)
/* We're ready to process the command in FIS byte 2. */
ide_exec_cmd(&s->dev[port].port, cmd_fis[2]);
- if (s->dev[port].port.ifs[0].status & READY_STAT) {
+ if ((s->dev[port].port.ifs[0].status & (READY_STAT|DRQ_STAT|BUSY_STAT)) ==
+ READY_STAT) {
ahci_write_fis_d2h(&s->dev[port], cmd_fis);
}
}
diff --git a/include/block/block_int.h b/include/block/block_int.h
index a48731d539..166606615c 100644
--- a/include/block/block_int.h
+++ b/include/block/block_int.h
@@ -156,8 +156,11 @@ struct BlockDriver {
const char *protocol_name;
int (*bdrv_truncate)(BlockDriverState *bs, int64_t offset);
+
int64_t (*bdrv_getlength)(BlockDriverState *bs);
+ bool has_variable_length;
int64_t (*bdrv_get_allocated_file_size)(BlockDriverState *bs);
+
int (*bdrv_write_compressed)(BlockDriverState *bs, int64_t sector_num,
const uint8_t *buf, int nb_sectors);
diff --git a/include/block/coroutine.h b/include/block/coroutine.h
index 4232569c53..4d5c0cfdd7 100644
--- a/include/block/coroutine.h
+++ b/include/block/coroutine.h
@@ -216,6 +216,15 @@ void qemu_co_rwlock_unlock(CoRwlock *lock);
void coroutine_fn co_sleep_ns(QEMUClockType type, int64_t ns);
/**
+ * Yield the coroutine for a given duration
+ *
+ * Behaves similarly to co_sleep_ns(), but the sleeping coroutine will be
+ * resumed when using qemu_aio_wait().
+ */
+void coroutine_fn co_aio_sleep_ns(AioContext *ctx, QEMUClockType type,
+ int64_t ns);
+
+/**
* Yield until a file descriptor becomes readable
*
* Note that this function clobbers the handlers for the file descriptor.
diff --git a/qapi-schema.json b/qapi-schema.json
index a003aa1763..81a375ba06 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -225,6 +225,27 @@
} }
##
+# @ImageInfoSpecificVmdk:
+#
+# @create_type: The create type of VMDK image
+#
+# @cid: Content id of image
+#
+# @parent-cid: Parent VMDK image's cid
+#
+# @extents: List of extent files
+#
+# Since: 1.7
+##
+{ 'type': 'ImageInfoSpecificVmdk',
+ 'data': {
+ 'create-type': 'str',
+ 'cid': 'int',
+ 'parent-cid': 'int',
+ 'extents': ['ImageInfo']
+ } }
+
+##
# @ImageInfoSpecific:
#
# A discriminated record of image format specific information structures.
@@ -234,7 +255,8 @@
{ 'union': 'ImageInfoSpecific',
'data': {
- 'qcow2': 'ImageInfoSpecificQCow2'
+ 'qcow2': 'ImageInfoSpecificQCow2',
+ 'vmdk': 'ImageInfoSpecificVmdk'
} }
##
@@ -256,6 +278,8 @@
#
# @encrypted: #optional true if the image is encrypted
#
+# @compressed: #optional true if the image is compressed (Since 1.7)
+#
# @backing-filename: #optional name of the backing file
#
# @full-backing-filename: #optional full path of the backing file
@@ -276,7 +300,7 @@
{ 'type': 'ImageInfo',
'data': {'filename': 'str', 'format': 'str', '*dirty-flag': 'bool',
'*actual-size': 'int', 'virtual-size': 'int',
- '*cluster-size': 'int', '*encrypted': 'bool',
+ '*cluster-size': 'int', '*encrypted': 'bool', '*compressed': 'bool',
'*backing-filename': 'str', '*full-backing-filename': 'str',
'*backing-filename-format': 'str', '*snapshots': ['SnapshotInfo'],
'*backing-image': 'ImageInfo',
diff --git a/qemu-coroutine-sleep.c b/qemu-coroutine-sleep.c
index f6db978c1d..ad78fbaa2a 100644
--- a/qemu-coroutine-sleep.c
+++ b/qemu-coroutine-sleep.c
@@ -13,6 +13,7 @@
#include "block/coroutine.h"
#include "qemu/timer.h"
+#include "block/aio.h"
typedef struct CoSleepCB {
QEMUTimer *ts;
@@ -37,3 +38,16 @@ void coroutine_fn co_sleep_ns(QEMUClockType type, int64_t ns)
timer_del(sleep_cb.ts);
timer_free(sleep_cb.ts);
}
+
+void coroutine_fn co_aio_sleep_ns(AioContext *ctx, QEMUClockType type,
+ int64_t ns)
+{
+ CoSleepCB sleep_cb = {
+ .co = qemu_coroutine_self(),
+ };
+ sleep_cb.ts = aio_timer_new(ctx, type, SCALE_NS, co_sleep_cb, &sleep_cb);
+ timer_mod(sleep_cb.ts, qemu_clock_get_ns(type) + ns);
+ qemu_coroutine_yield();
+ timer_del(sleep_cb.ts);
+ timer_free(sleep_cb.ts);
+}
diff --git a/qemu-img.c b/qemu-img.c
index 926f0a0feb..bf3fb4f8a6 100644
--- a/qemu-img.c
+++ b/qemu-img.c
@@ -607,7 +607,7 @@ static int img_check(int argc, char **argv)
if (output_format == OFORMAT_HUMAN) {
error_report("This image format does not support checks");
}
- ret = 1;
+ ret = 63;
goto fail;
}
diff --git a/tests/ide-test.c b/tests/ide-test.c
index 7307f1d336..bc824a8144 100644
--- a/tests/ide-test.c
+++ b/tests/ide-test.c
@@ -81,6 +81,7 @@ enum {
CMD_IDENTIFY = 0xec,
CMDF_ABORT = 0x100,
+ CMDF_NO_BM = 0x200,
};
enum {
@@ -192,6 +193,11 @@ static int send_dma_request(int cmd, uint64_t sector, int nb_sectors,
g_assert_not_reached();
}
+ if (flags & CMDF_NO_BM) {
+ qpci_config_writew(dev, PCI_COMMAND,
+ PCI_COMMAND_IO | PCI_COMMAND_MEMORY);
+ }
+
/* Select device 0 */
outb(IDE_BASE + reg_device, 0 | LBA);
@@ -352,6 +358,25 @@ static void test_bmdma_long_prdt(void)
assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR);
}
+static void test_bmdma_no_busmaster(void)
+{
+ uint8_t status;
+
+ /* No PRDT_EOT, each entry addr 0/size 64k, and in theory qemu shouldn't be
+ * able to access it anyway because the Bus Master bit in the PCI command
+ * register isn't set. This is complete nonsense, but it used to be pretty
+ * good at confusing and occasionally crashing qemu. */
+ PrdtEntry prdt[4096] = { };
+
+ status = send_dma_request(CMD_READ_DMA | CMDF_NO_BM, 0, 512,
+ prdt, ARRAY_SIZE(prdt));
+
+ /* Not entirely clear what the expected result is, but this is what we get
+ * in practice. At least we want to be aware of any changes. */
+ g_assert_cmphex(status, ==, BM_STS_ACTIVE | BM_STS_INTR);
+ assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR);
+}
+
static void test_bmdma_setup(void)
{
ide_test_start(
@@ -493,6 +518,7 @@ int main(int argc, char **argv)
qtest_add_func("/ide/bmdma/simple_rw", test_bmdma_simple_rw);
qtest_add_func("/ide/bmdma/short_prdt", test_bmdma_short_prdt);
qtest_add_func("/ide/bmdma/long_prdt", test_bmdma_long_prdt);
+ qtest_add_func("/ide/bmdma/no_busmaster", test_bmdma_no_busmaster);
qtest_add_func("/ide/bmdma/teardown", test_bmdma_teardown);
qtest_add_func("/ide/flush", test_flush);
diff --git a/tests/multiboot/Makefile b/tests/multiboot/Makefile
new file mode 100644
index 0000000000..34cdd81a90
--- /dev/null
+++ b/tests/multiboot/Makefile
@@ -0,0 +1,18 @@
+CC=gcc
+CCFLAGS=-m32 -Wall -Wextra -Werror -fno-stack-protector -nostdinc -fno-builtin
+ASFLAGS=-m32
+
+LD=ld
+LDFLAGS=-melf_i386 -T link.ld
+LIBS=$(shell $(CC) $(CCFLAGS) -print-libgcc-file-name)
+
+all: mmap.elf
+
+mmap.elf: start.o mmap.o libc.o
+ $(LD) $(LDFLAGS) -o $@ $^ $(LIBS)
+
+%.o: %.c
+ $(CC) $(CCFLAGS) -c -o $@ $^
+
+%.o: %.S
+ $(CC) $(ASFLAGS) -c -o $@ $^
diff --git a/tests/multiboot/libc.c b/tests/multiboot/libc.c
new file mode 100644
index 0000000000..05abbd92cc
--- /dev/null
+++ b/tests/multiboot/libc.c
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2013 Kevin Wolf <kwolf@redhat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "libc.h"
+
+static void print_char(char c)
+{
+ outb(0xe9, c);
+}
+
+static void print_str(char *s)
+{
+ while (*s) {
+ print_char(*s++);
+ }
+}
+
+static void print_num(uint64_t value, int base)
+{
+ char digits[] = "0123456789abcdef";
+ char buf[32] = { 0 };
+ int i = sizeof(buf) - 2;
+
+ do {
+ buf[i--] = digits[value % base];
+ value /= base;
+ } while (value);
+
+ print_str(&buf[i + 1]);
+}
+
+void printf(const char *fmt, ...)
+{
+ va_list ap;
+ uint64_t val;
+ char *str;
+ int base;
+ int has_long;
+ int alt_form;
+
+ va_start(ap, fmt);
+
+ for (; *fmt; fmt++) {
+ if (*fmt != '%') {
+ print_char(*fmt);
+ continue;
+ }
+ fmt++;
+
+ if (*fmt == '#') {
+ fmt++;
+ alt_form = 1;
+ } else {
+ alt_form = 0;
+ }
+
+ if (*fmt == 'l') {
+ fmt++;
+ if (*fmt == 'l') {
+ fmt++;
+ has_long = 2;
+ } else {
+ has_long = 1;
+ }
+ } else {
+ has_long = 0;
+ }
+
+ switch (*fmt) {
+ case 'x':
+ case 'p':
+ base = 16;
+ goto convert_number;
+ case 'd':
+ case 'i':
+ case 'u':
+ base = 10;
+ goto convert_number;
+ case 'o':
+ base = 8;
+ goto convert_number;
+
+ convert_number:
+ switch (has_long) {
+ case 0:
+ val = va_arg(ap, unsigned int);
+ break;
+ case 1:
+ val = va_arg(ap, unsigned long);
+ break;
+ case 2:
+ val = va_arg(ap, unsigned long long);
+ break;
+ }
+
+ if (alt_form && base == 16) {
+ print_str("0x");
+ }
+
+ print_num(val, base);
+ break;
+
+ case 's':
+ str = va_arg(ap, char*);
+ print_str(str);
+ break;
+ case '%':
+ print_char(*fmt);
+ break;
+ default:
+ print_char('%');
+ print_char(*fmt);
+ break;
+ }
+ }
+
+ va_end(ap);
+}
+
+
diff --git a/tests/multiboot/libc.h b/tests/multiboot/libc.h
new file mode 100644
index 0000000000..80eec5b7a0
--- /dev/null
+++ b/tests/multiboot/libc.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2013 Kevin Wolf <kwolf@redhat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef LIBC_H
+#define LIBC_H
+
+/* Integer types */
+
+typedef unsigned long long uint64_t;
+typedef unsigned int uint32_t;
+typedef unsigned short uint16_t;
+typedef unsigned char uint8_t;
+
+typedef signed long long int64_t;
+typedef signed int int32_t;
+typedef signed short int16_t;
+typedef signed char int8_t;
+
+typedef uint32_t uintptr_t;
+
+
+/* stdarg.h */
+
+typedef __builtin_va_list va_list;
+#define va_start(ap, X) __builtin_va_start(ap, X)
+#define va_arg(ap, type) __builtin_va_arg(ap, type)
+#define va_end(ap) __builtin_va_end(ap)
+
+
+/* Port I/O functions */
+
+static inline void outb(uint16_t port, uint8_t data)
+{
+ asm volatile ("outb %0, %1" : : "a" (data), "Nd" (port));
+}
+
+
+/* Misc functions */
+
+void printf(const char *fmt, ...);
+
+#endif
diff --git a/tests/multiboot/link.ld b/tests/multiboot/link.ld
new file mode 100644
index 0000000000..3d49b58c60
--- /dev/null
+++ b/tests/multiboot/link.ld
@@ -0,0 +1,19 @@
+ENTRY(_start)
+
+SECTIONS
+{
+ . = 0x100000;
+ .text : {
+ *(multiboot)
+ *(.text)
+ }
+ .data ALIGN(4096) : {
+ *(.data)
+ }
+ .rodata ALIGN(4096) : {
+ *(.rodata)
+ }
+ .bss ALIGN(4096) : {
+ *(.bss)
+ }
+}
diff --git a/tests/multiboot/mmap.c b/tests/multiboot/mmap.c
new file mode 100644
index 0000000000..766b003f38
--- /dev/null
+++ b/tests/multiboot/mmap.c
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2013 Kevin Wolf <kwolf@redhat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "libc.h"
+#include "multiboot.h"
+
+int test_main(uint32_t magic, struct mb_info *mbi)
+{
+ uintptr_t entry_addr;
+ struct mb_mmap_entry *entry;
+
+ (void) magic;
+
+ printf("Lower memory: %dk\n", mbi->mem_lower);
+ printf("Upper memory: %dk\n", mbi->mem_upper);
+
+ printf("\ne820 memory map:\n");
+
+ for (entry_addr = mbi->mmap_addr;
+ entry_addr < mbi->mmap_addr + mbi->mmap_length;
+ entry_addr += entry->size + 4)
+ {
+ entry = (struct mb_mmap_entry*) entry_addr;
+
+ printf("%#llx - %#llx: type %d [entry size: %d]\n",
+ entry->base_addr,
+ entry->base_addr + entry->length,
+ entry->type,
+ entry->size);
+ }
+
+ printf("\nmmap start: %#x\n", mbi->mmap_addr);
+ printf("mmap end: %#x\n", mbi->mmap_addr + mbi->mmap_length);
+ printf("real mmap end: %#x\n", entry_addr);
+
+ return 0;
+}
diff --git a/tests/multiboot/mmap.out b/tests/multiboot/mmap.out
new file mode 100644
index 0000000000..e70b6eb45d
--- /dev/null
+++ b/tests/multiboot/mmap.out
@@ -0,0 +1,93 @@
+
+
+
+=== Running test case: mmap.elf ===
+
+Lower memory: 639k
+Upper memory: 130040k
+
+e820 memory map:
+0x0 - 0x9fc00: type 1 [entry size: 20]
+0x9fc00 - 0xa0000: type 2 [entry size: 20]
+0xf0000 - 0x100000: type 2 [entry size: 20]
+0x100000 - 0x7ffe000: type 1 [entry size: 20]
+0x7ffe000 - 0x8000000: type 2 [entry size: 20]
+0xfffc0000 - 0x100000000: type 2 [entry size: 20]
+
+mmap start: 0x9000
+mmap end: 0x9090
+real mmap end: 0x9090
+
+
+=== Running test case: mmap.elf -m 1.1M ===
+
+Lower memory: 639k
+Upper memory: 96k
+
+e820 memory map:
+0x0 - 0x9fc00: type 1 [entry size: 20]
+0x9fc00 - 0xa0000: type 2 [entry size: 20]
+0xf0000 - 0x100000: type 2 [entry size: 20]
+0x100000 - 0x118000: type 1 [entry size: 20]
+0x118000 - 0x11a000: type 2 [entry size: 20]
+0xfffc0000 - 0x100000000: type 2 [entry size: 20]
+
+mmap start: 0x9000
+mmap end: 0x9090
+real mmap end: 0x9090
+
+
+=== Running test case: mmap.elf -m 2G ===
+
+Lower memory: 639k
+Upper memory: 2096120k
+
+e820 memory map:
+0x0 - 0x9fc00: type 1 [entry size: 20]
+0x9fc00 - 0xa0000: type 2 [entry size: 20]
+0xf0000 - 0x100000: type 2 [entry size: 20]
+0x100000 - 0x7fffe000: type 1 [entry size: 20]
+0x7fffe000 - 0x80000000: type 2 [entry size: 20]
+0xfffc0000 - 0x100000000: type 2 [entry size: 20]
+
+mmap start: 0x9000
+mmap end: 0x9090
+real mmap end: 0x9090
+
+
+=== Running test case: mmap.elf -m 4G ===
+
+Lower memory: 639k
+Upper memory: 3668984k
+
+e820 memory map:
+0x0 - 0x9fc00: type 1 [entry size: 20]
+0x9fc00 - 0xa0000: type 2 [entry size: 20]
+0xf0000 - 0x100000: type 2 [entry size: 20]
+0x100000 - 0xdfffe000: type 1 [entry size: 20]
+0xdfffe000 - 0xe0000000: type 2 [entry size: 20]
+0xfffc0000 - 0x100000000: type 2 [entry size: 20]
+0x100000000 - 0x120000000: type 1 [entry size: 20]
+
+mmap start: 0x9000
+mmap end: 0x90a8
+real mmap end: 0x90a8
+
+
+=== Running test case: mmap.elf -m 8G ===
+
+Lower memory: 639k
+Upper memory: 3668984k
+
+e820 memory map:
+0x0 - 0x9fc00: type 1 [entry size: 20]
+0x9fc00 - 0xa0000: type 2 [entry size: 20]
+0xf0000 - 0x100000: type 2 [entry size: 20]
+0x100000 - 0xdfffe000: type 1 [entry size: 20]
+0xdfffe000 - 0xe0000000: type 2 [entry size: 20]
+0xfffc0000 - 0x100000000: type 2 [entry size: 20]
+0x100000000 - 0x220000000: type 1 [entry size: 20]
+
+mmap start: 0x9000
+mmap end: 0x90a8
+real mmap end: 0x90a8
diff --git a/tests/multiboot/multiboot.h b/tests/multiboot/multiboot.h
new file mode 100644
index 0000000000..4eb1fbe5d4
--- /dev/null
+++ b/tests/multiboot/multiboot.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2013 Kevin Wolf <kwolf@redhat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef MULTIBOOT_H
+#define MULTIBOOT_H
+
+#include "libc.h"
+
+struct mb_info {
+ uint32_t flags;
+ uint32_t mem_lower;
+ uint32_t mem_upper;
+ uint32_t boot_device;
+ uint32_t cmdline;
+ uint32_t mods_count;
+ uint32_t mods_addr;
+ char syms[16];
+ uint32_t mmap_length;
+ uint32_t mmap_addr;
+ uint32_t drives_length;
+ uint32_t drives_addr;
+ uint32_t config_table;
+ uint32_t boot_loader_name;
+ uint32_t apm_table;
+ uint32_t vbe_control_info;
+ uint32_t vbe_mode_info;
+ uint16_t vbe_mode;
+ uint16_t vbe_interface_seg;
+ uint16_t vbe_interface_off;
+ uint16_t vbe_interface_len;
+} __attribute__((packed));
+
+struct mb_module {
+ uint32_t mod_start;
+ uint32_t mod_end;
+ uint32_t string;
+ uint32_t reserved;
+} __attribute__((packed));
+
+struct mb_mmap_entry {
+ uint32_t size;
+ uint64_t base_addr;
+ uint64_t length;
+ uint32_t type;
+} __attribute__((packed));
+
+#endif
diff --git a/tests/multiboot/run_test.sh b/tests/multiboot/run_test.sh
new file mode 100755
index 0000000000..97a9a49f8b
--- /dev/null
+++ b/tests/multiboot/run_test.sh
@@ -0,0 +1,81 @@
+#!/bin/bash
+
+# Copyright (c) 2013 Kevin Wolf <kwolf@redhat.com>
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+QEMU=${QEMU:-"../../x86_64-softmmu/qemu-system-x86_64"}
+
+run_qemu() {
+ local kernel=$1
+ shift
+
+ echo -e "\n\n=== Running test case: $kernel $@ ===\n" >> test.log
+
+ $QEMU \
+ -kernel $kernel \
+ -display none \
+ -device isa-debugcon,chardev=stdio \
+ -chardev file,path=test.out,id=stdio \
+ -device isa-debug-exit,iobase=0xf4,iosize=0x4 \
+ "$@"
+ ret=$?
+
+ cat test.out >> test.log
+}
+
+mmap() {
+ run_qemu mmap.elf
+ run_qemu mmap.elf -m 1.1M
+ run_qemu mmap.elf -m 2G
+ run_qemu mmap.elf -m 4G
+ run_qemu mmap.elf -m 8G
+}
+
+
+make all
+
+for t in mmap; do
+
+ echo > test.log
+ $t
+
+ debugexit=$((ret & 0x1))
+ ret=$((ret >> 1))
+ pass=1
+
+ if [ $debugexit != 1 ]; then
+ echo -e "\e[31m ?? \e[0m $t (no debugexit used, exit code $ret)"
+ pass=0
+ elif [ $ret != 0 ]; then
+ echo -e "\e[31mFAIL\e[0m $t (exit code $ret)"
+ pass=0
+ fi
+
+ if ! diff $t.out test.log > /dev/null 2>&1; then
+ echo -e "\e[31mFAIL\e[0m $t (output difference)"
+ diff -u $t.out test.log
+ pass=0
+ fi
+
+ if [ $pass == 1 ]; then
+ echo -e "\e[32mPASS\e[0m $t"
+ fi
+
+done
diff --git a/tests/multiboot/start.S b/tests/multiboot/start.S
new file mode 100644
index 0000000000..7d33959650
--- /dev/null
+++ b/tests/multiboot/start.S
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2013 Kevin Wolf <kwolf@redhat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+.section multiboot
+
+#define MB_MAGIC 0x1badb002
+#define MB_FLAGS 0x0
+#define MB_CHECKSUM -(MB_MAGIC + MB_FLAGS)
+
+.align 4
+.int MB_MAGIC
+.int MB_FLAGS
+.int MB_CHECKSUM
+
+.section .text
+.global _start
+_start:
+ mov $stack, %esp
+ push %ebx
+ push %eax
+ call test_main
+
+ /* Test device exit */
+ outl %eax, $0xf4
+
+ cli
+ hlt
+ jmp .
+
+.section bss
+.space 8192
+stack:
diff --git a/tests/qemu-iotests/030 b/tests/qemu-iotests/030
index ae56f3b808..d0f96ea0e1 100755
--- a/tests/qemu-iotests/030
+++ b/tests/qemu-iotests/030
@@ -388,7 +388,9 @@ class TestStreamStop(iotests.QMPTestCase):
def setUp(self):
qemu_img('create', backing_img, str(TestStreamStop.image_len))
+ qemu_io('-c', 'write -P 0x1 0 32M', backing_img)
qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img)
+ qemu_io('-c', 'write -P 0x1 32M 32M', test_img)
self.vm = iotests.VM().add_drive(test_img)
self.vm.launch()
@@ -414,7 +416,9 @@ class TestSetSpeed(iotests.QMPTestCase):
def setUp(self):
qemu_img('create', backing_img, str(TestSetSpeed.image_len))
+ qemu_io('-c', 'write -P 0x1 0 32M', backing_img)
qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img)
+ qemu_io('-c', 'write -P 0x1 32M 32M', test_img)
self.vm = iotests.VM().add_drive(test_img)
self.vm.launch()
diff --git a/tests/qemu-iotests/040 b/tests/qemu-iotests/040
index aad535a74b..a2e18c56d4 100755
--- a/tests/qemu-iotests/040
+++ b/tests/qemu-iotests/040
@@ -54,22 +54,12 @@ class ImageCommitTestCase(iotests.QMPTestCase):
self.assert_no_active_commit()
- def create_image(self, name, size):
- file = open(name, 'w')
- i = 0
- while i < size:
- sector = struct.pack('>l504xl', i / 512, i / 512)
- file.write(sector)
- i = i + 512
- file.close()
-
-
class TestSingleDrive(ImageCommitTestCase):
image_len = 1 * 1024 * 1024
test_len = 1 * 1024 * 256
def setUp(self):
- self.create_image(backing_img, TestSingleDrive.image_len)
+ iotests.create_image(backing_img, TestSingleDrive.image_len)
qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, mid_img)
qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % mid_img, test_img)
qemu_io('-c', 'write -P 0xab 0 524288', backing_img)
@@ -167,7 +157,7 @@ class TestRelativePaths(ImageCommitTestCase):
except OSError as exception:
if exception.errno != errno.EEXIST:
raise
- self.create_image(self.backing_img_abs, TestRelativePaths.image_len)
+ iotests.create_image(self.backing_img_abs, TestRelativePaths.image_len)
qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % self.backing_img_abs, self.mid_img_abs)
qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % self.mid_img_abs, self.test_img)
qemu_img('rebase', '-u', '-b', self.backing_img, self.mid_img_abs)
diff --git a/tests/qemu-iotests/051.out b/tests/qemu-iotests/051.out
index 2839e32807..15deef6dc3 100644
--- a/tests/qemu-iotests/051.out
+++ b/tests/qemu-iotests/051.out
@@ -24,7 +24,7 @@ QEMU X.Y.Z monitor - type 'help' for more information
(qemu) iininfinfoinfo info binfo blinfo bloinfo blocinfo block
ide0-hd0: TEST_DIR/t.qcow2 (qcow2)
Backing file: TEST_DIR/t.qcow2.orig (chain depth: 1)
- [not inserted](qemu) qququiquit
+(qemu) qququiquit
=== Enable and disable lazy refcounting on the command line, plus some invalid values ===
diff --git a/tests/qemu-iotests/059 b/tests/qemu-iotests/059
index b81c575d94..6a27ac978b 100755
--- a/tests/qemu-iotests/059
+++ b/tests/qemu-iotests/059
@@ -69,7 +69,7 @@ poke_file "$TEST_IMG" "$grain_table_size_offset" "\x01\x00\x00\x00"
echo
echo "=== Testing monolithicFlat creation and opening ==="
IMGOPTS="subformat=monolithicFlat" _make_test_img 2G
-$QEMU_IMG info $TEST_IMG | _filter_testdir
+_img_info
echo
echo "=== Testing monolithicFlat with zeroed_grain ==="
diff --git a/tests/qemu-iotests/059.out b/tests/qemu-iotests/059.out
index 9b12efb466..2ded8a950a 100644
--- a/tests/qemu-iotests/059.out
+++ b/tests/qemu-iotests/059.out
@@ -18,10 +18,9 @@ no file open, try 'help open'
=== Testing monolithicFlat creation and opening ===
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2147483648
-image: TEST_DIR/t.vmdk
-file format: vmdk
+image: TEST_DIR/t.IMGFMT
+file format: IMGFMT
virtual size: 2.0G (2147483648 bytes)
-disk size: 4.0K
=== Testing monolithicFlat with zeroed_grain ===
qemu-img: TEST_DIR/t.IMGFMT: Flat image can't enable zeroed grain
diff --git a/tests/qemu-iotests/068 b/tests/qemu-iotests/068
new file mode 100755
index 0000000000..b72e55599b
--- /dev/null
+++ b/tests/qemu-iotests/068
@@ -0,0 +1,65 @@
+#!/bin/bash
+#
+# Test case for loading a saved VM state from a qcow2 image
+#
+# Copyright (C) 2013 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=mreitz@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
+
+# This tests qocw2-specific low-level functionality
+_supported_fmt qcow2
+_supported_proto generic
+_supported_os Linux
+
+IMGOPTS="compat=1.1"
+IMG_SIZE=128K
+
+echo
+echo "=== Saving and reloading a VM state to/from a qcow2 image ==="
+echo
+_make_test_img $IMG_SIZE
+# Give qemu some time to boot before saving the VM state
+bash -c 'sleep 1; echo -e "savevm 0\nquit"' |\
+ $QEMU -nographic -monitor stdio -serial none -hda "$TEST_IMG" |\
+ _filter_qemu
+# Now try to continue from that VM state (this should just work)
+echo quit |\
+ $QEMU -nographic -monitor stdio -serial none -hda "$TEST_IMG" -loadvm 0 |\
+ _filter_qemu
+
+# success, all done
+echo "*** done"
+rm -f $seq.full
+status=0
diff --git a/tests/qemu-iotests/068.out b/tests/qemu-iotests/068.out
new file mode 100644
index 0000000000..abe35a9f8c
--- /dev/null
+++ b/tests/qemu-iotests/068.out
@@ -0,0 +1,11 @@
+QA output created by 068
+
+=== Saving and reloading a VM state to/from a qcow2 image ===
+
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=131072
+QEMU X.Y.Z monitor - type 'help' for more information
+(qemu) ssasavsavesavevsavevmsavevm savevm 0
+(qemu) qququiquit
+QEMU X.Y.Z monitor - type 'help' for more information
+(qemu) qququiquit
+*** done
diff --git a/tests/qemu-iotests/069 b/tests/qemu-iotests/069
new file mode 100755
index 0000000000..3042803a81
--- /dev/null
+++ b/tests/qemu-iotests/069
@@ -0,0 +1,59 @@
+#!/bin/bash
+#
+# Test case for deleting a backing file
+#
+# Copyright (C) 2013 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=mreitz@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 cow qed qcow qcow2 vmdk
+_supported_proto generic
+_supported_os Linux
+
+IMG_SIZE=128K
+
+echo
+echo "=== Creating an image with a backing file and deleting that file ==="
+echo
+TEST_IMG="$TEST_IMG.base" _make_test_img $IMG_SIZE
+_make_test_img -b "$TEST_IMG.base" $IMG_SIZE
+rm -f "$TEST_IMG.base"
+# Just open the image and close it right again (this should print an error message)
+$QEMU_IO -c quit "$TEST_IMG" 2>&1 | _filter_testdir | _filter_imgfmt
+
+# success, all done
+echo "*** done"
+rm -f $seq.full
+status=0
diff --git a/tests/qemu-iotests/069.out b/tests/qemu-iotests/069.out
new file mode 100644
index 0000000000..364881429c
--- /dev/null
+++ b/tests/qemu-iotests/069.out
@@ -0,0 +1,8 @@
+QA output created by 069
+
+=== Creating an image with a backing file and deleting that file ===
+
+Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=131072
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=131072 backing_file='TEST_DIR/t.IMGFMT.base'
+qemu-io: can't open device TEST_DIR/t.IMGFMT: Could not open file: No such file or directory
+*** done
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
index 13c5500f54..c57ff35843 100644
--- a/tests/qemu-iotests/group
+++ b/tests/qemu-iotests/group
@@ -73,3 +73,5 @@
065 rw auto
066 rw auto
067 rw auto
+068 rw auto
+069 rw auto