diff options
author | Anthony Liguori <anthony@codemonkey.ws> | 2013-10-31 17:02:26 +0100 |
---|---|---|
committer | Anthony Liguori <anthony@codemonkey.ws> | 2013-10-31 17:02:26 +0100 |
commit | a126050a103c924b03388a9a64ce9af8c96b0969 (patch) | |
tree | c93b9c5cdb8b2a0256845e596e198f1c65c4fcde | |
parent | ef5cfe5bbd8bb05a51afaf7ab313769eb9ef44b6 (diff) | |
parent | f4c129a38a5430b7342a7a23f53a22831154612f (diff) |
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>
35 files changed, 1156 insertions, 163 deletions
@@ -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 @@ -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) i[K[Din[K[D[Dinf[K[D[D[Dinfo[K[D[D[D[Dinfo [K[D[D[D[D[Dinfo b[K[D[D[D[D[D[Dinfo bl[K[D[D[D[D[D[D[Dinfo blo[K[D[D[D[D[D[D[D[Dinfo bloc[K[D[D[D[D[D[D[D[D[Dinfo block[K ide0-hd0: TEST_DIR/t.qcow2 (qcow2) Backing file: TEST_DIR/t.qcow2.orig (chain depth: 1) - [not inserted](qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K === 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) s[K[Dsa[K[D[Dsav[K[D[D[Dsave[K[D[D[D[Dsavev[K[D[D[D[D[Dsavevm[K[D[D[D[D[D[Dsavevm [K[D[D[D[D[D[D[Dsavevm 0[K +(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +*** 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 |