Block patches for 1.7.0-rc0 (v2)
-----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.14 (GNU/Linux) iQIcBAABAgAGBQJScnrnAAoJEH8JsnLIjy/WhhsP/2No1yEGNzfhw0WLDsEGBJI7 zjG+QkRMO4q2t256SxNr84KBFJlYKBvGrx+W8xC66AdvR1feL5hmWdXAMTJovx6Z 3Qt59RI9iISZ2OEtc9FhdsC+dSdM/3qie17XuuSCqifsi4xLjIZK/s18+RnLa0t/ nRObYP4prRl0c3o1gKaUvNz2wkIqctQAIe8UQkn6R1vPC6D60m/H9dDj4Kj68HO0 ICsF4AXBR/V2a8gU36/PGexBVyfgC4HOeuN0qNSTgYOKxLuNR+SrlzzhHE+jZTs5 GASm3vg/vUgBOO1759X5T8hveO6yu8XL82l+/d5nIK4gYGORIQZT74dyV5JgQIlF Y47d0cF28+C/fuL1jh7c+2HY5WmmJQosMi9CaCBj0lvH0k5caEjqwPeHtRBmEyu3 1wAcLQJowZrWB5ez9MjezsaL4sPCymvB/4F443xdz5V19mE41bLZGW2EIT7MXHY7 IcwLU/opx76GMOFfWVMA7jeQkjiPaqGeaQHJzdnGUzIthqyiTigQMfi5P3nXGDic uQi+KrqP9lNpJlZk4xGQnFovHNmKZrnLhUvqOIPk7/wKMvlU6ewdzp5Fnwzqw4MW uJ/6eBJYolMyY+q37AH3Q6ZUkwTJi9O1drCPA0Ogr/dJiCyAiOoKuL0N74VabpcD AahXw+yYV0qh6H4YjOzW =wGCx -----END PGP SIGNATURE----- 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>
This commit is contained in:
commit
a126050a10
14
block.c
14
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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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,
|
||||
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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,
|
||||
|
352
block/sheepdog.c
352
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;
|
||||
|
||||
/*
|
||||
* 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;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void coroutine_fn resend_aioreq(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;
|
||||
}
|
||||
|
||||
aio_req->oid = vid_to_data_oid(s->inode.vdi_id,
|
||||
data_oid_to_idx(aio_req->oid));
|
||||
|
||||
/* 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));
|
||||
|
68
block/vmdk.c
68
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,
|
||||
};
|
||||
|
@ -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
|
||||
|
4
exec.c
4
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;
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -215,6 +215,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
|
||||
*
|
||||
|
@ -224,6 +224,27 @@
|
||||
'*lazy-refcounts': 'bool'
|
||||
} }
|
||||
|
||||
##
|
||||
# @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:
|
||||
#
|
||||
@ -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',
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
18
tests/multiboot/Makefile
Normal file
18
tests/multiboot/Makefile
Normal file
@ -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 $@ $^
|
139
tests/multiboot/libc.c
Normal file
139
tests/multiboot/libc.c
Normal file
@ -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);
|
||||
}
|
||||
|
||||
|
61
tests/multiboot/libc.h
Normal file
61
tests/multiboot/libc.h
Normal file
@ -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
|
19
tests/multiboot/link.ld
Normal file
19
tests/multiboot/link.ld
Normal file
@ -0,0 +1,19 @@
|
||||
ENTRY(_start)
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
. = 0x100000;
|
||||
.text : {
|
||||
*(multiboot)
|
||||
*(.text)
|
||||
}
|
||||
.data ALIGN(4096) : {
|
||||
*(.data)
|
||||
}
|
||||
.rodata ALIGN(4096) : {
|
||||
*(.rodata)
|
||||
}
|
||||
.bss ALIGN(4096) : {
|
||||
*(.bss)
|
||||
}
|
||||
}
|
56
tests/multiboot/mmap.c
Normal file
56
tests/multiboot/mmap.c
Normal file
@ -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;
|
||||
}
|
93
tests/multiboot/mmap.out
Normal file
93
tests/multiboot/mmap.out
Normal file
@ -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
|
66
tests/multiboot/multiboot.h
Normal file
66
tests/multiboot/multiboot.h
Normal file
@ -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
|
81
tests/multiboot/run_test.sh
Executable file
81
tests/multiboot/run_test.sh
Executable file
@ -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
|
51
tests/multiboot/start.S
Normal file
51
tests/multiboot/start.S
Normal file
@ -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:
|
@ -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()
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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 ===
|
||||
|
@ -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 ==="
|
||||
|
@ -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
|
||||
|
65
tests/qemu-iotests/068
Executable file
65
tests/qemu-iotests/068
Executable file
@ -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
|
11
tests/qemu-iotests/068.out
Normal file
11
tests/qemu-iotests/068.out
Normal file
@ -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
|
59
tests/qemu-iotests/069
Executable file
59
tests/qemu-iotests/069
Executable file
@ -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
|
8
tests/qemu-iotests/069.out
Normal file
8
tests/qemu-iotests/069.out
Normal file
@ -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
|
@ -73,3 +73,5 @@
|
||||
065 rw auto
|
||||
066 rw auto
|
||||
067 rw auto
|
||||
068 rw auto
|
||||
069 rw auto
|
||||
|
Loading…
Reference in New Issue
Block a user