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);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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 $@ $^
 | 
			
		||||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,19 @@
 | 
			
		|||
ENTRY(_start)
 | 
			
		||||
 | 
			
		||||
SECTIONS
 | 
			
		||||
{
 | 
			
		||||
    . = 0x100000;
 | 
			
		||||
    .text : {
 | 
			
		||||
        *(multiboot)
 | 
			
		||||
        *(.text)
 | 
			
		||||
    }
 | 
			
		||||
    .data ALIGN(4096) : {
 | 
			
		||||
        *(.data)
 | 
			
		||||
    }
 | 
			
		||||
    .rodata ALIGN(4096) : {
 | 
			
		||||
        *(.rodata)
 | 
			
		||||
    }
 | 
			
		||||
    .bss ALIGN(4096) : {
 | 
			
		||||
        *(.bss)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			@ -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