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