Block layer patches
-----BEGIN PGP SIGNATURE----- iQIcBAABAgAGBQJanYJPAAoJEH8JsnLIjy/WxjUQAJA+DTOmGXvaNpMs65BrU79K /r/iGVrzHv/RMLmrWMnqj96W9SnpMuiAP9hVLNsekqClY9q4ME4DpGcXhWfhSvF5 FC51ehvFJdfo8cPorsevcqNj60iWebjcx3lFfUq2606UOyYih3oijYxr6gSwWbRc GAgdGMqsvGYpzgqAQVEWHUhaX0La49/OzY42aR+E+LCBNfTYvlydvyoc+tUTdIpW 1eM/ASGndGsN0Cf2vxlbKgJ0/P6v+cRZuuIDhKZqre+YG+yM+pq7yZb+o7nf/P36 TPR93BsT7FSVAizRK7VFRuPIynHpiaxYygrJERCXF0sxsV4OlKjpmt/uUPamWFh+ 46Jx2NK1AuAx87BdErgmA119ObO3oAPxK0+2p981obb6SphTbbPxDj6SOlYCt4mJ mhff4JtIiwCmDSckAwd2mkBI1Tvl9qqcELrpyd2t2eU4ec2vf7fPd85EsK/Mq6Kr dbfqFvjNaaMxChoqFgkHAveYJ7zYqRFI2IY5o9c1QyZehCGPWjScxHXZZYdpDl59 YF9DkYQDOyvEX2jmMECaO1r/0nnO+BqQHu5ItJuTte9rjP9Q0do3iBISiIefewtf yji6/QNn2hFrnr1HPAwLFFC3kPgc8Mq8mIUb53j8vG/01KhVRCcnJm2K6D4IUwLZ S6ZnQJB97eE4y7YR5dNt =2axz -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/kevin/tags/for-upstream' into staging Block layer patches # gpg: Signature made Mon 05 Mar 2018 17:45:51 GMT # gpg: using RSA key 7F09B272C88F2FD6 # gpg: Good signature from "Kevin Wolf <kwolf@redhat.com>" # Primary key fingerprint: DC3D EB15 9A9A F95D 3D74 56FE 7F09 B272 C88F 2FD6 * remotes/kevin/tags/for-upstream: (38 commits) block: Fix NULL dereference on empty drive error qcow2: Replace align_offset() with ROUND_UP() block/ssh: Add basic .bdrv_truncate() block/ssh: Make ssh_grow_file() blocking block/ssh: Pull ssh_grow_file() from ssh_create() qemu-img: Make resize error message more general qcow2: make qcow2_co_create2() a coroutine_fn block: rename .bdrv_create() to .bdrv_co_create_opts() Revert "IDE: Do not flush empty CDROM drives" block: test blk_aio_flush() with blk->root == NULL block: add BlockBackend->in_flight counter block: extract AIO_WAIT_WHILE() from BlockDriverState aio: rename aio_context_in_iothread() to in_aio_context_home_thread() docs: document how to use the l2-cache-entry-size parameter specs/qcow2: Fix documentation of the compressed cluster descriptor iotest 033: add misaligned write-zeroes test via truncate block: fix write with zero flag set and iovector provided block: Drop unused .bdrv_co_get_block_status() vvfat: Switch to .bdrv_co_block_status() vpc: Switch to .bdrv_co_block_status() ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org> # Conflicts: # include/block/block.h
This commit is contained in:
		
						commit
						58e2e17dba
					
				
							
								
								
									
										11
									
								
								block.c
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								block.c
									
									
									
									
									
								
							| @ -418,7 +418,7 @@ static void coroutine_fn bdrv_create_co_entry(void *opaque) | ||||
|     CreateCo *cco = opaque; | ||||
|     assert(cco->drv); | ||||
| 
 | ||||
|     ret = cco->drv->bdrv_create(cco->filename, cco->opts, &local_err); | ||||
|     ret = cco->drv->bdrv_co_create_opts(cco->filename, cco->opts, &local_err); | ||||
|     error_propagate(&cco->err, local_err); | ||||
|     cco->ret = ret; | ||||
| } | ||||
| @ -437,7 +437,7 @@ int bdrv_create(BlockDriver *drv, const char* filename, | ||||
|         .err = NULL, | ||||
|     }; | ||||
| 
 | ||||
|     if (!drv->bdrv_create) { | ||||
|     if (!drv->bdrv_co_create_opts) { | ||||
|         error_setg(errp, "Driver '%s' does not support image creation", drv->format_name); | ||||
|         ret = -ENOTSUP; | ||||
|         goto out; | ||||
| @ -4711,7 +4711,12 @@ out: | ||||
| 
 | ||||
| AioContext *bdrv_get_aio_context(BlockDriverState *bs) | ||||
| { | ||||
|     return bs->aio_context; | ||||
|     return bs ? bs->aio_context : qemu_get_aio_context(); | ||||
| } | ||||
| 
 | ||||
| AioWait *bdrv_get_aio_wait(BlockDriverState *bs) | ||||
| { | ||||
|     return bs ? &bs->wait : NULL; | ||||
| } | ||||
| 
 | ||||
| void bdrv_coroutine_enter(BlockDriverState *bs, Coroutine *co) | ||||
|  | ||||
| @ -627,15 +627,17 @@ static int coroutine_fn blkdebug_co_pdiscard(BlockDriverState *bs, | ||||
|     return bdrv_co_pdiscard(bs->file->bs, offset, bytes); | ||||
| } | ||||
| 
 | ||||
| static int64_t coroutine_fn blkdebug_co_get_block_status( | ||||
|     BlockDriverState *bs, int64_t sector_num, int nb_sectors, int *pnum, | ||||
|     BlockDriverState **file) | ||||
| static int coroutine_fn blkdebug_co_block_status(BlockDriverState *bs, | ||||
|                                                  bool want_zero, | ||||
|                                                  int64_t offset, | ||||
|                                                  int64_t bytes, | ||||
|                                                  int64_t *pnum, | ||||
|                                                  int64_t *map, | ||||
|                                                  BlockDriverState **file) | ||||
| { | ||||
|     assert(QEMU_IS_ALIGNED(sector_num | nb_sectors, | ||||
|                            DIV_ROUND_UP(bs->bl.request_alignment, | ||||
|                                         BDRV_SECTOR_SIZE))); | ||||
|     return bdrv_co_get_block_status_from_file(bs, sector_num, nb_sectors, | ||||
|                                               pnum, file); | ||||
|     assert(QEMU_IS_ALIGNED(offset | bytes, bs->bl.request_alignment)); | ||||
|     return bdrv_co_block_status_from_file(bs, want_zero, offset, bytes, | ||||
|                                           pnum, map, file); | ||||
| } | ||||
| 
 | ||||
| static void blkdebug_close(BlockDriverState *bs) | ||||
| @ -907,7 +909,7 @@ static BlockDriver bdrv_blkdebug = { | ||||
|     .bdrv_co_flush_to_disk  = blkdebug_co_flush, | ||||
|     .bdrv_co_pwrite_zeroes  = blkdebug_co_pwrite_zeroes, | ||||
|     .bdrv_co_pdiscard       = blkdebug_co_pdiscard, | ||||
|     .bdrv_co_get_block_status = blkdebug_co_get_block_status, | ||||
|     .bdrv_co_block_status   = blkdebug_co_block_status, | ||||
| 
 | ||||
|     .bdrv_debug_event           = blkdebug_debug_event, | ||||
|     .bdrv_debug_breakpoint      = blkdebug_debug_breakpoint, | ||||
|  | ||||
| @ -73,6 +73,14 @@ struct BlockBackend { | ||||
|     int quiesce_counter; | ||||
|     VMChangeStateEntry *vmsh; | ||||
|     bool force_allow_inactivate; | ||||
| 
 | ||||
|     /* Number of in-flight aio requests.  BlockDriverState also counts
 | ||||
|      * in-flight requests but aio requests can exist even when blk->root is | ||||
|      * NULL, so we cannot rely on its counter for that case. | ||||
|      * Accessed with atomic ops. | ||||
|      */ | ||||
|     unsigned int in_flight; | ||||
|     AioWait wait; | ||||
| }; | ||||
| 
 | ||||
| typedef struct BlockBackendAIOCB { | ||||
| @ -1225,11 +1233,22 @@ int blk_make_zero(BlockBackend *blk, BdrvRequestFlags flags) | ||||
|     return bdrv_make_zero(blk->root, flags); | ||||
| } | ||||
| 
 | ||||
| static void blk_inc_in_flight(BlockBackend *blk) | ||||
| { | ||||
|     atomic_inc(&blk->in_flight); | ||||
| } | ||||
| 
 | ||||
| static void blk_dec_in_flight(BlockBackend *blk) | ||||
| { | ||||
|     atomic_dec(&blk->in_flight); | ||||
|     aio_wait_kick(&blk->wait); | ||||
| } | ||||
| 
 | ||||
| static void error_callback_bh(void *opaque) | ||||
| { | ||||
|     struct BlockBackendAIOCB *acb = opaque; | ||||
| 
 | ||||
|     bdrv_dec_in_flight(acb->common.bs); | ||||
|     blk_dec_in_flight(acb->blk); | ||||
|     acb->common.cb(acb->common.opaque, acb->ret); | ||||
|     qemu_aio_unref(acb); | ||||
| } | ||||
| @ -1240,7 +1259,7 @@ BlockAIOCB *blk_abort_aio_request(BlockBackend *blk, | ||||
| { | ||||
|     struct BlockBackendAIOCB *acb; | ||||
| 
 | ||||
|     bdrv_inc_in_flight(blk_bs(blk)); | ||||
|     blk_inc_in_flight(blk); | ||||
|     acb = blk_aio_get(&block_backend_aiocb_info, blk, cb, opaque); | ||||
|     acb->blk = blk; | ||||
|     acb->ret = ret; | ||||
| @ -1263,7 +1282,7 @@ static const AIOCBInfo blk_aio_em_aiocb_info = { | ||||
| static void blk_aio_complete(BlkAioEmAIOCB *acb) | ||||
| { | ||||
|     if (acb->has_returned) { | ||||
|         bdrv_dec_in_flight(acb->common.bs); | ||||
|         blk_dec_in_flight(acb->rwco.blk); | ||||
|         acb->common.cb(acb->common.opaque, acb->rwco.ret); | ||||
|         qemu_aio_unref(acb); | ||||
|     } | ||||
| @ -1284,7 +1303,7 @@ static BlockAIOCB *blk_aio_prwv(BlockBackend *blk, int64_t offset, int bytes, | ||||
|     BlkAioEmAIOCB *acb; | ||||
|     Coroutine *co; | ||||
| 
 | ||||
|     bdrv_inc_in_flight(blk_bs(blk)); | ||||
|     blk_inc_in_flight(blk); | ||||
|     acb = blk_aio_get(&blk_aio_em_aiocb_info, blk, cb, opaque); | ||||
|     acb->rwco = (BlkRwCo) { | ||||
|         .blk    = blk, | ||||
| @ -1521,14 +1540,41 @@ int blk_flush(BlockBackend *blk) | ||||
| 
 | ||||
| void blk_drain(BlockBackend *blk) | ||||
| { | ||||
|     if (blk_bs(blk)) { | ||||
|         bdrv_drain(blk_bs(blk)); | ||||
|     BlockDriverState *bs = blk_bs(blk); | ||||
| 
 | ||||
|     if (bs) { | ||||
|         bdrv_drained_begin(bs); | ||||
|     } | ||||
| 
 | ||||
|     /* We may have -ENOMEDIUM completions in flight */ | ||||
|     AIO_WAIT_WHILE(&blk->wait, | ||||
|             blk_get_aio_context(blk), | ||||
|             atomic_mb_read(&blk->in_flight) > 0); | ||||
| 
 | ||||
|     if (bs) { | ||||
|         bdrv_drained_end(bs); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void blk_drain_all(void) | ||||
| { | ||||
|     bdrv_drain_all(); | ||||
|     BlockBackend *blk = NULL; | ||||
| 
 | ||||
|     bdrv_drain_all_begin(); | ||||
| 
 | ||||
|     while ((blk = blk_all_next(blk)) != NULL) { | ||||
|         AioContext *ctx = blk_get_aio_context(blk); | ||||
| 
 | ||||
|         aio_context_acquire(ctx); | ||||
| 
 | ||||
|         /* We may have -ENOMEDIUM completions in flight */ | ||||
|         AIO_WAIT_WHILE(&blk->wait, ctx, | ||||
|                 atomic_mb_read(&blk->in_flight) > 0); | ||||
| 
 | ||||
|         aio_context_release(ctx); | ||||
|     } | ||||
| 
 | ||||
|     bdrv_drain_all_end(); | ||||
| } | ||||
| 
 | ||||
| void blk_set_on_error(BlockBackend *blk, BlockdevOnError on_read_error, | ||||
| @ -1569,10 +1615,11 @@ static void send_qmp_error_event(BlockBackend *blk, | ||||
|                                  bool is_read, int error) | ||||
| { | ||||
|     IoOperationType optype; | ||||
|     BlockDriverState *bs = blk_bs(blk); | ||||
| 
 | ||||
|     optype = is_read ? IO_OPERATION_TYPE_READ : IO_OPERATION_TYPE_WRITE; | ||||
|     qapi_event_send_block_io_error(blk_name(blk), | ||||
|                                    bdrv_get_node_name(blk_bs(blk)), optype, | ||||
|     qapi_event_send_block_io_error(blk_name(blk), !!bs, | ||||
|                                    bs ? bdrv_get_node_name(bs) : NULL, optype, | ||||
|                                    action, blk_iostatus_is_enabled(blk), | ||||
|                                    error == ENOSPC, strerror(error), | ||||
|                                    &error_abort); | ||||
|  | ||||
| @ -265,7 +265,7 @@ static void bdrv_commit_top_child_perm(BlockDriverState *bs, BdrvChild *c, | ||||
| static BlockDriver bdrv_commit_top = { | ||||
|     .format_name                = "commit_top", | ||||
|     .bdrv_co_preadv             = bdrv_commit_top_preadv, | ||||
|     .bdrv_co_get_block_status   = bdrv_co_get_block_status_from_backing, | ||||
|     .bdrv_co_block_status       = bdrv_co_block_status_from_backing, | ||||
|     .bdrv_refresh_filename      = bdrv_commit_top_refresh_filename, | ||||
|     .bdrv_close                 = bdrv_commit_top_close, | ||||
|     .bdrv_child_perm            = bdrv_commit_top_child_perm, | ||||
|  | ||||
| @ -556,9 +556,9 @@ static int block_crypto_open_luks(BlockDriverState *bs, | ||||
|                                      bs, options, flags, errp); | ||||
| } | ||||
| 
 | ||||
| static int block_crypto_create_luks(const char *filename, | ||||
|                                     QemuOpts *opts, | ||||
|                                     Error **errp) | ||||
| static int coroutine_fn block_crypto_co_create_opts_luks(const char *filename, | ||||
|                                                          QemuOpts *opts, | ||||
|                                                          Error **errp) | ||||
| { | ||||
|     return block_crypto_create_generic(Q_CRYPTO_BLOCK_FORMAT_LUKS, | ||||
|                                        filename, opts, errp); | ||||
| @ -617,7 +617,7 @@ BlockDriver bdrv_crypto_luks = { | ||||
|     .bdrv_open          = block_crypto_open_luks, | ||||
|     .bdrv_close         = block_crypto_close, | ||||
|     .bdrv_child_perm    = bdrv_format_default_perms, | ||||
|     .bdrv_create        = block_crypto_create_luks, | ||||
|     .bdrv_co_create_opts = block_crypto_co_create_opts_luks, | ||||
|     .bdrv_truncate      = block_crypto_truncate, | ||||
|     .create_opts        = &block_crypto_create_opts_luks, | ||||
| 
 | ||||
|  | ||||
| @ -1982,7 +1982,8 @@ static int64_t raw_get_allocated_file_size(BlockDriverState *bs) | ||||
|     return (int64_t)st.st_blocks * 512; | ||||
| } | ||||
| 
 | ||||
| static int raw_create(const char *filename, QemuOpts *opts, Error **errp) | ||||
| static int coroutine_fn raw_co_create_opts(const char *filename, QemuOpts *opts, | ||||
|                                            Error **errp) | ||||
| { | ||||
|     int fd; | ||||
|     int result = 0; | ||||
| @ -2131,25 +2132,24 @@ static int find_allocation(BlockDriverState *bs, off_t start, | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Returns the allocation status of the specified sectors. | ||||
|  * Returns the allocation status of the specified offset. | ||||
|  * | ||||
|  * If 'sector_num' is beyond the end of the disk image the return value is 0 | ||||
|  * and 'pnum' is set to 0. | ||||
|  * The block layer guarantees 'offset' and 'bytes' are within bounds. | ||||
|  * | ||||
|  * 'pnum' is set to the number of sectors (including and immediately following | ||||
|  * the specified sector) that are known to be in the same | ||||
|  * 'pnum' is set to the number of bytes (including and immediately following | ||||
|  * the specified offset) that are known to be in the same | ||||
|  * allocated/unallocated state. | ||||
|  * | ||||
|  * 'nb_sectors' is the max value 'pnum' should be set to.  If nb_sectors goes | ||||
|  * beyond the end of the disk image it will be clamped. | ||||
|  * 'bytes' is the max value 'pnum' should be set to. | ||||
|  */ | ||||
| static int64_t coroutine_fn raw_co_get_block_status(BlockDriverState *bs, | ||||
|                                                     int64_t sector_num, | ||||
|                                                     int nb_sectors, int *pnum, | ||||
|                                                     BlockDriverState **file) | ||||
| static int coroutine_fn raw_co_block_status(BlockDriverState *bs, | ||||
|                                             bool want_zero, | ||||
|                                             int64_t offset, | ||||
|                                             int64_t bytes, int64_t *pnum, | ||||
|                                             int64_t *map, | ||||
|                                             BlockDriverState **file) | ||||
| { | ||||
|     off_t start, data = 0, hole = 0; | ||||
|     int64_t total_size; | ||||
|     off_t data = 0, hole = 0; | ||||
|     int ret; | ||||
| 
 | ||||
|     ret = fd_open(bs); | ||||
| @ -2157,39 +2157,36 @@ static int64_t coroutine_fn raw_co_get_block_status(BlockDriverState *bs, | ||||
|         return ret; | ||||
|     } | ||||
| 
 | ||||
|     start = sector_num * BDRV_SECTOR_SIZE; | ||||
|     total_size = bdrv_getlength(bs); | ||||
|     if (total_size < 0) { | ||||
|         return total_size; | ||||
|     } else if (start >= total_size) { | ||||
|         *pnum = 0; | ||||
|         return 0; | ||||
|     } else if (start + nb_sectors * BDRV_SECTOR_SIZE > total_size) { | ||||
|         nb_sectors = DIV_ROUND_UP(total_size - start, BDRV_SECTOR_SIZE); | ||||
|     if (!want_zero) { | ||||
|         *pnum = bytes; | ||||
|         *map = offset; | ||||
|         *file = bs; | ||||
|         return BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID; | ||||
|     } | ||||
| 
 | ||||
|     ret = find_allocation(bs, start, &data, &hole); | ||||
|     ret = find_allocation(bs, offset, &data, &hole); | ||||
|     if (ret == -ENXIO) { | ||||
|         /* Trailing hole */ | ||||
|         *pnum = nb_sectors; | ||||
|         *pnum = bytes; | ||||
|         ret = BDRV_BLOCK_ZERO; | ||||
|     } else if (ret < 0) { | ||||
|         /* No info available, so pretend there are no holes */ | ||||
|         *pnum = nb_sectors; | ||||
|         *pnum = bytes; | ||||
|         ret = BDRV_BLOCK_DATA; | ||||
|     } else if (data == start) { | ||||
|         /* On a data extent, compute sectors to the end of the extent,
 | ||||
|     } else if (data == offset) { | ||||
|         /* On a data extent, compute bytes to the end of the extent,
 | ||||
|          * possibly including a partial sector at EOF. */ | ||||
|         *pnum = MIN(nb_sectors, DIV_ROUND_UP(hole - start, BDRV_SECTOR_SIZE)); | ||||
|         *pnum = MIN(bytes, hole - offset); | ||||
|         ret = BDRV_BLOCK_DATA; | ||||
|     } else { | ||||
|         /* On a hole, compute sectors to the beginning of the next extent.  */ | ||||
|         assert(hole == start); | ||||
|         *pnum = MIN(nb_sectors, (data - start) / BDRV_SECTOR_SIZE); | ||||
|         /* On a hole, compute bytes to the beginning of the next extent.  */ | ||||
|         assert(hole == offset); | ||||
|         *pnum = MIN(bytes, data - offset); | ||||
|         ret = BDRV_BLOCK_ZERO; | ||||
|     } | ||||
|     *map = offset; | ||||
|     *file = bs; | ||||
|     return ret | BDRV_BLOCK_OFFSET_VALID | start; | ||||
|     return ret | BDRV_BLOCK_OFFSET_VALID; | ||||
| } | ||||
| 
 | ||||
| static coroutine_fn BlockAIOCB *raw_aio_pdiscard(BlockDriverState *bs, | ||||
| @ -2280,9 +2277,9 @@ BlockDriver bdrv_file = { | ||||
|     .bdrv_reopen_commit = raw_reopen_commit, | ||||
|     .bdrv_reopen_abort = raw_reopen_abort, | ||||
|     .bdrv_close = raw_close, | ||||
|     .bdrv_create = raw_create, | ||||
|     .bdrv_co_create_opts = raw_co_create_opts, | ||||
|     .bdrv_has_zero_init = bdrv_has_zero_init_1, | ||||
|     .bdrv_co_get_block_status = raw_co_get_block_status, | ||||
|     .bdrv_co_block_status = raw_co_block_status, | ||||
|     .bdrv_co_pwrite_zeroes = raw_co_pwrite_zeroes, | ||||
| 
 | ||||
|     .bdrv_co_preadv         = raw_co_preadv, | ||||
| @ -2684,8 +2681,8 @@ static coroutine_fn int hdev_co_pwrite_zeroes(BlockDriverState *bs, | ||||
|     return -ENOTSUP; | ||||
| } | ||||
| 
 | ||||
| static int hdev_create(const char *filename, QemuOpts *opts, | ||||
|                        Error **errp) | ||||
| static int coroutine_fn hdev_co_create_opts(const char *filename, QemuOpts *opts, | ||||
|                                             Error **errp) | ||||
| { | ||||
|     int fd; | ||||
|     int ret = 0; | ||||
| @ -2758,7 +2755,7 @@ static BlockDriver bdrv_host_device = { | ||||
|     .bdrv_reopen_prepare = raw_reopen_prepare, | ||||
|     .bdrv_reopen_commit  = raw_reopen_commit, | ||||
|     .bdrv_reopen_abort   = raw_reopen_abort, | ||||
|     .bdrv_create         = hdev_create, | ||||
|     .bdrv_co_create_opts = hdev_co_create_opts, | ||||
|     .create_opts         = &raw_create_opts, | ||||
|     .bdrv_co_pwrite_zeroes = hdev_co_pwrite_zeroes, | ||||
| 
 | ||||
| @ -2880,7 +2877,7 @@ static BlockDriver bdrv_host_cdrom = { | ||||
|     .bdrv_reopen_prepare = raw_reopen_prepare, | ||||
|     .bdrv_reopen_commit  = raw_reopen_commit, | ||||
|     .bdrv_reopen_abort   = raw_reopen_abort, | ||||
|     .bdrv_create         = hdev_create, | ||||
|     .bdrv_co_create_opts = hdev_co_create_opts, | ||||
|     .create_opts         = &raw_create_opts, | ||||
| 
 | ||||
| 
 | ||||
| @ -3011,7 +3008,7 @@ static BlockDriver bdrv_host_cdrom = { | ||||
|     .bdrv_reopen_prepare = raw_reopen_prepare, | ||||
|     .bdrv_reopen_commit  = raw_reopen_commit, | ||||
|     .bdrv_reopen_abort   = raw_reopen_abort, | ||||
|     .bdrv_create        = hdev_create, | ||||
|     .bdrv_co_create_opts = hdev_co_create_opts, | ||||
|     .create_opts        = &raw_create_opts, | ||||
| 
 | ||||
|     .bdrv_co_preadv         = raw_co_preadv, | ||||
|  | ||||
| @ -553,7 +553,8 @@ static int64_t raw_get_allocated_file_size(BlockDriverState *bs) | ||||
|     return st.st_size; | ||||
| } | ||||
| 
 | ||||
| static int raw_create(const char *filename, QemuOpts *opts, Error **errp) | ||||
| static int coroutine_fn raw_co_create_opts(const char *filename, QemuOpts *opts, | ||||
|                                            Error **errp) | ||||
| { | ||||
|     int fd; | ||||
|     int64_t total_size = 0; | ||||
| @ -599,7 +600,7 @@ BlockDriver bdrv_file = { | ||||
|     .bdrv_file_open     = raw_open, | ||||
|     .bdrv_refresh_limits = raw_probe_alignment, | ||||
|     .bdrv_close         = raw_close, | ||||
|     .bdrv_create        = raw_create, | ||||
|     .bdrv_co_create_opts = raw_co_create_opts, | ||||
|     .bdrv_has_zero_init = bdrv_has_zero_init_1, | ||||
| 
 | ||||
|     .bdrv_aio_readv     = raw_aio_readv, | ||||
|  | ||||
| @ -1021,8 +1021,9 @@ static int qemu_gluster_do_truncate(struct glfs_fd *fd, int64_t offset, | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| static int qemu_gluster_create(const char *filename, | ||||
|                                QemuOpts *opts, Error **errp) | ||||
| static int coroutine_fn qemu_gluster_co_create_opts(const char *filename, | ||||
|                                                     QemuOpts *opts, | ||||
|                                                     Error **errp) | ||||
| { | ||||
|     BlockdevOptionsGluster *gconf; | ||||
|     struct glfs *glfs; | ||||
| @ -1362,68 +1363,66 @@ exit: | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Returns the allocation status of the specified sectors. | ||||
|  * Returns the allocation status of the specified offset. | ||||
|  * | ||||
|  * If 'sector_num' is beyond the end of the disk image the return value is 0 | ||||
|  * and 'pnum' is set to 0. | ||||
|  * The block layer guarantees 'offset' and 'bytes' are within bounds. | ||||
|  * | ||||
|  * 'pnum' is set to the number of sectors (including and immediately following | ||||
|  * the specified sector) that are known to be in the same | ||||
|  * 'pnum' is set to the number of bytes (including and immediately following | ||||
|  * the specified offset) that are known to be in the same | ||||
|  * allocated/unallocated state. | ||||
|  * | ||||
|  * 'nb_sectors' is the max value 'pnum' should be set to.  If nb_sectors goes | ||||
|  * beyond the end of the disk image it will be clamped. | ||||
|  * 'bytes' is the max value 'pnum' should be set to. | ||||
|  * | ||||
|  * (Based on raw_co_get_block_status() from file-posix.c.) | ||||
|  * (Based on raw_co_block_status() from file-posix.c.) | ||||
|  */ | ||||
| static int64_t coroutine_fn qemu_gluster_co_get_block_status( | ||||
|         BlockDriverState *bs, int64_t sector_num, int nb_sectors, int *pnum, | ||||
|         BlockDriverState **file) | ||||
| static int coroutine_fn qemu_gluster_co_block_status(BlockDriverState *bs, | ||||
|                                                      bool want_zero, | ||||
|                                                      int64_t offset, | ||||
|                                                      int64_t bytes, | ||||
|                                                      int64_t *pnum, | ||||
|                                                      int64_t *map, | ||||
|                                                      BlockDriverState **file) | ||||
| { | ||||
|     BDRVGlusterState *s = bs->opaque; | ||||
|     off_t start, data = 0, hole = 0; | ||||
|     int64_t total_size; | ||||
|     off_t data = 0, hole = 0; | ||||
|     int ret = -EINVAL; | ||||
| 
 | ||||
|     if (!s->fd) { | ||||
|         return ret; | ||||
|     } | ||||
| 
 | ||||
|     start = sector_num * BDRV_SECTOR_SIZE; | ||||
|     total_size = bdrv_getlength(bs); | ||||
|     if (total_size < 0) { | ||||
|         return total_size; | ||||
|     } else if (start >= total_size) { | ||||
|         *pnum = 0; | ||||
|         return 0; | ||||
|     } else if (start + nb_sectors * BDRV_SECTOR_SIZE > total_size) { | ||||
|         nb_sectors = DIV_ROUND_UP(total_size - start, BDRV_SECTOR_SIZE); | ||||
|     if (!want_zero) { | ||||
|         *pnum = bytes; | ||||
|         *map = offset; | ||||
|         *file = bs; | ||||
|         return BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID; | ||||
|     } | ||||
| 
 | ||||
|     ret = find_allocation(bs, start, &data, &hole); | ||||
|     ret = find_allocation(bs, offset, &data, &hole); | ||||
|     if (ret == -ENXIO) { | ||||
|         /* Trailing hole */ | ||||
|         *pnum = nb_sectors; | ||||
|         *pnum = bytes; | ||||
|         ret = BDRV_BLOCK_ZERO; | ||||
|     } else if (ret < 0) { | ||||
|         /* No info available, so pretend there are no holes */ | ||||
|         *pnum = nb_sectors; | ||||
|         *pnum = bytes; | ||||
|         ret = BDRV_BLOCK_DATA; | ||||
|     } else if (data == start) { | ||||
|         /* On a data extent, compute sectors to the end of the extent,
 | ||||
|     } else if (data == offset) { | ||||
|         /* On a data extent, compute bytes to the end of the extent,
 | ||||
|          * possibly including a partial sector at EOF. */ | ||||
|         *pnum = MIN(nb_sectors, DIV_ROUND_UP(hole - start, BDRV_SECTOR_SIZE)); | ||||
|         *pnum = MIN(bytes, hole - offset); | ||||
|         ret = BDRV_BLOCK_DATA; | ||||
|     } else { | ||||
|         /* On a hole, compute sectors to the beginning of the next extent.  */ | ||||
|         assert(hole == start); | ||||
|         *pnum = MIN(nb_sectors, (data - start) / BDRV_SECTOR_SIZE); | ||||
|         /* On a hole, compute bytes to the beginning of the next extent.  */ | ||||
|         assert(hole == offset); | ||||
|         *pnum = MIN(bytes, data - offset); | ||||
|         ret = BDRV_BLOCK_ZERO; | ||||
|     } | ||||
| 
 | ||||
|     *map = offset; | ||||
|     *file = bs; | ||||
| 
 | ||||
|     return ret | BDRV_BLOCK_OFFSET_VALID | start; | ||||
|     return ret | BDRV_BLOCK_OFFSET_VALID; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| @ -1437,7 +1436,7 @@ static BlockDriver bdrv_gluster = { | ||||
|     .bdrv_reopen_commit           = qemu_gluster_reopen_commit, | ||||
|     .bdrv_reopen_abort            = qemu_gluster_reopen_abort, | ||||
|     .bdrv_close                   = qemu_gluster_close, | ||||
|     .bdrv_create                  = qemu_gluster_create, | ||||
|     .bdrv_co_create_opts          = qemu_gluster_co_create_opts, | ||||
|     .bdrv_getlength               = qemu_gluster_getlength, | ||||
|     .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size, | ||||
|     .bdrv_truncate                = qemu_gluster_truncate, | ||||
| @ -1451,7 +1450,7 @@ static BlockDriver bdrv_gluster = { | ||||
| #ifdef CONFIG_GLUSTERFS_ZEROFILL | ||||
|     .bdrv_co_pwrite_zeroes        = qemu_gluster_co_pwrite_zeroes, | ||||
| #endif | ||||
|     .bdrv_co_get_block_status     = qemu_gluster_co_get_block_status, | ||||
|     .bdrv_co_block_status         = qemu_gluster_co_block_status, | ||||
|     .create_opts                  = &qemu_gluster_create_opts, | ||||
| }; | ||||
| 
 | ||||
| @ -1465,7 +1464,7 @@ static BlockDriver bdrv_gluster_tcp = { | ||||
|     .bdrv_reopen_commit           = qemu_gluster_reopen_commit, | ||||
|     .bdrv_reopen_abort            = qemu_gluster_reopen_abort, | ||||
|     .bdrv_close                   = qemu_gluster_close, | ||||
|     .bdrv_create                  = qemu_gluster_create, | ||||
|     .bdrv_co_create_opts          = qemu_gluster_co_create_opts, | ||||
|     .bdrv_getlength               = qemu_gluster_getlength, | ||||
|     .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size, | ||||
|     .bdrv_truncate                = qemu_gluster_truncate, | ||||
| @ -1479,7 +1478,7 @@ static BlockDriver bdrv_gluster_tcp = { | ||||
| #ifdef CONFIG_GLUSTERFS_ZEROFILL | ||||
|     .bdrv_co_pwrite_zeroes        = qemu_gluster_co_pwrite_zeroes, | ||||
| #endif | ||||
|     .bdrv_co_get_block_status     = qemu_gluster_co_get_block_status, | ||||
|     .bdrv_co_block_status         = qemu_gluster_co_block_status, | ||||
|     .create_opts                  = &qemu_gluster_create_opts, | ||||
| }; | ||||
| 
 | ||||
| @ -1493,7 +1492,7 @@ static BlockDriver bdrv_gluster_unix = { | ||||
|     .bdrv_reopen_commit           = qemu_gluster_reopen_commit, | ||||
|     .bdrv_reopen_abort            = qemu_gluster_reopen_abort, | ||||
|     .bdrv_close                   = qemu_gluster_close, | ||||
|     .bdrv_create                  = qemu_gluster_create, | ||||
|     .bdrv_co_create_opts          = qemu_gluster_co_create_opts, | ||||
|     .bdrv_getlength               = qemu_gluster_getlength, | ||||
|     .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size, | ||||
|     .bdrv_truncate                = qemu_gluster_truncate, | ||||
| @ -1507,7 +1506,7 @@ static BlockDriver bdrv_gluster_unix = { | ||||
| #ifdef CONFIG_GLUSTERFS_ZEROFILL | ||||
|     .bdrv_co_pwrite_zeroes        = qemu_gluster_co_pwrite_zeroes, | ||||
| #endif | ||||
|     .bdrv_co_get_block_status     = qemu_gluster_co_get_block_status, | ||||
|     .bdrv_co_block_status         = qemu_gluster_co_block_status, | ||||
|     .create_opts                  = &qemu_gluster_create_opts, | ||||
| }; | ||||
| 
 | ||||
| @ -1527,7 +1526,7 @@ static BlockDriver bdrv_gluster_rdma = { | ||||
|     .bdrv_reopen_commit           = qemu_gluster_reopen_commit, | ||||
|     .bdrv_reopen_abort            = qemu_gluster_reopen_abort, | ||||
|     .bdrv_close                   = qemu_gluster_close, | ||||
|     .bdrv_create                  = qemu_gluster_create, | ||||
|     .bdrv_co_create_opts          = qemu_gluster_co_create_opts, | ||||
|     .bdrv_getlength               = qemu_gluster_getlength, | ||||
|     .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size, | ||||
|     .bdrv_truncate                = qemu_gluster_truncate, | ||||
| @ -1541,7 +1540,7 @@ static BlockDriver bdrv_gluster_rdma = { | ||||
| #ifdef CONFIG_GLUSTERFS_ZEROFILL | ||||
|     .bdrv_co_pwrite_zeroes        = qemu_gluster_co_pwrite_zeroes, | ||||
| #endif | ||||
|     .bdrv_co_get_block_status     = qemu_gluster_co_get_block_status, | ||||
|     .bdrv_co_block_status         = qemu_gluster_co_block_status, | ||||
|     .create_opts                  = &qemu_gluster_create_opts, | ||||
| }; | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										98
									
								
								block/io.c
									
									
									
									
									
								
							
							
						
						
									
										98
									
								
								block/io.c
									
									
									
									
									
								
							| @ -25,6 +25,7 @@ | ||||
| #include "qemu/osdep.h" | ||||
| #include "trace.h" | ||||
| #include "sysemu/block-backend.h" | ||||
| #include "block/aio-wait.h" | ||||
| #include "block/blockjob.h" | ||||
| #include "block/blockjob_int.h" | ||||
| #include "block/block_int.h" | ||||
| @ -587,16 +588,9 @@ void bdrv_inc_in_flight(BlockDriverState *bs) | ||||
|     atomic_inc(&bs->in_flight); | ||||
| } | ||||
| 
 | ||||
| static void dummy_bh_cb(void *opaque) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| void bdrv_wakeup(BlockDriverState *bs) | ||||
| { | ||||
|     /* The barrier (or an atomic op) is in the caller.  */ | ||||
|     if (atomic_read(&bs->wakeup)) { | ||||
|         aio_bh_schedule_oneshot(qemu_get_aio_context(), dummy_bh_cb, NULL); | ||||
|     } | ||||
|     aio_wait_kick(bdrv_get_aio_wait(bs)); | ||||
| } | ||||
| 
 | ||||
| void bdrv_dec_in_flight(BlockDriverState *bs) | ||||
| @ -1701,7 +1695,7 @@ int coroutine_fn bdrv_co_pwritev(BdrvChild *child, | ||||
|      */ | ||||
|     tracked_request_begin(&req, bs, offset, bytes, BDRV_TRACKED_WRITE); | ||||
| 
 | ||||
|     if (!qiov) { | ||||
|     if (flags & BDRV_REQ_ZERO_WRITE) { | ||||
|         ret = bdrv_co_do_zero_pwritev(child, offset, bytes, flags, &req); | ||||
|         goto out; | ||||
|     } | ||||
| @ -1868,30 +1862,34 @@ typedef struct BdrvCoBlockStatusData { | ||||
|     bool done; | ||||
| } BdrvCoBlockStatusData; | ||||
| 
 | ||||
| int64_t coroutine_fn bdrv_co_get_block_status_from_file(BlockDriverState *bs, | ||||
|                                                         int64_t sector_num, | ||||
|                                                         int nb_sectors, | ||||
|                                                         int *pnum, | ||||
|                                                         BlockDriverState **file) | ||||
| int coroutine_fn bdrv_co_block_status_from_file(BlockDriverState *bs, | ||||
|                                                 bool want_zero, | ||||
|                                                 int64_t offset, | ||||
|                                                 int64_t bytes, | ||||
|                                                 int64_t *pnum, | ||||
|                                                 int64_t *map, | ||||
|                                                 BlockDriverState **file) | ||||
| { | ||||
|     assert(bs->file && bs->file->bs); | ||||
|     *pnum = nb_sectors; | ||||
|     *pnum = bytes; | ||||
|     *map = offset; | ||||
|     *file = bs->file->bs; | ||||
|     return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID | | ||||
|            (sector_num << BDRV_SECTOR_BITS); | ||||
|     return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID; | ||||
| } | ||||
| 
 | ||||
| int64_t coroutine_fn bdrv_co_get_block_status_from_backing(BlockDriverState *bs, | ||||
|                                                            int64_t sector_num, | ||||
|                                                            int nb_sectors, | ||||
|                                                            int *pnum, | ||||
|                                                            BlockDriverState **file) | ||||
| int coroutine_fn bdrv_co_block_status_from_backing(BlockDriverState *bs, | ||||
|                                                    bool want_zero, | ||||
|                                                    int64_t offset, | ||||
|                                                    int64_t bytes, | ||||
|                                                    int64_t *pnum, | ||||
|                                                    int64_t *map, | ||||
|                                                    BlockDriverState **file) | ||||
| { | ||||
|     assert(bs->backing && bs->backing->bs); | ||||
|     *pnum = nb_sectors; | ||||
|     *pnum = bytes; | ||||
|     *map = offset; | ||||
|     *file = bs->backing->bs; | ||||
|     return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID | | ||||
|            (sector_num << BDRV_SECTOR_BITS); | ||||
|     return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
| @ -1899,10 +1897,10 @@ int64_t coroutine_fn bdrv_co_get_block_status_from_backing(BlockDriverState *bs, | ||||
|  * Drivers not implementing the functionality are assumed to not support | ||||
|  * backing files, hence all their sectors are reported as allocated. | ||||
|  * | ||||
|  * If 'want_zero' is true, the caller is querying for mapping purposes, | ||||
|  * and the result should include BDRV_BLOCK_OFFSET_VALID and | ||||
|  * BDRV_BLOCK_ZERO where possible; otherwise, the result may omit those | ||||
|  * bits particularly if it allows for a larger value in 'pnum'. | ||||
|  * If 'want_zero' is true, the caller is querying for mapping | ||||
|  * purposes, with a focus on valid BDRV_BLOCK_OFFSET_VALID, _DATA, and | ||||
|  * _ZERO where possible; otherwise, the result favors larger 'pnum', | ||||
|  * with a focus on accurate BDRV_BLOCK_ALLOCATED. | ||||
|  * | ||||
|  * If 'offset' is beyond the end of the disk image the return value is | ||||
|  * BDRV_BLOCK_EOF and 'pnum' is set to 0. | ||||
| @ -1959,7 +1957,7 @@ static int coroutine_fn bdrv_co_block_status(BlockDriverState *bs, | ||||
| 
 | ||||
|     /* Must be non-NULL or bdrv_getlength() would have failed */ | ||||
|     assert(bs->drv); | ||||
|     if (!bs->drv->bdrv_co_get_block_status) { | ||||
|     if (!bs->drv->bdrv_co_block_status) { | ||||
|         *pnum = bytes; | ||||
|         ret = BDRV_BLOCK_DATA | BDRV_BLOCK_ALLOCATED; | ||||
|         if (offset + bytes == total_size) { | ||||
| @ -1976,44 +1974,24 @@ static int coroutine_fn bdrv_co_block_status(BlockDriverState *bs, | ||||
|     bdrv_inc_in_flight(bs); | ||||
| 
 | ||||
|     /* Round out to request_alignment boundaries */ | ||||
|     /* TODO: until we have a byte-based driver callback, we also have to
 | ||||
|      * round out to sectors, even if that is bigger than request_alignment */ | ||||
|     align = MAX(bs->bl.request_alignment, BDRV_SECTOR_SIZE); | ||||
|     align = bs->bl.request_alignment; | ||||
|     aligned_offset = QEMU_ALIGN_DOWN(offset, align); | ||||
|     aligned_bytes = ROUND_UP(offset + bytes, align) - aligned_offset; | ||||
| 
 | ||||
|     { | ||||
|         int count; /* sectors */ | ||||
|         int64_t longret; | ||||
| 
 | ||||
|         assert(QEMU_IS_ALIGNED(aligned_offset | aligned_bytes, | ||||
|                                BDRV_SECTOR_SIZE)); | ||||
|         /*
 | ||||
|          * The contract allows us to return pnum smaller than bytes, even | ||||
|          * if the next query would see the same status; we truncate the | ||||
|          * request to avoid overflowing the driver's 32-bit interface. | ||||
|          */ | ||||
|         longret = bs->drv->bdrv_co_get_block_status( | ||||
|             bs, aligned_offset >> BDRV_SECTOR_BITS, | ||||
|             MIN(INT_MAX, aligned_bytes) >> BDRV_SECTOR_BITS, &count, | ||||
|             &local_file); | ||||
|         if (longret < 0) { | ||||
|             assert(INT_MIN <= longret); | ||||
|             ret = longret; | ||||
|             goto out; | ||||
|         } | ||||
|         if (longret & BDRV_BLOCK_OFFSET_VALID) { | ||||
|             local_map = longret & BDRV_BLOCK_OFFSET_MASK; | ||||
|         } | ||||
|         ret = longret & ~BDRV_BLOCK_OFFSET_MASK; | ||||
|         *pnum = count * BDRV_SECTOR_SIZE; | ||||
|     ret = bs->drv->bdrv_co_block_status(bs, want_zero, aligned_offset, | ||||
|                                         aligned_bytes, pnum, &local_map, | ||||
|                                         &local_file); | ||||
|     if (ret < 0) { | ||||
|         *pnum = 0; | ||||
|         goto out; | ||||
|     } | ||||
| 
 | ||||
|     /*
 | ||||
|      * The driver's result must be a multiple of request_alignment. | ||||
|      * The driver's result must be a non-zero multiple of request_alignment. | ||||
|      * Clamp pnum and adjust map to original request. | ||||
|      */ | ||||
|     assert(QEMU_IS_ALIGNED(*pnum, align) && align > offset - aligned_offset); | ||||
|     assert(*pnum && QEMU_IS_ALIGNED(*pnum, align) && | ||||
|            align > offset - aligned_offset); | ||||
|     *pnum -= offset - aligned_offset; | ||||
|     if (*pnum > bytes) { | ||||
|         *pnum = bytes; | ||||
|  | ||||
							
								
								
									
										164
									
								
								block/iscsi.c
									
									
									
									
									
								
							
							
						
						
									
										164
									
								
								block/iscsi.c
									
									
									
									
									
								
							| @ -86,7 +86,7 @@ typedef struct IscsiLun { | ||||
|     unsigned long *allocmap; | ||||
|     unsigned long *allocmap_valid; | ||||
|     long allocmap_size; | ||||
|     int cluster_sectors; | ||||
|     int cluster_size; | ||||
|     bool use_16_for_rw; | ||||
|     bool write_protected; | ||||
|     bool lbpme; | ||||
| @ -430,9 +430,10 @@ static int iscsi_allocmap_init(IscsiLun *iscsilun, int open_flags) | ||||
| { | ||||
|     iscsi_allocmap_free(iscsilun); | ||||
| 
 | ||||
|     assert(iscsilun->cluster_size); | ||||
|     iscsilun->allocmap_size = | ||||
|         DIV_ROUND_UP(sector_lun2qemu(iscsilun->num_blocks, iscsilun), | ||||
|                      iscsilun->cluster_sectors); | ||||
|         DIV_ROUND_UP(iscsilun->num_blocks * iscsilun->block_size, | ||||
|                      iscsilun->cluster_size); | ||||
| 
 | ||||
|     iscsilun->allocmap = bitmap_try_new(iscsilun->allocmap_size); | ||||
|     if (!iscsilun->allocmap) { | ||||
| @ -440,7 +441,7 @@ static int iscsi_allocmap_init(IscsiLun *iscsilun, int open_flags) | ||||
|     } | ||||
| 
 | ||||
|     if (open_flags & BDRV_O_NOCACHE) { | ||||
|         /* in case that cache.direct = on all allocmap entries are
 | ||||
|         /* when cache.direct = on all allocmap entries are
 | ||||
|          * treated as invalid to force a relookup of the block | ||||
|          * status on every read request */ | ||||
|         return 0; | ||||
| @ -457,8 +458,8 @@ static int iscsi_allocmap_init(IscsiLun *iscsilun, int open_flags) | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| iscsi_allocmap_update(IscsiLun *iscsilun, int64_t sector_num, | ||||
|                       int nb_sectors, bool allocated, bool valid) | ||||
| iscsi_allocmap_update(IscsiLun *iscsilun, int64_t offset, | ||||
|                       int64_t bytes, bool allocated, bool valid) | ||||
| { | ||||
|     int64_t cl_num_expanded, nb_cls_expanded, cl_num_shrunk, nb_cls_shrunk; | ||||
| 
 | ||||
| @ -466,13 +467,13 @@ iscsi_allocmap_update(IscsiLun *iscsilun, int64_t sector_num, | ||||
|         return; | ||||
|     } | ||||
|     /* expand to entirely contain all affected clusters */ | ||||
|     cl_num_expanded = sector_num / iscsilun->cluster_sectors; | ||||
|     nb_cls_expanded = DIV_ROUND_UP(sector_num + nb_sectors, | ||||
|                                    iscsilun->cluster_sectors) - cl_num_expanded; | ||||
|     assert(iscsilun->cluster_size); | ||||
|     cl_num_expanded = offset / iscsilun->cluster_size; | ||||
|     nb_cls_expanded = DIV_ROUND_UP(offset + bytes, | ||||
|                                    iscsilun->cluster_size) - cl_num_expanded; | ||||
|     /* shrink to touch only completely contained clusters */ | ||||
|     cl_num_shrunk = DIV_ROUND_UP(sector_num, iscsilun->cluster_sectors); | ||||
|     nb_cls_shrunk = (sector_num + nb_sectors) / iscsilun->cluster_sectors | ||||
|                       - cl_num_shrunk; | ||||
|     cl_num_shrunk = DIV_ROUND_UP(offset, iscsilun->cluster_size); | ||||
|     nb_cls_shrunk = (offset + bytes) / iscsilun->cluster_size - cl_num_shrunk; | ||||
|     if (allocated) { | ||||
|         bitmap_set(iscsilun->allocmap, cl_num_expanded, nb_cls_expanded); | ||||
|     } else { | ||||
| @ -495,26 +496,26 @@ iscsi_allocmap_update(IscsiLun *iscsilun, int64_t sector_num, | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| iscsi_allocmap_set_allocated(IscsiLun *iscsilun, int64_t sector_num, | ||||
|                              int nb_sectors) | ||||
| iscsi_allocmap_set_allocated(IscsiLun *iscsilun, int64_t offset, | ||||
|                              int64_t bytes) | ||||
| { | ||||
|     iscsi_allocmap_update(iscsilun, sector_num, nb_sectors, true, true); | ||||
|     iscsi_allocmap_update(iscsilun, offset, bytes, true, true); | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| iscsi_allocmap_set_unallocated(IscsiLun *iscsilun, int64_t sector_num, | ||||
|                                int nb_sectors) | ||||
| iscsi_allocmap_set_unallocated(IscsiLun *iscsilun, int64_t offset, | ||||
|                                int64_t bytes) | ||||
| { | ||||
|     /* Note: if cache.direct=on the fifth argument to iscsi_allocmap_update
 | ||||
|      * is ignored, so this will in effect be an iscsi_allocmap_set_invalid. | ||||
|      */ | ||||
|     iscsi_allocmap_update(iscsilun, sector_num, nb_sectors, false, true); | ||||
|     iscsi_allocmap_update(iscsilun, offset, bytes, false, true); | ||||
| } | ||||
| 
 | ||||
| static void iscsi_allocmap_set_invalid(IscsiLun *iscsilun, int64_t sector_num, | ||||
|                                        int nb_sectors) | ||||
| static void iscsi_allocmap_set_invalid(IscsiLun *iscsilun, int64_t offset, | ||||
|                                        int64_t bytes) | ||||
| { | ||||
|     iscsi_allocmap_update(iscsilun, sector_num, nb_sectors, false, false); | ||||
|     iscsi_allocmap_update(iscsilun, offset, bytes, false, false); | ||||
| } | ||||
| 
 | ||||
| static void iscsi_allocmap_invalidate(IscsiLun *iscsilun) | ||||
| @ -528,28 +529,30 @@ static void iscsi_allocmap_invalidate(IscsiLun *iscsilun) | ||||
| } | ||||
| 
 | ||||
| static inline bool | ||||
| iscsi_allocmap_is_allocated(IscsiLun *iscsilun, int64_t sector_num, | ||||
|                             int nb_sectors) | ||||
| iscsi_allocmap_is_allocated(IscsiLun *iscsilun, int64_t offset, | ||||
|                             int64_t bytes) | ||||
| { | ||||
|     unsigned long size; | ||||
|     if (iscsilun->allocmap == NULL) { | ||||
|         return true; | ||||
|     } | ||||
|     size = DIV_ROUND_UP(sector_num + nb_sectors, iscsilun->cluster_sectors); | ||||
|     assert(iscsilun->cluster_size); | ||||
|     size = DIV_ROUND_UP(offset + bytes, iscsilun->cluster_size); | ||||
|     return !(find_next_bit(iscsilun->allocmap, size, | ||||
|                            sector_num / iscsilun->cluster_sectors) == size); | ||||
|                            offset / iscsilun->cluster_size) == size); | ||||
| } | ||||
| 
 | ||||
| static inline bool iscsi_allocmap_is_valid(IscsiLun *iscsilun, | ||||
|                                            int64_t sector_num, int nb_sectors) | ||||
|                                            int64_t offset, int64_t bytes) | ||||
| { | ||||
|     unsigned long size; | ||||
|     if (iscsilun->allocmap_valid == NULL) { | ||||
|         return false; | ||||
|     } | ||||
|     size = DIV_ROUND_UP(sector_num + nb_sectors, iscsilun->cluster_sectors); | ||||
|     assert(iscsilun->cluster_size); | ||||
|     size = DIV_ROUND_UP(offset + bytes, iscsilun->cluster_size); | ||||
|     return (find_next_zero_bit(iscsilun->allocmap_valid, size, | ||||
|                                sector_num / iscsilun->cluster_sectors) == size); | ||||
|                                offset / iscsilun->cluster_size) == size); | ||||
| } | ||||
| 
 | ||||
| static int coroutine_fn | ||||
| @ -631,14 +634,16 @@ retry: | ||||
|     } | ||||
| 
 | ||||
|     if (iTask.status != SCSI_STATUS_GOOD) { | ||||
|         iscsi_allocmap_set_invalid(iscsilun, sector_num, nb_sectors); | ||||
|         iscsi_allocmap_set_invalid(iscsilun, sector_num * BDRV_SECTOR_SIZE, | ||||
|                                    nb_sectors * BDRV_SECTOR_SIZE); | ||||
|         error_report("iSCSI WRITE10/16 failed at lba %" PRIu64 ": %s", lba, | ||||
|                      iTask.err_str); | ||||
|         r = iTask.err_code; | ||||
|         goto out_unlock; | ||||
|     } | ||||
| 
 | ||||
|     iscsi_allocmap_set_allocated(iscsilun, sector_num, nb_sectors); | ||||
|     iscsi_allocmap_set_allocated(iscsilun, sector_num * BDRV_SECTOR_SIZE, | ||||
|                                  nb_sectors * BDRV_SECTOR_SIZE); | ||||
| 
 | ||||
| out_unlock: | ||||
|     qemu_mutex_unlock(&iscsilun->mutex); | ||||
| @ -648,36 +653,36 @@ out_unlock: | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| static int64_t coroutine_fn iscsi_co_get_block_status(BlockDriverState *bs, | ||||
|                                                   int64_t sector_num, | ||||
|                                                   int nb_sectors, int *pnum, | ||||
|                                                   BlockDriverState **file) | ||||
| static int coroutine_fn iscsi_co_block_status(BlockDriverState *bs, | ||||
|                                               bool want_zero, int64_t offset, | ||||
|                                               int64_t bytes, int64_t *pnum, | ||||
|                                               int64_t *map, | ||||
|                                               BlockDriverState **file) | ||||
| { | ||||
|     IscsiLun *iscsilun = bs->opaque; | ||||
|     struct scsi_get_lba_status *lbas = NULL; | ||||
|     struct scsi_lba_status_descriptor *lbasd = NULL; | ||||
|     struct IscsiTask iTask; | ||||
|     uint64_t lba; | ||||
|     int64_t ret; | ||||
|     int ret; | ||||
| 
 | ||||
|     iscsi_co_init_iscsitask(iscsilun, &iTask); | ||||
| 
 | ||||
|     if (!is_sector_request_lun_aligned(sector_num, nb_sectors, iscsilun)) { | ||||
|         ret = -EINVAL; | ||||
|         goto out; | ||||
|     } | ||||
|     assert(QEMU_IS_ALIGNED(offset | bytes, iscsilun->block_size)); | ||||
| 
 | ||||
|     /* default to all sectors allocated */ | ||||
|     ret = BDRV_BLOCK_DATA; | ||||
|     ret |= (sector_num << BDRV_SECTOR_BITS) | BDRV_BLOCK_OFFSET_VALID; | ||||
|     *pnum = nb_sectors; | ||||
|     ret = BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID; | ||||
|     if (map) { | ||||
|         *map = offset; | ||||
|     } | ||||
|     *pnum = bytes; | ||||
| 
 | ||||
|     /* LUN does not support logical block provisioning */ | ||||
|     if (!iscsilun->lbpme) { | ||||
|         goto out; | ||||
|     } | ||||
| 
 | ||||
|     lba = sector_qemu2lun(sector_num, iscsilun); | ||||
|     lba = offset / iscsilun->block_size; | ||||
| 
 | ||||
|     qemu_mutex_lock(&iscsilun->mutex); | ||||
| retry: | ||||
| @ -722,12 +727,12 @@ retry: | ||||
| 
 | ||||
|     lbasd = &lbas->descriptors[0]; | ||||
| 
 | ||||
|     if (sector_qemu2lun(sector_num, iscsilun) != lbasd->lba) { | ||||
|     if (lba != lbasd->lba) { | ||||
|         ret = -EIO; | ||||
|         goto out_unlock; | ||||
|     } | ||||
| 
 | ||||
|     *pnum = sector_lun2qemu(lbasd->num_blocks, iscsilun); | ||||
|     *pnum = lbasd->num_blocks * iscsilun->block_size; | ||||
| 
 | ||||
|     if (lbasd->provisioning == SCSI_PROVISIONING_TYPE_DEALLOCATED || | ||||
|         lbasd->provisioning == SCSI_PROVISIONING_TYPE_ANCHORED) { | ||||
| @ -738,13 +743,13 @@ retry: | ||||
|     } | ||||
| 
 | ||||
|     if (ret & BDRV_BLOCK_ZERO) { | ||||
|         iscsi_allocmap_set_unallocated(iscsilun, sector_num, *pnum); | ||||
|         iscsi_allocmap_set_unallocated(iscsilun, offset, *pnum); | ||||
|     } else { | ||||
|         iscsi_allocmap_set_allocated(iscsilun, sector_num, *pnum); | ||||
|         iscsi_allocmap_set_allocated(iscsilun, offset, *pnum); | ||||
|     } | ||||
| 
 | ||||
|     if (*pnum > nb_sectors) { | ||||
|         *pnum = nb_sectors; | ||||
|     if (*pnum > bytes) { | ||||
|         *pnum = bytes; | ||||
|     } | ||||
| out_unlock: | ||||
|     qemu_mutex_unlock(&iscsilun->mutex); | ||||
| @ -753,7 +758,7 @@ out: | ||||
|     if (iTask.task != NULL) { | ||||
|         scsi_free_scsi_task(iTask.task); | ||||
|     } | ||||
|     if (ret > 0 && ret & BDRV_BLOCK_OFFSET_VALID) { | ||||
|     if (ret > 0 && ret & BDRV_BLOCK_OFFSET_VALID && file) { | ||||
|         *file = bs; | ||||
|     } | ||||
|     return ret; | ||||
| @ -780,29 +785,37 @@ static int coroutine_fn iscsi_co_readv(BlockDriverState *bs, | ||||
|     /* if cache.direct is off and we have a valid entry in our allocation map
 | ||||
|      * we can skip checking the block status and directly return zeroes if | ||||
|      * the request falls within an unallocated area */ | ||||
|     if (iscsi_allocmap_is_valid(iscsilun, sector_num, nb_sectors) && | ||||
|         !iscsi_allocmap_is_allocated(iscsilun, sector_num, nb_sectors)) { | ||||
|     if (iscsi_allocmap_is_valid(iscsilun, sector_num * BDRV_SECTOR_SIZE, | ||||
|                                 nb_sectors * BDRV_SECTOR_SIZE) && | ||||
|         !iscsi_allocmap_is_allocated(iscsilun, sector_num * BDRV_SECTOR_SIZE, | ||||
|                                      nb_sectors * BDRV_SECTOR_SIZE)) { | ||||
|             qemu_iovec_memset(iov, 0, 0x00, iov->size); | ||||
|             return 0; | ||||
|     } | ||||
| 
 | ||||
|     if (nb_sectors >= ISCSI_CHECKALLOC_THRES && | ||||
|         !iscsi_allocmap_is_valid(iscsilun, sector_num, nb_sectors) && | ||||
|         !iscsi_allocmap_is_allocated(iscsilun, sector_num, nb_sectors)) { | ||||
|         int pnum; | ||||
|         BlockDriverState *file; | ||||
|         !iscsi_allocmap_is_valid(iscsilun, sector_num * BDRV_SECTOR_SIZE, | ||||
|                                  nb_sectors * BDRV_SECTOR_SIZE) && | ||||
|         !iscsi_allocmap_is_allocated(iscsilun, sector_num * BDRV_SECTOR_SIZE, | ||||
|                                      nb_sectors * BDRV_SECTOR_SIZE)) { | ||||
|         int64_t pnum; | ||||
|         /* check the block status from the beginning of the cluster
 | ||||
|          * containing the start sector */ | ||||
|         int64_t ret = iscsi_co_get_block_status(bs, | ||||
|                           sector_num - sector_num % iscsilun->cluster_sectors, | ||||
|                           BDRV_REQUEST_MAX_SECTORS, &pnum, &file); | ||||
|         int64_t head; | ||||
|         int ret; | ||||
| 
 | ||||
|         assert(iscsilun->cluster_size); | ||||
|         head = (sector_num * BDRV_SECTOR_SIZE) % iscsilun->cluster_size; | ||||
|         ret = iscsi_co_block_status(bs, true, | ||||
|                                     sector_num * BDRV_SECTOR_SIZE - head, | ||||
|                                     BDRV_REQUEST_MAX_BYTES, &pnum, NULL, NULL); | ||||
|         if (ret < 0) { | ||||
|             return ret; | ||||
|         } | ||||
|         /* if the whole request falls into an unallocated area we can avoid
 | ||||
|          * to read and directly return zeroes instead */ | ||||
|          * reading and directly return zeroes instead */ | ||||
|         if (ret & BDRV_BLOCK_ZERO && | ||||
|             pnum >= nb_sectors + sector_num % iscsilun->cluster_sectors) { | ||||
|             pnum >= nb_sectors * BDRV_SECTOR_SIZE + head) { | ||||
|             qemu_iovec_memset(iov, 0, 0x00, iov->size); | ||||
|             return 0; | ||||
|         } | ||||
| @ -1146,8 +1159,7 @@ retry: | ||||
|         goto retry; | ||||
|     } | ||||
| 
 | ||||
|     iscsi_allocmap_set_invalid(iscsilun, offset >> BDRV_SECTOR_BITS, | ||||
|                                bytes >> BDRV_SECTOR_BITS); | ||||
|     iscsi_allocmap_set_invalid(iscsilun, offset, bytes); | ||||
| 
 | ||||
|     if (iTask.status == SCSI_STATUS_CHECK_CONDITION) { | ||||
|         /* the target might fail with a check condition if it
 | ||||
| @ -1260,8 +1272,7 @@ retry: | ||||
|     } | ||||
| 
 | ||||
|     if (iTask.status != SCSI_STATUS_GOOD) { | ||||
|         iscsi_allocmap_set_invalid(iscsilun, offset >> BDRV_SECTOR_BITS, | ||||
|                                    bytes >> BDRV_SECTOR_BITS); | ||||
|         iscsi_allocmap_set_invalid(iscsilun, offset, bytes); | ||||
|         error_report("iSCSI WRITESAME10/16 failed at lba %" PRIu64 ": %s", | ||||
|                      lba, iTask.err_str); | ||||
|         r = iTask.err_code; | ||||
| @ -1269,11 +1280,9 @@ retry: | ||||
|     } | ||||
| 
 | ||||
|     if (flags & BDRV_REQ_MAY_UNMAP) { | ||||
|         iscsi_allocmap_set_invalid(iscsilun, offset >> BDRV_SECTOR_BITS, | ||||
|                                    bytes >> BDRV_SECTOR_BITS); | ||||
|         iscsi_allocmap_set_invalid(iscsilun, offset, bytes); | ||||
|     } else { | ||||
|         iscsi_allocmap_set_allocated(iscsilun, offset >> BDRV_SECTOR_BITS, | ||||
|                                      bytes >> BDRV_SECTOR_BITS); | ||||
|         iscsi_allocmap_set_allocated(iscsilun, offset, bytes); | ||||
|     } | ||||
| 
 | ||||
| out_unlock: | ||||
| @ -1953,8 +1962,8 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|      * reasonable size */ | ||||
|     if (iscsilun->bl.opt_unmap_gran * iscsilun->block_size >= 4 * 1024 && | ||||
|         iscsilun->bl.opt_unmap_gran * iscsilun->block_size <= 16 * 1024 * 1024) { | ||||
|         iscsilun->cluster_sectors = (iscsilun->bl.opt_unmap_gran * | ||||
|                                      iscsilun->block_size) >> BDRV_SECTOR_BITS; | ||||
|         iscsilun->cluster_size = iscsilun->bl.opt_unmap_gran * | ||||
|             iscsilun->block_size; | ||||
|         if (iscsilun->lbprz) { | ||||
|             ret = iscsi_allocmap_init(iscsilun, bs->open_flags); | ||||
|         } | ||||
| @ -2108,7 +2117,8 @@ static int iscsi_truncate(BlockDriverState *bs, int64_t offset, | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| static int iscsi_create(const char *filename, QemuOpts *opts, Error **errp) | ||||
| static int coroutine_fn iscsi_co_create_opts(const char *filename, QemuOpts *opts, | ||||
|                                              Error **errp) | ||||
| { | ||||
|     int ret = 0; | ||||
|     int64_t total_size = 0; | ||||
| @ -2163,7 +2173,7 @@ static int iscsi_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) | ||||
| { | ||||
|     IscsiLun *iscsilun = bs->opaque; | ||||
|     bdi->unallocated_blocks_are_zero = iscsilun->lbprz; | ||||
|     bdi->cluster_size = iscsilun->cluster_sectors * BDRV_SECTOR_SIZE; | ||||
|     bdi->cluster_size = iscsilun->cluster_size; | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| @ -2195,7 +2205,7 @@ static BlockDriver bdrv_iscsi = { | ||||
|     .bdrv_parse_filename    = iscsi_parse_filename, | ||||
|     .bdrv_file_open         = iscsi_open, | ||||
|     .bdrv_close             = iscsi_close, | ||||
|     .bdrv_create            = iscsi_create, | ||||
|     .bdrv_co_create_opts    = iscsi_co_create_opts, | ||||
|     .create_opts            = &iscsi_create_opts, | ||||
|     .bdrv_reopen_prepare    = iscsi_reopen_prepare, | ||||
|     .bdrv_reopen_commit     = iscsi_reopen_commit, | ||||
| @ -2206,7 +2216,7 @@ static BlockDriver bdrv_iscsi = { | ||||
|     .bdrv_truncate   = iscsi_truncate, | ||||
|     .bdrv_refresh_limits = iscsi_refresh_limits, | ||||
| 
 | ||||
|     .bdrv_co_get_block_status = iscsi_co_get_block_status, | ||||
|     .bdrv_co_block_status  = iscsi_co_block_status, | ||||
|     .bdrv_co_pdiscard      = iscsi_co_pdiscard, | ||||
|     .bdrv_co_pwrite_zeroes = iscsi_co_pwrite_zeroes, | ||||
|     .bdrv_co_readv         = iscsi_co_readv, | ||||
| @ -2230,7 +2240,7 @@ static BlockDriver bdrv_iser = { | ||||
|     .bdrv_parse_filename    = iscsi_parse_filename, | ||||
|     .bdrv_file_open         = iscsi_open, | ||||
|     .bdrv_close             = iscsi_close, | ||||
|     .bdrv_create            = iscsi_create, | ||||
|     .bdrv_co_create_opts    = iscsi_co_create_opts, | ||||
|     .create_opts            = &iscsi_create_opts, | ||||
|     .bdrv_reopen_prepare    = iscsi_reopen_prepare, | ||||
|     .bdrv_reopen_commit     = iscsi_reopen_commit, | ||||
| @ -2241,7 +2251,7 @@ static BlockDriver bdrv_iser = { | ||||
|     .bdrv_truncate   = iscsi_truncate, | ||||
|     .bdrv_refresh_limits = iscsi_refresh_limits, | ||||
| 
 | ||||
|     .bdrv_co_get_block_status = iscsi_co_get_block_status, | ||||
|     .bdrv_co_block_status  = iscsi_co_block_status, | ||||
|     .bdrv_co_pdiscard      = iscsi_co_pdiscard, | ||||
|     .bdrv_co_pwrite_zeroes = iscsi_co_pwrite_zeroes, | ||||
|     .bdrv_co_readv         = iscsi_co_readv, | ||||
|  | ||||
| @ -1094,7 +1094,7 @@ static BlockDriver bdrv_mirror_top = { | ||||
|     .bdrv_co_pwrite_zeroes      = bdrv_mirror_top_pwrite_zeroes, | ||||
|     .bdrv_co_pdiscard           = bdrv_mirror_top_pdiscard, | ||||
|     .bdrv_co_flush              = bdrv_mirror_top_flush, | ||||
|     .bdrv_co_get_block_status   = bdrv_co_get_block_status_from_backing, | ||||
|     .bdrv_co_block_status       = bdrv_co_block_status_from_backing, | ||||
|     .bdrv_refresh_filename      = bdrv_mirror_top_refresh_filename, | ||||
|     .bdrv_close                 = bdrv_mirror_top_close, | ||||
|     .bdrv_child_perm            = bdrv_mirror_top_child_perm, | ||||
|  | ||||
| @ -684,7 +684,8 @@ static QemuOptsList nfs_create_opts = { | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| static int nfs_file_create(const char *url, QemuOpts *opts, Error **errp) | ||||
| static int coroutine_fn nfs_file_co_create_opts(const char *url, QemuOpts *opts, | ||||
|                                                 Error **errp) | ||||
| { | ||||
|     int64_t ret, total_size; | ||||
|     NFSClient *client = g_new0(NFSClient, 1); | ||||
| @ -897,7 +898,7 @@ static BlockDriver bdrv_nfs = { | ||||
| 
 | ||||
|     .bdrv_file_open                 = nfs_file_open, | ||||
|     .bdrv_close                     = nfs_file_close, | ||||
|     .bdrv_create                    = nfs_file_create, | ||||
|     .bdrv_co_create_opts            = nfs_file_co_create_opts, | ||||
|     .bdrv_reopen_prepare            = nfs_reopen_prepare, | ||||
| 
 | ||||
|     .bdrv_co_preadv                 = nfs_co_preadv, | ||||
|  | ||||
							
								
								
									
										23
									
								
								block/null.c
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								block/null.c
									
									
									
									
									
								
							| @ -223,22 +223,23 @@ static int null_reopen_prepare(BDRVReopenState *reopen_state, | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| static int64_t coroutine_fn null_co_get_block_status(BlockDriverState *bs, | ||||
|                                                      int64_t sector_num, | ||||
|                                                      int nb_sectors, int *pnum, | ||||
|                                                      BlockDriverState **file) | ||||
| static int coroutine_fn null_co_block_status(BlockDriverState *bs, | ||||
|                                              bool want_zero, int64_t offset, | ||||
|                                              int64_t bytes, int64_t *pnum, | ||||
|                                              int64_t *map, | ||||
|                                              BlockDriverState **file) | ||||
| { | ||||
|     BDRVNullState *s = bs->opaque; | ||||
|     off_t start = sector_num * BDRV_SECTOR_SIZE; | ||||
|     int ret = BDRV_BLOCK_OFFSET_VALID; | ||||
| 
 | ||||
|     *pnum = nb_sectors; | ||||
|     *pnum = bytes; | ||||
|     *map = offset; | ||||
|     *file = bs; | ||||
| 
 | ||||
|     if (s->read_zeroes) { | ||||
|         return BDRV_BLOCK_OFFSET_VALID | start | BDRV_BLOCK_ZERO; | ||||
|     } else { | ||||
|         return BDRV_BLOCK_OFFSET_VALID | start; | ||||
|         ret |= BDRV_BLOCK_ZERO; | ||||
|     } | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| static void null_refresh_filename(BlockDriverState *bs, QDict *opts) | ||||
| @ -270,7 +271,7 @@ static BlockDriver bdrv_null_co = { | ||||
|     .bdrv_co_flush_to_disk  = null_co_flush, | ||||
|     .bdrv_reopen_prepare    = null_reopen_prepare, | ||||
| 
 | ||||
|     .bdrv_co_get_block_status   = null_co_get_block_status, | ||||
|     .bdrv_co_block_status   = null_co_block_status, | ||||
| 
 | ||||
|     .bdrv_refresh_filename  = null_refresh_filename, | ||||
| }; | ||||
| @ -290,7 +291,7 @@ static BlockDriver bdrv_null_aio = { | ||||
|     .bdrv_aio_flush         = null_aio_flush, | ||||
|     .bdrv_reopen_prepare    = null_reopen_prepare, | ||||
| 
 | ||||
|     .bdrv_co_get_block_status   = null_co_get_block_status, | ||||
|     .bdrv_co_block_status   = null_co_block_status, | ||||
| 
 | ||||
|     .bdrv_refresh_filename  = null_refresh_filename, | ||||
| }; | ||||
|  | ||||
							
								
								
									
										14
									
								
								block/nvme.c
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								block/nvme.c
									
									
									
									
									
								
							| @ -1072,18 +1072,6 @@ static int nvme_reopen_prepare(BDRVReopenState *reopen_state, | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| static int64_t coroutine_fn nvme_co_get_block_status(BlockDriverState *bs, | ||||
|                                                      int64_t sector_num, | ||||
|                                                      int nb_sectors, int *pnum, | ||||
|                                                      BlockDriverState **file) | ||||
| { | ||||
|     *pnum = nb_sectors; | ||||
|     *file = bs; | ||||
| 
 | ||||
|     return BDRV_BLOCK_ALLOCATED | BDRV_BLOCK_OFFSET_VALID | | ||||
|            (sector_num << BDRV_SECTOR_BITS); | ||||
| } | ||||
| 
 | ||||
| static void nvme_refresh_filename(BlockDriverState *bs, QDict *opts) | ||||
| { | ||||
|     QINCREF(opts); | ||||
| @ -1183,8 +1171,6 @@ static BlockDriver bdrv_nvme = { | ||||
|     .bdrv_co_flush_to_disk    = nvme_co_flush, | ||||
|     .bdrv_reopen_prepare      = nvme_reopen_prepare, | ||||
| 
 | ||||
|     .bdrv_co_get_block_status = nvme_co_get_block_status, | ||||
| 
 | ||||
|     .bdrv_refresh_filename    = nvme_refresh_filename, | ||||
|     .bdrv_refresh_limits      = nvme_refresh_limits, | ||||
| 
 | ||||
|  | ||||
| @ -261,23 +261,31 @@ static coroutine_fn int parallels_co_flush_to_os(BlockDriverState *bs) | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static int64_t coroutine_fn parallels_co_get_block_status(BlockDriverState *bs, | ||||
|         int64_t sector_num, int nb_sectors, int *pnum, BlockDriverState **file) | ||||
| static int coroutine_fn parallels_co_block_status(BlockDriverState *bs, | ||||
|                                                   bool want_zero, | ||||
|                                                   int64_t offset, | ||||
|                                                   int64_t bytes, | ||||
|                                                   int64_t *pnum, | ||||
|                                                   int64_t *map, | ||||
|                                                   BlockDriverState **file) | ||||
| { | ||||
|     BDRVParallelsState *s = bs->opaque; | ||||
|     int64_t offset; | ||||
|     int count; | ||||
| 
 | ||||
|     assert(QEMU_IS_ALIGNED(offset | bytes, BDRV_SECTOR_SIZE)); | ||||
|     qemu_co_mutex_lock(&s->lock); | ||||
|     offset = block_status(s, sector_num, nb_sectors, pnum); | ||||
|     offset = block_status(s, offset >> BDRV_SECTOR_BITS, | ||||
|                           bytes >> BDRV_SECTOR_BITS, &count); | ||||
|     qemu_co_mutex_unlock(&s->lock); | ||||
| 
 | ||||
|     *pnum = count * BDRV_SECTOR_SIZE; | ||||
|     if (offset < 0) { | ||||
|         return 0; | ||||
|     } | ||||
| 
 | ||||
|     *map = offset * BDRV_SECTOR_SIZE; | ||||
|     *file = bs->file->bs; | ||||
|     return (offset << BDRV_SECTOR_BITS) | | ||||
|         BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID; | ||||
|     return BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID; | ||||
| } | ||||
| 
 | ||||
| static coroutine_fn int parallels_co_writev(BlockDriverState *bs, | ||||
| @ -467,7 +475,9 @@ static int parallels_check(BlockDriverState *bs, BdrvCheckResult *res, | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static int parallels_create(const char *filename, QemuOpts *opts, Error **errp) | ||||
| static int coroutine_fn parallels_co_create_opts(const char *filename, | ||||
|                                                  QemuOpts *opts, | ||||
|                                                  Error **errp) | ||||
| { | ||||
|     int64_t total_size, cl_size; | ||||
|     uint8_t tmp[BDRV_SECTOR_SIZE]; | ||||
| @ -782,13 +792,13 @@ static BlockDriver bdrv_parallels = { | ||||
|     .bdrv_open		= parallels_open, | ||||
|     .bdrv_close		= parallels_close, | ||||
|     .bdrv_child_perm          = bdrv_format_default_perms, | ||||
|     .bdrv_co_get_block_status = parallels_co_get_block_status, | ||||
|     .bdrv_co_block_status     = parallels_co_block_status, | ||||
|     .bdrv_has_zero_init       = bdrv_has_zero_init_1, | ||||
|     .bdrv_co_flush_to_os      = parallels_co_flush_to_os, | ||||
|     .bdrv_co_readv  = parallels_co_readv, | ||||
|     .bdrv_co_writev = parallels_co_writev, | ||||
|     .supports_backing = true, | ||||
|     .bdrv_create    = parallels_create, | ||||
|     .bdrv_co_create_opts = parallels_co_create_opts, | ||||
|     .bdrv_check     = parallels_check, | ||||
|     .create_opts    = ¶llels_create_opts, | ||||
| }; | ||||
|  | ||||
							
								
								
									
										32
									
								
								block/qcow.c
									
									
									
									
									
								
							
							
						
						
									
										32
									
								
								block/qcow.c
									
									
									
									
									
								
							| @ -524,23 +524,28 @@ static int get_cluster_offset(BlockDriverState *bs, | ||||
|     return 1; | ||||
| } | ||||
| 
 | ||||
| static int64_t coroutine_fn qcow_co_get_block_status(BlockDriverState *bs, | ||||
|         int64_t sector_num, int nb_sectors, int *pnum, BlockDriverState **file) | ||||
| static int coroutine_fn qcow_co_block_status(BlockDriverState *bs, | ||||
|                                              bool want_zero, | ||||
|                                              int64_t offset, int64_t bytes, | ||||
|                                              int64_t *pnum, int64_t *map, | ||||
|                                              BlockDriverState **file) | ||||
| { | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     int index_in_cluster, n, ret; | ||||
|     int index_in_cluster, ret; | ||||
|     int64_t n; | ||||
|     uint64_t cluster_offset; | ||||
| 
 | ||||
|     qemu_co_mutex_lock(&s->lock); | ||||
|     ret = get_cluster_offset(bs, sector_num << 9, 0, 0, 0, 0, &cluster_offset); | ||||
|     ret = get_cluster_offset(bs, offset, 0, 0, 0, 0, &cluster_offset); | ||||
|     qemu_co_mutex_unlock(&s->lock); | ||||
|     if (ret < 0) { | ||||
|         return ret; | ||||
|     } | ||||
|     index_in_cluster = sector_num & (s->cluster_sectors - 1); | ||||
|     n = s->cluster_sectors - index_in_cluster; | ||||
|     if (n > nb_sectors) | ||||
|         n = nb_sectors; | ||||
|     index_in_cluster = offset & (s->cluster_size - 1); | ||||
|     n = s->cluster_size - index_in_cluster; | ||||
|     if (n > bytes) { | ||||
|         n = bytes; | ||||
|     } | ||||
|     *pnum = n; | ||||
|     if (!cluster_offset) { | ||||
|         return 0; | ||||
| @ -548,9 +553,9 @@ static int64_t coroutine_fn qcow_co_get_block_status(BlockDriverState *bs, | ||||
|     if ((cluster_offset & QCOW_OFLAG_COMPRESSED) || s->crypto) { | ||||
|         return BDRV_BLOCK_DATA; | ||||
|     } | ||||
|     cluster_offset |= (index_in_cluster << BDRV_SECTOR_BITS); | ||||
|     *map = cluster_offset | index_in_cluster; | ||||
|     *file = bs->file->bs; | ||||
|     return BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | cluster_offset; | ||||
|     return BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID; | ||||
| } | ||||
| 
 | ||||
| static int decompress_buffer(uint8_t *out_buf, int out_buf_size, | ||||
| @ -805,7 +810,8 @@ static void qcow_close(BlockDriverState *bs) | ||||
|     error_free(s->migration_blocker); | ||||
| } | ||||
| 
 | ||||
| static int qcow_create(const char *filename, QemuOpts *opts, Error **errp) | ||||
| static int coroutine_fn qcow_co_create_opts(const char *filename, QemuOpts *opts, | ||||
|                                             Error **errp) | ||||
| { | ||||
|     int header_size, backing_filename_len, l1_size, shift, i; | ||||
|     QCowHeader header; | ||||
| @ -1122,13 +1128,13 @@ static BlockDriver bdrv_qcow = { | ||||
|     .bdrv_close		= qcow_close, | ||||
|     .bdrv_child_perm        = bdrv_format_default_perms, | ||||
|     .bdrv_reopen_prepare    = qcow_reopen_prepare, | ||||
|     .bdrv_create            = qcow_create, | ||||
|     .bdrv_co_create_opts    = qcow_co_create_opts, | ||||
|     .bdrv_has_zero_init     = bdrv_has_zero_init_1, | ||||
|     .supports_backing       = true, | ||||
| 
 | ||||
|     .bdrv_co_readv          = qcow_co_readv, | ||||
|     .bdrv_co_writev         = qcow_co_writev, | ||||
|     .bdrv_co_get_block_status   = qcow_co_get_block_status, | ||||
|     .bdrv_co_block_status   = qcow_co_block_status, | ||||
| 
 | ||||
|     .bdrv_make_empty        = qcow_make_empty, | ||||
|     .bdrv_co_pwritev_compressed = qcow_co_pwritev_compressed, | ||||
|  | ||||
| @ -413,8 +413,8 @@ static inline void bitmap_dir_entry_to_be(Qcow2BitmapDirEntry *entry) | ||||
| 
 | ||||
| static inline int calc_dir_entry_size(size_t name_size, size_t extra_data_size) | ||||
| { | ||||
|     return align_offset(sizeof(Qcow2BitmapDirEntry) + | ||||
|                         name_size + extra_data_size, 8); | ||||
|     int size = sizeof(Qcow2BitmapDirEntry) + name_size + extra_data_size; | ||||
|     return ROUND_UP(size, 8); | ||||
| } | ||||
| 
 | ||||
| static inline int dir_entry_size(Qcow2BitmapDirEntry *entry) | ||||
|  | ||||
| @ -126,11 +126,11 @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size, | ||||
| 
 | ||||
|     new_l1_size2 = sizeof(uint64_t) * new_l1_size; | ||||
|     new_l1_table = qemu_try_blockalign(bs->file->bs, | ||||
|                                        align_offset(new_l1_size2, 512)); | ||||
|                                        ROUND_UP(new_l1_size2, 512)); | ||||
|     if (new_l1_table == NULL) { | ||||
|         return -ENOMEM; | ||||
|     } | ||||
|     memset(new_l1_table, 0, align_offset(new_l1_size2, 512)); | ||||
|     memset(new_l1_table, 0, ROUND_UP(new_l1_size2, 512)); | ||||
| 
 | ||||
|     if (s->l1_size) { | ||||
|         memcpy(new_l1_table, s->l1_table, s->l1_size * sizeof(uint64_t)); | ||||
|  | ||||
| @ -1204,7 +1204,7 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs, | ||||
|      * l1_table_offset when it is the current s->l1_table_offset! Be careful | ||||
|      * when changing this! */ | ||||
|     if (l1_table_offset != s->l1_table_offset) { | ||||
|         l1_table = g_try_malloc0(align_offset(l1_size2, 512)); | ||||
|         l1_table = g_try_malloc0(ROUND_UP(l1_size2, 512)); | ||||
|         if (l1_size2 && l1_table == NULL) { | ||||
|             ret = -ENOMEM; | ||||
|             goto fail; | ||||
| @ -2553,7 +2553,7 @@ int qcow2_check_metadata_overlap(BlockDriverState *bs, int ign, int64_t offset, | ||||
|     } | ||||
| 
 | ||||
|     /* align range to test to cluster boundaries */ | ||||
|     size = align_offset(offset_into_cluster(s, offset) + size, s->cluster_size); | ||||
|     size = ROUND_UP(offset_into_cluster(s, offset) + size, s->cluster_size); | ||||
|     offset = start_of_cluster(s, offset); | ||||
| 
 | ||||
|     if ((chk & QCOW2_OL_ACTIVE_L1) && s->l1_size) { | ||||
|  | ||||
| @ -66,7 +66,7 @@ int qcow2_read_snapshots(BlockDriverState *bs) | ||||
| 
 | ||||
|     for(i = 0; i < s->nb_snapshots; i++) { | ||||
|         /* Read statically sized part of the snapshot header */ | ||||
|         offset = align_offset(offset, 8); | ||||
|         offset = ROUND_UP(offset, 8); | ||||
|         ret = bdrv_pread(bs->file, offset, &h, sizeof(h)); | ||||
|         if (ret < 0) { | ||||
|             goto fail; | ||||
| @ -155,7 +155,7 @@ static int qcow2_write_snapshots(BlockDriverState *bs) | ||||
|     offset = 0; | ||||
|     for(i = 0; i < s->nb_snapshots; i++) { | ||||
|         sn = s->snapshots + i; | ||||
|         offset = align_offset(offset, 8); | ||||
|         offset = ROUND_UP(offset, 8); | ||||
|         offset += sizeof(h); | ||||
|         offset += sizeof(extra); | ||||
|         offset += strlen(sn->id_str); | ||||
| @ -215,7 +215,7 @@ static int qcow2_write_snapshots(BlockDriverState *bs) | ||||
|         assert(id_str_size <= UINT16_MAX && name_size <= UINT16_MAX); | ||||
|         h.id_str_size = cpu_to_be16(id_str_size); | ||||
|         h.name_size = cpu_to_be16(name_size); | ||||
|         offset = align_offset(offset, 8); | ||||
|         offset = ROUND_UP(offset, 8); | ||||
| 
 | ||||
|         ret = bdrv_pwrite(bs->file, offset, &h, sizeof(h)); | ||||
|         if (ret < 0) { | ||||
| @ -441,7 +441,7 @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) | ||||
|     /* The VM state isn't needed any more in the active L1 table; in fact, it
 | ||||
|      * hurts by causing expensive COW for the next snapshot. */ | ||||
|     qcow2_cluster_discard(bs, qcow2_vm_state_offset(s), | ||||
|                           align_offset(sn->vm_state_size, s->cluster_size), | ||||
|                           ROUND_UP(sn->vm_state_size, s->cluster_size), | ||||
|                           QCOW2_DISCARD_NEVER, false); | ||||
| 
 | ||||
| #ifdef DEBUG_ALLOC | ||||
| @ -710,7 +710,7 @@ int qcow2_snapshot_load_tmp(BlockDriverState *bs, | ||||
|     } | ||||
|     new_l1_bytes = sn->l1_size * sizeof(uint64_t); | ||||
|     new_l1_table = qemu_try_blockalign(bs->file->bs, | ||||
|                                        align_offset(new_l1_bytes, 512)); | ||||
|                                        ROUND_UP(new_l1_bytes, 512)); | ||||
|     if (new_l1_table == NULL) { | ||||
|         return -ENOMEM; | ||||
|     } | ||||
|  | ||||
| @ -1377,7 +1377,7 @@ static int qcow2_do_open(BlockDriverState *bs, QDict *options, int flags, | ||||
| 
 | ||||
|     if (s->l1_size > 0) { | ||||
|         s->l1_table = qemu_try_blockalign(bs->file->bs, | ||||
|             align_offset(s->l1_size * sizeof(uint64_t), 512)); | ||||
|             ROUND_UP(s->l1_size * sizeof(uint64_t), 512)); | ||||
|         if (s->l1_table == NULL) { | ||||
|             error_setg(errp, "Could not allocate L1 table"); | ||||
|             ret = -ENOMEM; | ||||
| @ -1668,32 +1668,34 @@ static void qcow2_join_options(QDict *options, QDict *old_options) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static int64_t coroutine_fn qcow2_co_get_block_status(BlockDriverState *bs, | ||||
|         int64_t sector_num, int nb_sectors, int *pnum, BlockDriverState **file) | ||||
| static int coroutine_fn qcow2_co_block_status(BlockDriverState *bs, | ||||
|                                               bool want_zero, | ||||
|                                               int64_t offset, int64_t count, | ||||
|                                               int64_t *pnum, int64_t *map, | ||||
|                                               BlockDriverState **file) | ||||
| { | ||||
|     BDRVQcow2State *s = bs->opaque; | ||||
|     uint64_t cluster_offset; | ||||
|     int index_in_cluster, ret; | ||||
|     unsigned int bytes; | ||||
|     int64_t status = 0; | ||||
|     int status = 0; | ||||
| 
 | ||||
|     bytes = MIN(INT_MAX, nb_sectors * BDRV_SECTOR_SIZE); | ||||
|     bytes = MIN(INT_MAX, count); | ||||
|     qemu_co_mutex_lock(&s->lock); | ||||
|     ret = qcow2_get_cluster_offset(bs, sector_num << BDRV_SECTOR_BITS, &bytes, | ||||
|                                    &cluster_offset); | ||||
|     ret = qcow2_get_cluster_offset(bs, offset, &bytes, &cluster_offset); | ||||
|     qemu_co_mutex_unlock(&s->lock); | ||||
|     if (ret < 0) { | ||||
|         return ret; | ||||
|     } | ||||
| 
 | ||||
|     *pnum = bytes >> BDRV_SECTOR_BITS; | ||||
|     *pnum = bytes; | ||||
| 
 | ||||
|     if (cluster_offset != 0 && ret != QCOW2_CLUSTER_COMPRESSED && | ||||
|         !s->crypto) { | ||||
|         index_in_cluster = sector_num & (s->cluster_sectors - 1); | ||||
|         cluster_offset |= (index_in_cluster << BDRV_SECTOR_BITS); | ||||
|         index_in_cluster = offset & (s->cluster_size - 1); | ||||
|         *map = cluster_offset | index_in_cluster; | ||||
|         *file = bs->file->bs; | ||||
|         status |= BDRV_BLOCK_OFFSET_VALID | cluster_offset; | ||||
|         status |= BDRV_BLOCK_OFFSET_VALID; | ||||
|     } | ||||
|     if (ret == QCOW2_CLUSTER_ZERO_PLAIN || ret == QCOW2_CLUSTER_ZERO_ALLOC) { | ||||
|         status |= BDRV_BLOCK_ZERO; | ||||
| @ -2638,19 +2640,19 @@ static int64_t qcow2_calc_prealloc_size(int64_t total_size, | ||||
| { | ||||
|     int64_t meta_size = 0; | ||||
|     uint64_t nl1e, nl2e; | ||||
|     int64_t aligned_total_size = align_offset(total_size, cluster_size); | ||||
|     int64_t aligned_total_size = ROUND_UP(total_size, cluster_size); | ||||
| 
 | ||||
|     /* header: 1 cluster */ | ||||
|     meta_size += cluster_size; | ||||
| 
 | ||||
|     /* total size of L2 tables */ | ||||
|     nl2e = aligned_total_size / cluster_size; | ||||
|     nl2e = align_offset(nl2e, cluster_size / sizeof(uint64_t)); | ||||
|     nl2e = ROUND_UP(nl2e, cluster_size / sizeof(uint64_t)); | ||||
|     meta_size += nl2e * sizeof(uint64_t); | ||||
| 
 | ||||
|     /* total size of L1 tables */ | ||||
|     nl1e = nl2e * sizeof(uint64_t) / cluster_size; | ||||
|     nl1e = align_offset(nl1e, cluster_size / sizeof(uint64_t)); | ||||
|     nl1e = ROUND_UP(nl1e, cluster_size / sizeof(uint64_t)); | ||||
|     meta_size += nl1e * sizeof(uint64_t); | ||||
| 
 | ||||
|     /* total size of refcount table and blocks */ | ||||
| @ -2721,11 +2723,12 @@ static uint64_t qcow2_opt_get_refcount_bits_del(QemuOpts *opts, int version, | ||||
|     return refcount_bits; | ||||
| } | ||||
| 
 | ||||
| static int qcow2_create2(const char *filename, int64_t total_size, | ||||
|                          const char *backing_file, const char *backing_format, | ||||
|                          int flags, size_t cluster_size, PreallocMode prealloc, | ||||
|                          QemuOpts *opts, int version, int refcount_order, | ||||
|                          const char *encryptfmt, Error **errp) | ||||
| static int coroutine_fn | ||||
| qcow2_co_create2(const char *filename, int64_t total_size, | ||||
|                  const char *backing_file, const char *backing_format, | ||||
|                  int flags, size_t cluster_size, PreallocMode prealloc, | ||||
|                  QemuOpts *opts, int version, int refcount_order, | ||||
|                  const char *encryptfmt, Error **errp) | ||||
| { | ||||
|     QDict *options; | ||||
| 
 | ||||
| @ -2912,7 +2915,8 @@ out: | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| static int qcow2_create(const char *filename, QemuOpts *opts, Error **errp) | ||||
| static int coroutine_fn qcow2_co_create_opts(const char *filename, QemuOpts *opts, | ||||
|                                              Error **errp) | ||||
| { | ||||
|     char *backing_file = NULL; | ||||
|     char *backing_fmt = NULL; | ||||
| @ -2993,9 +2997,9 @@ static int qcow2_create(const char *filename, QemuOpts *opts, Error **errp) | ||||
| 
 | ||||
|     refcount_order = ctz32(refcount_bits); | ||||
| 
 | ||||
|     ret = qcow2_create2(filename, size, backing_file, backing_fmt, flags, | ||||
|                         cluster_size, prealloc, opts, version, refcount_order, | ||||
|                         encryptfmt, &local_err); | ||||
|     ret = qcow2_co_create2(filename, size, backing_file, backing_fmt, flags, | ||||
|                            cluster_size, prealloc, opts, version, refcount_order, | ||||
|                            encryptfmt, &local_err); | ||||
|     error_propagate(errp, local_err); | ||||
| 
 | ||||
| finish: | ||||
| @ -3704,8 +3708,8 @@ static BlockMeasureInfo *qcow2_measure(QemuOpts *opts, BlockDriverState *in_bs, | ||||
|     has_backing_file = !!optstr; | ||||
|     g_free(optstr); | ||||
| 
 | ||||
|     virtual_size = align_offset(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), | ||||
|                                 cluster_size); | ||||
|     virtual_size = qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0); | ||||
|     virtual_size = ROUND_UP(virtual_size, cluster_size); | ||||
| 
 | ||||
|     /* Check that virtual disk size is valid */ | ||||
|     l2_tables = DIV_ROUND_UP(virtual_size / cluster_size, | ||||
| @ -3725,7 +3729,7 @@ static BlockMeasureInfo *qcow2_measure(QemuOpts *opts, BlockDriverState *in_bs, | ||||
|             goto err; | ||||
|         } | ||||
| 
 | ||||
|         virtual_size = align_offset(ssize, cluster_size); | ||||
|         virtual_size = ROUND_UP(ssize, cluster_size); | ||||
| 
 | ||||
|         if (has_backing_file) { | ||||
|             /* We don't how much of the backing chain is shared by the input
 | ||||
| @ -4348,9 +4352,9 @@ BlockDriver bdrv_qcow2 = { | ||||
|     .bdrv_reopen_abort    = qcow2_reopen_abort, | ||||
|     .bdrv_join_options    = qcow2_join_options, | ||||
|     .bdrv_child_perm      = bdrv_format_default_perms, | ||||
|     .bdrv_create        = qcow2_create, | ||||
|     .bdrv_co_create_opts  = qcow2_co_create_opts, | ||||
|     .bdrv_has_zero_init = bdrv_has_zero_init_1, | ||||
|     .bdrv_co_get_block_status = qcow2_co_get_block_status, | ||||
|     .bdrv_co_block_status = qcow2_co_block_status, | ||||
| 
 | ||||
|     .bdrv_co_preadv         = qcow2_co_preadv, | ||||
|     .bdrv_co_pwritev        = qcow2_co_pwritev, | ||||
|  | ||||
| @ -480,12 +480,6 @@ static inline int offset_to_l2_slice_index(BDRVQcow2State *s, int64_t offset) | ||||
|     return (offset >> s->cluster_bits) & (s->l2_slice_size - 1); | ||||
| } | ||||
| 
 | ||||
| static inline int64_t align_offset(int64_t offset, int n) | ||||
| { | ||||
|     offset = (offset + n - 1) & ~(n - 1); | ||||
|     return offset; | ||||
| } | ||||
| 
 | ||||
| static inline int64_t qcow2_vm_state_offset(BDRVQcow2State *s) | ||||
| { | ||||
|     return (int64_t)s->l1_vm_state_index << (s->cluster_bits + s->l2_bits); | ||||
|  | ||||
							
								
								
									
										90
									
								
								block/qed.c
									
									
									
									
									
								
							
							
						
						
									
										90
									
								
								block/qed.c
									
									
									
									
									
								
							| @ -638,7 +638,9 @@ out: | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| static int bdrv_qed_create(const char *filename, QemuOpts *opts, Error **errp) | ||||
| static int coroutine_fn bdrv_qed_co_create_opts(const char *filename, | ||||
|                                                 QemuOpts *opts, | ||||
|                                                 Error **errp) | ||||
| { | ||||
|     uint64_t image_size = 0; | ||||
|     uint32_t cluster_size = QED_DEFAULT_CLUSTER_SIZE; | ||||
| @ -688,74 +690,46 @@ finish: | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| typedef struct { | ||||
|     BlockDriverState *bs; | ||||
|     Coroutine *co; | ||||
|     uint64_t pos; | ||||
|     int64_t status; | ||||
|     int *pnum; | ||||
|     BlockDriverState **file; | ||||
| } QEDIsAllocatedCB; | ||||
| 
 | ||||
| /* Called with table_lock held.  */ | ||||
| static void qed_is_allocated_cb(void *opaque, int ret, uint64_t offset, size_t len) | ||||
| { | ||||
|     QEDIsAllocatedCB *cb = opaque; | ||||
|     BDRVQEDState *s = cb->bs->opaque; | ||||
|     *cb->pnum = len / BDRV_SECTOR_SIZE; | ||||
|     switch (ret) { | ||||
|     case QED_CLUSTER_FOUND: | ||||
|         offset |= qed_offset_into_cluster(s, cb->pos); | ||||
|         cb->status = BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | offset; | ||||
|         *cb->file = cb->bs->file->bs; | ||||
|         break; | ||||
|     case QED_CLUSTER_ZERO: | ||||
|         cb->status = BDRV_BLOCK_ZERO; | ||||
|         break; | ||||
|     case QED_CLUSTER_L2: | ||||
|     case QED_CLUSTER_L1: | ||||
|         cb->status = 0; | ||||
|         break; | ||||
|     default: | ||||
|         assert(ret < 0); | ||||
|         cb->status = ret; | ||||
|         break; | ||||
|     } | ||||
| 
 | ||||
|     if (cb->co) { | ||||
|         aio_co_wake(cb->co); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static int64_t coroutine_fn bdrv_qed_co_get_block_status(BlockDriverState *bs, | ||||
|                                                  int64_t sector_num, | ||||
|                                                  int nb_sectors, int *pnum, | ||||
| static int coroutine_fn bdrv_qed_co_block_status(BlockDriverState *bs, | ||||
|                                                  bool want_zero, | ||||
|                                                  int64_t pos, int64_t bytes, | ||||
|                                                  int64_t *pnum, int64_t *map, | ||||
|                                                  BlockDriverState **file) | ||||
| { | ||||
|     BDRVQEDState *s = bs->opaque; | ||||
|     size_t len = (size_t)nb_sectors * BDRV_SECTOR_SIZE; | ||||
|     QEDIsAllocatedCB cb = { | ||||
|         .bs = bs, | ||||
|         .pos = (uint64_t)sector_num * BDRV_SECTOR_SIZE, | ||||
|         .status = BDRV_BLOCK_OFFSET_MASK, | ||||
|         .pnum = pnum, | ||||
|         .file = file, | ||||
|     }; | ||||
|     size_t len = MIN(bytes, SIZE_MAX); | ||||
|     int status; | ||||
|     QEDRequest request = { .l2_table = NULL }; | ||||
|     uint64_t offset; | ||||
|     int ret; | ||||
| 
 | ||||
|     qemu_co_mutex_lock(&s->table_lock); | ||||
|     ret = qed_find_cluster(s, &request, cb.pos, &len, &offset); | ||||
|     qed_is_allocated_cb(&cb, ret, offset, len); | ||||
|     ret = qed_find_cluster(s, &request, pos, &len, &offset); | ||||
| 
 | ||||
|     /* The callback was invoked immediately */ | ||||
|     assert(cb.status != BDRV_BLOCK_OFFSET_MASK); | ||||
|     *pnum = len; | ||||
|     switch (ret) { | ||||
|     case QED_CLUSTER_FOUND: | ||||
|         *map = offset | qed_offset_into_cluster(s, pos); | ||||
|         status = BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID; | ||||
|         *file = bs->file->bs; | ||||
|         break; | ||||
|     case QED_CLUSTER_ZERO: | ||||
|         status = BDRV_BLOCK_ZERO; | ||||
|         break; | ||||
|     case QED_CLUSTER_L2: | ||||
|     case QED_CLUSTER_L1: | ||||
|         status = 0; | ||||
|         break; | ||||
|     default: | ||||
|         assert(ret < 0); | ||||
|         status = ret; | ||||
|         break; | ||||
|     } | ||||
| 
 | ||||
|     qed_unref_l2_cache_entry(request.l2_table); | ||||
|     qemu_co_mutex_unlock(&s->table_lock); | ||||
| 
 | ||||
|     return cb.status; | ||||
|     return status; | ||||
| } | ||||
| 
 | ||||
| static BDRVQEDState *acb_to_s(QEDAIOCB *acb) | ||||
| @ -1592,9 +1566,9 @@ static BlockDriver bdrv_qed = { | ||||
|     .bdrv_close               = bdrv_qed_close, | ||||
|     .bdrv_reopen_prepare      = bdrv_qed_reopen_prepare, | ||||
|     .bdrv_child_perm          = bdrv_format_default_perms, | ||||
|     .bdrv_create              = bdrv_qed_create, | ||||
|     .bdrv_co_create_opts      = bdrv_qed_co_create_opts, | ||||
|     .bdrv_has_zero_init       = bdrv_has_zero_init_1, | ||||
|     .bdrv_co_get_block_status = bdrv_qed_co_get_block_status, | ||||
|     .bdrv_co_block_status     = bdrv_qed_co_block_status, | ||||
|     .bdrv_co_readv            = bdrv_qed_co_readv, | ||||
|     .bdrv_co_writev           = bdrv_qed_co_writev, | ||||
|     .bdrv_co_pwrite_zeroes    = bdrv_qed_co_pwrite_zeroes, | ||||
|  | ||||
| @ -250,17 +250,17 @@ fail: | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| static int64_t coroutine_fn raw_co_get_block_status(BlockDriverState *bs, | ||||
|                                             int64_t sector_num, | ||||
|                                             int nb_sectors, int *pnum, | ||||
| static int coroutine_fn raw_co_block_status(BlockDriverState *bs, | ||||
|                                             bool want_zero, int64_t offset, | ||||
|                                             int64_t bytes, int64_t *pnum, | ||||
|                                             int64_t *map, | ||||
|                                             BlockDriverState **file) | ||||
| { | ||||
|     BDRVRawState *s = bs->opaque; | ||||
|     *pnum = nb_sectors; | ||||
|     *pnum = bytes; | ||||
|     *file = bs->file->bs; | ||||
|     sector_num += s->offset / BDRV_SECTOR_SIZE; | ||||
|     return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID | | ||||
|            (sector_num << BDRV_SECTOR_BITS); | ||||
|     *map = offset + s->offset; | ||||
|     return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID; | ||||
| } | ||||
| 
 | ||||
| static int coroutine_fn raw_co_pwrite_zeroes(BlockDriverState *bs, | ||||
| @ -396,7 +396,8 @@ static int raw_has_zero_init(BlockDriverState *bs) | ||||
|     return bdrv_has_zero_init(bs->file->bs); | ||||
| } | ||||
| 
 | ||||
| static int raw_create(const char *filename, QemuOpts *opts, Error **errp) | ||||
| static int coroutine_fn raw_co_create_opts(const char *filename, QemuOpts *opts, | ||||
|                                            Error **errp) | ||||
| { | ||||
|     return bdrv_create_file(filename, opts, errp); | ||||
| } | ||||
| @ -491,12 +492,12 @@ BlockDriver bdrv_raw = { | ||||
|     .bdrv_open            = &raw_open, | ||||
|     .bdrv_close           = &raw_close, | ||||
|     .bdrv_child_perm      = bdrv_filter_default_perms, | ||||
|     .bdrv_create          = &raw_create, | ||||
|     .bdrv_co_create_opts  = &raw_co_create_opts, | ||||
|     .bdrv_co_preadv       = &raw_co_preadv, | ||||
|     .bdrv_co_pwritev      = &raw_co_pwritev, | ||||
|     .bdrv_co_pwrite_zeroes = &raw_co_pwrite_zeroes, | ||||
|     .bdrv_co_pdiscard     = &raw_co_pdiscard, | ||||
|     .bdrv_co_get_block_status = &raw_co_get_block_status, | ||||
|     .bdrv_co_block_status = &raw_co_block_status, | ||||
|     .bdrv_truncate        = &raw_truncate, | ||||
|     .bdrv_getlength       = &raw_getlength, | ||||
|     .has_variable_length  = true, | ||||
|  | ||||
| @ -351,7 +351,9 @@ static QemuOptsList runtime_opts = { | ||||
|     }, | ||||
| }; | ||||
| 
 | ||||
| static int qemu_rbd_create(const char *filename, QemuOpts *opts, Error **errp) | ||||
| static int coroutine_fn qemu_rbd_co_create_opts(const char *filename, | ||||
|                                                 QemuOpts *opts, | ||||
|                                                 Error **errp) | ||||
| { | ||||
|     Error *local_err = NULL; | ||||
|     int64_t bytes = 0; | ||||
| @ -1132,7 +1134,7 @@ static BlockDriver bdrv_rbd = { | ||||
|     .bdrv_file_open         = qemu_rbd_open, | ||||
|     .bdrv_close             = qemu_rbd_close, | ||||
|     .bdrv_reopen_prepare    = qemu_rbd_reopen_prepare, | ||||
|     .bdrv_create            = qemu_rbd_create, | ||||
|     .bdrv_co_create_opts    = qemu_rbd_co_create_opts, | ||||
|     .bdrv_has_zero_init     = bdrv_has_zero_init_1, | ||||
|     .bdrv_get_info          = qemu_rbd_getinfo, | ||||
|     .create_opts            = &qemu_rbd_create_opts, | ||||
|  | ||||
| @ -1959,8 +1959,8 @@ static int parse_block_size_shift(BDRVSheepdogState *s, QemuOpts *opt) | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| static int sd_create(const char *filename, QemuOpts *opts, | ||||
|                      Error **errp) | ||||
| static int coroutine_fn sd_co_create_opts(const char *filename, QemuOpts *opts, | ||||
|                                           Error **errp) | ||||
| { | ||||
|     Error *err = NULL; | ||||
|     int ret = 0; | ||||
| @ -3004,19 +3004,19 @@ static coroutine_fn int sd_co_pdiscard(BlockDriverState *bs, int64_t offset, | ||||
|     return acb.ret; | ||||
| } | ||||
| 
 | ||||
| static coroutine_fn int64_t | ||||
| sd_co_get_block_status(BlockDriverState *bs, int64_t sector_num, int nb_sectors, | ||||
|                        int *pnum, BlockDriverState **file) | ||||
| static coroutine_fn int | ||||
| sd_co_block_status(BlockDriverState *bs, bool want_zero, int64_t offset, | ||||
|                    int64_t bytes, int64_t *pnum, int64_t *map, | ||||
|                    BlockDriverState **file) | ||||
| { | ||||
|     BDRVSheepdogState *s = bs->opaque; | ||||
|     SheepdogInode *inode = &s->inode; | ||||
|     uint32_t object_size = (UINT32_C(1) << inode->block_size_shift); | ||||
|     uint64_t offset = sector_num * BDRV_SECTOR_SIZE; | ||||
|     unsigned long start = offset / object_size, | ||||
|                   end = DIV_ROUND_UP((sector_num + nb_sectors) * | ||||
|                                      BDRV_SECTOR_SIZE, object_size); | ||||
|                   end = DIV_ROUND_UP(offset + bytes, object_size); | ||||
|     unsigned long idx; | ||||
|     int64_t ret = BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | offset; | ||||
|     *map = offset; | ||||
|     int ret = BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID; | ||||
| 
 | ||||
|     for (idx = start; idx < end; idx++) { | ||||
|         if (inode->data_vdi_id[idx] == 0) { | ||||
| @ -3033,9 +3033,9 @@ sd_co_get_block_status(BlockDriverState *bs, int64_t sector_num, int nb_sectors, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     *pnum = (idx - start) * object_size / BDRV_SECTOR_SIZE; | ||||
|     if (*pnum > nb_sectors) { | ||||
|         *pnum = nb_sectors; | ||||
|     *pnum = (idx - start) * object_size; | ||||
|     if (*pnum > bytes) { | ||||
|         *pnum = bytes; | ||||
|     } | ||||
|     if (ret > 0 && ret & BDRV_BLOCK_OFFSET_VALID) { | ||||
|         *file = bs; | ||||
| @ -3103,7 +3103,7 @@ static BlockDriver bdrv_sheepdog = { | ||||
|     .bdrv_reopen_commit           = sd_reopen_commit, | ||||
|     .bdrv_reopen_abort            = sd_reopen_abort, | ||||
|     .bdrv_close                   = sd_close, | ||||
|     .bdrv_create                  = sd_create, | ||||
|     .bdrv_co_create_opts          = sd_co_create_opts, | ||||
|     .bdrv_has_zero_init           = bdrv_has_zero_init_1, | ||||
|     .bdrv_getlength               = sd_getlength, | ||||
|     .bdrv_get_allocated_file_size = sd_get_allocated_file_size, | ||||
| @ -3113,7 +3113,7 @@ static BlockDriver bdrv_sheepdog = { | ||||
|     .bdrv_co_writev               = sd_co_writev, | ||||
|     .bdrv_co_flush_to_disk        = sd_co_flush_to_disk, | ||||
|     .bdrv_co_pdiscard             = sd_co_pdiscard, | ||||
|     .bdrv_co_get_block_status     = sd_co_get_block_status, | ||||
|     .bdrv_co_block_status         = sd_co_block_status, | ||||
| 
 | ||||
|     .bdrv_snapshot_create         = sd_snapshot_create, | ||||
|     .bdrv_snapshot_goto           = sd_snapshot_goto, | ||||
| @ -3139,7 +3139,7 @@ static BlockDriver bdrv_sheepdog_tcp = { | ||||
|     .bdrv_reopen_commit           = sd_reopen_commit, | ||||
|     .bdrv_reopen_abort            = sd_reopen_abort, | ||||
|     .bdrv_close                   = sd_close, | ||||
|     .bdrv_create                  = sd_create, | ||||
|     .bdrv_co_create_opts          = sd_co_create_opts, | ||||
|     .bdrv_has_zero_init           = bdrv_has_zero_init_1, | ||||
|     .bdrv_getlength               = sd_getlength, | ||||
|     .bdrv_get_allocated_file_size = sd_get_allocated_file_size, | ||||
| @ -3149,7 +3149,7 @@ static BlockDriver bdrv_sheepdog_tcp = { | ||||
|     .bdrv_co_writev               = sd_co_writev, | ||||
|     .bdrv_co_flush_to_disk        = sd_co_flush_to_disk, | ||||
|     .bdrv_co_pdiscard             = sd_co_pdiscard, | ||||
|     .bdrv_co_get_block_status     = sd_co_get_block_status, | ||||
|     .bdrv_co_block_status         = sd_co_block_status, | ||||
| 
 | ||||
|     .bdrv_snapshot_create         = sd_snapshot_create, | ||||
|     .bdrv_snapshot_goto           = sd_snapshot_goto, | ||||
| @ -3175,7 +3175,7 @@ static BlockDriver bdrv_sheepdog_unix = { | ||||
|     .bdrv_reopen_commit           = sd_reopen_commit, | ||||
|     .bdrv_reopen_abort            = sd_reopen_abort, | ||||
|     .bdrv_close                   = sd_close, | ||||
|     .bdrv_create                  = sd_create, | ||||
|     .bdrv_co_create_opts          = sd_co_create_opts, | ||||
|     .bdrv_has_zero_init           = bdrv_has_zero_init_1, | ||||
|     .bdrv_getlength               = sd_getlength, | ||||
|     .bdrv_get_allocated_file_size = sd_get_allocated_file_size, | ||||
| @ -3185,7 +3185,7 @@ static BlockDriver bdrv_sheepdog_unix = { | ||||
|     .bdrv_co_writev               = sd_co_writev, | ||||
|     .bdrv_co_flush_to_disk        = sd_co_flush_to_disk, | ||||
|     .bdrv_co_pdiscard             = sd_co_pdiscard, | ||||
|     .bdrv_co_get_block_status     = sd_co_get_block_status, | ||||
|     .bdrv_co_block_status         = sd_co_block_status, | ||||
| 
 | ||||
|     .bdrv_snapshot_create         = sd_snapshot_create, | ||||
|     .bdrv_snapshot_goto           = sd_snapshot_goto, | ||||
|  | ||||
							
								
								
									
										66
									
								
								block/ssh.c
									
									
									
									
									
								
							
							
						
						
									
										66
									
								
								block/ssh.c
									
									
									
									
									
								
							| @ -803,6 +803,33 @@ static int ssh_file_open(BlockDriverState *bs, QDict *options, int bdrv_flags, | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| /* Note: This is a blocking operation */ | ||||
| static int ssh_grow_file(BDRVSSHState *s, int64_t offset, Error **errp) | ||||
| { | ||||
|     ssize_t ret; | ||||
|     char c[1] = { '\0' }; | ||||
|     int was_blocking = libssh2_session_get_blocking(s->session); | ||||
| 
 | ||||
|     /* offset must be strictly greater than the current size so we do
 | ||||
|      * not overwrite anything */ | ||||
|     assert(offset > 0 && offset > s->attrs.filesize); | ||||
| 
 | ||||
|     libssh2_session_set_blocking(s->session, 1); | ||||
| 
 | ||||
|     libssh2_sftp_seek64(s->sftp_handle, offset - 1); | ||||
|     ret = libssh2_sftp_write(s->sftp_handle, c, 1); | ||||
| 
 | ||||
|     libssh2_session_set_blocking(s->session, was_blocking); | ||||
| 
 | ||||
|     if (ret < 0) { | ||||
|         sftp_error_setg(errp, s, "Failed to grow file"); | ||||
|         return -EIO; | ||||
|     } | ||||
| 
 | ||||
|     s->attrs.filesize = offset; | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| static QemuOptsList ssh_create_opts = { | ||||
|     .name = "ssh-create-opts", | ||||
|     .head = QTAILQ_HEAD_INITIALIZER(ssh_create_opts.head), | ||||
| @ -816,14 +843,13 @@ static QemuOptsList ssh_create_opts = { | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| static int ssh_create(const char *filename, QemuOpts *opts, Error **errp) | ||||
| static int coroutine_fn ssh_co_create_opts(const char *filename, QemuOpts *opts, | ||||
|                                            Error **errp) | ||||
| { | ||||
|     int r, ret; | ||||
|     int64_t total_size = 0; | ||||
|     QDict *uri_options = NULL; | ||||
|     BDRVSSHState s; | ||||
|     ssize_t r2; | ||||
|     char c[1] = { '\0' }; | ||||
| 
 | ||||
|     ssh_state_init(&s); | ||||
| 
 | ||||
| @ -849,14 +875,10 @@ static int ssh_create(const char *filename, QemuOpts *opts, Error **errp) | ||||
|     } | ||||
| 
 | ||||
|     if (total_size > 0) { | ||||
|         libssh2_sftp_seek64(s.sftp_handle, total_size-1); | ||||
|         r2 = libssh2_sftp_write(s.sftp_handle, c, 1); | ||||
|         if (r2 < 0) { | ||||
|             sftp_error_setg(errp, &s, "truncate failed"); | ||||
|             ret = -EINVAL; | ||||
|         ret = ssh_grow_file(&s, total_size, errp); | ||||
|         if (ret < 0) { | ||||
|             goto out; | ||||
|         } | ||||
|         s.attrs.filesize = total_size; | ||||
|     } | ||||
| 
 | ||||
|     ret = 0; | ||||
| @ -1198,18 +1220,42 @@ static int64_t ssh_getlength(BlockDriverState *bs) | ||||
|     return length; | ||||
| } | ||||
| 
 | ||||
| static int ssh_truncate(BlockDriverState *bs, int64_t offset, | ||||
|                         PreallocMode prealloc, Error **errp) | ||||
| { | ||||
|     BDRVSSHState *s = bs->opaque; | ||||
| 
 | ||||
|     if (prealloc != PREALLOC_MODE_OFF) { | ||||
|         error_setg(errp, "Unsupported preallocation mode '%s'", | ||||
|                    PreallocMode_str(prealloc)); | ||||
|         return -ENOTSUP; | ||||
|     } | ||||
| 
 | ||||
|     if (offset < s->attrs.filesize) { | ||||
|         error_setg(errp, "ssh driver does not support shrinking files"); | ||||
|         return -ENOTSUP; | ||||
|     } | ||||
| 
 | ||||
|     if (offset == s->attrs.filesize) { | ||||
|         return 0; | ||||
|     } | ||||
| 
 | ||||
|     return ssh_grow_file(s, offset, errp); | ||||
| } | ||||
| 
 | ||||
| static BlockDriver bdrv_ssh = { | ||||
|     .format_name                  = "ssh", | ||||
|     .protocol_name                = "ssh", | ||||
|     .instance_size                = sizeof(BDRVSSHState), | ||||
|     .bdrv_parse_filename          = ssh_parse_filename, | ||||
|     .bdrv_file_open               = ssh_file_open, | ||||
|     .bdrv_create                  = ssh_create, | ||||
|     .bdrv_co_create_opts          = ssh_co_create_opts, | ||||
|     .bdrv_close                   = ssh_close, | ||||
|     .bdrv_has_zero_init           = ssh_has_zero_init, | ||||
|     .bdrv_co_readv                = ssh_co_readv, | ||||
|     .bdrv_co_writev               = ssh_co_writev, | ||||
|     .bdrv_getlength               = ssh_getlength, | ||||
|     .bdrv_truncate                = ssh_truncate, | ||||
|     .bdrv_co_flush_to_disk        = ssh_co_flush, | ||||
|     .create_opts                  = &ssh_create_opts, | ||||
| }; | ||||
|  | ||||
| @ -240,7 +240,7 @@ static BlockDriver bdrv_throttle = { | ||||
|     .bdrv_reopen_prepare                =   throttle_reopen_prepare, | ||||
|     .bdrv_reopen_commit                 =   throttle_reopen_commit, | ||||
|     .bdrv_reopen_abort                  =   throttle_reopen_abort, | ||||
|     .bdrv_co_get_block_status           =   bdrv_co_get_block_status_from_file, | ||||
|     .bdrv_co_block_status               =   bdrv_co_block_status_from_file, | ||||
| 
 | ||||
|     .bdrv_co_drain_begin                =   throttle_co_drain_begin, | ||||
|     .bdrv_co_drain_end                  =   throttle_co_drain_end, | ||||
|  | ||||
							
								
								
									
										50
									
								
								block/vdi.c
									
									
									
									
									
								
							
							
						
						
									
										50
									
								
								block/vdi.c
									
									
									
									
									
								
							| @ -87,12 +87,18 @@ | ||||
| #define DEFAULT_CLUSTER_SIZE (1 * MiB) | ||||
| 
 | ||||
| #if defined(CONFIG_VDI_DEBUG) | ||||
| #define logout(fmt, ...) \ | ||||
|                 fprintf(stderr, "vdi\t%-24s" fmt, __func__, ##__VA_ARGS__) | ||||
| #define VDI_DEBUG 1 | ||||
| #else | ||||
| #define logout(fmt, ...) ((void)0) | ||||
| #define VDI_DEBUG 0 | ||||
| #endif | ||||
| 
 | ||||
| #define logout(fmt, ...) \ | ||||
|     do {                                                                \ | ||||
|         if (VDI_DEBUG) {                                                \ | ||||
|             fprintf(stderr, "vdi\t%-24s" fmt, __func__, ##__VA_ARGS__); \ | ||||
|         }                                                               \ | ||||
|     } while (0) | ||||
| 
 | ||||
| /* Image signature. */ | ||||
| #define VDI_SIGNATURE 0xbeda107f | ||||
| 
 | ||||
| @ -166,8 +172,6 @@ typedef struct { | ||||
|     uint32_t *bmap; | ||||
|     /* Size of block (bytes). */ | ||||
|     uint32_t block_size; | ||||
|     /* Size of block (sectors). */ | ||||
|     uint32_t block_sectors; | ||||
|     /* First sector of block map. */ | ||||
|     uint32_t bmap_sector; | ||||
|     /* VDI header (converted to host endianness). */ | ||||
| @ -457,7 +461,6 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|     bs->total_sectors = header.disk_size / SECTOR_SIZE; | ||||
| 
 | ||||
|     s->block_size = header.block_size; | ||||
|     s->block_sectors = header.block_size / SECTOR_SIZE; | ||||
|     s->bmap_sector = header.offset_bmap / SECTOR_SIZE; | ||||
|     s->header = header; | ||||
| 
 | ||||
| @ -503,33 +506,29 @@ static int vdi_reopen_prepare(BDRVReopenState *state, | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| static int64_t coroutine_fn vdi_co_get_block_status(BlockDriverState *bs, | ||||
|         int64_t sector_num, int nb_sectors, int *pnum, BlockDriverState **file) | ||||
| static int coroutine_fn vdi_co_block_status(BlockDriverState *bs, | ||||
|                                             bool want_zero, | ||||
|                                             int64_t offset, int64_t bytes, | ||||
|                                             int64_t *pnum, int64_t *map, | ||||
|                                             BlockDriverState **file) | ||||
| { | ||||
|     /* TODO: Check for too large sector_num (in bdrv_is_allocated or here). */ | ||||
|     BDRVVdiState *s = (BDRVVdiState *)bs->opaque; | ||||
|     size_t bmap_index = sector_num / s->block_sectors; | ||||
|     size_t sector_in_block = sector_num % s->block_sectors; | ||||
|     int n_sectors = s->block_sectors - sector_in_block; | ||||
|     size_t bmap_index = offset / s->block_size; | ||||
|     size_t index_in_block = offset % s->block_size; | ||||
|     uint32_t bmap_entry = le32_to_cpu(s->bmap[bmap_index]); | ||||
|     uint64_t offset; | ||||
|     int result; | ||||
| 
 | ||||
|     logout("%p, %" PRId64 ", %d, %p\n", bs, sector_num, nb_sectors, pnum); | ||||
|     if (n_sectors > nb_sectors) { | ||||
|         n_sectors = nb_sectors; | ||||
|     } | ||||
|     *pnum = n_sectors; | ||||
|     logout("%p, %" PRId64 ", %" PRId64 ", %p\n", bs, offset, bytes, pnum); | ||||
|     *pnum = MIN(s->block_size - index_in_block, bytes); | ||||
|     result = VDI_IS_ALLOCATED(bmap_entry); | ||||
|     if (!result) { | ||||
|         return 0; | ||||
|     } | ||||
| 
 | ||||
|     offset = s->header.offset_data + | ||||
|                               (uint64_t)bmap_entry * s->block_size + | ||||
|                               sector_in_block * SECTOR_SIZE; | ||||
|     *map = s->header.offset_data + (uint64_t)bmap_entry * s->block_size + | ||||
|         index_in_block; | ||||
|     *file = bs->file->bs; | ||||
|     return BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | offset; | ||||
|     return BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID; | ||||
| } | ||||
| 
 | ||||
| static int coroutine_fn | ||||
| @ -717,7 +716,8 @@ nonallocating_write: | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| static int vdi_create(const char *filename, QemuOpts *opts, Error **errp) | ||||
| static int coroutine_fn vdi_co_create_opts(const char *filename, QemuOpts *opts, | ||||
|                                            Error **errp) | ||||
| { | ||||
|     int ret = 0; | ||||
|     uint64_t bytes = 0; | ||||
| @ -895,9 +895,9 @@ static BlockDriver bdrv_vdi = { | ||||
|     .bdrv_close = vdi_close, | ||||
|     .bdrv_reopen_prepare = vdi_reopen_prepare, | ||||
|     .bdrv_child_perm          = bdrv_format_default_perms, | ||||
|     .bdrv_create = vdi_create, | ||||
|     .bdrv_co_create_opts = vdi_co_create_opts, | ||||
|     .bdrv_has_zero_init = bdrv_has_zero_init_1, | ||||
|     .bdrv_co_get_block_status = vdi_co_get_block_status, | ||||
|     .bdrv_co_block_status = vdi_co_block_status, | ||||
|     .bdrv_make_empty = vdi_make_empty, | ||||
| 
 | ||||
|     .bdrv_co_preadv     = vdi_co_preadv, | ||||
|  | ||||
| @ -1792,7 +1792,8 @@ exit: | ||||
|  *    .---- ~ ----------- ~ ------------ ~ ---------------- ~ -----------. | ||||
|  *   1MB | ||||
|  */ | ||||
| static int vhdx_create(const char *filename, QemuOpts *opts, Error **errp) | ||||
| static int coroutine_fn vhdx_co_create_opts(const char *filename, QemuOpts *opts, | ||||
|                                             Error **errp) | ||||
| { | ||||
|     int ret = 0; | ||||
|     uint64_t image_size = (uint64_t) 2 * GiB; | ||||
| @ -2003,7 +2004,7 @@ static BlockDriver bdrv_vhdx = { | ||||
|     .bdrv_child_perm        = bdrv_format_default_perms, | ||||
|     .bdrv_co_readv          = vhdx_co_readv, | ||||
|     .bdrv_co_writev         = vhdx_co_writev, | ||||
|     .bdrv_create            = vhdx_create, | ||||
|     .bdrv_co_create_opts    = vhdx_co_create_opts, | ||||
|     .bdrv_get_info          = vhdx_get_info, | ||||
|     .bdrv_check             = vhdx_check, | ||||
|     .bdrv_has_zero_init     = bdrv_has_zero_init_1, | ||||
|  | ||||
							
								
								
									
										43
									
								
								block/vmdk.c
									
									
									
									
									
								
							
							
						
						
									
										43
									
								
								block/vmdk.c
									
									
									
									
									
								
							| @ -1304,33 +1304,27 @@ static inline uint64_t vmdk_find_offset_in_cluster(VmdkExtent *extent, | ||||
|     return extent_relative_offset % cluster_size; | ||||
| } | ||||
| 
 | ||||
| static inline uint64_t vmdk_find_index_in_cluster(VmdkExtent *extent, | ||||
|                                                   int64_t sector_num) | ||||
| { | ||||
|     uint64_t offset; | ||||
|     offset = vmdk_find_offset_in_cluster(extent, sector_num * BDRV_SECTOR_SIZE); | ||||
|     return offset / BDRV_SECTOR_SIZE; | ||||
| } | ||||
| 
 | ||||
| static int64_t coroutine_fn vmdk_co_get_block_status(BlockDriverState *bs, | ||||
|         int64_t sector_num, int nb_sectors, int *pnum, BlockDriverState **file) | ||||
| static int coroutine_fn vmdk_co_block_status(BlockDriverState *bs, | ||||
|                                              bool want_zero, | ||||
|                                              int64_t offset, int64_t bytes, | ||||
|                                              int64_t *pnum, int64_t *map, | ||||
|                                              BlockDriverState **file) | ||||
| { | ||||
|     BDRVVmdkState *s = bs->opaque; | ||||
|     int64_t index_in_cluster, n, ret; | ||||
|     uint64_t offset; | ||||
|     uint64_t cluster_offset; | ||||
|     VmdkExtent *extent; | ||||
| 
 | ||||
|     extent = find_extent(s, sector_num, NULL); | ||||
|     extent = find_extent(s, offset >> BDRV_SECTOR_BITS, NULL); | ||||
|     if (!extent) { | ||||
|         return 0; | ||||
|         return -EIO; | ||||
|     } | ||||
|     qemu_co_mutex_lock(&s->lock); | ||||
|     ret = get_cluster_offset(bs, extent, NULL, | ||||
|                              sector_num * 512, false, &offset, | ||||
|     ret = get_cluster_offset(bs, extent, NULL, offset, false, &cluster_offset, | ||||
|                              0, 0); | ||||
|     qemu_co_mutex_unlock(&s->lock); | ||||
| 
 | ||||
|     index_in_cluster = vmdk_find_index_in_cluster(extent, sector_num); | ||||
|     index_in_cluster = vmdk_find_offset_in_cluster(extent, offset); | ||||
|     switch (ret) { | ||||
|     case VMDK_ERROR: | ||||
|         ret = -EIO; | ||||
| @ -1345,18 +1339,14 @@ static int64_t coroutine_fn vmdk_co_get_block_status(BlockDriverState *bs, | ||||
|         ret = BDRV_BLOCK_DATA; | ||||
|         if (!extent->compressed) { | ||||
|             ret |= BDRV_BLOCK_OFFSET_VALID; | ||||
|             ret |= (offset + (index_in_cluster << BDRV_SECTOR_BITS)) | ||||
|                     & BDRV_BLOCK_OFFSET_MASK; | ||||
|             *map = cluster_offset + index_in_cluster; | ||||
|         } | ||||
|         *file = extent->file->bs; | ||||
|         break; | ||||
|     } | ||||
| 
 | ||||
|     n = extent->cluster_sectors - index_in_cluster; | ||||
|     if (n > nb_sectors) { | ||||
|         n = nb_sectors; | ||||
|     } | ||||
|     *pnum = n; | ||||
|     n = extent->cluster_sectors * BDRV_SECTOR_SIZE - index_in_cluster; | ||||
|     *pnum = MIN(n, bytes); | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| @ -1892,7 +1882,8 @@ static int filename_decompose(const char *filename, char *path, char *prefix, | ||||
|     return VMDK_OK; | ||||
| } | ||||
| 
 | ||||
| static int vmdk_create(const char *filename, QemuOpts *opts, Error **errp) | ||||
| static int coroutine_fn vmdk_co_create_opts(const char *filename, QemuOpts *opts, | ||||
|                                             Error **errp) | ||||
| { | ||||
|     int idx = 0; | ||||
|     BlockBackend *new_blk = NULL; | ||||
| @ -2408,9 +2399,9 @@ static BlockDriver bdrv_vmdk = { | ||||
|     .bdrv_co_pwritev_compressed   = vmdk_co_pwritev_compressed, | ||||
|     .bdrv_co_pwrite_zeroes        = vmdk_co_pwrite_zeroes, | ||||
|     .bdrv_close                   = vmdk_close, | ||||
|     .bdrv_create                  = vmdk_create, | ||||
|     .bdrv_co_create_opts          = vmdk_co_create_opts, | ||||
|     .bdrv_co_flush_to_disk        = vmdk_co_flush, | ||||
|     .bdrv_co_get_block_status     = vmdk_co_get_block_status, | ||||
|     .bdrv_co_block_status         = vmdk_co_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, | ||||
|  | ||||
							
								
								
									
										50
									
								
								block/vpc.c
									
									
									
									
									
								
							
							
						
						
									
										50
									
								
								block/vpc.c
									
									
									
									
									
								
							| @ -706,53 +706,54 @@ fail: | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| static int64_t coroutine_fn vpc_co_get_block_status(BlockDriverState *bs, | ||||
|         int64_t sector_num, int nb_sectors, int *pnum, BlockDriverState **file) | ||||
| static int coroutine_fn vpc_co_block_status(BlockDriverState *bs, | ||||
|                                             bool want_zero, | ||||
|                                             int64_t offset, int64_t bytes, | ||||
|                                             int64_t *pnum, int64_t *map, | ||||
|                                             BlockDriverState **file) | ||||
| { | ||||
|     BDRVVPCState *s = bs->opaque; | ||||
|     VHDFooter *footer = (VHDFooter*) s->footer_buf; | ||||
|     int64_t start, offset; | ||||
|     int64_t image_offset; | ||||
|     bool allocated; | ||||
|     int64_t ret; | ||||
|     int n; | ||||
|     int ret; | ||||
|     int64_t n; | ||||
| 
 | ||||
|     if (be32_to_cpu(footer->type) == VHD_FIXED) { | ||||
|         *pnum = nb_sectors; | ||||
|         *pnum = bytes; | ||||
|         *map = offset; | ||||
|         *file = bs->file->bs; | ||||
|         return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID | | ||||
|                (sector_num << BDRV_SECTOR_BITS); | ||||
|         return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID; | ||||
|     } | ||||
| 
 | ||||
|     qemu_co_mutex_lock(&s->lock); | ||||
| 
 | ||||
|     offset = get_image_offset(bs, sector_num << BDRV_SECTOR_BITS, false, NULL); | ||||
|     start = offset; | ||||
|     allocated = (offset != -1); | ||||
|     image_offset = get_image_offset(bs, offset, false, NULL); | ||||
|     allocated = (image_offset != -1); | ||||
|     *pnum = 0; | ||||
|     ret = 0; | ||||
| 
 | ||||
|     do { | ||||
|         /* All sectors in a block are contiguous (without using the bitmap) */ | ||||
|         n = ROUND_UP(sector_num + 1, s->block_size / BDRV_SECTOR_SIZE) | ||||
|           - sector_num; | ||||
|         n = MIN(n, nb_sectors); | ||||
|         n = ROUND_UP(offset + 1, s->block_size) - offset; | ||||
|         n = MIN(n, bytes); | ||||
| 
 | ||||
|         *pnum += n; | ||||
|         sector_num += n; | ||||
|         nb_sectors -= n; | ||||
|         offset += n; | ||||
|         bytes -= n; | ||||
|         /* *pnum can't be greater than one block for allocated
 | ||||
|          * sectors since there is always a bitmap in between. */ | ||||
|         if (allocated) { | ||||
|             *file = bs->file->bs; | ||||
|             ret = BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | start; | ||||
|             *map = image_offset; | ||||
|             ret = BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID; | ||||
|             break; | ||||
|         } | ||||
|         if (nb_sectors == 0) { | ||||
|         if (bytes == 0) { | ||||
|             break; | ||||
|         } | ||||
|         offset = get_image_offset(bs, sector_num << BDRV_SECTOR_BITS, false, | ||||
|                                   NULL); | ||||
|     } while (offset == -1); | ||||
|         image_offset = get_image_offset(bs, offset, false, NULL); | ||||
|     } while (image_offset == -1); | ||||
| 
 | ||||
|     qemu_co_mutex_unlock(&s->lock); | ||||
|     return ret; | ||||
| @ -896,7 +897,8 @@ static int create_fixed_disk(BlockBackend *blk, uint8_t *buf, | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| static int vpc_create(const char *filename, QemuOpts *opts, Error **errp) | ||||
| static int coroutine_fn vpc_co_create_opts(const char *filename, QemuOpts *opts, | ||||
|                                            Error **errp) | ||||
| { | ||||
|     uint8_t buf[1024]; | ||||
|     VHDFooter *footer = (VHDFooter *) buf; | ||||
| @ -1094,11 +1096,11 @@ static BlockDriver bdrv_vpc = { | ||||
|     .bdrv_close             = vpc_close, | ||||
|     .bdrv_reopen_prepare    = vpc_reopen_prepare, | ||||
|     .bdrv_child_perm        = bdrv_format_default_perms, | ||||
|     .bdrv_create            = vpc_create, | ||||
|     .bdrv_co_create_opts    = vpc_co_create_opts, | ||||
| 
 | ||||
|     .bdrv_co_preadv             = vpc_co_preadv, | ||||
|     .bdrv_co_pwritev            = vpc_co_pwritev, | ||||
|     .bdrv_co_get_block_status   = vpc_co_get_block_status, | ||||
|     .bdrv_co_block_status       = vpc_co_block_status, | ||||
| 
 | ||||
|     .bdrv_get_info          = vpc_get_info, | ||||
| 
 | ||||
|  | ||||
| @ -3088,15 +3088,13 @@ vvfat_co_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes, | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| static int64_t coroutine_fn vvfat_co_get_block_status(BlockDriverState *bs, | ||||
|         int64_t sector_num, int nb_sectors, int *n, BlockDriverState **file) | ||||
| static int coroutine_fn vvfat_co_block_status(BlockDriverState *bs, | ||||
|                                               bool want_zero, int64_t offset, | ||||
|                                               int64_t bytes, int64_t *n, | ||||
|                                               int64_t *map, | ||||
|                                               BlockDriverState **file) | ||||
| { | ||||
|     *n = bs->total_sectors - sector_num; | ||||
|     if (*n > nb_sectors) { | ||||
|         *n = nb_sectors; | ||||
|     } else if (*n < 0) { | ||||
|         return 0; | ||||
|     } | ||||
|     *n = bytes; | ||||
|     return BDRV_BLOCK_DATA; | ||||
| } | ||||
| 
 | ||||
| @ -3257,7 +3255,7 @@ static BlockDriver bdrv_vvfat = { | ||||
| 
 | ||||
|     .bdrv_co_preadv         = vvfat_co_preadv, | ||||
|     .bdrv_co_pwritev        = vvfat_co_pwritev, | ||||
|     .bdrv_co_get_block_status = vvfat_co_get_block_status, | ||||
|     .bdrv_co_block_status   = vvfat_co_block_status, | ||||
| }; | ||||
| 
 | ||||
| static void bdrv_vvfat_init(void) | ||||
|  | ||||
| @ -426,10 +426,20 @@ Standard Cluster Descriptor: | ||||
| 
 | ||||
| Compressed Clusters Descriptor (x = 62 - (cluster_bits - 8)): | ||||
| 
 | ||||
|     Bit  0 -  x:    Host cluster offset. This is usually _not_ aligned to a | ||||
|                     cluster boundary! | ||||
|     Bit  0 - x-1:   Host cluster offset. This is usually _not_ aligned to a | ||||
|                     cluster or sector boundary! | ||||
| 
 | ||||
|        x+1 - 61:    Compressed size of the images in sectors of 512 bytes | ||||
|          x - 61:    Number of additional 512-byte sectors used for the | ||||
|                     compressed data, beyond the sector containing the offset | ||||
|                     in the previous field. Some of these sectors may reside | ||||
|                     in the next contiguous host cluster. | ||||
| 
 | ||||
|                     Note that the compressed data does not necessarily occupy | ||||
|                     all of the bytes in the final sector; rather, decompression | ||||
|                     stops when it has produced a cluster of data. | ||||
| 
 | ||||
|                     Another compressed cluster may map to the tail of the final | ||||
|                     sector used by this compressed cluster. | ||||
| 
 | ||||
| If a cluster is unallocated, read requests shall read the data from the backing | ||||
| file (except if bit 0 in the Standard Cluster Descriptor is set). If there is | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| qcow2 L2/refcount cache configuration | ||||
| ===================================== | ||||
| Copyright (C) 2015 Igalia, S.L. | ||||
| Copyright (C) 2015, 2018 Igalia, S.L. | ||||
| Author: Alberto Garcia <berto@igalia.com> | ||||
| 
 | ||||
| This work is licensed under the terms of the GNU GPL, version 2 or | ||||
| @ -118,8 +118,8 @@ There are three options available, and all of them take bytes: | ||||
| 
 | ||||
| There are two things that need to be taken into account: | ||||
| 
 | ||||
|  - Both caches must have a size that is a multiple of the cluster | ||||
|    size. | ||||
|  - Both caches must have a size that is a multiple of the cluster size | ||||
|    (or the cache entry size: see "Using smaller cache sizes" below). | ||||
| 
 | ||||
|  - If you only set one of the options above, QEMU will automatically | ||||
|    adjust the others so that the L2 cache is 4 times bigger than the | ||||
| @ -143,6 +143,46 @@ much less often than the L2 cache, so it's perfectly reasonable to | ||||
| keep it small. | ||||
| 
 | ||||
| 
 | ||||
| Using smaller cache entries | ||||
| --------------------------- | ||||
| The qcow2 L2 cache stores complete tables by default. This means that | ||||
| if QEMU needs an entry from an L2 table then the whole table is read | ||||
| from disk and is kept in the cache. If the cache is full then a | ||||
| complete table needs to be evicted first. | ||||
| 
 | ||||
| This can be inefficient with large cluster sizes since it results in | ||||
| more disk I/O and wastes more cache memory. | ||||
| 
 | ||||
| Since QEMU 2.12 you can change the size of the L2 cache entry and make | ||||
| it smaller than the cluster size. This can be configured using the | ||||
| "l2-cache-entry-size" parameter: | ||||
| 
 | ||||
|    -drive file=hd.qcow2,l2-cache-size=2097152,l2-cache-entry-size=4096 | ||||
| 
 | ||||
| Some things to take into account: | ||||
| 
 | ||||
|  - The L2 cache entry size has the same restrictions as the cluster | ||||
|    size (power of two, at least 512 bytes). | ||||
| 
 | ||||
|  - Smaller entry sizes generally improve the cache efficiency and make | ||||
|    disk I/O faster. This is particularly true with solid state drives | ||||
|    so it's a good idea to reduce the entry size in those cases. With | ||||
|    rotating hard drives the situation is a bit more complicated so you | ||||
|    should test it first and stay with the default size if unsure. | ||||
| 
 | ||||
|  - Try different entry sizes to see which one gives faster performance | ||||
|    in your case. The block size of the host filesystem is generally a | ||||
|    good default (usually 4096 bytes in the case of ext4). | ||||
| 
 | ||||
|  - Only the L2 cache can be configured this way. The refcount cache | ||||
|    always uses the cluster size as the entry size. | ||||
| 
 | ||||
|  - If the L2 cache is big enough to hold all of the image's L2 tables | ||||
|    (as explained in the "Choosing the right cache sizes" section | ||||
|    earlier in this document) then none of this is necessary and you | ||||
|    can omit the "l2-cache-entry-size" parameter altogether. | ||||
| 
 | ||||
| 
 | ||||
| Reducing the memory usage | ||||
| ------------------------- | ||||
| It is possible to clean unused cache entries in order to reduce the | ||||
|  | ||||
| @ -1087,15 +1087,7 @@ static void ide_flush_cache(IDEState *s) | ||||
|     s->status |= BUSY_STAT; | ||||
|     ide_set_retry(s); | ||||
|     block_acct_start(blk_get_stats(s->blk), &s->acct, 0, BLOCK_ACCT_FLUSH); | ||||
| 
 | ||||
|     if (blk_bs(s->blk)) { | ||||
|         s->pio_aiocb = blk_aio_flush(s->blk, ide_flush_cb, s); | ||||
|     } else { | ||||
|         /* XXX blk_aio_flush() crashes when blk_bs(blk) is NULL, remove this
 | ||||
|          * temporary workaround when blk_aio_*() functions handle NULL blk_bs. | ||||
|          */ | ||||
|         ide_flush_cb(s, 0); | ||||
|     } | ||||
|     s->pio_aiocb = blk_aio_flush(s->blk, ide_flush_cb, s); | ||||
| } | ||||
| 
 | ||||
| static void ide_cfata_metadata_inquiry(IDEState *s) | ||||
|  | ||||
							
								
								
									
										116
									
								
								include/block/aio-wait.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								include/block/aio-wait.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,116 @@ | ||||
| /*
 | ||||
|  * AioContext wait support | ||||
|  * | ||||
|  * Copyright (C) 2018 Red Hat, Inc. | ||||
|  * | ||||
|  * 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 QEMU_AIO_WAIT_H | ||||
| #define QEMU_AIO_WAIT_H | ||||
| 
 | ||||
| #include "block/aio.h" | ||||
| 
 | ||||
| /**
 | ||||
|  * AioWait: | ||||
|  * | ||||
|  * An object that facilitates synchronous waiting on a condition.  The main | ||||
|  * loop can wait on an operation running in an IOThread as follows: | ||||
|  * | ||||
|  *   AioWait *wait = ...; | ||||
|  *   AioContext *ctx = ...; | ||||
|  *   MyWork work = { .done = false }; | ||||
|  *   schedule_my_work_in_iothread(ctx, &work); | ||||
|  *   AIO_WAIT_WHILE(wait, ctx, !work.done); | ||||
|  * | ||||
|  * The IOThread must call aio_wait_kick() to notify the main loop when | ||||
|  * work.done changes: | ||||
|  * | ||||
|  *   static void do_work(...) | ||||
|  *   { | ||||
|  *       ... | ||||
|  *       work.done = true; | ||||
|  *       aio_wait_kick(wait); | ||||
|  *   } | ||||
|  */ | ||||
| typedef struct { | ||||
|     /* Is the main loop waiting for a kick?  Accessed with atomic ops. */ | ||||
|     bool need_kick; | ||||
| } AioWait; | ||||
| 
 | ||||
| /**
 | ||||
|  * AIO_WAIT_WHILE: | ||||
|  * @wait: the aio wait object | ||||
|  * @ctx: the aio context | ||||
|  * @cond: wait while this conditional expression is true | ||||
|  * | ||||
|  * Wait while a condition is true.  Use this to implement synchronous | ||||
|  * operations that require event loop activity. | ||||
|  * | ||||
|  * The caller must be sure that something calls aio_wait_kick() when the value | ||||
|  * of @cond might have changed. | ||||
|  * | ||||
|  * The caller's thread must be the IOThread that owns @ctx or the main loop | ||||
|  * thread (with @ctx acquired exactly once).  This function cannot be used to | ||||
|  * wait on conditions between two IOThreads since that could lead to deadlock, | ||||
|  * go via the main loop instead. | ||||
|  */ | ||||
| #define AIO_WAIT_WHILE(wait, ctx, cond) ({                  \ | ||||
|     bool waited_ = false;                                   \ | ||||
|     bool busy_ = true;                                      \ | ||||
|     AioWait *wait_ = (wait);                                \ | ||||
|     AioContext *ctx_ = (ctx);                               \ | ||||
|     if (in_aio_context_home_thread(ctx_)) {                 \ | ||||
|         while ((cond) || busy_) {                           \ | ||||
|             busy_ = aio_poll(ctx_, (cond));                 \ | ||||
|             waited_ |= !!(cond) | busy_;                    \ | ||||
|         }                                                   \ | ||||
|     } else {                                                \ | ||||
|         assert(qemu_get_current_aio_context() ==            \ | ||||
|                qemu_get_aio_context());                     \ | ||||
|         assert(!wait_->need_kick);                          \ | ||||
|         /* Set wait_->need_kick before evaluating cond.  */ \ | ||||
|         atomic_mb_set(&wait_->need_kick, true);             \ | ||||
|         while (busy_) {                                     \ | ||||
|             if ((cond)) {                                   \ | ||||
|                 waited_ = busy_ = true;                     \ | ||||
|                 aio_context_release(ctx_);                  \ | ||||
|                 aio_poll(qemu_get_aio_context(), true);     \ | ||||
|                 aio_context_acquire(ctx_);                  \ | ||||
|             } else {                                        \ | ||||
|                 busy_ = aio_poll(ctx_, false);              \ | ||||
|                 waited_ |= busy_;                           \ | ||||
|             }                                               \ | ||||
|         }                                                   \ | ||||
|         atomic_set(&wait_->need_kick, false);               \ | ||||
|     }                                                       \ | ||||
|     waited_; }) | ||||
| 
 | ||||
| /**
 | ||||
|  * aio_wait_kick: | ||||
|  * @wait: the aio wait object that should re-evaluate its condition | ||||
|  * | ||||
|  * Wake up the main thread if it is waiting on AIO_WAIT_WHILE().  During | ||||
|  * synchronous operations performed in an IOThread, the main thread lets the | ||||
|  * IOThread's event loop run, waiting for the operation to complete.  A | ||||
|  * aio_wait_kick() call will wake up the main thread. | ||||
|  */ | ||||
| void aio_wait_kick(AioWait *wait); | ||||
| 
 | ||||
| #endif /* QEMU_AIO_WAIT */ | ||||
| @ -534,11 +534,14 @@ void aio_co_enter(AioContext *ctx, struct Coroutine *co); | ||||
| AioContext *qemu_get_current_aio_context(void); | ||||
| 
 | ||||
| /**
 | ||||
|  * in_aio_context_home_thread: | ||||
|  * @ctx: the aio context | ||||
|  * | ||||
|  * Return whether we are running in the I/O thread that manages @ctx. | ||||
|  * Return whether we are running in the thread that normally runs @ctx.  Note | ||||
|  * that acquiring/releasing ctx does not affect the outcome, each AioContext | ||||
|  * still only has one home thread that is responsible for running it. | ||||
|  */ | ||||
| static inline bool aio_context_in_iothread(AioContext *ctx) | ||||
| static inline bool in_aio_context_home_thread(AioContext *ctx) | ||||
| { | ||||
|     return ctx == qemu_get_current_aio_context(); | ||||
| } | ||||
|  | ||||
| @ -3,6 +3,7 @@ | ||||
| 
 | ||||
| #include "block/aio.h" | ||||
| #include "qapi/qapi-types-block-core.h" | ||||
| #include "block/aio-wait.h" | ||||
| #include "qemu/iov.h" | ||||
| #include "qemu/coroutine.h" | ||||
| #include "block/accounting.h" | ||||
| @ -115,19 +116,19 @@ typedef struct HDGeometry { | ||||
|  * BDRV_BLOCK_ZERO: offset reads as zero | ||||
|  * BDRV_BLOCK_OFFSET_VALID: an associated offset exists for accessing raw data | ||||
|  * BDRV_BLOCK_ALLOCATED: the content of the block is determined by this | ||||
|  *                       layer (short for DATA || ZERO), set by block layer | ||||
|  * BDRV_BLOCK_EOF: the returned pnum covers through end of file for this layer | ||||
|  *                       layer rather than any backing, set by block layer | ||||
|  * BDRV_BLOCK_EOF: the returned pnum covers through end of file for this | ||||
|  *                 layer, set by block layer | ||||
|  * | ||||
|  * Internal flag: | ||||
|  * BDRV_BLOCK_RAW: for use by passthrough drivers, such as raw, to request | ||||
|  *                 that the block layer recompute the answer from the returned | ||||
|  *                 BDS; must be accompanied by just BDRV_BLOCK_OFFSET_VALID. | ||||
|  * | ||||
|  * If BDRV_BLOCK_OFFSET_VALID is set, bits 9-62 (BDRV_BLOCK_OFFSET_MASK) of | ||||
|  * the return value (old interface) or the entire map parameter (new | ||||
|  * interface) represent the offset in the returned BDS that is allocated for | ||||
|  * the corresponding raw data.  However, whether that offset actually | ||||
|  * contains data also depends on BDRV_BLOCK_DATA, as follows: | ||||
|  * If BDRV_BLOCK_OFFSET_VALID is set, the map parameter represents the | ||||
|  * host offset within the returned BDS that is allocated for the | ||||
|  * corresponding raw guest data.  However, whether that offset | ||||
|  * actually contains data also depends on BDRV_BLOCK_DATA, as follows: | ||||
|  * | ||||
|  * DATA ZERO OFFSET_VALID | ||||
|  *  t    t        t       sectors read as zero, returned file is zero at offset | ||||
| @ -367,41 +368,14 @@ void bdrv_drain_all_begin(void); | ||||
| void bdrv_drain_all_end(void); | ||||
| void bdrv_drain_all(void); | ||||
| 
 | ||||
| /* Returns NULL when bs == NULL */ | ||||
| AioWait *bdrv_get_aio_wait(BlockDriverState *bs); | ||||
| 
 | ||||
| #define BDRV_POLL_WHILE(bs, cond) ({                       \ | ||||
|     bool waited_ = false;                                  \ | ||||
|     bool busy_ = true;                                     \ | ||||
|     BlockDriverState *bs_ = (bs);                          \ | ||||
|     AioContext *ctx_ = bdrv_get_aio_context(bs_);          \ | ||||
|     if (aio_context_in_iothread(ctx_)) {                   \ | ||||
|         while ((cond) || busy_) {                          \ | ||||
|             busy_ = aio_poll(ctx_, (cond));                \ | ||||
|             waited_ |= !!(cond) | busy_;                   \ | ||||
|         }                                                  \ | ||||
|     } else {                                               \ | ||||
|         assert(qemu_get_current_aio_context() ==           \ | ||||
|                qemu_get_aio_context());                    \ | ||||
|         /* Ask bdrv_dec_in_flight to wake up the main      \
 | ||||
|          * QEMU AioContext.  Extra I/O threads never take  \ | ||||
|          * other I/O threads' AioContexts (see for example \ | ||||
|          * block_job_defer_to_main_loop for how to do it). \ | ||||
|          */                                                \ | ||||
|         assert(!bs_->wakeup);                              \ | ||||
|         /* Set bs->wakeup before evaluating cond.  */      \ | ||||
|         atomic_mb_set(&bs_->wakeup, true);                 \ | ||||
|         while (busy_) {                                    \ | ||||
|             if ((cond)) {                                  \ | ||||
|                 waited_ = busy_ = true;                    \ | ||||
|                 aio_context_release(ctx_);                 \ | ||||
|                 aio_poll(qemu_get_aio_context(), true);    \ | ||||
|                 aio_context_acquire(ctx_);                 \ | ||||
|             } else {                                       \ | ||||
|                 busy_ = aio_poll(ctx_, false);             \ | ||||
|                 waited_ |= busy_;                          \ | ||||
|             }                                              \ | ||||
|         }                                                  \ | ||||
|         atomic_set(&bs_->wakeup, false);                   \ | ||||
|     }                                                      \ | ||||
|     waited_; }) | ||||
|     AIO_WAIT_WHILE(bdrv_get_aio_wait(bs_),                 \ | ||||
|                    bdrv_get_aio_context(bs_),              \ | ||||
|                    cond); }) | ||||
| 
 | ||||
| int bdrv_pdiscard(BlockDriverState *bs, int64_t offset, int bytes); | ||||
| int bdrv_co_pdiscard(BlockDriverState *bs, int64_t offset, int bytes); | ||||
|  | ||||
| @ -26,6 +26,7 @@ | ||||
| 
 | ||||
| #include "block/accounting.h" | ||||
| #include "block/block.h" | ||||
| #include "block/aio-wait.h" | ||||
| #include "qemu/queue.h" | ||||
| #include "qemu/coroutine.h" | ||||
| #include "qemu/stats64.h" | ||||
| @ -128,7 +129,8 @@ struct BlockDriver { | ||||
|     int (*bdrv_file_open)(BlockDriverState *bs, QDict *options, int flags, | ||||
|                           Error **errp); | ||||
|     void (*bdrv_close)(BlockDriverState *bs); | ||||
|     int (*bdrv_create)(const char *filename, QemuOpts *opts, Error **errp); | ||||
|     int coroutine_fn (*bdrv_co_create_opts)(const char *filename, QemuOpts *opts, | ||||
|                                        Error **errp); | ||||
|     int (*bdrv_make_empty)(BlockDriverState *bs); | ||||
| 
 | ||||
|     void (*bdrv_refresh_filename)(BlockDriverState *bs, QDict *options); | ||||
| @ -202,15 +204,22 @@ struct BlockDriver { | ||||
|     /*
 | ||||
|      * Building block for bdrv_block_status[_above] and | ||||
|      * bdrv_is_allocated[_above].  The driver should answer only | ||||
|      * according to the current layer, and should not set | ||||
|      * BDRV_BLOCK_ALLOCATED, but may set BDRV_BLOCK_RAW.  See block.h | ||||
|      * for the meaning of _DATA, _ZERO, and _OFFSET_VALID.  The block | ||||
|      * layer guarantees input aligned to request_alignment, as well as | ||||
|      * non-NULL pnum and file. | ||||
|      * according to the current layer, and should only need to set | ||||
|      * BDRV_BLOCK_DATA, BDRV_BLOCK_ZERO, BDRV_BLOCK_OFFSET_VALID, | ||||
|      * and/or BDRV_BLOCK_RAW; if the current layer defers to a backing | ||||
|      * layer, the result should be 0 (and not BDRV_BLOCK_ZERO).  See | ||||
|      * block.h for the overall meaning of the bits.  As a hint, the | ||||
|      * flag want_zero is true if the caller cares more about precise | ||||
|      * mappings (favor accurate _OFFSET_VALID/_ZERO) or false for | ||||
|      * overall allocation (favor larger *pnum, perhaps by reporting | ||||
|      * _DATA instead of _ZERO).  The block layer guarantees input | ||||
|      * clamped to bdrv_getlength() and aligned to request_alignment, | ||||
|      * as well as non-NULL pnum, map, and file; in turn, the driver | ||||
|      * must return an error or set pnum to an aligned non-zero value. | ||||
|      */ | ||||
|     int64_t coroutine_fn (*bdrv_co_get_block_status)(BlockDriverState *bs, | ||||
|         int64_t sector_num, int nb_sectors, int *pnum, | ||||
|         BlockDriverState **file); | ||||
|     int coroutine_fn (*bdrv_co_block_status)(BlockDriverState *bs, | ||||
|         bool want_zero, int64_t offset, int64_t bytes, int64_t *pnum, | ||||
|         int64_t *map, BlockDriverState **file); | ||||
| 
 | ||||
|     /*
 | ||||
|      * Invalidate any cached meta-data. | ||||
| @ -709,10 +718,8 @@ struct BlockDriverState { | ||||
|     unsigned int in_flight; | ||||
|     unsigned int serialising_in_flight; | ||||
| 
 | ||||
|     /* Internal to BDRV_POLL_WHILE and bdrv_wakeup.  Accessed with atomic
 | ||||
|      * ops. | ||||
|      */ | ||||
|     bool wakeup; | ||||
|     /* Kicked to signal main loop when a request completes. */ | ||||
|     AioWait wait; | ||||
| 
 | ||||
|     /* counter for nested bdrv_io_plug.
 | ||||
|      * Accessed with atomic ops. | ||||
| @ -1031,23 +1038,27 @@ void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c, | ||||
|                                uint64_t *nperm, uint64_t *nshared); | ||||
| 
 | ||||
| /*
 | ||||
|  * Default implementation for drivers to pass bdrv_co_get_block_status() to | ||||
|  * Default implementation for drivers to pass bdrv_co_block_status() to | ||||
|  * their file. | ||||
|  */ | ||||
| int64_t coroutine_fn bdrv_co_get_block_status_from_file(BlockDriverState *bs, | ||||
|                                                         int64_t sector_num, | ||||
|                                                         int nb_sectors, | ||||
|                                                         int *pnum, | ||||
|                                                         BlockDriverState **file); | ||||
| int coroutine_fn bdrv_co_block_status_from_file(BlockDriverState *bs, | ||||
|                                                 bool want_zero, | ||||
|                                                 int64_t offset, | ||||
|                                                 int64_t bytes, | ||||
|                                                 int64_t *pnum, | ||||
|                                                 int64_t *map, | ||||
|                                                 BlockDriverState **file); | ||||
| /*
 | ||||
|  * Default implementation for drivers to pass bdrv_co_get_block_status() to | ||||
|  * Default implementation for drivers to pass bdrv_co_block_status() to | ||||
|  * their backing file. | ||||
|  */ | ||||
| int64_t coroutine_fn bdrv_co_get_block_status_from_backing(BlockDriverState *bs, | ||||
|                                                            int64_t sector_num, | ||||
|                                                            int nb_sectors, | ||||
|                                                            int *pnum, | ||||
|                                                            BlockDriverState **file); | ||||
| int coroutine_fn bdrv_co_block_status_from_backing(BlockDriverState *bs, | ||||
|                                                    bool want_zero, | ||||
|                                                    int64_t offset, | ||||
|                                                    int64_t bytes, | ||||
|                                                    int64_t *pnum, | ||||
|                                                    int64_t *map, | ||||
|                                                    BlockDriverState **file); | ||||
| const char *bdrv_get_parent_name(const BlockDriverState *bs); | ||||
| void blk_dev_change_media_cb(BlockBackend *blk, bool load, Error **errp); | ||||
| bool blk_dev_has_removable_media(BlockBackend *blk); | ||||
|  | ||||
| @ -3676,7 +3676,8 @@ | ||||
| # | ||||
| # @node-name: node name. Note that errors may be reported for the root node | ||||
| #             that is directly attached to a guest device rather than for the | ||||
| #             node where the error occurred. (Since: 2.8) | ||||
| #             node where the error occurred. The node name is not present if | ||||
| #             the drive is empty. (Since: 2.8) | ||||
| # | ||||
| # @operation: I/O operation | ||||
| # | ||||
| @ -3707,7 +3708,8 @@ | ||||
| # | ||||
| ## | ||||
| { 'event': 'BLOCK_IO_ERROR', | ||||
|   'data': { 'device': 'str', 'node-name': 'str', 'operation': 'IoOperationType', | ||||
|   'data': { 'device': 'str', '*node-name': 'str', | ||||
|             'operation': 'IoOperationType', | ||||
|             'action': 'BlockErrorAction', '*nospace': 'bool', | ||||
|             'reason': 'str' } } | ||||
| 
 | ||||
|  | ||||
| @ -3469,7 +3469,7 @@ static int img_resize(int argc, char **argv) | ||||
|         } | ||||
|     } | ||||
|     if (optind != argc - 1) { | ||||
|         error_exit("Expecting one image file name"); | ||||
|         error_exit("Expecting image file name and size"); | ||||
|     } | ||||
|     filename = argv[optind++]; | ||||
| 
 | ||||
|  | ||||
| @ -92,6 +92,7 @@ gcov-files-test-hbitmap-y = blockjob.c | ||||
| check-unit-y += tests/test-bdrv-drain$(EXESUF) | ||||
| check-unit-y += tests/test-blockjob$(EXESUF) | ||||
| check-unit-y += tests/test-blockjob-txn$(EXESUF) | ||||
| check-unit-y += tests/test-block-backend$(EXESUF) | ||||
| check-unit-y += tests/test-x86-cpuid$(EXESUF) | ||||
| # all code tested by test-x86-cpuid is inside topology.h
 | ||||
| gcov-files-test-x86-cpuid-y = | ||||
| @ -622,6 +623,7 @@ tests/test-throttle$(EXESUF): tests/test-throttle.o $(test-block-obj-y) | ||||
| tests/test-bdrv-drain$(EXESUF): tests/test-bdrv-drain.o $(test-block-obj-y) $(test-util-obj-y) | ||||
| tests/test-blockjob$(EXESUF): tests/test-blockjob.o $(test-block-obj-y) $(test-util-obj-y) | ||||
| tests/test-blockjob-txn$(EXESUF): tests/test-blockjob-txn.o $(test-block-obj-y) $(test-util-obj-y) | ||||
| tests/test-block-backend$(EXESUF): tests/test-block-backend.o $(test-block-obj-y) $(test-util-obj-y) | ||||
| tests/test-thread-pool$(EXESUF): tests/test-thread-pool.o $(test-block-obj-y) | ||||
| tests/test-iov$(EXESUF): tests/test-iov.o $(test-util-obj-y) | ||||
| tests/test-hbitmap$(EXESUF): tests/test-hbitmap.o $(test-util-obj-y) $(test-crypto-obj-y) | ||||
|  | ||||
| @ -64,6 +64,9 @@ do_test() | ||||
| 	} | $QEMU_IO $IO_EXTRA_ARGS | ||||
| } | ||||
| 
 | ||||
| echo | ||||
| echo "=== Test aligned and misaligned write zeroes operations ===" | ||||
| 
 | ||||
| for write_zero_cmd in "write -z" "aio_write -z"; do | ||||
| for align in 512 4k; do | ||||
| 	echo | ||||
| @ -102,7 +105,33 @@ for align in 512 4k; do | ||||
| done | ||||
| done | ||||
| 
 | ||||
| 
 | ||||
| # Trigger truncate that would shrink qcow2 L1 table, which is done by | ||||
| #   clearing one entry (8 bytes) with bdrv_co_pwrite_zeroes() | ||||
| 
 | ||||
| echo | ||||
| echo "=== Test misaligned write zeroes via truncate ===" | ||||
| echo | ||||
| 
 | ||||
| # any size will do, but the smaller the size the smaller the required image | ||||
| CLUSTER_SIZE=$((4 * 1024)) | ||||
| L2_COVERAGE=$(($CLUSTER_SIZE * $CLUSTER_SIZE / 8)) | ||||
| _make_test_img $(($L2_COVERAGE * 2)) | ||||
| 
 | ||||
| do_test 512 "write -P 1 0 0x200" "$TEST_IMG" | _filter_qemu_io | ||||
| # next L2 table | ||||
| do_test 512 "write -P 1 $L2_COVERAGE 0x200" "$TEST_IMG" | _filter_qemu_io | ||||
| 
 | ||||
| # only interested in qcow2 here; also other formats might respond with | ||||
| #  "not supported" error message | ||||
| if [ $IMGFMT = "qcow2" ]; then | ||||
|     do_test 512 "truncate $L2_COVERAGE" "$TEST_IMG" | _filter_qemu_io | ||||
| fi | ||||
| 
 | ||||
| do_test 512 "read -P 1 0 0x200" "$TEST_IMG" | _filter_qemu_io | ||||
| 
 | ||||
| # success, all done | ||||
| echo | ||||
| echo "*** done" | ||||
| rm -f $seq.full | ||||
| status=0 | ||||
|  | ||||
| @ -1,6 +1,8 @@ | ||||
| QA output created by 033 | ||||
| Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 | ||||
| 
 | ||||
| === Test aligned and misaligned write zeroes operations === | ||||
| 
 | ||||
| == preparing image == | ||||
| wrote 1024/1024 bytes at offset 512 | ||||
| 1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||||
| @ -164,4 +166,15 @@ read 512/512 bytes at offset 512 | ||||
| read 3072/3072 bytes at offset 1024 | ||||
| 3 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||||
| 
 | ||||
| 
 | ||||
| === Test misaligned write zeroes via truncate === | ||||
| 
 | ||||
| Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4194304 | ||||
| wrote 512/512 bytes at offset 0 | ||||
| 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||||
| wrote 512/512 bytes at offset 2097152 | ||||
| 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||||
| read 512/512 bytes at offset 0 | ||||
| 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||||
| 
 | ||||
| *** done | ||||
|  | ||||
							
								
								
									
										82
									
								
								tests/test-block-backend.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								tests/test-block-backend.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,82 @@ | ||||
| /*
 | ||||
|  * BlockBackend tests | ||||
|  * | ||||
|  * Copyright (c) 2017 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 "qemu/osdep.h" | ||||
| #include "block/block.h" | ||||
| #include "sysemu/block-backend.h" | ||||
| #include "qapi/error.h" | ||||
| 
 | ||||
| static void test_drain_aio_error_flush_cb(void *opaque, int ret) | ||||
| { | ||||
|     bool *completed = opaque; | ||||
| 
 | ||||
|     g_assert(ret == -ENOMEDIUM); | ||||
|     *completed = true; | ||||
| } | ||||
| 
 | ||||
| static void test_drain_aio_error(void) | ||||
| { | ||||
|     BlockBackend *blk = blk_new(BLK_PERM_ALL, BLK_PERM_ALL); | ||||
|     BlockAIOCB *acb; | ||||
|     bool completed = false; | ||||
| 
 | ||||
|     acb = blk_aio_flush(blk, test_drain_aio_error_flush_cb, &completed); | ||||
|     g_assert(acb != NULL); | ||||
|     g_assert(completed == false); | ||||
| 
 | ||||
|     blk_drain(blk); | ||||
|     g_assert(completed == true); | ||||
| 
 | ||||
|     blk_unref(blk); | ||||
| } | ||||
| 
 | ||||
| static void test_drain_all_aio_error(void) | ||||
| { | ||||
|     BlockBackend *blk = blk_new(BLK_PERM_ALL, BLK_PERM_ALL); | ||||
|     BlockAIOCB *acb; | ||||
|     bool completed = false; | ||||
| 
 | ||||
|     acb = blk_aio_flush(blk, test_drain_aio_error_flush_cb, &completed); | ||||
|     g_assert(acb != NULL); | ||||
|     g_assert(completed == false); | ||||
| 
 | ||||
|     blk_drain_all(); | ||||
|     g_assert(completed == true); | ||||
| 
 | ||||
|     blk_unref(blk); | ||||
| } | ||||
| 
 | ||||
| int main(int argc, char **argv) | ||||
| { | ||||
|     bdrv_init(); | ||||
|     qemu_init_main_loop(&error_abort); | ||||
| 
 | ||||
|     g_test_init(&argc, &argv, NULL); | ||||
| 
 | ||||
|     g_test_add_func("/block-backend/drain_aio_error", test_drain_aio_error); | ||||
|     g_test_add_func("/block-backend/drain_all_aio_error", | ||||
|                     test_drain_all_aio_error); | ||||
| 
 | ||||
|     return g_test_run(); | ||||
| } | ||||
| @ -1,7 +1,7 @@ | ||||
| util-obj-y = osdep.o cutils.o unicode.o qemu-timer-common.o | ||||
| util-obj-y += bufferiszero.o | ||||
| util-obj-y += lockcnt.o | ||||
| util-obj-y += aiocb.o async.o thread-pool.o qemu-timer.o | ||||
| util-obj-y += aiocb.o async.o aio-wait.o thread-pool.o qemu-timer.o | ||||
| util-obj-y += main-loop.o iohandler.o | ||||
| util-obj-$(CONFIG_POSIX) += aio-posix.o | ||||
| util-obj-$(CONFIG_POSIX) += compatfd.o | ||||
|  | ||||
							
								
								
									
										40
									
								
								util/aio-wait.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								util/aio-wait.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,40 @@ | ||||
| /*
 | ||||
|  * AioContext wait support | ||||
|  * | ||||
|  * Copyright (C) 2018 Red Hat, Inc. | ||||
|  * | ||||
|  * 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 "qemu/osdep.h" | ||||
| #include "qemu/main-loop.h" | ||||
| #include "block/aio-wait.h" | ||||
| 
 | ||||
| static void dummy_bh_cb(void *opaque) | ||||
| { | ||||
|     /* The point is to make AIO_WAIT_WHILE()'s aio_poll() return */ | ||||
| } | ||||
| 
 | ||||
| void aio_wait_kick(AioWait *wait) | ||||
| { | ||||
|     /* The barrier (or an atomic op) is in the caller.  */ | ||||
|     if (atomic_read(&wait->need_kick)) { | ||||
|         aio_bh_schedule_oneshot(qemu_get_aio_context(), dummy_bh_cb, NULL); | ||||
|     } | ||||
| } | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Peter Maydell
						Peter Maydell