Block patches
-----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iQEcBAABCAAGBQJWsOYgAAoJEDuxQgLoOKyt/DUH/1hIzxS+qyh3iko0B5O0aWny PJObgy4004T9yClYDOPRvoNfoFAw63iOkfnMArnzTETEaPR94DJXYM0uTLY8Pyht wNcAZm44FlYKhEOOXqFslE6Z1arOR2s2wTYGn4s6BvkGbGlHCWr7N7n5trMpLAMJ /jAT+PNAgJrxlI/vVDeitFhQYABwXmBdPpf1kgn28IepBAI62de1rJubX27WoWuQ j1+VwhbKCWFwE4PqDHVVo2Wm+Gv5A53alB6Adpx6+5xXYLWBBq/nNg1SwDXfD0+q gk7Qiwso+7fFWMX6mx2qgvdXBI2R4NlLoQAzA6sjWF1v4hf76/wjNQLsg+1+qXo= =BEb2 -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/maxreitz/tags/pull-block-for-peter-2016-02-02' into staging Block patches # gpg: Signature made Tue 02 Feb 2016 17:23:44 GMT using RSA key ID E838ACAD # gpg: Good signature from "Max Reitz <mreitz@redhat.com>" * remotes/maxreitz/tags/pull-block-for-peter-2016-02-02: (50 commits) block: qemu-iotests - add test for snapshot, commit, snapshot bug block: set device_list.tqe_prev to NULL on BDS removal iotests: Add "qemu-img map" test for VMDK extents qemu-img: Make MapEntry a QAPI struct qemu-img: In "map", use the returned "file" from bdrv_get_block_status block: Use returned *file in bdrv_co_get_block_status vmdk: Return extent's file in bdrv_get_block_status vmdk: Fix calculation of block status's offset vpc: Assign bs->file->bs to file in vpc_co_get_block_status vdi: Assign bs->file->bs to file in vdi_co_get_block_status sheepdog: Assign bs to file in sd_co_get_block_status qed: Assign bs->file->bs to file in bdrv_qed_co_get_block_status parallels: Assign bs->file->bs to file in parallels_co_get_block_status iscsi: Assign bs to file in iscsi_co_get_block_status raw: Assign bs to file in raw_co_get_block_status qcow2: Assign bs->file->bs to file in qcow2_co_get_block_status qcow: Assign bs->file->bs to file in qcow_co_get_block_status block: Add "file" output parameter to block status query functions block: acquire in bdrv_query_image_info iotests: Add test for block jobs and BDS ejection ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
		
						commit
						c65db7705b
					
				
							
								
								
									
										108
									
								
								block.c
									
									
									
									
									
								
							
							
						
						
									
										108
									
								
								block.c
									
									
									
									
									
								
							| @ -79,6 +79,9 @@ struct BdrvStates bdrv_states = QTAILQ_HEAD_INITIALIZER(bdrv_states); | ||||
| static QTAILQ_HEAD(, BlockDriverState) graph_bdrv_states = | ||||
|     QTAILQ_HEAD_INITIALIZER(graph_bdrv_states); | ||||
| 
 | ||||
| static QTAILQ_HEAD(, BlockDriverState) all_bdrv_states = | ||||
|     QTAILQ_HEAD_INITIALIZER(all_bdrv_states); | ||||
| 
 | ||||
| static QLIST_HEAD(, BlockDriver) bdrv_drivers = | ||||
|     QLIST_HEAD_INITIALIZER(bdrv_drivers); | ||||
| 
 | ||||
| @ -88,9 +91,13 @@ static int bdrv_open_inherit(BlockDriverState **pbs, const char *filename, | ||||
|                              const BdrvChildRole *child_role, Error **errp); | ||||
| 
 | ||||
| static void bdrv_dirty_bitmap_truncate(BlockDriverState *bs); | ||||
| static void bdrv_release_named_dirty_bitmaps(BlockDriverState *bs); | ||||
| 
 | ||||
| /* If non-zero, use only whitelisted block drivers */ | ||||
| static int use_bdrv_whitelist; | ||||
| 
 | ||||
| static void bdrv_close(BlockDriverState *bs); | ||||
| 
 | ||||
| #ifdef _WIN32 | ||||
| static int is_windows_drive_prefix(const char *filename) | ||||
| { | ||||
| @ -257,19 +264,15 @@ BlockDriverState *bdrv_new(void) | ||||
|     for (i = 0; i < BLOCK_OP_TYPE_MAX; i++) { | ||||
|         QLIST_INIT(&bs->op_blockers[i]); | ||||
|     } | ||||
|     notifier_list_init(&bs->close_notifiers); | ||||
|     notifier_with_return_list_init(&bs->before_write_notifiers); | ||||
|     qemu_co_queue_init(&bs->throttled_reqs[0]); | ||||
|     qemu_co_queue_init(&bs->throttled_reqs[1]); | ||||
|     bs->refcnt = 1; | ||||
|     bs->aio_context = qemu_get_aio_context(); | ||||
| 
 | ||||
|     return bs; | ||||
| } | ||||
|     QTAILQ_INSERT_TAIL(&all_bdrv_states, bs, bs_list); | ||||
| 
 | ||||
| void bdrv_add_close_notifier(BlockDriverState *bs, Notifier *notify) | ||||
| { | ||||
|     notifier_list_add(&bs->close_notifiers, notify); | ||||
|     return bs; | ||||
| } | ||||
| 
 | ||||
| BlockDriver *bdrv_find_format(const char *format_name) | ||||
| @ -2138,13 +2141,11 @@ void bdrv_reopen_abort(BDRVReopenState *reopen_state) | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void bdrv_close(BlockDriverState *bs) | ||||
| static void bdrv_close(BlockDriverState *bs) | ||||
| { | ||||
|     BdrvAioNotifier *ban, *ban_next; | ||||
| 
 | ||||
|     if (bs->job) { | ||||
|         block_job_cancel_sync(bs->job); | ||||
|     } | ||||
|     assert(!bs->job); | ||||
| 
 | ||||
|     /* Disable I/O limits and drain all pending throttled requests */ | ||||
|     if (bs->throttle_state) { | ||||
| @ -2155,7 +2156,8 @@ void bdrv_close(BlockDriverState *bs) | ||||
|     bdrv_flush(bs); | ||||
|     bdrv_drain(bs); /* in case flush left pending I/O */ | ||||
| 
 | ||||
|     notifier_list_notify(&bs->close_notifiers, bs); | ||||
|     bdrv_release_named_dirty_bitmaps(bs); | ||||
|     assert(QLIST_EMPTY(&bs->dirty_bitmaps)); | ||||
| 
 | ||||
|     if (bs->blk) { | ||||
|         blk_dev_change_media_cb(bs->blk, false); | ||||
| @ -2210,14 +2212,44 @@ void bdrv_close(BlockDriverState *bs) | ||||
| void bdrv_close_all(void) | ||||
| { | ||||
|     BlockDriverState *bs; | ||||
|     AioContext *aio_context; | ||||
| 
 | ||||
|     QTAILQ_FOREACH(bs, &bdrv_states, device_list) { | ||||
|         AioContext *aio_context = bdrv_get_aio_context(bs); | ||||
|     /* Drop references from requests still in flight, such as canceled block
 | ||||
|      * jobs whose AIO context has not been polled yet */ | ||||
|     bdrv_drain_all(); | ||||
| 
 | ||||
|     blk_remove_all_bs(); | ||||
|     blockdev_close_all_bdrv_states(); | ||||
| 
 | ||||
|     /* Cancel all block jobs */ | ||||
|     while (!QTAILQ_EMPTY(&all_bdrv_states)) { | ||||
|         QTAILQ_FOREACH(bs, &all_bdrv_states, bs_list) { | ||||
|             aio_context = bdrv_get_aio_context(bs); | ||||
| 
 | ||||
|             aio_context_acquire(aio_context); | ||||
|         bdrv_close(bs); | ||||
|             if (bs->job) { | ||||
|                 block_job_cancel_sync(bs->job); | ||||
|                 aio_context_release(aio_context); | ||||
|                 break; | ||||
|             } | ||||
|             aio_context_release(aio_context); | ||||
|         } | ||||
| 
 | ||||
|         /* All the remaining BlockDriverStates are referenced directly or
 | ||||
|          * indirectly from block jobs, so there needs to be at least one BDS | ||||
|          * directly used by a block job */ | ||||
|         assert(bs); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /* Note that bs->device_list.tqe_prev is initially null,
 | ||||
|  * and gets set to non-null by QTAILQ_INSERT_TAIL().  Establish | ||||
|  * the useful invariant "bs in bdrv_states iff bs->tqe_prev" by | ||||
|  * resetting it to null on remove.  */ | ||||
| void bdrv_device_remove(BlockDriverState *bs) | ||||
| { | ||||
|     QTAILQ_REMOVE(&bdrv_states, bs, device_list); | ||||
|     bs->device_list.tqe_prev = NULL; | ||||
| } | ||||
| 
 | ||||
| /* make a BlockDriverState anonymous by removing from bdrv_state and
 | ||||
| @ -2225,16 +2257,10 @@ void bdrv_close_all(void) | ||||
|    Also, NULL terminate the device_name to prevent double remove */ | ||||
| void bdrv_make_anon(BlockDriverState *bs) | ||||
| { | ||||
|     /*
 | ||||
|      * Take care to remove bs from bdrv_states only when it's actually | ||||
|      * in it.  Note that bs->device_list.tqe_prev is initially null, | ||||
|      * and gets set to non-null by QTAILQ_INSERT_TAIL().  Establish | ||||
|      * the useful invariant "bs in bdrv_states iff bs->tqe_prev" by | ||||
|      * resetting it to null on remove. | ||||
|      */ | ||||
|     /* Take care to remove bs from bdrv_states only when it's actually
 | ||||
|      * in it. */ | ||||
|     if (bs->device_list.tqe_prev) { | ||||
|         QTAILQ_REMOVE(&bdrv_states, bs, device_list); | ||||
|         bs->device_list.tqe_prev = NULL; | ||||
|         bdrv_device_remove(bs); | ||||
|     } | ||||
|     if (bs->node_name[0] != '\0') { | ||||
|         QTAILQ_REMOVE(&graph_bdrv_states, bs, node_list); | ||||
| @ -2275,7 +2301,7 @@ static void change_parent_backing_link(BlockDriverState *from, | ||||
|         if (!to->device_list.tqe_prev) { | ||||
|             QTAILQ_INSERT_BEFORE(from, to, device_list); | ||||
|         } | ||||
|         QTAILQ_REMOVE(&bdrv_states, from, device_list); | ||||
|         bdrv_device_remove(from); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @ -2366,13 +2392,14 @@ static void bdrv_delete(BlockDriverState *bs) | ||||
|     assert(!bs->job); | ||||
|     assert(bdrv_op_blocker_is_empty(bs)); | ||||
|     assert(!bs->refcnt); | ||||
|     assert(QLIST_EMPTY(&bs->dirty_bitmaps)); | ||||
| 
 | ||||
|     bdrv_close(bs); | ||||
| 
 | ||||
|     /* remove from list, if necessary */ | ||||
|     bdrv_make_anon(bs); | ||||
| 
 | ||||
|     QTAILQ_REMOVE(&all_bdrv_states, bs, bs_list); | ||||
| 
 | ||||
|     g_free(bs); | ||||
| } | ||||
| 
 | ||||
| @ -3582,19 +3609,38 @@ static void bdrv_dirty_bitmap_truncate(BlockDriverState *bs) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void bdrv_release_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap) | ||||
| static void bdrv_do_release_matching_dirty_bitmap(BlockDriverState *bs, | ||||
|                                                   BdrvDirtyBitmap *bitmap, | ||||
|                                                   bool only_named) | ||||
| { | ||||
|     BdrvDirtyBitmap *bm, *next; | ||||
|     QLIST_FOREACH_SAFE(bm, &bs->dirty_bitmaps, list, next) { | ||||
|         if (bm == bitmap) { | ||||
|         if ((!bitmap || bm == bitmap) && (!only_named || bm->name)) { | ||||
|             assert(!bdrv_dirty_bitmap_frozen(bm)); | ||||
|             QLIST_REMOVE(bitmap, list); | ||||
|             hbitmap_free(bitmap->bitmap); | ||||
|             g_free(bitmap->name); | ||||
|             g_free(bitmap); | ||||
|             QLIST_REMOVE(bm, list); | ||||
|             hbitmap_free(bm->bitmap); | ||||
|             g_free(bm->name); | ||||
|             g_free(bm); | ||||
| 
 | ||||
|             if (bitmap) { | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void bdrv_release_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap) | ||||
| { | ||||
|     bdrv_do_release_matching_dirty_bitmap(bs, bitmap, false); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Release all named dirty bitmaps attached to a BDS (for use in bdrv_close()). | ||||
|  * There must not be any frozen bitmaps attached. | ||||
|  */ | ||||
| static void bdrv_release_named_dirty_bitmaps(BlockDriverState *bs) | ||||
| { | ||||
|     bdrv_do_release_matching_dirty_bitmap(bs, NULL, true); | ||||
| } | ||||
| 
 | ||||
| void bdrv_disable_dirty_bitmap(BdrvDirtyBitmap *bitmap) | ||||
|  | ||||
| @ -49,6 +49,8 @@ struct BlockBackend { | ||||
|     BlockdevOnError on_read_error, on_write_error; | ||||
|     bool iostatus_enabled; | ||||
|     BlockDeviceIoStatus iostatus; | ||||
| 
 | ||||
|     NotifierList remove_bs_notifiers, insert_bs_notifiers; | ||||
| }; | ||||
| 
 | ||||
| typedef struct BlockBackendAIOCB { | ||||
| @ -99,6 +101,8 @@ BlockBackend *blk_new(const char *name, Error **errp) | ||||
|     blk = g_new0(BlockBackend, 1); | ||||
|     blk->name = g_strdup(name); | ||||
|     blk->refcnt = 1; | ||||
|     notifier_list_init(&blk->remove_bs_notifiers); | ||||
|     notifier_list_init(&blk->insert_bs_notifiers); | ||||
|     QTAILQ_INSERT_TAIL(&blk_backends, blk, link); | ||||
|     return blk; | ||||
| } | ||||
| @ -162,11 +166,10 @@ static void blk_delete(BlockBackend *blk) | ||||
|     assert(!blk->refcnt); | ||||
|     assert(!blk->dev); | ||||
|     if (blk->bs) { | ||||
|         assert(blk->bs->blk == blk); | ||||
|         blk->bs->blk = NULL; | ||||
|         bdrv_unref(blk->bs); | ||||
|         blk->bs = NULL; | ||||
|         blk_remove_bs(blk); | ||||
|     } | ||||
|     assert(QLIST_EMPTY(&blk->remove_bs_notifiers.notifiers)); | ||||
|     assert(QLIST_EMPTY(&blk->insert_bs_notifiers.notifiers)); | ||||
|     if (blk->root_state.throttle_state) { | ||||
|         g_free(blk->root_state.throttle_group); | ||||
|         throttle_group_unref(blk->root_state.throttle_state); | ||||
| @ -220,6 +223,21 @@ void blk_unref(BlockBackend *blk) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void blk_remove_all_bs(void) | ||||
| { | ||||
|     BlockBackend *blk; | ||||
| 
 | ||||
|     QTAILQ_FOREACH(blk, &blk_backends, link) { | ||||
|         AioContext *ctx = blk_get_aio_context(blk); | ||||
| 
 | ||||
|         aio_context_acquire(ctx); | ||||
|         if (blk->bs) { | ||||
|             blk_remove_bs(blk); | ||||
|         } | ||||
|         aio_context_release(ctx); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Return the BlockBackend after @blk. | ||||
|  * If @blk is null, return the first one. | ||||
| @ -345,6 +363,10 @@ void blk_hide_on_behalf_of_hmp_drive_del(BlockBackend *blk) | ||||
|  */ | ||||
| void blk_remove_bs(BlockBackend *blk) | ||||
| { | ||||
|     assert(blk->bs->blk == blk); | ||||
| 
 | ||||
|     notifier_list_notify(&blk->remove_bs_notifiers, blk); | ||||
| 
 | ||||
|     blk_update_root_state(blk); | ||||
| 
 | ||||
|     blk->bs->blk = NULL; | ||||
| @ -361,6 +383,8 @@ void blk_insert_bs(BlockBackend *blk, BlockDriverState *bs) | ||||
|     bdrv_ref(bs); | ||||
|     blk->bs = bs; | ||||
|     bs->blk = blk; | ||||
| 
 | ||||
|     notifier_list_notify(&blk->insert_bs_notifiers, blk); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
| @ -458,6 +482,14 @@ bool blk_dev_has_removable_media(BlockBackend *blk) | ||||
|     return !blk->dev || (blk->dev_ops && blk->dev_ops->change_media_cb); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Does @blk's attached device model have a tray? | ||||
|  */ | ||||
| bool blk_dev_has_tray(BlockBackend *blk) | ||||
| { | ||||
|     return blk->dev_ops && blk->dev_ops->is_tray_open; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Notify @blk's attached device model of a media eject request. | ||||
|  * If @force is true, the medium is about to be yanked out forcefully. | ||||
| @ -474,7 +506,7 @@ void blk_dev_eject_request(BlockBackend *blk, bool force) | ||||
|  */ | ||||
| bool blk_dev_is_tray_open(BlockBackend *blk) | ||||
| { | ||||
|     if (blk->dev_ops && blk->dev_ops->is_tray_open) { | ||||
|     if (blk_dev_has_tray(blk)) { | ||||
|         return blk->dev_ops->is_tray_open(blk->dev_opaque); | ||||
|     } | ||||
|     return false; | ||||
| @ -1118,11 +1150,14 @@ void blk_remove_aio_context_notifier(BlockBackend *blk, | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void blk_add_close_notifier(BlockBackend *blk, Notifier *notify) | ||||
| void blk_add_remove_bs_notifier(BlockBackend *blk, Notifier *notify) | ||||
| { | ||||
|     if (blk->bs) { | ||||
|         bdrv_add_close_notifier(blk->bs, notify); | ||||
|     } | ||||
|     notifier_list_add(&blk->remove_bs_notifiers, notify); | ||||
| } | ||||
| 
 | ||||
| void blk_add_insert_bs_notifier(BlockBackend *blk, Notifier *notify) | ||||
| { | ||||
|     notifier_list_add(&blk->insert_bs_notifiers, notify); | ||||
| } | ||||
| 
 | ||||
| void blk_io_plug(BlockBackend *blk) | ||||
|  | ||||
							
								
								
									
										44
									
								
								block/io.c
									
									
									
									
									
								
							
							
						
						
									
										44
									
								
								block/io.c
									
									
									
									
									
								
							| @ -664,6 +664,7 @@ int bdrv_write_zeroes(BlockDriverState *bs, int64_t sector_num, | ||||
| int bdrv_make_zero(BlockDriverState *bs, BdrvRequestFlags flags) | ||||
| { | ||||
|     int64_t target_sectors, ret, nb_sectors, sector_num = 0; | ||||
|     BlockDriverState *file; | ||||
|     int n; | ||||
| 
 | ||||
|     target_sectors = bdrv_nb_sectors(bs); | ||||
| @ -676,7 +677,7 @@ int bdrv_make_zero(BlockDriverState *bs, BdrvRequestFlags flags) | ||||
|         if (nb_sectors <= 0) { | ||||
|             return 0; | ||||
|         } | ||||
|         ret = bdrv_get_block_status(bs, sector_num, nb_sectors, &n); | ||||
|         ret = bdrv_get_block_status(bs, sector_num, nb_sectors, &n, &file); | ||||
|         if (ret < 0) { | ||||
|             error_report("error getting block status at sector %" PRId64 ": %s", | ||||
|                          sector_num, strerror(-ret)); | ||||
| @ -1466,6 +1467,7 @@ int bdrv_flush_all(void) | ||||
| typedef struct BdrvCoGetBlockStatusData { | ||||
|     BlockDriverState *bs; | ||||
|     BlockDriverState *base; | ||||
|     BlockDriverState **file; | ||||
|     int64_t sector_num; | ||||
|     int nb_sectors; | ||||
|     int *pnum; | ||||
| @ -1487,10 +1489,14 @@ typedef struct BdrvCoGetBlockStatusData { | ||||
|  * | ||||
|  * '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. | ||||
|  * | ||||
|  * If returned value is positive and BDRV_BLOCK_OFFSET_VALID bit is set, 'file' | ||||
|  * points to the BDS which the sector range is allocated in. | ||||
|  */ | ||||
| static int64_t coroutine_fn bdrv_co_get_block_status(BlockDriverState *bs, | ||||
|                                                      int64_t sector_num, | ||||
|                                                      int nb_sectors, int *pnum) | ||||
|                                                      int nb_sectors, int *pnum, | ||||
|                                                      BlockDriverState **file) | ||||
| { | ||||
|     int64_t total_sectors; | ||||
|     int64_t n; | ||||
| @ -1520,7 +1526,9 @@ static int64_t coroutine_fn bdrv_co_get_block_status(BlockDriverState *bs, | ||||
|         return ret; | ||||
|     } | ||||
| 
 | ||||
|     ret = bs->drv->bdrv_co_get_block_status(bs, sector_num, nb_sectors, pnum); | ||||
|     *file = NULL; | ||||
|     ret = bs->drv->bdrv_co_get_block_status(bs, sector_num, nb_sectors, pnum, | ||||
|                                             file); | ||||
|     if (ret < 0) { | ||||
|         *pnum = 0; | ||||
|         return ret; | ||||
| @ -1529,7 +1537,7 @@ static int64_t coroutine_fn bdrv_co_get_block_status(BlockDriverState *bs, | ||||
|     if (ret & BDRV_BLOCK_RAW) { | ||||
|         assert(ret & BDRV_BLOCK_OFFSET_VALID); | ||||
|         return bdrv_get_block_status(bs->file->bs, ret >> BDRV_SECTOR_BITS, | ||||
|                                      *pnum, pnum); | ||||
|                                      *pnum, pnum, file); | ||||
|     } | ||||
| 
 | ||||
|     if (ret & (BDRV_BLOCK_DATA | BDRV_BLOCK_ZERO)) { | ||||
| @ -1546,13 +1554,14 @@ static int64_t coroutine_fn bdrv_co_get_block_status(BlockDriverState *bs, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     if (bs->file && | ||||
|     if (*file && *file != bs && | ||||
|         (ret & BDRV_BLOCK_DATA) && !(ret & BDRV_BLOCK_ZERO) && | ||||
|         (ret & BDRV_BLOCK_OFFSET_VALID)) { | ||||
|         BlockDriverState *file2; | ||||
|         int file_pnum; | ||||
| 
 | ||||
|         ret2 = bdrv_co_get_block_status(bs->file->bs, ret >> BDRV_SECTOR_BITS, | ||||
|                                         *pnum, &file_pnum); | ||||
|         ret2 = bdrv_co_get_block_status(*file, ret >> BDRV_SECTOR_BITS, | ||||
|                                         *pnum, &file_pnum, &file2); | ||||
|         if (ret2 >= 0) { | ||||
|             /* Ignore errors.  This is just providing extra information, it
 | ||||
|              * is useful but not necessary. | ||||
| @ -1577,14 +1586,15 @@ static int64_t coroutine_fn bdrv_co_get_block_status_above(BlockDriverState *bs, | ||||
|         BlockDriverState *base, | ||||
|         int64_t sector_num, | ||||
|         int nb_sectors, | ||||
|         int *pnum) | ||||
|         int *pnum, | ||||
|         BlockDriverState **file) | ||||
| { | ||||
|     BlockDriverState *p; | ||||
|     int64_t ret = 0; | ||||
| 
 | ||||
|     assert(bs != base); | ||||
|     for (p = bs; p != base; p = backing_bs(p)) { | ||||
|         ret = bdrv_co_get_block_status(p, sector_num, nb_sectors, pnum); | ||||
|         ret = bdrv_co_get_block_status(p, sector_num, nb_sectors, pnum, file); | ||||
|         if (ret < 0 || ret & BDRV_BLOCK_ALLOCATED) { | ||||
|             break; | ||||
|         } | ||||
| @ -1603,7 +1613,8 @@ static void coroutine_fn bdrv_get_block_status_above_co_entry(void *opaque) | ||||
|     data->ret = bdrv_co_get_block_status_above(data->bs, data->base, | ||||
|                                                data->sector_num, | ||||
|                                                data->nb_sectors, | ||||
|                                                data->pnum); | ||||
|                                                data->pnum, | ||||
|                                                data->file); | ||||
|     data->done = true; | ||||
| } | ||||
| 
 | ||||
| @ -1615,12 +1626,14 @@ static void coroutine_fn bdrv_get_block_status_above_co_entry(void *opaque) | ||||
| int64_t bdrv_get_block_status_above(BlockDriverState *bs, | ||||
|                                     BlockDriverState *base, | ||||
|                                     int64_t sector_num, | ||||
|                                     int nb_sectors, int *pnum) | ||||
|                                     int nb_sectors, int *pnum, | ||||
|                                     BlockDriverState **file) | ||||
| { | ||||
|     Coroutine *co; | ||||
|     BdrvCoGetBlockStatusData data = { | ||||
|         .bs = bs, | ||||
|         .base = base, | ||||
|         .file = file, | ||||
|         .sector_num = sector_num, | ||||
|         .nb_sectors = nb_sectors, | ||||
|         .pnum = pnum, | ||||
| @ -1644,16 +1657,19 @@ int64_t bdrv_get_block_status_above(BlockDriverState *bs, | ||||
| 
 | ||||
| int64_t bdrv_get_block_status(BlockDriverState *bs, | ||||
|                               int64_t sector_num, | ||||
|                               int nb_sectors, int *pnum) | ||||
|                               int nb_sectors, int *pnum, | ||||
|                               BlockDriverState **file) | ||||
| { | ||||
|     return bdrv_get_block_status_above(bs, backing_bs(bs), | ||||
|                                        sector_num, nb_sectors, pnum); | ||||
|                                        sector_num, nb_sectors, pnum, file); | ||||
| } | ||||
| 
 | ||||
| int coroutine_fn bdrv_is_allocated(BlockDriverState *bs, int64_t sector_num, | ||||
|                                    int nb_sectors, int *pnum) | ||||
| { | ||||
|     int64_t ret = bdrv_get_block_status(bs, sector_num, nb_sectors, pnum); | ||||
|     BlockDriverState *file; | ||||
|     int64_t ret = bdrv_get_block_status(bs, sector_num, nb_sectors, pnum, | ||||
|                                         &file); | ||||
|     if (ret < 0) { | ||||
|         return ret; | ||||
|     } | ||||
|  | ||||
| @ -532,7 +532,8 @@ static bool iscsi_allocationmap_is_allocated(IscsiLun *iscsilun, | ||||
| 
 | ||||
| static int64_t coroutine_fn iscsi_co_get_block_status(BlockDriverState *bs, | ||||
|                                                   int64_t sector_num, | ||||
|                                                   int nb_sectors, int *pnum) | ||||
|                                                   int nb_sectors, int *pnum, | ||||
|                                                   BlockDriverState **file) | ||||
| { | ||||
|     IscsiLun *iscsilun = bs->opaque; | ||||
|     struct scsi_get_lba_status *lbas = NULL; | ||||
| @ -624,6 +625,9 @@ out: | ||||
|     if (iTask.task != NULL) { | ||||
|         scsi_free_scsi_task(iTask.task); | ||||
|     } | ||||
|     if (ret > 0 && ret & BDRV_BLOCK_OFFSET_VALID) { | ||||
|         *file = bs; | ||||
|     } | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| @ -650,7 +654,8 @@ static int coroutine_fn iscsi_co_readv(BlockDriverState *bs, | ||||
|         !iscsi_allocationmap_is_allocated(iscsilun, sector_num, nb_sectors)) { | ||||
|         int64_t ret; | ||||
|         int pnum; | ||||
|         ret = iscsi_co_get_block_status(bs, sector_num, INT_MAX, &pnum); | ||||
|         BlockDriverState *file; | ||||
|         ret = iscsi_co_get_block_status(bs, sector_num, INT_MAX, &pnum, &file); | ||||
|         if (ret < 0) { | ||||
|             return ret; | ||||
|         } | ||||
|  | ||||
| @ -168,6 +168,7 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s) | ||||
|     MirrorOp *op; | ||||
|     int pnum; | ||||
|     int64_t ret; | ||||
|     BlockDriverState *file; | ||||
| 
 | ||||
|     max_iov = MIN(source->bl.max_iov, s->target->bl.max_iov); | ||||
| 
 | ||||
| @ -306,7 +307,7 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s) | ||||
|     trace_mirror_one_iteration(s, sector_num, nb_sectors); | ||||
| 
 | ||||
|     ret = bdrv_get_block_status_above(source, NULL, sector_num, | ||||
|                                       nb_sectors, &pnum); | ||||
|                                       nb_sectors, &pnum, &file); | ||||
|     if (ret < 0 || pnum < nb_sectors || | ||||
|             (ret & BDRV_BLOCK_DATA && !(ret & BDRV_BLOCK_ZERO))) { | ||||
|         bdrv_aio_readv(source, sector_num, &op->qiov, nb_sectors, | ||||
|  | ||||
| @ -261,7 +261,7 @@ 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) | ||||
|         int64_t sector_num, int nb_sectors, int *pnum, BlockDriverState **file) | ||||
| { | ||||
|     BDRVParallelsState *s = bs->opaque; | ||||
|     int64_t offset; | ||||
| @ -274,6 +274,7 @@ static int64_t coroutine_fn parallels_co_get_block_status(BlockDriverState *bs, | ||||
|         return 0; | ||||
|     } | ||||
| 
 | ||||
|     *file = bs->file->bs; | ||||
|     return (offset << BDRV_SECTOR_BITS) | | ||||
|         BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID; | ||||
| } | ||||
|  | ||||
							
								
								
									
										11
									
								
								block/qapi.c
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								block/qapi.c
									
									
									
									
									
								
							| @ -211,11 +211,13 @@ void bdrv_query_image_info(BlockDriverState *bs, | ||||
|     Error *err = NULL; | ||||
|     ImageInfo *info; | ||||
| 
 | ||||
|     aio_context_acquire(bdrv_get_aio_context(bs)); | ||||
| 
 | ||||
|     size = bdrv_getlength(bs); | ||||
|     if (size < 0) { | ||||
|         error_setg_errno(errp, -size, "Can't get size of device '%s'", | ||||
|                          bdrv_get_device_name(bs)); | ||||
|         return; | ||||
|         goto out; | ||||
|     } | ||||
| 
 | ||||
|     info = g_new0(ImageInfo, 1); | ||||
| @ -283,10 +285,13 @@ void bdrv_query_image_info(BlockDriverState *bs, | ||||
|     default: | ||||
|         error_propagate(errp, err); | ||||
|         qapi_free_ImageInfo(info); | ||||
|         return; | ||||
|         goto out; | ||||
|     } | ||||
| 
 | ||||
|     *p_info = info; | ||||
| 
 | ||||
| out: | ||||
|     aio_context_release(bdrv_get_aio_context(bs)); | ||||
| } | ||||
| 
 | ||||
| /* @p_info will be set only on success. */ | ||||
| @ -300,7 +305,7 @@ static void bdrv_query_info(BlockBackend *blk, BlockInfo **p_info, | ||||
|     info->locked = blk_dev_is_medium_locked(blk); | ||||
|     info->removable = blk_dev_has_removable_media(blk); | ||||
| 
 | ||||
|     if (blk_dev_has_removable_media(blk)) { | ||||
|     if (blk_dev_has_tray(blk)) { | ||||
|         info->has_tray_open = true; | ||||
|         info->tray_open = blk_dev_is_tray_open(blk); | ||||
|     } | ||||
|  | ||||
| @ -489,7 +489,7 @@ static uint64_t get_cluster_offset(BlockDriverState *bs, | ||||
| } | ||||
| 
 | ||||
| static int64_t coroutine_fn qcow_co_get_block_status(BlockDriverState *bs, | ||||
|         int64_t sector_num, int nb_sectors, int *pnum) | ||||
|         int64_t sector_num, int nb_sectors, int *pnum, BlockDriverState **file) | ||||
| { | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     int index_in_cluster, n; | ||||
| @ -510,6 +510,7 @@ static int64_t coroutine_fn qcow_co_get_block_status(BlockDriverState *bs, | ||||
|         return BDRV_BLOCK_DATA; | ||||
|     } | ||||
|     cluster_offset |= (index_in_cluster << BDRV_SECTOR_BITS); | ||||
|     *file = bs->file->bs; | ||||
|     return BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | cluster_offset; | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -1330,7 +1330,7 @@ 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) | ||||
|         int64_t sector_num, int nb_sectors, int *pnum, BlockDriverState **file) | ||||
| { | ||||
|     BDRVQcow2State *s = bs->opaque; | ||||
|     uint64_t cluster_offset; | ||||
| @ -1349,6 +1349,7 @@ static int64_t coroutine_fn qcow2_co_get_block_status(BlockDriverState *bs, | ||||
|         !s->cipher) { | ||||
|         index_in_cluster = sector_num & (s->cluster_sectors - 1); | ||||
|         cluster_offset |= (index_in_cluster << BDRV_SECTOR_BITS); | ||||
|         *file = bs->file->bs; | ||||
|         status |= BDRV_BLOCK_OFFSET_VALID | cluster_offset; | ||||
|     } | ||||
|     if (ret == QCOW2_CLUSTER_ZERO) { | ||||
|  | ||||
| @ -693,6 +693,7 @@ typedef struct { | ||||
|     uint64_t pos; | ||||
|     int64_t status; | ||||
|     int *pnum; | ||||
|     BlockDriverState **file; | ||||
| } QEDIsAllocatedCB; | ||||
| 
 | ||||
| static void qed_is_allocated_cb(void *opaque, int ret, uint64_t offset, size_t len) | ||||
| @ -704,6 +705,7 @@ static void qed_is_allocated_cb(void *opaque, int ret, uint64_t offset, size_t l | ||||
|     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; | ||||
| @ -725,7 +727,8 @@ static void qed_is_allocated_cb(void *opaque, int ret, uint64_t offset, size_t l | ||||
| 
 | ||||
| static int64_t coroutine_fn bdrv_qed_co_get_block_status(BlockDriverState *bs, | ||||
|                                                  int64_t sector_num, | ||||
|                                                  int nb_sectors, int *pnum) | ||||
|                                                  int nb_sectors, int *pnum, | ||||
|                                                  BlockDriverState **file) | ||||
| { | ||||
|     BDRVQEDState *s = bs->opaque; | ||||
|     size_t len = (size_t)nb_sectors * BDRV_SECTOR_SIZE; | ||||
| @ -734,6 +737,7 @@ static int64_t coroutine_fn bdrv_qed_co_get_block_status(BlockDriverState *bs, | ||||
|         .pos = (uint64_t)sector_num * BDRV_SECTOR_SIZE, | ||||
|         .status = BDRV_BLOCK_OFFSET_MASK, | ||||
|         .pnum = pnum, | ||||
|         .file = file, | ||||
|     }; | ||||
|     QEDRequest request = { .l2_table = NULL }; | ||||
| 
 | ||||
|  | ||||
| @ -1818,7 +1818,8 @@ static int find_allocation(BlockDriverState *bs, off_t start, | ||||
|  */ | ||||
| static int64_t coroutine_fn raw_co_get_block_status(BlockDriverState *bs, | ||||
|                                                     int64_t sector_num, | ||||
|                                                     int nb_sectors, int *pnum) | ||||
|                                                     int nb_sectors, int *pnum, | ||||
|                                                     BlockDriverState **file) | ||||
| { | ||||
|     off_t start, data = 0, hole = 0; | ||||
|     int64_t total_size; | ||||
| @ -1860,6 +1861,7 @@ static int64_t coroutine_fn raw_co_get_block_status(BlockDriverState *bs, | ||||
|         *pnum = MIN(nb_sectors, (data - start) / BDRV_SECTOR_SIZE); | ||||
|         ret = BDRV_BLOCK_ZERO; | ||||
|     } | ||||
|     *file = bs; | ||||
|     return ret | BDRV_BLOCK_OFFSET_VALID | start; | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -115,9 +115,11 @@ fail: | ||||
| 
 | ||||
| static int64_t coroutine_fn raw_co_get_block_status(BlockDriverState *bs, | ||||
|                                             int64_t sector_num, | ||||
|                                             int nb_sectors, int *pnum) | ||||
|                                             int nb_sectors, int *pnum, | ||||
|                                             BlockDriverState **file) | ||||
| { | ||||
|     *pnum = nb_sectors; | ||||
|     *file = bs->file->bs; | ||||
|     return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID | BDRV_BLOCK_DATA | | ||||
|            (sector_num << BDRV_SECTOR_BITS); | ||||
| } | ||||
|  | ||||
| @ -2708,7 +2708,7 @@ retry: | ||||
| 
 | ||||
| static coroutine_fn int64_t | ||||
| sd_co_get_block_status(BlockDriverState *bs, int64_t sector_num, int nb_sectors, | ||||
|                        int *pnum) | ||||
|                        int *pnum, BlockDriverState **file) | ||||
| { | ||||
|     BDRVSheepdogState *s = bs->opaque; | ||||
|     SheepdogInode *inode = &s->inode; | ||||
| @ -2739,6 +2739,9 @@ sd_co_get_block_status(BlockDriverState *bs, int64_t sector_num, int nb_sectors, | ||||
|     if (*pnum > nb_sectors) { | ||||
|         *pnum = nb_sectors; | ||||
|     } | ||||
|     if (ret > 0 && ret & BDRV_BLOCK_OFFSET_VALID) { | ||||
|         *file = bs; | ||||
|     } | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -527,7 +527,7 @@ static int vdi_reopen_prepare(BDRVReopenState *state, | ||||
| } | ||||
| 
 | ||||
| static int64_t coroutine_fn vdi_co_get_block_status(BlockDriverState *bs, | ||||
|         int64_t sector_num, int nb_sectors, int *pnum) | ||||
|         int64_t sector_num, int nb_sectors, int *pnum, BlockDriverState **file) | ||||
| { | ||||
|     /* TODO: Check for too large sector_num (in bdrv_is_allocated or here). */ | ||||
|     BDRVVdiState *s = (BDRVVdiState *)bs->opaque; | ||||
| @ -551,6 +551,7 @@ static int64_t coroutine_fn vdi_co_get_block_status(BlockDriverState *bs, | ||||
|     offset = s->header.offset_data + | ||||
|                               (uint64_t)bmap_entry * s->block_size + | ||||
|                               sector_in_block * SECTOR_SIZE; | ||||
|     *file = bs->file->bs; | ||||
|     return BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | offset; | ||||
| } | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										18
									
								
								block/vmdk.c
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								block/vmdk.c
									
									
									
									
									
								
							| @ -571,6 +571,7 @@ static int vmdk_open_vmdk4(BlockDriverState *bs, | ||||
|     VmdkExtent *extent; | ||||
|     BDRVVmdkState *s = bs->opaque; | ||||
|     int64_t l1_backup_offset = 0; | ||||
|     bool compressed; | ||||
| 
 | ||||
|     ret = bdrv_pread(file->bs, sizeof(magic), &header, sizeof(header)); | ||||
|     if (ret < 0) { | ||||
| @ -645,6 +646,8 @@ static int vmdk_open_vmdk4(BlockDriverState *bs, | ||||
|         header = footer.header; | ||||
|     } | ||||
| 
 | ||||
|     compressed = | ||||
|         le16_to_cpu(header.compressAlgorithm) == VMDK4_COMPRESSION_DEFLATE; | ||||
|     if (le32_to_cpu(header.version) > 3) { | ||||
|         char buf[64]; | ||||
|         snprintf(buf, sizeof(buf), "VMDK version %" PRId32, | ||||
| @ -652,7 +655,8 @@ static int vmdk_open_vmdk4(BlockDriverState *bs, | ||||
|         error_setg(errp, QERR_UNKNOWN_BLOCK_FORMAT_FEATURE, | ||||
|                    bdrv_get_device_or_node_name(bs), "vmdk", buf); | ||||
|         return -ENOTSUP; | ||||
|     } else if (le32_to_cpu(header.version) == 3 && (flags & BDRV_O_RDWR)) { | ||||
|     } else if (le32_to_cpu(header.version) == 3 && (flags & BDRV_O_RDWR) && | ||||
|                !compressed) { | ||||
|         /* VMware KB 2064959 explains that version 3 added support for
 | ||||
|          * persistent changed block tracking (CBT), and backup software can | ||||
|          * read it as version=1 if it doesn't care about the changed area | ||||
| @ -1257,7 +1261,7 @@ static inline uint64_t vmdk_find_index_in_cluster(VmdkExtent *extent, | ||||
| } | ||||
| 
 | ||||
| static int64_t coroutine_fn vmdk_co_get_block_status(BlockDriverState *bs, | ||||
|         int64_t sector_num, int nb_sectors, int *pnum) | ||||
|         int64_t sector_num, int nb_sectors, int *pnum, BlockDriverState **file) | ||||
| { | ||||
|     BDRVVmdkState *s = bs->opaque; | ||||
|     int64_t index_in_cluster, n, ret; | ||||
| @ -1274,6 +1278,7 @@ static int64_t coroutine_fn vmdk_co_get_block_status(BlockDriverState *bs, | ||||
|                              0, 0); | ||||
|     qemu_co_mutex_unlock(&s->lock); | ||||
| 
 | ||||
|     index_in_cluster = vmdk_find_index_in_cluster(extent, sector_num); | ||||
|     switch (ret) { | ||||
|     case VMDK_ERROR: | ||||
|         ret = -EIO; | ||||
| @ -1286,14 +1291,15 @@ static int64_t coroutine_fn vmdk_co_get_block_status(BlockDriverState *bs, | ||||
|         break; | ||||
|     case VMDK_OK: | ||||
|         ret = BDRV_BLOCK_DATA; | ||||
|         if (extent->file == bs->file && !extent->compressed) { | ||||
|             ret |= BDRV_BLOCK_OFFSET_VALID | offset; | ||||
|         if (!extent->compressed) { | ||||
|             ret |= BDRV_BLOCK_OFFSET_VALID; | ||||
|             ret |= (offset + (index_in_cluster << BDRV_SECTOR_BITS)) | ||||
|                     & BDRV_BLOCK_OFFSET_MASK; | ||||
|         } | ||||
| 
 | ||||
|         *file = extent->file->bs; | ||||
|         break; | ||||
|     } | ||||
| 
 | ||||
|     index_in_cluster = vmdk_find_index_in_cluster(extent, sector_num); | ||||
|     n = extent->cluster_sectors - index_in_cluster; | ||||
|     if (n > nb_sectors) { | ||||
|         n = nb_sectors; | ||||
|  | ||||
| @ -579,7 +579,7 @@ static coroutine_fn int vpc_co_write(BlockDriverState *bs, int64_t sector_num, | ||||
| } | ||||
| 
 | ||||
| static int64_t coroutine_fn vpc_co_get_block_status(BlockDriverState *bs, | ||||
|         int64_t sector_num, int nb_sectors, int *pnum) | ||||
|         int64_t sector_num, int nb_sectors, int *pnum, BlockDriverState **file) | ||||
| { | ||||
|     BDRVVPCState *s = bs->opaque; | ||||
|     VHDFooter *footer = (VHDFooter*) s->footer_buf; | ||||
| @ -589,6 +589,7 @@ static int64_t coroutine_fn vpc_co_get_block_status(BlockDriverState *bs, | ||||
| 
 | ||||
|     if (be32_to_cpu(footer->type) == VHD_FIXED) { | ||||
|         *pnum = nb_sectors; | ||||
|         *file = bs->file->bs; | ||||
|         return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID | BDRV_BLOCK_DATA | | ||||
|                (sector_num << BDRV_SECTOR_BITS); | ||||
|     } | ||||
| @ -610,6 +611,7 @@ static int64_t coroutine_fn vpc_co_get_block_status(BlockDriverState *bs, | ||||
|         /* *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; | ||||
|             return BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | start; | ||||
|         } | ||||
|         if (nb_sectors == 0) { | ||||
|  | ||||
| @ -2884,7 +2884,7 @@ static coroutine_fn int vvfat_co_write(BlockDriverState *bs, int64_t sector_num, | ||||
| } | ||||
| 
 | ||||
| static int64_t coroutine_fn vvfat_co_get_block_status(BlockDriverState *bs, | ||||
| 	int64_t sector_num, int nb_sectors, int* n) | ||||
| 	int64_t sector_num, int nb_sectors, int *n, BlockDriverState **file) | ||||
| { | ||||
|     BDRVVVFATState* s = bs->opaque; | ||||
|     *n = s->sector_count - sector_num; | ||||
|  | ||||
| @ -45,37 +45,11 @@ void qmp_nbd_server_start(SocketAddress *addr, Error **errp) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Hook into the BlockBackend notifiers to close the export when the | ||||
|  * backend is closed. | ||||
|  */ | ||||
| typedef struct NBDCloseNotifier { | ||||
|     Notifier n; | ||||
|     NBDExport *exp; | ||||
|     QTAILQ_ENTRY(NBDCloseNotifier) next; | ||||
| } NBDCloseNotifier; | ||||
| 
 | ||||
| static QTAILQ_HEAD(, NBDCloseNotifier) close_notifiers = | ||||
|     QTAILQ_HEAD_INITIALIZER(close_notifiers); | ||||
| 
 | ||||
| static void nbd_close_notifier(Notifier *n, void *data) | ||||
| { | ||||
|     NBDCloseNotifier *cn = DO_UPCAST(NBDCloseNotifier, n, n); | ||||
| 
 | ||||
|     notifier_remove(&cn->n); | ||||
|     QTAILQ_REMOVE(&close_notifiers, cn, next); | ||||
| 
 | ||||
|     nbd_export_close(cn->exp); | ||||
|     nbd_export_put(cn->exp); | ||||
|     g_free(cn); | ||||
| } | ||||
| 
 | ||||
| void qmp_nbd_server_add(const char *device, bool has_writable, bool writable, | ||||
|                         Error **errp) | ||||
| { | ||||
|     BlockBackend *blk; | ||||
|     NBDExport *exp; | ||||
|     NBDCloseNotifier *n; | ||||
| 
 | ||||
|     if (server_fd == -1) { | ||||
|         error_setg(errp, "NBD server not running"); | ||||
| @ -113,19 +87,15 @@ void qmp_nbd_server_add(const char *device, bool has_writable, bool writable, | ||||
| 
 | ||||
|     nbd_export_set_name(exp, device); | ||||
| 
 | ||||
|     n = g_new0(NBDCloseNotifier, 1); | ||||
|     n->n.notify = nbd_close_notifier; | ||||
|     n->exp = exp; | ||||
|     blk_add_close_notifier(blk, &n->n); | ||||
|     QTAILQ_INSERT_TAIL(&close_notifiers, n, next); | ||||
|     /* The list of named exports has a strong reference to this export now and
 | ||||
|      * our only way of accessing it is through nbd_export_find(), so we can drop | ||||
|      * the strong reference that is @exp. */ | ||||
|     nbd_export_put(exp); | ||||
| } | ||||
| 
 | ||||
| void qmp_nbd_server_stop(Error **errp) | ||||
| { | ||||
|     while (!QTAILQ_EMPTY(&close_notifiers)) { | ||||
|         NBDCloseNotifier *cn = QTAILQ_FIRST(&close_notifiers); | ||||
|         nbd_close_notifier(&cn->n, nbd_export_get_blockdev(cn->exp)); | ||||
|     } | ||||
|     nbd_export_close_all(); | ||||
| 
 | ||||
|     if (server_fd != -1) { | ||||
|         qemu_set_fd_handler(server_fd, NULL, NULL, NULL); | ||||
|  | ||||
							
								
								
									
										64
									
								
								blockdev.c
									
									
									
									
									
								
							
							
						
						
									
										64
									
								
								blockdev.c
									
									
									
									
									
								
							| @ -50,6 +50,9 @@ | ||||
| #include "trace.h" | ||||
| #include "sysemu/arch_init.h" | ||||
| 
 | ||||
| static QTAILQ_HEAD(, BlockDriverState) monitor_bdrv_states = | ||||
|     QTAILQ_HEAD_INITIALIZER(monitor_bdrv_states); | ||||
| 
 | ||||
| static const char *const if_name[IF_COUNT] = { | ||||
|     [IF_NONE] = "none", | ||||
|     [IF_IDE] = "ide", | ||||
| @ -702,6 +705,19 @@ fail: | ||||
|     return NULL; | ||||
| } | ||||
| 
 | ||||
| void blockdev_close_all_bdrv_states(void) | ||||
| { | ||||
|     BlockDriverState *bs, *next_bs; | ||||
| 
 | ||||
|     QTAILQ_FOREACH_SAFE(bs, &monitor_bdrv_states, monitor_list, next_bs) { | ||||
|         AioContext *ctx = bdrv_get_aio_context(bs); | ||||
| 
 | ||||
|         aio_context_acquire(ctx); | ||||
|         bdrv_unref(bs); | ||||
|         aio_context_release(ctx); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void qemu_opt_rename(QemuOpts *opts, const char *from, const char *to, | ||||
|                             Error **errp) | ||||
| { | ||||
| @ -2304,6 +2320,11 @@ void qmp_blockdev_open_tray(const char *device, bool has_force, bool force, | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     if (!blk_dev_has_tray(blk)) { | ||||
|         /* Ignore this command on tray-less devices */ | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     if (blk_dev_is_tray_open(blk)) { | ||||
|         return; | ||||
|     } | ||||
| @ -2334,6 +2355,11 @@ void qmp_blockdev_close_tray(const char *device, Error **errp) | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     if (!blk_dev_has_tray(blk)) { | ||||
|         /* Ignore this command on tray-less devices */ | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     if (!blk_dev_is_tray_open(blk)) { | ||||
|         return; | ||||
|     } | ||||
| @ -2363,7 +2389,7 @@ void qmp_x_blockdev_remove_medium(const char *device, Error **errp) | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     if (has_device && !blk_dev_is_tray_open(blk)) { | ||||
|     if (has_device && blk_dev_has_tray(blk) && !blk_dev_is_tray_open(blk)) { | ||||
|         error_setg(errp, "Tray of device '%s' is not open", device); | ||||
|         return; | ||||
|     } | ||||
| @ -2382,12 +2408,19 @@ void qmp_x_blockdev_remove_medium(const char *device, Error **errp) | ||||
| 
 | ||||
|     /* This follows the convention established by bdrv_make_anon() */ | ||||
|     if (bs->device_list.tqe_prev) { | ||||
|         QTAILQ_REMOVE(&bdrv_states, bs, device_list); | ||||
|         bs->device_list.tqe_prev = NULL; | ||||
|         bdrv_device_remove(bs); | ||||
|     } | ||||
| 
 | ||||
|     blk_remove_bs(blk); | ||||
| 
 | ||||
|     if (!blk_dev_has_tray(blk)) { | ||||
|         /* For tray-less devices, blockdev-open-tray is a no-op (or may not be
 | ||||
|          * called at all); therefore, the medium needs to be ejected here. | ||||
|          * Do it after blk_remove_bs() so blk_is_inserted(blk) returns the @load | ||||
|          * value passed here (i.e. false). */ | ||||
|         blk_dev_change_media_cb(blk, false); | ||||
|     } | ||||
| 
 | ||||
| out: | ||||
|     aio_context_release(aio_context); | ||||
| } | ||||
| @ -2413,7 +2446,7 @@ static void qmp_blockdev_insert_anon_medium(const char *device, | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     if (has_device && !blk_dev_is_tray_open(blk)) { | ||||
|     if (has_device && blk_dev_has_tray(blk) && !blk_dev_is_tray_open(blk)) { | ||||
|         error_setg(errp, "Tray of device '%s' is not open", device); | ||||
|         return; | ||||
|     } | ||||
| @ -2426,6 +2459,15 @@ static void qmp_blockdev_insert_anon_medium(const char *device, | ||||
|     blk_insert_bs(blk, bs); | ||||
| 
 | ||||
|     QTAILQ_INSERT_TAIL(&bdrv_states, bs, device_list); | ||||
| 
 | ||||
|     if (!blk_dev_has_tray(blk)) { | ||||
|         /* For tray-less devices, blockdev-close-tray is a no-op (or may not be
 | ||||
|          * called at all); therefore, the medium needs to be pushed into the | ||||
|          * slot here. | ||||
|          * Do it after blk_insert_bs() so blk_is_inserted(blk) returns the @load | ||||
|          * value passed here (i.e. true). */ | ||||
|         blk_dev_change_media_cb(blk, true); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void qmp_x_blockdev_insert_medium(const char *device, const char *node_name, | ||||
| @ -2765,7 +2807,7 @@ void hmp_drive_del(Monitor *mon, const QDict *qdict) | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         bdrv_close(bs); | ||||
|         blk_remove_bs(blk); | ||||
|     } | ||||
| 
 | ||||
|     /* if we have a device attached to this BlockDriverState
 | ||||
| @ -3848,12 +3890,15 @@ void qmp_blockdev_add(BlockdevOptions *options, Error **errp) | ||||
|         if (!bs) { | ||||
|             goto fail; | ||||
|         } | ||||
| 
 | ||||
|         QTAILQ_INSERT_TAIL(&monitor_bdrv_states, bs, monitor_list); | ||||
|     } | ||||
| 
 | ||||
|     if (bs && bdrv_key_required(bs)) { | ||||
|         if (blk) { | ||||
|             blk_unref(blk); | ||||
|         } else { | ||||
|             QTAILQ_REMOVE(&monitor_bdrv_states, bs, monitor_list); | ||||
|             bdrv_unref(bs); | ||||
|         } | ||||
|         error_setg(errp, "blockdev-add doesn't support encrypted devices"); | ||||
| @ -3913,7 +3958,13 @@ void qmp_x_blockdev_del(bool has_id, const char *id, | ||||
|             goto out; | ||||
|         } | ||||
| 
 | ||||
|         if (bs->refcnt > 1 || !QLIST_EMPTY(&bs->parents)) { | ||||
|         if (!blk && !bs->monitor_list.tqe_prev) { | ||||
|             error_setg(errp, "Node %s is not owned by the monitor", | ||||
|                        bs->node_name); | ||||
|             goto out; | ||||
|         } | ||||
| 
 | ||||
|         if (bs->refcnt > 1) { | ||||
|             error_setg(errp, "Block device %s is in use", | ||||
|                        bdrv_get_device_or_node_name(bs)); | ||||
|             goto out; | ||||
| @ -3923,6 +3974,7 @@ void qmp_x_blockdev_del(bool has_id, const char *id, | ||||
|     if (blk) { | ||||
|         blk_unref(blk); | ||||
|     } else { | ||||
|         QTAILQ_REMOVE(&monitor_bdrv_states, bs, monitor_list); | ||||
|         bdrv_unref(bs); | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -278,14 +278,6 @@ void block_job_iostatus_reset(BlockJob *job) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| struct BlockFinishData { | ||||
|     BlockJob *job; | ||||
|     BlockCompletionFunc *cb; | ||||
|     void *opaque; | ||||
|     bool cancelled; | ||||
|     int ret; | ||||
| }; | ||||
| 
 | ||||
| static int block_job_finish_sync(BlockJob *job, | ||||
|                                  void (*finish)(BlockJob *, Error **errp), | ||||
|                                  Error **errp) | ||||
|  | ||||
| @ -40,6 +40,8 @@ struct VirtIOBlockDataPlane { | ||||
|     EventNotifier *guest_notifier;  /* irq */ | ||||
|     QEMUBH *bh;                     /* bh for guest notification */ | ||||
| 
 | ||||
|     Notifier insert_notifier, remove_notifier; | ||||
| 
 | ||||
|     /* Note that these EventNotifiers are assigned by value.  This is
 | ||||
|      * fine as long as you do not call event_notifier_cleanup on them | ||||
|      * (because you don't own the file descriptor or handle; you just | ||||
| @ -137,6 +139,54 @@ static void handle_notify(EventNotifier *e) | ||||
|     blk_io_unplug(s->conf->conf.blk); | ||||
| } | ||||
| 
 | ||||
| static void data_plane_set_up_op_blockers(VirtIOBlockDataPlane *s) | ||||
| { | ||||
|     assert(!s->blocker); | ||||
|     error_setg(&s->blocker, "block device is in use by data plane"); | ||||
|     blk_op_block_all(s->conf->conf.blk, s->blocker); | ||||
|     blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_RESIZE, s->blocker); | ||||
|     blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_DRIVE_DEL, s->blocker); | ||||
|     blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_BACKUP_SOURCE, s->blocker); | ||||
|     blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_CHANGE, s->blocker); | ||||
|     blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_COMMIT_SOURCE, s->blocker); | ||||
|     blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_COMMIT_TARGET, s->blocker); | ||||
|     blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_EJECT, s->blocker); | ||||
|     blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_EXTERNAL_SNAPSHOT, | ||||
|                    s->blocker); | ||||
|     blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_INTERNAL_SNAPSHOT, | ||||
|                    s->blocker); | ||||
|     blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_INTERNAL_SNAPSHOT_DELETE, | ||||
|                    s->blocker); | ||||
|     blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_MIRROR_SOURCE, s->blocker); | ||||
|     blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_STREAM, s->blocker); | ||||
|     blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_REPLACE, s->blocker); | ||||
| } | ||||
| 
 | ||||
| static void data_plane_remove_op_blockers(VirtIOBlockDataPlane *s) | ||||
| { | ||||
|     if (s->blocker) { | ||||
|         blk_op_unblock_all(s->conf->conf.blk, s->blocker); | ||||
|         error_free(s->blocker); | ||||
|         s->blocker = NULL; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void data_plane_blk_insert_notifier(Notifier *n, void *data) | ||||
| { | ||||
|     VirtIOBlockDataPlane *s = container_of(n, VirtIOBlockDataPlane, | ||||
|                                            insert_notifier); | ||||
|     assert(s->conf->conf.blk == data); | ||||
|     data_plane_set_up_op_blockers(s); | ||||
| } | ||||
| 
 | ||||
| static void data_plane_blk_remove_notifier(Notifier *n, void *data) | ||||
| { | ||||
|     VirtIOBlockDataPlane *s = container_of(n, VirtIOBlockDataPlane, | ||||
|                                            remove_notifier); | ||||
|     assert(s->conf->conf.blk == data); | ||||
|     data_plane_remove_op_blockers(s); | ||||
| } | ||||
| 
 | ||||
| /* Context: QEMU global mutex held */ | ||||
| void virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *conf, | ||||
|                                   VirtIOBlockDataPlane **dataplane, | ||||
| @ -179,22 +229,12 @@ void virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *conf, | ||||
|     s->ctx = iothread_get_aio_context(s->iothread); | ||||
|     s->bh = aio_bh_new(s->ctx, notify_guest_bh, s); | ||||
| 
 | ||||
|     error_setg(&s->blocker, "block device is in use by data plane"); | ||||
|     blk_op_block_all(conf->conf.blk, s->blocker); | ||||
|     blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_RESIZE, s->blocker); | ||||
|     blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_DRIVE_DEL, s->blocker); | ||||
|     blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_BACKUP_SOURCE, s->blocker); | ||||
|     blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_CHANGE, s->blocker); | ||||
|     blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_COMMIT_SOURCE, s->blocker); | ||||
|     blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_COMMIT_TARGET, s->blocker); | ||||
|     blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_EJECT, s->blocker); | ||||
|     blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_EXTERNAL_SNAPSHOT, s->blocker); | ||||
|     blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_INTERNAL_SNAPSHOT, s->blocker); | ||||
|     blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_INTERNAL_SNAPSHOT_DELETE, | ||||
|                    s->blocker); | ||||
|     blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_MIRROR_SOURCE, s->blocker); | ||||
|     blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_STREAM, s->blocker); | ||||
|     blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_REPLACE, s->blocker); | ||||
|     s->insert_notifier.notify = data_plane_blk_insert_notifier; | ||||
|     s->remove_notifier.notify = data_plane_blk_remove_notifier; | ||||
|     blk_add_insert_bs_notifier(conf->conf.blk, &s->insert_notifier); | ||||
|     blk_add_remove_bs_notifier(conf->conf.blk, &s->remove_notifier); | ||||
| 
 | ||||
|     data_plane_set_up_op_blockers(s); | ||||
| 
 | ||||
|     *dataplane = s; | ||||
| } | ||||
| @ -207,8 +247,9 @@ void virtio_blk_data_plane_destroy(VirtIOBlockDataPlane *s) | ||||
|     } | ||||
| 
 | ||||
|     virtio_blk_data_plane_stop(s); | ||||
|     blk_op_unblock_all(s->conf->conf.blk, s->blocker); | ||||
|     error_free(s->blocker); | ||||
|     data_plane_remove_op_blockers(s); | ||||
|     notifier_remove(&s->insert_notifier); | ||||
|     notifier_remove(&s->remove_notifier); | ||||
|     qemu_bh_delete(s->bh); | ||||
|     object_unref(OBJECT(s->iothread)); | ||||
|     g_free(s); | ||||
|  | ||||
| @ -173,7 +173,6 @@ typedef struct FDrive { | ||||
|     uint8_t media_changed;    /* Is media changed       */ | ||||
|     uint8_t media_rate;       /* Data rate of medium    */ | ||||
| 
 | ||||
|     bool media_inserted;      /* Is there a medium in the tray */ | ||||
|     bool media_validated;     /* Have we validated the media? */ | ||||
| } FDrive; | ||||
| 
 | ||||
| @ -249,7 +248,7 @@ static int fd_seek(FDrive *drv, uint8_t head, uint8_t track, uint8_t sect, | ||||
| #endif | ||||
|         drv->head = head; | ||||
|         if (drv->track != track) { | ||||
|             if (drv->media_inserted) { | ||||
|             if (drv->blk != NULL && blk_is_inserted(drv->blk)) { | ||||
|                 drv->media_changed = 0; | ||||
|             } | ||||
|             ret = 1; | ||||
| @ -258,7 +257,7 @@ static int fd_seek(FDrive *drv, uint8_t head, uint8_t track, uint8_t sect, | ||||
|         drv->sect = sect; | ||||
|     } | ||||
| 
 | ||||
|     if (!drv->media_inserted) { | ||||
|     if (drv->blk == NULL || !blk_is_inserted(drv->blk)) { | ||||
|         ret = 2; | ||||
|     } | ||||
| 
 | ||||
| @ -288,7 +287,9 @@ static int pick_geometry(FDrive *drv) | ||||
|     bool magic = drv->drive == FLOPPY_DRIVE_TYPE_AUTO; | ||||
| 
 | ||||
|     /* We can only pick a geometry if we have a diskette. */ | ||||
|     if (!drv->media_inserted || drv->drive == FLOPPY_DRIVE_TYPE_NONE) { | ||||
|     if (!drv->blk || !blk_is_inserted(drv->blk) || | ||||
|         drv->drive == FLOPPY_DRIVE_TYPE_NONE) | ||||
|     { | ||||
|         return -1; | ||||
|     } | ||||
| 
 | ||||
| @ -390,7 +391,7 @@ static void fd_revalidate(FDrive *drv) | ||||
|     FLOPPY_DPRINTF("revalidate\n"); | ||||
|     if (drv->blk != NULL) { | ||||
|         drv->ro = blk_is_read_only(drv->blk); | ||||
|         if (!drv->media_inserted) { | ||||
|         if (!blk_is_inserted(drv->blk)) { | ||||
|             FLOPPY_DPRINTF("No disk in drive\n"); | ||||
|             drv->disk = FLOPPY_DRIVE_TYPE_NONE; | ||||
|         } else if (!drv->media_validated) { | ||||
| @ -793,7 +794,7 @@ static bool fdrive_media_changed_needed(void *opaque) | ||||
| { | ||||
|     FDrive *drive = opaque; | ||||
| 
 | ||||
|     return (drive->media_inserted && drive->media_changed != 1); | ||||
|     return (drive->blk != NULL && drive->media_changed != 1); | ||||
| } | ||||
| 
 | ||||
| static const VMStateDescription vmstate_fdrive_media_changed = { | ||||
| @ -2285,22 +2286,13 @@ static void fdctrl_change_cb(void *opaque, bool load) | ||||
| { | ||||
|     FDrive *drive = opaque; | ||||
| 
 | ||||
|     drive->media_inserted = load && drive->blk && blk_is_inserted(drive->blk); | ||||
| 
 | ||||
|     drive->media_changed = 1; | ||||
|     drive->media_validated = false; | ||||
|     fd_revalidate(drive); | ||||
| } | ||||
| 
 | ||||
| static bool fdctrl_is_tray_open(void *opaque) | ||||
| { | ||||
|     FDrive *drive = opaque; | ||||
|     return !drive->media_inserted; | ||||
| } | ||||
| 
 | ||||
| static const BlockDevOps fdctrl_block_ops = { | ||||
|     .change_media_cb = fdctrl_change_cb, | ||||
|     .is_tray_open = fdctrl_is_tray_open, | ||||
| }; | ||||
| 
 | ||||
| /* Init functions */ | ||||
| @ -2327,7 +2319,6 @@ static void fdctrl_connect_drives(FDCtrl *fdctrl, Error **errp) | ||||
|         fd_init(drive); | ||||
|         if (drive->blk) { | ||||
|             blk_set_dev_ops(drive->blk, &fdctrl_block_ops, drive); | ||||
|             drive->media_inserted = blk_is_inserted(drive->blk); | ||||
|             pick_drive_type(drive); | ||||
|         } | ||||
|         fd_revalidate(drive); | ||||
|  | ||||
| @ -758,6 +758,22 @@ static void virtio_scsi_change(SCSIBus *bus, SCSIDevice *dev, SCSISense sense) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void virtio_scsi_blk_insert_notifier(Notifier *n, void *data) | ||||
| { | ||||
|     VirtIOSCSIBlkChangeNotifier *cn = DO_UPCAST(VirtIOSCSIBlkChangeNotifier, | ||||
|                                                 n, n); | ||||
|     assert(cn->sd->conf.blk == data); | ||||
|     blk_op_block_all(cn->sd->conf.blk, cn->s->blocker); | ||||
| } | ||||
| 
 | ||||
| static void virtio_scsi_blk_remove_notifier(Notifier *n, void *data) | ||||
| { | ||||
|     VirtIOSCSIBlkChangeNotifier *cn = DO_UPCAST(VirtIOSCSIBlkChangeNotifier, | ||||
|                                                 n, n); | ||||
|     assert(cn->sd->conf.blk == data); | ||||
|     blk_op_unblock_all(cn->sd->conf.blk, cn->s->blocker); | ||||
| } | ||||
| 
 | ||||
| static void virtio_scsi_hotplug(HotplugHandler *hotplug_dev, DeviceState *dev, | ||||
|                                 Error **errp) | ||||
| { | ||||
| @ -766,6 +782,8 @@ static void virtio_scsi_hotplug(HotplugHandler *hotplug_dev, DeviceState *dev, | ||||
|     SCSIDevice *sd = SCSI_DEVICE(dev); | ||||
| 
 | ||||
|     if (s->ctx && !s->dataplane_disabled) { | ||||
|         VirtIOSCSIBlkChangeNotifier *insert_notifier, *remove_notifier; | ||||
| 
 | ||||
|         if (blk_op_is_blocked(sd->conf.blk, BLOCK_OP_TYPE_DATAPLANE, errp)) { | ||||
|             return; | ||||
|         } | ||||
| @ -773,6 +791,20 @@ static void virtio_scsi_hotplug(HotplugHandler *hotplug_dev, DeviceState *dev, | ||||
|         aio_context_acquire(s->ctx); | ||||
|         blk_set_aio_context(sd->conf.blk, s->ctx); | ||||
|         aio_context_release(s->ctx); | ||||
| 
 | ||||
|         insert_notifier = g_new0(VirtIOSCSIBlkChangeNotifier, 1); | ||||
|         insert_notifier->n.notify = virtio_scsi_blk_insert_notifier; | ||||
|         insert_notifier->s = s; | ||||
|         insert_notifier->sd = sd; | ||||
|         blk_add_insert_bs_notifier(sd->conf.blk, &insert_notifier->n); | ||||
|         QTAILQ_INSERT_TAIL(&s->insert_notifiers, insert_notifier, next); | ||||
| 
 | ||||
|         remove_notifier = g_new0(VirtIOSCSIBlkChangeNotifier, 1); | ||||
|         remove_notifier->n.notify = virtio_scsi_blk_remove_notifier; | ||||
|         remove_notifier->s = s; | ||||
|         remove_notifier->sd = sd; | ||||
|         blk_add_remove_bs_notifier(sd->conf.blk, &remove_notifier->n); | ||||
|         QTAILQ_INSERT_TAIL(&s->remove_notifiers, remove_notifier, next); | ||||
|     } | ||||
| 
 | ||||
|     if (virtio_vdev_has_feature(vdev, VIRTIO_SCSI_F_HOTPLUG)) { | ||||
| @ -788,6 +820,7 @@ static void virtio_scsi_hotunplug(HotplugHandler *hotplug_dev, DeviceState *dev, | ||||
|     VirtIODevice *vdev = VIRTIO_DEVICE(hotplug_dev); | ||||
|     VirtIOSCSI *s = VIRTIO_SCSI(vdev); | ||||
|     SCSIDevice *sd = SCSI_DEVICE(dev); | ||||
|     VirtIOSCSIBlkChangeNotifier *insert_notifier, *remove_notifier; | ||||
| 
 | ||||
|     if (virtio_vdev_has_feature(vdev, VIRTIO_SCSI_F_HOTPLUG)) { | ||||
|         virtio_scsi_push_event(s, sd, | ||||
| @ -798,6 +831,25 @@ static void virtio_scsi_hotunplug(HotplugHandler *hotplug_dev, DeviceState *dev, | ||||
|     if (s->ctx) { | ||||
|         blk_op_unblock_all(sd->conf.blk, s->blocker); | ||||
|     } | ||||
| 
 | ||||
|     QTAILQ_FOREACH(insert_notifier, &s->insert_notifiers, next) { | ||||
|         if (insert_notifier->sd == sd) { | ||||
|             notifier_remove(&insert_notifier->n); | ||||
|             QTAILQ_REMOVE(&s->insert_notifiers, insert_notifier, next); | ||||
|             g_free(insert_notifier); | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     QTAILQ_FOREACH(remove_notifier, &s->remove_notifiers, next) { | ||||
|         if (remove_notifier->sd == sd) { | ||||
|             notifier_remove(&remove_notifier->n); | ||||
|             QTAILQ_REMOVE(&s->remove_notifiers, remove_notifier, next); | ||||
|             g_free(remove_notifier); | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     qdev_simple_device_unplug_cb(hotplug_dev, dev, errp); | ||||
| } | ||||
| 
 | ||||
| @ -912,6 +964,9 @@ static void virtio_scsi_device_realize(DeviceState *dev, Error **errp) | ||||
|     add_migration_state_change_notifier(&s->migration_state_notifier); | ||||
| 
 | ||||
|     error_setg(&s->blocker, "block device is in use by data plane"); | ||||
| 
 | ||||
|     QTAILQ_INIT(&s->insert_notifiers); | ||||
|     QTAILQ_INIT(&s->remove_notifiers); | ||||
| } | ||||
| 
 | ||||
| static void virtio_scsi_instance_init(Object *obj) | ||||
|  | ||||
| @ -111,9 +111,10 @@ typedef struct HDGeometry { | ||||
| 
 | ||||
| /*
 | ||||
|  * Allocation status flags | ||||
|  * BDRV_BLOCK_DATA: data is read from bs->file or another file | ||||
|  * BDRV_BLOCK_DATA: data is read from a file returned by bdrv_get_block_status. | ||||
|  * BDRV_BLOCK_ZERO: sectors read as zero | ||||
|  * BDRV_BLOCK_OFFSET_VALID: sector stored in bs->file as raw data | ||||
|  * BDRV_BLOCK_OFFSET_VALID: sector stored as raw data in a file returned by | ||||
|  *                          bdrv_get_block_status. | ||||
|  * BDRV_BLOCK_ALLOCATED: the content of the block is determined by this | ||||
|  *                       layer (as opposed to the backing file) | ||||
|  * BDRV_BLOCK_RAW: used internally to indicate that the request | ||||
| @ -198,6 +199,7 @@ int bdrv_create(BlockDriver *drv, const char* filename, | ||||
| int bdrv_create_file(const char *filename, QemuOpts *opts, Error **errp); | ||||
| BlockDriverState *bdrv_new_root(void); | ||||
| BlockDriverState *bdrv_new(void); | ||||
| void bdrv_device_remove(BlockDriverState *bs); | ||||
| void bdrv_make_anon(BlockDriverState *bs); | ||||
| void bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top); | ||||
| void bdrv_replace_in_backing_chain(BlockDriverState *old, | ||||
| @ -225,8 +227,6 @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state, | ||||
|                         BlockReopenQueue *queue, Error **errp); | ||||
| void bdrv_reopen_commit(BDRVReopenState *reopen_state); | ||||
| void bdrv_reopen_abort(BDRVReopenState *reopen_state); | ||||
| void bdrv_close(BlockDriverState *bs); | ||||
| void bdrv_add_close_notifier(BlockDriverState *bs, Notifier *notify); | ||||
| int bdrv_read(BlockDriverState *bs, int64_t sector_num, | ||||
|               uint8_t *buf, int nb_sectors); | ||||
| int bdrv_read_unthrottled(BlockDriverState *bs, int64_t sector_num, | ||||
| @ -386,11 +386,13 @@ int bdrv_has_zero_init(BlockDriverState *bs); | ||||
| bool bdrv_unallocated_blocks_are_zero(BlockDriverState *bs); | ||||
| bool bdrv_can_write_zeroes_with_unmap(BlockDriverState *bs); | ||||
| int64_t bdrv_get_block_status(BlockDriverState *bs, int64_t sector_num, | ||||
|                               int nb_sectors, int *pnum); | ||||
|                               int nb_sectors, int *pnum, | ||||
|                               BlockDriverState **file); | ||||
| int64_t bdrv_get_block_status_above(BlockDriverState *bs, | ||||
|                                     BlockDriverState *base, | ||||
|                                     int64_t sector_num, | ||||
|                                     int nb_sectors, int *pnum); | ||||
|                                     int nb_sectors, int *pnum, | ||||
|                                     BlockDriverState **file); | ||||
| int bdrv_is_allocated(BlockDriverState *bs, int64_t sector_num, int nb_sectors, | ||||
|                       int *pnum); | ||||
| int bdrv_is_allocated_above(BlockDriverState *top, BlockDriverState *base, | ||||
|  | ||||
| @ -166,7 +166,8 @@ struct BlockDriver { | ||||
|     int coroutine_fn (*bdrv_co_discard)(BlockDriverState *bs, | ||||
|         int64_t sector_num, int nb_sectors); | ||||
|     int64_t coroutine_fn (*bdrv_co_get_block_status)(BlockDriverState *bs, | ||||
|         int64_t sector_num, int nb_sectors, int *pnum); | ||||
|         int64_t sector_num, int nb_sectors, int *pnum, | ||||
|         BlockDriverState **file); | ||||
| 
 | ||||
|     /*
 | ||||
|      * Invalidate any cached meta-data. | ||||
| @ -403,8 +404,6 @@ struct BlockDriverState { | ||||
|     BdrvChild *backing; | ||||
|     BdrvChild *file; | ||||
| 
 | ||||
|     NotifierList close_notifiers; | ||||
| 
 | ||||
|     /* Callback before write request is processed */ | ||||
|     NotifierWithReturnList before_write_notifiers; | ||||
| 
 | ||||
| @ -445,6 +444,10 @@ struct BlockDriverState { | ||||
|     QTAILQ_ENTRY(BlockDriverState) node_list; | ||||
|     /* element of the list of "drives" the guest sees */ | ||||
|     QTAILQ_ENTRY(BlockDriverState) device_list; | ||||
|     /* element of the list of all BlockDriverStates (all_bdrv_states) */ | ||||
|     QTAILQ_ENTRY(BlockDriverState) bs_list; | ||||
|     /* element of the list of monitor-owned BDS */ | ||||
|     QTAILQ_ENTRY(BlockDriverState) monitor_list; | ||||
|     QLIST_HEAD(, BdrvDirtyBitmap) dirty_bitmaps; | ||||
|     int refcnt; | ||||
| 
 | ||||
| @ -695,6 +698,7 @@ void blk_set_bs(BlockBackend *blk, BlockDriverState *bs); | ||||
| 
 | ||||
| void blk_dev_change_media_cb(BlockBackend *blk, bool load); | ||||
| bool blk_dev_has_removable_media(BlockBackend *blk); | ||||
| bool blk_dev_has_tray(BlockBackend *blk); | ||||
| void blk_dev_eject_request(BlockBackend *blk, bool force); | ||||
| bool blk_dev_is_tray_open(BlockBackend *blk); | ||||
| bool blk_dev_is_medium_locked(BlockBackend *blk); | ||||
| @ -706,4 +710,6 @@ bool bdrv_requests_pending(BlockDriverState *bs); | ||||
| void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out); | ||||
| void bdrv_undo_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap *in); | ||||
| 
 | ||||
| void blockdev_close_all_bdrv_states(void); | ||||
| 
 | ||||
| #endif /* BLOCK_INT_H */ | ||||
|  | ||||
| @ -76,6 +76,13 @@ typedef struct VirtIOSCSICommon { | ||||
|     VirtQueue **cmd_vqs; | ||||
| } VirtIOSCSICommon; | ||||
| 
 | ||||
| typedef struct VirtIOSCSIBlkChangeNotifier { | ||||
|     Notifier n; | ||||
|     struct VirtIOSCSI *s; | ||||
|     SCSIDevice *sd; | ||||
|     QTAILQ_ENTRY(VirtIOSCSIBlkChangeNotifier) next; | ||||
| } VirtIOSCSIBlkChangeNotifier; | ||||
| 
 | ||||
| typedef struct VirtIOSCSI { | ||||
|     VirtIOSCSICommon parent_obj; | ||||
| 
 | ||||
| @ -86,6 +93,9 @@ typedef struct VirtIOSCSI { | ||||
|     /* Fields for dataplane below */ | ||||
|     AioContext *ctx; /* one iothread per virtio-scsi-pci for now */ | ||||
| 
 | ||||
|     QTAILQ_HEAD(, VirtIOSCSIBlkChangeNotifier) insert_notifiers; | ||||
|     QTAILQ_HEAD(, VirtIOSCSIBlkChangeNotifier) remove_notifiers; | ||||
| 
 | ||||
|     /* Vring is used instead of vq in dataplane code, because of the underlying
 | ||||
|      * memory layer thread safety */ | ||||
|     VirtIOSCSIVring *ctrl_vring; | ||||
|  | ||||
| @ -68,6 +68,7 @@ BlockBackend *blk_new_open(const char *name, const char *filename, | ||||
| int blk_get_refcnt(BlockBackend *blk); | ||||
| void blk_ref(BlockBackend *blk); | ||||
| void blk_unref(BlockBackend *blk); | ||||
| void blk_remove_all_bs(void); | ||||
| const char *blk_name(BlockBackend *blk); | ||||
| BlockBackend *blk_by_name(const char *name); | ||||
| BlockBackend *blk_next(BlockBackend *blk); | ||||
| @ -164,7 +165,8 @@ void blk_remove_aio_context_notifier(BlockBackend *blk, | ||||
|                                                                   void *), | ||||
|                                      void (*detach_aio_context)(void *), | ||||
|                                      void *opaque); | ||||
| void blk_add_close_notifier(BlockBackend *blk, Notifier *notify); | ||||
| void blk_add_remove_bs_notifier(BlockBackend *blk, Notifier *notify); | ||||
| void blk_add_insert_bs_notifier(BlockBackend *blk, Notifier *notify); | ||||
| void blk_io_plug(BlockBackend *blk); | ||||
| void blk_io_unplug(BlockBackend *blk); | ||||
| BlockAcctStats *blk_get_stats(BlockBackend *blk); | ||||
|  | ||||
							
								
								
									
										16
									
								
								nbd/server.c
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								nbd/server.c
									
									
									
									
									
								
							| @ -64,6 +64,8 @@ struct NBDExport { | ||||
|     QTAILQ_ENTRY(NBDExport) next; | ||||
| 
 | ||||
|     AioContext *ctx; | ||||
| 
 | ||||
|     Notifier eject_notifier; | ||||
| }; | ||||
| 
 | ||||
| static QTAILQ_HEAD(, NBDExport) exports = QTAILQ_HEAD_INITIALIZER(exports); | ||||
| @ -644,6 +646,12 @@ static void blk_aio_detach(void *opaque) | ||||
|     exp->ctx = NULL; | ||||
| } | ||||
| 
 | ||||
| static void nbd_eject_notifier(Notifier *n, void *data) | ||||
| { | ||||
|     NBDExport *exp = container_of(n, NBDExport, eject_notifier); | ||||
|     nbd_export_close(exp); | ||||
| } | ||||
| 
 | ||||
| NBDExport *nbd_export_new(BlockBackend *blk, off_t dev_offset, off_t size, | ||||
|                           uint32_t nbdflags, void (*close)(NBDExport *), | ||||
|                           Error **errp) | ||||
| @ -666,6 +674,10 @@ NBDExport *nbd_export_new(BlockBackend *blk, off_t dev_offset, off_t size, | ||||
|     exp->ctx = blk_get_aio_context(blk); | ||||
|     blk_ref(blk); | ||||
|     blk_add_aio_context_notifier(blk, blk_aio_attached, blk_aio_detach, exp); | ||||
| 
 | ||||
|     exp->eject_notifier.notify = nbd_eject_notifier; | ||||
|     blk_add_remove_bs_notifier(blk, &exp->eject_notifier); | ||||
| 
 | ||||
|     /*
 | ||||
|      * NBD exports are used for non-shared storage migration.  Make sure | ||||
|      * that BDRV_O_INACTIVE is cleared and the image is ready for write | ||||
| @ -747,6 +759,7 @@ void nbd_export_put(NBDExport *exp) | ||||
|         } | ||||
| 
 | ||||
|         if (exp->blk) { | ||||
|             notifier_remove(&exp->eject_notifier); | ||||
|             blk_remove_aio_context_notifier(exp->blk, blk_aio_attached, | ||||
|                                             blk_aio_detach, exp); | ||||
|             blk_unref(exp->blk); | ||||
| @ -1082,8 +1095,7 @@ static coroutine_fn void nbd_co_client_start(void *opaque) | ||||
|         nbd_export_get(exp); | ||||
|     } | ||||
|     if (nbd_negotiate(data)) { | ||||
|         shutdown(client->sock, 2); | ||||
|         client->close(client); | ||||
|         client_close(client); | ||||
|         goto out; | ||||
|     } | ||||
|     qemu_co_mutex_init(&client->send_lock); | ||||
|  | ||||
| @ -185,6 +185,33 @@ | ||||
|            '*total-clusters': 'int', '*allocated-clusters': 'int', | ||||
|            '*fragmented-clusters': 'int', '*compressed-clusters': 'int' } } | ||||
| 
 | ||||
| ## | ||||
| # @MapEntry: | ||||
| # | ||||
| # Mapping information from a virtual block range to a host file range | ||||
| # | ||||
| # @start: the start byte of the mapped virtual range | ||||
| # | ||||
| # @length: the number of bytes of the mapped virtual range | ||||
| # | ||||
| # @data: whether the mapped range has data | ||||
| # | ||||
| # @zero: whether the virtual blocks are zeroed | ||||
| # | ||||
| # @depth: the depth of the mapping | ||||
| # | ||||
| # @offset: #optional the offset in file that the virtual sectors are mapped to | ||||
| # | ||||
| # @filename: #optional filename that is referred to by @offset | ||||
| # | ||||
| # Since: 2.6 | ||||
| # | ||||
| ## | ||||
| { 'struct': 'MapEntry', | ||||
|   'data': {'start': 'int', 'length': 'int', 'data': 'bool', | ||||
|            'zero': 'bool', 'depth': 'int', '*offset': 'int', | ||||
|            '*filename': 'str' } } | ||||
| 
 | ||||
| ## | ||||
| # @BlockdevCacheInfo | ||||
| # | ||||
| @ -382,8 +409,8 @@ | ||||
| # @locked: True if the guest has locked this device from having its media | ||||
| #          removed | ||||
| # | ||||
| # @tray_open: #optional True if the device has a tray and it is open | ||||
| #             (only present if removable is true) | ||||
| # @tray_open: #optional True if the device's tray is open | ||||
| #             (only present if it has a tray) | ||||
| # | ||||
| # @dirty-bitmaps: #optional dirty bitmaps information (only present if the | ||||
| #                 driver has one or more dirty bitmaps) (Since 2.0) | ||||
| @ -2098,8 +2125,7 @@ | ||||
| #   respond to the eject request | ||||
| # - if the BlockBackend denoted by @device does not have a guest device attached | ||||
| #   to it | ||||
| # - if the guest device does not have an actual tray and is empty, for instance | ||||
| #   for floppy disk drives | ||||
| # - if the guest device does not have an actual tray | ||||
| # | ||||
| # @device: block device name | ||||
| # | ||||
|  | ||||
							
								
								
									
										84
									
								
								qemu-img.c
									
									
									
									
									
								
							
							
						
						
									
										84
									
								
								qemu-img.c
									
									
									
									
									
								
							| @ -1072,13 +1072,15 @@ static int img_compare(int argc, char **argv) | ||||
| 
 | ||||
|     for (;;) { | ||||
|         int64_t status1, status2; | ||||
|         BlockDriverState *file; | ||||
| 
 | ||||
|         nb_sectors = sectors_to_process(total_sectors, sector_num); | ||||
|         if (nb_sectors <= 0) { | ||||
|             break; | ||||
|         } | ||||
|         status1 = bdrv_get_block_status_above(bs1, NULL, sector_num, | ||||
|                                               total_sectors1 - sector_num, | ||||
|                                               &pnum1); | ||||
|                                               &pnum1, &file); | ||||
|         if (status1 < 0) { | ||||
|             ret = 3; | ||||
|             error_report("Sector allocation test failed for %s", filename1); | ||||
| @ -1088,7 +1090,7 @@ static int img_compare(int argc, char **argv) | ||||
| 
 | ||||
|         status2 = bdrv_get_block_status_above(bs2, NULL, sector_num, | ||||
|                                               total_sectors2 - sector_num, | ||||
|                                               &pnum2); | ||||
|                                               &pnum2, &file); | ||||
|         if (status2 < 0) { | ||||
|             ret = 3; | ||||
|             error_report("Sector allocation test failed for %s", filename2); | ||||
| @ -1271,9 +1273,10 @@ static int convert_iteration_sectors(ImgConvertState *s, int64_t sector_num) | ||||
|     n = MIN(s->total_sectors - sector_num, BDRV_REQUEST_MAX_SECTORS); | ||||
| 
 | ||||
|     if (s->sector_next_status <= sector_num) { | ||||
|         BlockDriverState *file; | ||||
|         ret = bdrv_get_block_status(blk_bs(s->src[s->src_cur]), | ||||
|                                     sector_num - s->src_cur_offset, | ||||
|                                     n, &n); | ||||
|                                     n, &n, &file); | ||||
|         if (ret < 0) { | ||||
|             return ret; | ||||
|         } | ||||
| @ -2144,47 +2147,37 @@ static int img_info(int argc, char **argv) | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| typedef struct MapEntry { | ||||
|     int flags; | ||||
|     int depth; | ||||
|     int64_t start; | ||||
|     int64_t length; | ||||
|     int64_t offset; | ||||
|     BlockDriverState *bs; | ||||
| } MapEntry; | ||||
| 
 | ||||
| static void dump_map_entry(OutputFormat output_format, MapEntry *e, | ||||
|                            MapEntry *next) | ||||
| { | ||||
|     switch (output_format) { | ||||
|     case OFORMAT_HUMAN: | ||||
|         if ((e->flags & BDRV_BLOCK_DATA) && | ||||
|             !(e->flags & BDRV_BLOCK_OFFSET_VALID)) { | ||||
|         if (e->data && !e->has_offset) { | ||||
|             error_report("File contains external, encrypted or compressed clusters."); | ||||
|             exit(1); | ||||
|         } | ||||
|         if ((e->flags & (BDRV_BLOCK_DATA|BDRV_BLOCK_ZERO)) == BDRV_BLOCK_DATA) { | ||||
|         if (e->data && !e->zero) { | ||||
|             printf("%#-16"PRIx64"%#-16"PRIx64"%#-16"PRIx64"%s\n", | ||||
|                    e->start, e->length, e->offset, e->bs->filename); | ||||
|                    e->start, e->length, | ||||
|                    e->has_offset ? e->offset : 0, | ||||
|                    e->has_filename ? e->filename : ""); | ||||
|         } | ||||
|         /* This format ignores the distinction between 0, ZERO and ZERO|DATA.
 | ||||
|          * Modify the flags here to allow more coalescing. | ||||
|          */ | ||||
|         if (next && | ||||
|             (next->flags & (BDRV_BLOCK_DATA|BDRV_BLOCK_ZERO)) != BDRV_BLOCK_DATA) { | ||||
|             next->flags &= ~BDRV_BLOCK_DATA; | ||||
|             next->flags |= BDRV_BLOCK_ZERO; | ||||
|         if (next && (!next->data || next->zero)) { | ||||
|             next->data = false; | ||||
|             next->zero = true; | ||||
|         } | ||||
|         break; | ||||
|     case OFORMAT_JSON: | ||||
|         printf("%s{ \"start\": %"PRId64", \"length\": %"PRId64", \"depth\": %d," | ||||
|                " \"zero\": %s, \"data\": %s", | ||||
|         printf("%s{ \"start\": %"PRId64", \"length\": %"PRId64"," | ||||
|                " \"depth\": %"PRId64", \"zero\": %s, \"data\": %s", | ||||
|                (e->start == 0 ? "[" : ",\n"), | ||||
|                e->start, e->length, e->depth, | ||||
|                (e->flags & BDRV_BLOCK_ZERO) ? "true" : "false", | ||||
|                (e->flags & BDRV_BLOCK_DATA) ? "true" : "false"); | ||||
|         if (e->flags & BDRV_BLOCK_OFFSET_VALID) { | ||||
|                e->zero ? "true" : "false", | ||||
|                e->data ? "true" : "false"); | ||||
|         if (e->has_offset) { | ||||
|             printf(", \"offset\": %"PRId64"", e->offset); | ||||
|         } | ||||
|         putchar('}'); | ||||
| @ -2201,6 +2194,7 @@ static int get_block_status(BlockDriverState *bs, int64_t sector_num, | ||||
| { | ||||
|     int64_t ret; | ||||
|     int depth; | ||||
|     BlockDriverState *file; | ||||
| 
 | ||||
|     /* As an optimization, we could cache the current range of unallocated
 | ||||
|      * clusters in each file of the chain, and avoid querying the same | ||||
| @ -2209,7 +2203,8 @@ static int get_block_status(BlockDriverState *bs, int64_t sector_num, | ||||
| 
 | ||||
|     depth = 0; | ||||
|     for (;;) { | ||||
|         ret = bdrv_get_block_status(bs, sector_num, nb_sectors, &nb_sectors); | ||||
|         ret = bdrv_get_block_status(bs, sector_num, nb_sectors, &nb_sectors, | ||||
|                                     &file); | ||||
|         if (ret < 0) { | ||||
|             return ret; | ||||
|         } | ||||
| @ -2228,13 +2223,39 @@ static int get_block_status(BlockDriverState *bs, int64_t sector_num, | ||||
| 
 | ||||
|     e->start = sector_num * BDRV_SECTOR_SIZE; | ||||
|     e->length = nb_sectors * BDRV_SECTOR_SIZE; | ||||
|     e->flags = ret & ~BDRV_BLOCK_OFFSET_MASK; | ||||
|     e->data = !!(ret & BDRV_BLOCK_DATA); | ||||
|     e->zero = !!(ret & BDRV_BLOCK_ZERO); | ||||
|     e->offset = ret & BDRV_BLOCK_OFFSET_MASK; | ||||
|     e->has_offset = !!(ret & BDRV_BLOCK_OFFSET_VALID); | ||||
|     e->depth = depth; | ||||
|     e->bs = bs; | ||||
|     if (file && e->has_offset) { | ||||
|         e->has_filename = true; | ||||
|         e->filename = file->filename; | ||||
|     } | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| static inline bool entry_mergeable(const MapEntry *curr, const MapEntry *next) | ||||
| { | ||||
|     if (curr->length == 0) { | ||||
|         return false; | ||||
|     } | ||||
|     if (curr->zero != next->zero || | ||||
|         curr->data != next->data || | ||||
|         curr->depth != next->depth || | ||||
|         curr->has_filename != next->has_filename || | ||||
|         curr->has_offset != next->has_offset) { | ||||
|         return false; | ||||
|     } | ||||
|     if (curr->has_filename && strcmp(curr->filename, next->filename)) { | ||||
|         return false; | ||||
|     } | ||||
|     if (curr->has_offset && curr->offset + curr->length != next->offset) { | ||||
|         return false; | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| static int img_map(int argc, char **argv) | ||||
| { | ||||
|     int c; | ||||
| @ -2316,10 +2337,7 @@ static int img_map(int argc, char **argv) | ||||
|             goto out; | ||||
|         } | ||||
| 
 | ||||
|         if (curr.length != 0 && curr.flags == next.flags && | ||||
|             curr.depth == next.depth && | ||||
|             ((curr.flags & BDRV_BLOCK_OFFSET_VALID) == 0 || | ||||
|              curr.offset + curr.length == next.offset)) { | ||||
|         if (entry_mergeable(&curr, &next)) { | ||||
|             curr.length += next.length; | ||||
|             continue; | ||||
|         } | ||||
|  | ||||
| @ -1,5 +1,6 @@ | ||||
| stub-obj-y += arch-query-cpu-def.o | ||||
| stub-obj-y += bdrv-commit-all.o | ||||
| stub-obj-y += blockdev-close-all-bdrv-states.o | ||||
| stub-obj-y += clock-warp.o | ||||
| stub-obj-y += cpu-get-clock.o | ||||
| stub-obj-y += cpu-get-icount.o | ||||
|  | ||||
							
								
								
									
										5
									
								
								stubs/blockdev-close-all-bdrv-states.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								stubs/blockdev-close-all-bdrv-states.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,5 @@ | ||||
| #include "block/block_int.h" | ||||
| 
 | ||||
| void blockdev_close_all_bdrv_states(void) | ||||
| { | ||||
| } | ||||
| @ -304,7 +304,6 @@ static void test_media_insert(void) | ||||
|     qmp_discard_response("{'execute':'change', 'arguments':{" | ||||
|                          " 'device':'floppy0', 'target': %s, 'arg': 'raw' }}", | ||||
|                          test_image); | ||||
|     qmp_discard_response(""); /* ignore event (open -> close) */ | ||||
| 
 | ||||
|     dir = inb(FLOPPY_BASE + reg_dir); | ||||
|     assert_bit_set(dir, DSKCHG); | ||||
| @ -335,7 +334,6 @@ static void test_media_change(void) | ||||
|      * reset the bit. */ | ||||
|     qmp_discard_response("{'execute':'eject', 'arguments':{" | ||||
|                          " 'device':'floppy0' }}"); | ||||
|     qmp_discard_response(""); /* ignore event */ | ||||
| 
 | ||||
|     dir = inb(FLOPPY_BASE + reg_dir); | ||||
|     assert_bit_set(dir, DSKCHG); | ||||
|  | ||||
| @ -132,6 +132,16 @@ _img_info | ||||
| $QEMU_IO -c "write -P 0xa 900G 512" "$TEST_IMG" | _filter_qemu_io | ||||
| $QEMU_IO -c "read -v 900G 1024" "$TEST_IMG" | _filter_qemu_io | ||||
| 
 | ||||
| echo | ||||
| echo "=== Testing qemu-img map on extents ===" | ||||
| for fmt in monolithicSparse twoGbMaxExtentSparse; do | ||||
|     IMGOPTS="subformat=$fmt" _make_test_img 31G | ||||
|     $QEMU_IO -c "write 65024 1k" "$TEST_IMG" | _filter_qemu_io | ||||
|     $QEMU_IO -c "write 2147483136 1k" "$TEST_IMG" | _filter_qemu_io | ||||
|     $QEMU_IO -c "write 5G 1k" "$TEST_IMG" | _filter_qemu_io | ||||
|     $QEMU_IMG map "$TEST_IMG" | _filter_testdir | ||||
| done | ||||
| 
 | ||||
| echo | ||||
| echo "=== Testing afl image with a very large capacity ===" | ||||
| _use_sample_img afl9.vmdk.bz2 | ||||
|  | ||||
| @ -2335,6 +2335,31 @@ e1000003f0:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................ | ||||
| read 1024/1024 bytes at offset 966367641600 | ||||
| 1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||||
| 
 | ||||
| === Testing qemu-img map on extents === | ||||
| Formatting 'TEST_DIR/iotest-version3.IMGFMT', fmt=IMGFMT size=33285996544 subformat=monolithicSparse | ||||
| wrote 1024/1024 bytes at offset 65024 | ||||
| 1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||||
| wrote 1024/1024 bytes at offset 2147483136 | ||||
| 1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||||
| wrote 1024/1024 bytes at offset 5368709120 | ||||
| 1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||||
| Offset          Length          Mapped to       File | ||||
| 0               0x20000         0x3f0000        TEST_DIR/iotest-version3.vmdk | ||||
| 0x7fff0000      0x20000         0x410000        TEST_DIR/iotest-version3.vmdk | ||||
| 0x140000000     0x10000         0x430000        TEST_DIR/iotest-version3.vmdk | ||||
| Formatting 'TEST_DIR/iotest-version3.IMGFMT', fmt=IMGFMT size=33285996544 subformat=twoGbMaxExtentSparse | ||||
| wrote 1024/1024 bytes at offset 65024 | ||||
| 1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||||
| wrote 1024/1024 bytes at offset 2147483136 | ||||
| 1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||||
| wrote 1024/1024 bytes at offset 5368709120 | ||||
| 1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||||
| Offset          Length          Mapped to       File | ||||
| 0               0x20000         0x50000         TEST_DIR/iotest-version3-s001.vmdk | ||||
| 0x7fff0000      0x10000         0x70000         TEST_DIR/iotest-version3-s001.vmdk | ||||
| 0x80000000      0x10000         0x50000         TEST_DIR/iotest-version3-s002.vmdk | ||||
| 0x140000000     0x10000         0x50000         TEST_DIR/iotest-version3-s003.vmdk | ||||
| 
 | ||||
| === Testing afl image with a very large capacity === | ||||
| qemu-img: Can't get size of device 'image': File too large | ||||
| *** done | ||||
|  | ||||
| @ -169,7 +169,6 @@ Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,if=none,id=disk | ||||
|                 "file": "TEST_DIR/t.qcow2", | ||||
|                 "encryption_key_missing": false | ||||
|             }, | ||||
|             "tray_open": false, | ||||
|             "type": "unknown" | ||||
|         } | ||||
|     ] | ||||
| @ -289,7 +288,6 @@ Testing: | ||||
|                 "file": "TEST_DIR/t.qcow2", | ||||
|                 "encryption_key_missing": false | ||||
|             }, | ||||
|             "tray_open": false, | ||||
|             "type": "unknown" | ||||
|         } | ||||
|     ] | ||||
| @ -410,7 +408,6 @@ Testing: | ||||
|                 "file": "TEST_DIR/t.qcow2", | ||||
|                 "encryption_key_missing": false | ||||
|             }, | ||||
|             "tray_open": false, | ||||
|             "type": "unknown" | ||||
|         } | ||||
|     ] | ||||
| @ -501,7 +498,6 @@ Testing: | ||||
|                 "file": "TEST_DIR/t.qcow2", | ||||
|                 "encryption_key_missing": false | ||||
|             }, | ||||
|             "tray_open": false, | ||||
|             "type": "unknown" | ||||
|         } | ||||
|     ] | ||||
|  | ||||
| @ -49,17 +49,6 @@ wait_for_tcp_port() { | ||||
| 	done | ||||
| } | ||||
| 
 | ||||
| filter_nbd() { | ||||
| 	# nbd.c error messages contain function names and line numbers that are prone | ||||
| 	# to change.  Message ordering depends on timing between send and receive | ||||
| 	# callbacks sometimes, making them unreliable. | ||||
| 	# | ||||
| 	# Filter out the TCP port number since this changes between runs. | ||||
| 	sed -e 's#^.*nbd/.*\.c:.*##g' \ | ||||
| 	    -e 's#nbd:127\.0\.0\.1:[^:]*:#nbd:127\.0\.0\.1:PORT:#g' \ | ||||
|             -e 's#\(exportname=foo\|PORT\): Failed to .*$#\1#' | ||||
| } | ||||
| 
 | ||||
| check_disconnect() { | ||||
| 	event=$1 | ||||
| 	when=$2 | ||||
| @ -84,7 +73,7 @@ EOF | ||||
| 
 | ||||
| 	$PYTHON nbd-fault-injector.py $extra_args "127.0.0.1:$port" "$TEST_DIR/nbd-fault-injector.conf" 2>&1 >/dev/null & | ||||
| 	wait_for_tcp_port "127\\.0\\.0\\.1:$port" | ||||
| 	$QEMU_IO -c "read 0 512" "$nbd_url" 2>&1 | _filter_qemu_io | filter_nbd | ||||
| 	$QEMU_IO -c "read 0 512" "$nbd_url" 2>&1 | _filter_qemu_io | _filter_nbd | ||||
| 
 | ||||
| 	echo | ||||
| } | ||||
|  | ||||
| @ -51,7 +51,6 @@ no file open, try 'help open' | ||||
| 
 | ||||
| === Check disconnect after neg2 === | ||||
| 
 | ||||
| 
 | ||||
| read failed: Input/output error | ||||
| 
 | ||||
| === Check disconnect 8 neg2 === | ||||
| @ -66,42 +65,34 @@ no file open, try 'help open' | ||||
| 
 | ||||
| === Check disconnect before request === | ||||
| 
 | ||||
| 
 | ||||
| read failed: Input/output error | ||||
| 
 | ||||
| === Check disconnect after request === | ||||
| 
 | ||||
| 
 | ||||
| read failed: Input/output error | ||||
| 
 | ||||
| === Check disconnect before reply === | ||||
| 
 | ||||
| 
 | ||||
| read failed: Input/output error | ||||
| 
 | ||||
| === Check disconnect after reply === | ||||
| 
 | ||||
| 
 | ||||
| read failed: Input/output error | ||||
| 
 | ||||
| === Check disconnect 4 reply === | ||||
| 
 | ||||
| 
 | ||||
| read failed: Input/output error | ||||
| 
 | ||||
| === Check disconnect 8 reply === | ||||
| 
 | ||||
| 
 | ||||
| read failed: Input/output error | ||||
| 
 | ||||
| === Check disconnect before data === | ||||
| 
 | ||||
| 
 | ||||
| read failed: Input/output error | ||||
| 
 | ||||
| === Check disconnect after data === | ||||
| 
 | ||||
| 
 | ||||
| read 512/512 bytes at offset 0 | ||||
| 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||||
| 
 | ||||
| @ -132,7 +123,6 @@ no file open, try 'help open' | ||||
| 
 | ||||
| === Check disconnect after neg-classic === | ||||
| 
 | ||||
| 
 | ||||
| read failed: Input/output error | ||||
| 
 | ||||
| *** done | ||||
|  | ||||
							
								
								
									
										86
									
								
								tests/qemu-iotests/117
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										86
									
								
								tests/qemu-iotests/117
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,86 @@ | ||||
| #!/bin/bash | ||||
| # | ||||
| # Test case for shared BDS between backend trees | ||||
| # | ||||
| # Copyright (C) 2016 Red Hat, Inc. | ||||
| # | ||||
| # This program is free software; you can redistribute it and/or modify | ||||
| # it under the terms of the GNU General Public License as published by | ||||
| # the Free Software Foundation; either version 2 of the License, or | ||||
| # (at your option) any later version. | ||||
| # | ||||
| # This program is distributed in the hope that it will be useful, | ||||
| # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| # GNU General Public License for more details. | ||||
| # | ||||
| # You should have received a copy of the GNU General Public License | ||||
| # along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
| # | ||||
| 
 | ||||
| # creator | ||||
| owner=mreitz@redhat.com | ||||
| 
 | ||||
| seq="$(basename $0)" | ||||
| echo "QA output created by $seq" | ||||
| 
 | ||||
| here="$PWD" | ||||
| tmp=/tmp/$$ | ||||
| status=1	# failure is the default! | ||||
| 
 | ||||
| _cleanup() | ||||
| { | ||||
| 	_cleanup_test_img | ||||
| } | ||||
| trap "_cleanup; exit \$status" 0 1 2 3 15 | ||||
| 
 | ||||
| # get standard environment, filters and checks | ||||
| . ./common.rc | ||||
| . ./common.filter | ||||
| . ./common.qemu | ||||
| 
 | ||||
| _supported_fmt qcow2 | ||||
| _supported_proto file | ||||
| _supported_os Linux | ||||
| 
 | ||||
| _make_test_img 64k | ||||
| 
 | ||||
| _launch_qemu | ||||
| 
 | ||||
| _send_qemu_cmd $QEMU_HANDLE \ | ||||
|     "{ 'execute': 'qmp_capabilities' }" \ | ||||
|     'return' | ||||
| 
 | ||||
| _send_qemu_cmd $QEMU_HANDLE \ | ||||
|     "{ 'execute': 'blockdev-add', | ||||
|        'arguments': { 'options': { 'id': 'protocol', | ||||
|                                    'driver': 'file', | ||||
|                                    'filename': '$TEST_IMG' } } }" \ | ||||
|     'return' | ||||
| 
 | ||||
| _send_qemu_cmd $QEMU_HANDLE \ | ||||
|     "{ 'execute': 'blockdev-add', | ||||
|        'arguments': { 'options': { 'id': 'format', | ||||
|                                    'driver': '$IMGFMT', | ||||
|                                    'file': 'protocol' } } }" \ | ||||
|     'return' | ||||
| 
 | ||||
| _send_qemu_cmd $QEMU_HANDLE \ | ||||
|     "{ 'execute': 'human-monitor-command', | ||||
|        'arguments': { 'command-line': 'qemu-io format \"write -P 42 0 64k\"' } }" \ | ||||
|     'return' | ||||
| 
 | ||||
| _send_qemu_cmd $QEMU_HANDLE \ | ||||
|     "{ 'execute': 'quit' }" \ | ||||
|     'return' | ||||
| 
 | ||||
| wait=1 _cleanup_qemu | ||||
| 
 | ||||
| _check_test_img | ||||
| 
 | ||||
| $QEMU_IO -c 'read -P 42 0 64k' "$TEST_IMG" | _filter_qemu_io | ||||
| 
 | ||||
| # success, all done | ||||
| echo '*** done' | ||||
| rm -f $seq.full | ||||
| status=0 | ||||
							
								
								
									
										14
									
								
								tests/qemu-iotests/117.out
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								tests/qemu-iotests/117.out
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,14 @@ | ||||
| QA output created by 117 | ||||
| Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=65536 | ||||
| {"return": {}} | ||||
| {"return": {}} | ||||
| {"return": {}} | ||||
| wrote 65536/65536 bytes at offset 0 | ||||
| 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||||
| {"return": ""} | ||||
| {"return": {}} | ||||
| {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN"} | ||||
| No errors were found on the image. | ||||
| read 65536/65536 bytes at offset 0 | ||||
| 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||||
| *** done | ||||
| @ -42,6 +42,9 @@ class ChangeBaseClass(iotests.QMPTestCase): | ||||
|                     self.has_opened = True | ||||
| 
 | ||||
|     def wait_for_open(self): | ||||
|         if not self.has_real_tray: | ||||
|             return | ||||
| 
 | ||||
|         timeout = time.clock() + 3 | ||||
|         while not self.has_opened and time.clock() < timeout: | ||||
|             self.process_events() | ||||
| @ -49,6 +52,9 @@ class ChangeBaseClass(iotests.QMPTestCase): | ||||
|             self.fail('Timeout while waiting for the tray to open') | ||||
| 
 | ||||
|     def wait_for_close(self): | ||||
|         if not self.has_real_tray: | ||||
|             return | ||||
| 
 | ||||
|         timeout = time.clock() + 3 | ||||
|         while not self.has_closed and time.clock() < timeout: | ||||
|             self.process_events() | ||||
| @ -65,6 +71,7 @@ class GeneralChangeTestsBaseClass(ChangeBaseClass): | ||||
|         self.wait_for_close() | ||||
| 
 | ||||
|         result = self.vm.qmp('query-block') | ||||
|         if self.has_real_tray: | ||||
|             self.assert_qmp(result, 'return[0]/tray_open', False) | ||||
|         self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img) | ||||
| 
 | ||||
| @ -78,6 +85,7 @@ class GeneralChangeTestsBaseClass(ChangeBaseClass): | ||||
|         self.wait_for_close() | ||||
| 
 | ||||
|         result = self.vm.qmp('query-block') | ||||
|         if self.has_real_tray: | ||||
|             self.assert_qmp(result, 'return[0]/tray_open', False) | ||||
|         self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img) | ||||
| 
 | ||||
| @ -88,6 +96,7 @@ class GeneralChangeTestsBaseClass(ChangeBaseClass): | ||||
|         self.wait_for_open() | ||||
| 
 | ||||
|         result = self.vm.qmp('query-block') | ||||
|         if self.has_real_tray: | ||||
|             self.assert_qmp(result, 'return[0]/tray_open', True) | ||||
|         self.assert_qmp_absent(result, 'return[0]/inserted') | ||||
| 
 | ||||
| @ -98,6 +107,7 @@ class GeneralChangeTestsBaseClass(ChangeBaseClass): | ||||
|         self.wait_for_open() | ||||
| 
 | ||||
|         result = self.vm.qmp('query-block') | ||||
|         if self.has_real_tray: | ||||
|             self.assert_qmp(result, 'return[0]/tray_open', True) | ||||
|         self.assert_qmp_absent(result, 'return[0]/inserted') | ||||
| 
 | ||||
| @ -109,6 +119,7 @@ class GeneralChangeTestsBaseClass(ChangeBaseClass): | ||||
|         self.wait_for_close() | ||||
| 
 | ||||
|         result = self.vm.qmp('query-block') | ||||
|         if self.has_real_tray: | ||||
|             self.assert_qmp(result, 'return[0]/tray_open', False) | ||||
|         self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img) | ||||
| 
 | ||||
| @ -119,6 +130,7 @@ class GeneralChangeTestsBaseClass(ChangeBaseClass): | ||||
|         self.wait_for_open() | ||||
| 
 | ||||
|         result = self.vm.qmp('query-block') | ||||
|         if self.has_real_tray: | ||||
|             self.assert_qmp(result, 'return[0]/tray_open', True) | ||||
|         if self.was_empty == True: | ||||
|             self.assert_qmp_absent(result, 'return[0]/inserted') | ||||
| @ -132,10 +144,8 @@ class GeneralChangeTestsBaseClass(ChangeBaseClass): | ||||
|             self.wait_for_close() | ||||
| 
 | ||||
|         result = self.vm.qmp('query-block') | ||||
|         if self.has_real_tray or not self.was_empty: | ||||
|         if self.has_real_tray: | ||||
|             self.assert_qmp(result, 'return[0]/tray_open', False) | ||||
|         else: | ||||
|             self.assert_qmp(result, 'return[0]/tray_open', True) | ||||
|         if self.was_empty == True: | ||||
|             self.assert_qmp_absent(result, 'return[0]/inserted') | ||||
|         else: | ||||
| @ -148,20 +158,18 @@ class GeneralChangeTestsBaseClass(ChangeBaseClass): | ||||
|         self.wait_for_open() | ||||
| 
 | ||||
|         result = self.vm.qmp('query-block') | ||||
|         if self.has_real_tray: | ||||
|             self.assert_qmp(result, 'return[0]/tray_open', True) | ||||
|         self.assert_qmp_absent(result, 'return[0]/inserted') | ||||
| 
 | ||||
|         result = self.vm.qmp('blockdev-close-tray', device='drive0') | ||||
|         self.assert_qmp(result, 'return', {}) | ||||
| 
 | ||||
|         if self.has_real_tray: | ||||
|         self.wait_for_close() | ||||
| 
 | ||||
|         result = self.vm.qmp('query-block') | ||||
|         if self.has_real_tray: | ||||
|             self.assert_qmp(result, 'return[0]/tray_open', False) | ||||
|         else: | ||||
|             self.assert_qmp(result, 'return[0]/tray_open', True) | ||||
|         self.assert_qmp_absent(result, 'return[0]/inserted') | ||||
| 
 | ||||
|     def test_tray_open_change(self): | ||||
| @ -171,6 +179,7 @@ class GeneralChangeTestsBaseClass(ChangeBaseClass): | ||||
|         self.wait_for_open() | ||||
| 
 | ||||
|         result = self.vm.qmp('query-block') | ||||
|         if self.has_real_tray: | ||||
|             self.assert_qmp(result, 'return[0]/tray_open', True) | ||||
|         if self.was_empty == True: | ||||
|             self.assert_qmp_absent(result, 'return[0]/inserted') | ||||
| @ -185,6 +194,7 @@ class GeneralChangeTestsBaseClass(ChangeBaseClass): | ||||
|         self.wait_for_close() | ||||
| 
 | ||||
|         result = self.vm.qmp('query-block') | ||||
|         if self.has_real_tray: | ||||
|             self.assert_qmp(result, 'return[0]/tray_open', False) | ||||
|         self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img) | ||||
| 
 | ||||
| @ -202,6 +212,7 @@ class GeneralChangeTestsBaseClass(ChangeBaseClass): | ||||
|         self.wait_for_open() | ||||
| 
 | ||||
|         result = self.vm.qmp('query-block') | ||||
|         if self.has_real_tray: | ||||
|             self.assert_qmp(result, 'return[0]/tray_open', True) | ||||
|         if self.was_empty == True: | ||||
|             self.assert_qmp_absent(result, 'return[0]/inserted') | ||||
| @ -212,6 +223,7 @@ class GeneralChangeTestsBaseClass(ChangeBaseClass): | ||||
|         self.assert_qmp(result, 'return', {}) | ||||
| 
 | ||||
|         result = self.vm.qmp('query-block') | ||||
|         if self.has_real_tray: | ||||
|             self.assert_qmp(result, 'return[0]/tray_open', True) | ||||
|         self.assert_qmp_absent(result, 'return[0]/inserted') | ||||
| 
 | ||||
| @ -220,6 +232,7 @@ class GeneralChangeTestsBaseClass(ChangeBaseClass): | ||||
|         self.assert_qmp(result, 'return', {}) | ||||
| 
 | ||||
|         result = self.vm.qmp('query-block') | ||||
|         if self.has_real_tray: | ||||
|             self.assert_qmp(result, 'return[0]/tray_open', True) | ||||
|         self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img) | ||||
| 
 | ||||
| @ -229,6 +242,7 @@ class GeneralChangeTestsBaseClass(ChangeBaseClass): | ||||
|         self.wait_for_close() | ||||
| 
 | ||||
|         result = self.vm.qmp('query-block') | ||||
|         if self.has_real_tray: | ||||
|             self.assert_qmp(result, 'return[0]/tray_open', False) | ||||
|         self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img) | ||||
| 
 | ||||
| @ -239,16 +253,14 @@ class GeneralChangeTestsBaseClass(ChangeBaseClass): | ||||
|         self.assertEquals(self.vm.get_qmp_events(wait=False), []) | ||||
| 
 | ||||
|     def test_remove_on_closed(self): | ||||
|         if self.has_opened: | ||||
|             # Empty floppy drive | ||||
|         if not self.has_real_tray: | ||||
|             return | ||||
| 
 | ||||
|         result = self.vm.qmp('x-blockdev-remove-medium', device='drive0') | ||||
|         self.assert_qmp(result, 'error/class', 'GenericError') | ||||
| 
 | ||||
|     def test_insert_on_closed(self): | ||||
|         if self.has_opened: | ||||
|             # Empty floppy drive | ||||
|         if not self.has_real_tray: | ||||
|             return | ||||
| 
 | ||||
|         result = self.vm.qmp('blockdev-add', | ||||
| @ -366,7 +378,6 @@ class TestChangeReadOnly(ChangeBaseClass): | ||||
|         self.vm.launch() | ||||
| 
 | ||||
|         result = self.vm.qmp('query-block') | ||||
|         self.assert_qmp(result, 'return[0]/tray_open', False) | ||||
|         self.assert_qmp(result, 'return[0]/inserted/ro', True) | ||||
|         self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img) | ||||
| 
 | ||||
| @ -376,11 +387,7 @@ class TestChangeReadOnly(ChangeBaseClass): | ||||
|                                                        read_only_mode='retain') | ||||
|         self.assert_qmp(result, 'return', {}) | ||||
| 
 | ||||
|         self.wait_for_open() | ||||
|         self.wait_for_close() | ||||
| 
 | ||||
|         result = self.vm.qmp('query-block') | ||||
|         self.assert_qmp(result, 'return[0]/tray_open', False) | ||||
|         self.assert_qmp(result, 'return[0]/inserted/ro', True) | ||||
|         self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img) | ||||
| 
 | ||||
| @ -390,7 +397,6 @@ class TestChangeReadOnly(ChangeBaseClass): | ||||
|         self.vm.launch() | ||||
| 
 | ||||
|         result = self.vm.qmp('query-block') | ||||
|         self.assert_qmp(result, 'return[0]/tray_open', False) | ||||
|         self.assert_qmp(result, 'return[0]/inserted/ro', True) | ||||
|         self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img) | ||||
| 
 | ||||
| @ -400,11 +406,7 @@ class TestChangeReadOnly(ChangeBaseClass): | ||||
|                                                        read_only_mode='retain') | ||||
|         self.assert_qmp(result, 'return', {}) | ||||
| 
 | ||||
|         self.wait_for_open() | ||||
|         self.wait_for_close() | ||||
| 
 | ||||
|         result = self.vm.qmp('query-block') | ||||
|         self.assert_qmp(result, 'return[0]/tray_open', False) | ||||
|         self.assert_qmp(result, 'return[0]/inserted/ro', True) | ||||
|         self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img) | ||||
| 
 | ||||
| @ -414,7 +416,6 @@ class TestChangeReadOnly(ChangeBaseClass): | ||||
|         self.vm.launch() | ||||
| 
 | ||||
|         result = self.vm.qmp('query-block') | ||||
|         self.assert_qmp(result, 'return[0]/tray_open', False) | ||||
|         self.assert_qmp(result, 'return[0]/inserted/ro', False) | ||||
|         self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img) | ||||
| 
 | ||||
| @ -427,7 +428,6 @@ class TestChangeReadOnly(ChangeBaseClass): | ||||
|         self.assertEquals(self.vm.get_qmp_events(wait=False), []) | ||||
| 
 | ||||
|         result = self.vm.qmp('query-block') | ||||
|         self.assert_qmp(result, 'return[0]/tray_open', False) | ||||
|         self.assert_qmp(result, 'return[0]/inserted/ro', False) | ||||
|         self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img) | ||||
| 
 | ||||
| @ -437,7 +437,6 @@ class TestChangeReadOnly(ChangeBaseClass): | ||||
|         self.vm.launch() | ||||
| 
 | ||||
|         result = self.vm.qmp('query-block') | ||||
|         self.assert_qmp(result, 'return[0]/tray_open', False) | ||||
|         self.assert_qmp(result, 'return[0]/inserted/ro', True) | ||||
|         self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img) | ||||
| 
 | ||||
| @ -448,11 +447,7 @@ class TestChangeReadOnly(ChangeBaseClass): | ||||
|                              read_only_mode='read-write') | ||||
|         self.assert_qmp(result, 'return', {}) | ||||
| 
 | ||||
|         self.wait_for_open() | ||||
|         self.wait_for_close() | ||||
| 
 | ||||
|         result = self.vm.qmp('query-block') | ||||
|         self.assert_qmp(result, 'return[0]/tray_open', False) | ||||
|         self.assert_qmp(result, 'return[0]/inserted/ro', False) | ||||
|         self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img) | ||||
| 
 | ||||
| @ -462,7 +457,6 @@ class TestChangeReadOnly(ChangeBaseClass): | ||||
|         self.vm.launch() | ||||
| 
 | ||||
|         result = self.vm.qmp('query-block') | ||||
|         self.assert_qmp(result, 'return[0]/tray_open', False) | ||||
|         self.assert_qmp(result, 'return[0]/inserted/ro', False) | ||||
|         self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img) | ||||
| 
 | ||||
| @ -473,11 +467,7 @@ class TestChangeReadOnly(ChangeBaseClass): | ||||
|                              read_only_mode='read-only') | ||||
|         self.assert_qmp(result, 'return', {}) | ||||
| 
 | ||||
|         self.wait_for_open() | ||||
|         self.wait_for_close() | ||||
| 
 | ||||
|         result = self.vm.qmp('query-block') | ||||
|         self.assert_qmp(result, 'return[0]/tray_open', False) | ||||
|         self.assert_qmp(result, 'return[0]/inserted/ro', True) | ||||
|         self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img) | ||||
| 
 | ||||
| @ -486,7 +476,6 @@ class TestChangeReadOnly(ChangeBaseClass): | ||||
|         self.vm.launch() | ||||
| 
 | ||||
|         result = self.vm.qmp('query-block') | ||||
|         self.assert_qmp(result, 'return[0]/tray_open', False) | ||||
|         self.assert_qmp(result, 'return[0]/inserted/ro', False) | ||||
|         self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img) | ||||
| 
 | ||||
| @ -497,11 +486,7 @@ class TestChangeReadOnly(ChangeBaseClass): | ||||
|                              read_only_mode='read-only') | ||||
|         self.assert_qmp(result, 'return', {}) | ||||
| 
 | ||||
|         self.wait_for_open() | ||||
|         self.wait_for_close() | ||||
| 
 | ||||
|         result = self.vm.qmp('query-block') | ||||
|         self.assert_qmp(result, 'return[0]/tray_open', False) | ||||
|         self.assert_qmp(result, 'return[0]/inserted/ro', True) | ||||
|         self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img) | ||||
| 
 | ||||
| @ -511,7 +496,6 @@ class TestChangeReadOnly(ChangeBaseClass): | ||||
|         self.vm.launch() | ||||
| 
 | ||||
|         result = self.vm.qmp('query-block') | ||||
|         self.assert_qmp(result, 'return[0]/tray_open', False) | ||||
|         self.assert_qmp(result, 'return[0]/inserted/ro', False) | ||||
|         self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img) | ||||
| 
 | ||||
| @ -522,10 +506,7 @@ class TestChangeReadOnly(ChangeBaseClass): | ||||
|                              read_only_mode='read-write') | ||||
|         self.assert_qmp(result, 'error/class', 'GenericError') | ||||
| 
 | ||||
|         self.assertEquals(self.vm.get_qmp_events(wait=False), []) | ||||
| 
 | ||||
|         result = self.vm.qmp('query-block') | ||||
|         self.assert_qmp(result, 'return[0]/tray_open', False) | ||||
|         self.assert_qmp(result, 'return[0]/inserted/ro', False) | ||||
|         self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img) | ||||
| 
 | ||||
| @ -535,7 +516,6 @@ class TestChangeReadOnly(ChangeBaseClass): | ||||
|         self.vm.launch() | ||||
| 
 | ||||
|         result = self.vm.qmp('query-block') | ||||
|         self.assert_qmp(result, 'return[0]/tray_open', False) | ||||
|         self.assert_qmp(result, 'return[0]/inserted/ro', True) | ||||
|         self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img) | ||||
| 
 | ||||
| @ -545,11 +525,7 @@ class TestChangeReadOnly(ChangeBaseClass): | ||||
|                                                        read_only_mode='retain') | ||||
|         self.assert_qmp(result, 'return', {}) | ||||
| 
 | ||||
|         self.wait_for_open() | ||||
|         self.wait_for_close() | ||||
| 
 | ||||
|         result = self.vm.qmp('query-block') | ||||
|         self.assert_qmp(result, 'return[0]/tray_open', False) | ||||
|         self.assert_qmp(result, 'return[0]/inserted/ro', True) | ||||
|         self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img) | ||||
| 
 | ||||
| @ -559,7 +535,6 @@ class TestChangeReadOnly(ChangeBaseClass): | ||||
|         self.vm.launch() | ||||
| 
 | ||||
|         result = self.vm.qmp('query-block') | ||||
|         self.assert_qmp(result, 'return[0]/tray_open', False) | ||||
|         self.assert_qmp(result, 'return[0]/inserted/ro', False) | ||||
|         self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img) | ||||
| 
 | ||||
| @ -569,10 +544,7 @@ class TestChangeReadOnly(ChangeBaseClass): | ||||
|                                                        read_only_mode='retain') | ||||
|         self.assert_qmp(result, 'error/class', 'GenericError') | ||||
| 
 | ||||
|         self.assertEquals(self.vm.get_qmp_events(wait=False), []) | ||||
| 
 | ||||
|         result = self.vm.qmp('query-block') | ||||
|         self.assert_qmp(result, 'return[0]/tray_open', False) | ||||
|         self.assert_qmp(result, 'return[0]/inserted/ro', False) | ||||
|         self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img) | ||||
| 
 | ||||
| @ -582,7 +554,6 @@ class TestChangeReadOnly(ChangeBaseClass): | ||||
|         self.vm.launch() | ||||
| 
 | ||||
|         result = self.vm.qmp('query-block') | ||||
|         self.assert_qmp(result, 'return[0]/tray_open', False) | ||||
|         self.assert_qmp(result, 'return[0]/inserted/ro', False) | ||||
|         self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img) | ||||
| 
 | ||||
| @ -594,13 +565,7 @@ class TestChangeReadOnly(ChangeBaseClass): | ||||
|                                                'driver': 'file'}}) | ||||
|         self.assert_qmp(result, 'return', {}) | ||||
| 
 | ||||
|         result = self.vm.qmp('blockdev-open-tray', device='drive0', force=True) | ||||
|         self.assert_qmp(result, 'return', {}) | ||||
| 
 | ||||
|         self.wait_for_open() | ||||
| 
 | ||||
|         result = self.vm.qmp('query-block') | ||||
|         self.assert_qmp(result, 'return[0]/tray_open', True) | ||||
|         self.assert_qmp(result, 'return[0]/inserted/ro', False) | ||||
|         self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img) | ||||
| 
 | ||||
| @ -608,7 +573,6 @@ class TestChangeReadOnly(ChangeBaseClass): | ||||
|         self.assert_qmp(result, 'return', {}) | ||||
| 
 | ||||
|         result = self.vm.qmp('query-block') | ||||
|         self.assert_qmp(result, 'return[0]/tray_open', True) | ||||
|         self.assert_qmp_absent(result, 'return[0]/inserted') | ||||
| 
 | ||||
|         result = self.vm.qmp('x-blockdev-insert-medium', device='drive0', | ||||
| @ -616,17 +580,10 @@ class TestChangeReadOnly(ChangeBaseClass): | ||||
|         self.assert_qmp(result, 'return', {}) | ||||
| 
 | ||||
|         result = self.vm.qmp('query-block') | ||||
|         self.assert_qmp(result, 'return[0]/tray_open', True) | ||||
|         self.assert_qmp(result, 'return[0]/inserted/ro', True) | ||||
|         self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img) | ||||
| 
 | ||||
|         result = self.vm.qmp('blockdev-close-tray', device='drive0') | ||||
|         self.assert_qmp(result, 'return', {}) | ||||
| 
 | ||||
|         self.wait_for_close() | ||||
| 
 | ||||
|         result = self.vm.qmp('query-block') | ||||
|         self.assert_qmp(result, 'return[0]/tray_open', False) | ||||
|         self.assert_qmp(result, 'return[0]/inserted/ro', True) | ||||
|         self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img) | ||||
| 
 | ||||
| @ -648,7 +605,6 @@ class TestBlockJobsAfterCycle(ChangeBaseClass): | ||||
|         self.assert_qmp(result, 'return', {}) | ||||
| 
 | ||||
|         result = self.vm.qmp('query-block') | ||||
|         self.assert_qmp(result, 'return[0]/tray_open', False) | ||||
|         self.assert_qmp(result, 'return[0]/inserted/image/format', 'null-co') | ||||
| 
 | ||||
|         # For device-less BBs, calling blockdev-open-tray or blockdev-close-tray | ||||
| @ -671,7 +627,6 @@ class TestBlockJobsAfterCycle(ChangeBaseClass): | ||||
|         self.assert_qmp(result, 'return', {}) | ||||
| 
 | ||||
|         result = self.vm.qmp('query-block') | ||||
|         self.assert_qmp(result, 'return[0]/tray_open', False) | ||||
|         self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img) | ||||
| 
 | ||||
|     def tearDown(self): | ||||
| @ -717,4 +672,6 @@ if __name__ == '__main__': | ||||
|         # We need floppy and IDE CD-ROM | ||||
|         iotests.notrun('not suitable for this machine type: %s' % | ||||
|                        iotests.qemu_default_machine) | ||||
|     iotests.main() | ||||
|     # Need to support image creation | ||||
|     iotests.main(supported_fmts=['vpc', 'parallels', 'qcow', 'vdi', 'qcow2', | ||||
|                                  'vmdk', 'raw', 'vhdx', 'qed']) | ||||
|  | ||||
							
								
								
									
										92
									
								
								tests/qemu-iotests/140
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										92
									
								
								tests/qemu-iotests/140
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,92 @@ | ||||
| #!/bin/bash | ||||
| # | ||||
| # Test case for ejecting a BB with an NBD server attached to it | ||||
| # | ||||
| # Copyright (C) 2016 Red Hat, Inc. | ||||
| # | ||||
| # This program is free software; you can redistribute it and/or modify | ||||
| # it under the terms of the GNU General Public License as published by | ||||
| # the Free Software Foundation; either version 2 of the License, or | ||||
| # (at your option) any later version. | ||||
| # | ||||
| # This program is distributed in the hope that it will be useful, | ||||
| # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| # GNU General Public License for more details. | ||||
| # | ||||
| # You should have received a copy of the GNU General Public License | ||||
| # along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
| # | ||||
| 
 | ||||
| # creator | ||||
| owner=mreitz@redhat.com | ||||
| 
 | ||||
| seq="$(basename $0)" | ||||
| echo "QA output created by $seq" | ||||
| 
 | ||||
| here="$PWD" | ||||
| tmp=/tmp/$$ | ||||
| status=1	# failure is the default! | ||||
| 
 | ||||
| _cleanup() | ||||
| { | ||||
|     _cleanup_test_img | ||||
|     rm -f "$TEST_DIR/nbd" | ||||
| } | ||||
| trap "_cleanup; exit \$status" 0 1 2 3 15 | ||||
| 
 | ||||
| # get standard environment, filters and checks | ||||
| . ./common.rc | ||||
| . ./common.filter | ||||
| . ./common.qemu | ||||
| 
 | ||||
| _supported_fmt generic | ||||
| _supported_proto file | ||||
| _supported_os Linux | ||||
| 
 | ||||
| _make_test_img 64k | ||||
| 
 | ||||
| $QEMU_IO -c 'write -P 42 0 64k' "$TEST_IMG" | _filter_qemu_io | ||||
| 
 | ||||
| keep_stderr=y \ | ||||
| _launch_qemu -drive if=ide,media=cdrom,id=drv,file="$TEST_IMG",format=$IMGFMT \ | ||||
|     2> >(_filter_nbd) | ||||
| 
 | ||||
| _send_qemu_cmd $QEMU_HANDLE \ | ||||
|     "{ 'execute': 'qmp_capabilities' }" \ | ||||
|     'return' | ||||
| 
 | ||||
| _send_qemu_cmd $QEMU_HANDLE \ | ||||
|     "{ 'execute': 'nbd-server-start', | ||||
|        'arguments': { 'addr': { 'type': 'unix', | ||||
|                                 'data': { 'path': '$TEST_DIR/nbd' }}}}" \ | ||||
|     'return' | ||||
| 
 | ||||
| _send_qemu_cmd $QEMU_HANDLE \ | ||||
|     "{ 'execute': 'nbd-server-add', | ||||
|        'arguments': { 'device': 'drv' }}" \ | ||||
|     'return' | ||||
| 
 | ||||
| $QEMU_IO_PROG -f raw -c 'read -P 42 0 64k' \ | ||||
|     "nbd+unix:///drv?socket=$TEST_DIR/nbd" 2>&1 \ | ||||
|     | _filter_qemu_io | _filter_nbd | ||||
| 
 | ||||
| _send_qemu_cmd $QEMU_HANDLE \ | ||||
|     "{ 'execute': 'eject', | ||||
|        'arguments': { 'device': 'drv' }}" \ | ||||
|     'return' | ||||
| 
 | ||||
| $QEMU_IO_PROG -f raw -c close \ | ||||
|     "nbd+unix:///drv?socket=$TEST_DIR/nbd" 2>&1 \ | ||||
|     | _filter_qemu_io | _filter_nbd | ||||
| 
 | ||||
| _send_qemu_cmd $QEMU_HANDLE \ | ||||
|     "{ 'execute': 'quit' }" \ | ||||
|     'return' | ||||
| 
 | ||||
| wait=1 _cleanup_qemu | ||||
| 
 | ||||
| # success, all done | ||||
| echo '*** done' | ||||
| rm -f $seq.full | ||||
| status=0 | ||||
							
								
								
									
										16
									
								
								tests/qemu-iotests/140.out
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								tests/qemu-iotests/140.out
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,16 @@ | ||||
| QA output created by 140 | ||||
| Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=65536 | ||||
| wrote 65536/65536 bytes at offset 0 | ||||
| 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||||
| {"return": {}} | ||||
| {"return": {}} | ||||
| {"return": {}} | ||||
| read 65536/65536 bytes at offset 0 | ||||
| 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||||
| {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "DEVICE_TRAY_MOVED", "data": {"device": "drv", "tray-open": true}} | ||||
| {"return": {}} | ||||
| can't open device nbd+unix:///drv?socket=TEST_DIR/nbd: Failed to read export length | ||||
| no file open, try 'help open' | ||||
| {"return": {}} | ||||
| {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN"} | ||||
| *** done | ||||
							
								
								
									
										186
									
								
								tests/qemu-iotests/141
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										186
									
								
								tests/qemu-iotests/141
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,186 @@ | ||||
| #!/bin/bash | ||||
| # | ||||
| # Test case for ejecting BDSs with block jobs still running on them | ||||
| # | ||||
| # Copyright (C) 2016 Red Hat, Inc. | ||||
| # | ||||
| # This program is free software; you can redistribute it and/or modify | ||||
| # it under the terms of the GNU General Public License as published by | ||||
| # the Free Software Foundation; either version 2 of the License, or | ||||
| # (at your option) any later version. | ||||
| # | ||||
| # This program is distributed in the hope that it will be useful, | ||||
| # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| # GNU General Public License for more details. | ||||
| # | ||||
| # You should have received a copy of the GNU General Public License | ||||
| # along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
| # | ||||
| 
 | ||||
| # creator | ||||
| owner=mreitz@redhat.com | ||||
| 
 | ||||
| seq="$(basename $0)" | ||||
| echo "QA output created by $seq" | ||||
| 
 | ||||
| here="$PWD" | ||||
| tmp=/tmp/$$ | ||||
| status=1	# failure is the default! | ||||
| 
 | ||||
| _cleanup() | ||||
| { | ||||
|     _cleanup_test_img | ||||
|     rm -f "$TEST_DIR/{b,m,o}.$IMGFMT" | ||||
| } | ||||
| trap "_cleanup; exit \$status" 0 1 2 3 15 | ||||
| 
 | ||||
| # get standard environment, filters and checks | ||||
| . ./common.rc | ||||
| . ./common.filter | ||||
| . ./common.qemu | ||||
| 
 | ||||
| # Needs backing file and backing format support | ||||
| _supported_fmt qcow2 qed | ||||
| _supported_proto file | ||||
| _supported_os Linux | ||||
| 
 | ||||
| 
 | ||||
| test_blockjob() | ||||
| { | ||||
|     _send_qemu_cmd $QEMU_HANDLE \ | ||||
|         "{'execute': 'blockdev-add', | ||||
|           'arguments': { | ||||
|               'options': { | ||||
|                   'id': 'drv0', | ||||
|                   'driver': '$IMGFMT', | ||||
|                   'file': { | ||||
|                       'driver': 'file', | ||||
|                       'filename': '$TEST_IMG' | ||||
|                   }}}}" \ | ||||
|         'return' | ||||
| 
 | ||||
|     _send_qemu_cmd $QEMU_HANDLE \ | ||||
|         "$1" \ | ||||
|         "$2" \ | ||||
|         | _filter_img_create | ||||
| 
 | ||||
|     # We want this to return an error because the block job is still running | ||||
|     _send_qemu_cmd $QEMU_HANDLE \ | ||||
|         "{'execute': 'x-blockdev-remove-medium', | ||||
|           'arguments': {'device': 'drv0'}}" \ | ||||
|         'error' | ||||
| 
 | ||||
|     _send_qemu_cmd $QEMU_HANDLE \ | ||||
|         "{'execute': 'block-job-cancel', | ||||
|           'arguments': {'device': 'drv0'}}" \ | ||||
|         "$3" | ||||
| 
 | ||||
|     _send_qemu_cmd $QEMU_HANDLE \ | ||||
|         "{'execute': 'x-blockdev-del', | ||||
|           'arguments': {'id': 'drv0'}}" \ | ||||
|         'return' | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| TEST_IMG="$TEST_DIR/b.$IMGFMT" _make_test_img 1M | ||||
| TEST_IMG="$TEST_DIR/m.$IMGFMT" _make_test_img -b "$TEST_DIR/b.$IMGFMT" 1M | ||||
| _make_test_img -b "$TEST_DIR/m.$IMGFMT" 1M | ||||
| 
 | ||||
| _launch_qemu -nodefaults | ||||
| 
 | ||||
| _send_qemu_cmd $QEMU_HANDLE \ | ||||
|     "{'execute': 'qmp_capabilities'}" \ | ||||
|     'return' | ||||
| 
 | ||||
| echo | ||||
| echo '=== Testing drive-backup ===' | ||||
| echo | ||||
| 
 | ||||
| # drive-backup will not send BLOCK_JOB_READY by itself, and cancelling the job | ||||
| # will consequently result in BLOCK_JOB_CANCELLED being emitted. | ||||
| 
 | ||||
| test_blockjob \ | ||||
|     "{'execute': 'drive-backup', | ||||
|       'arguments': {'device': 'drv0', | ||||
|                     'target': '$TEST_DIR/o.$IMGFMT', | ||||
|                     'format': '$IMGFMT', | ||||
|                     'sync': 'none'}}" \ | ||||
|     'return' \ | ||||
|     'BLOCK_JOB_CANCELLED' | ||||
| 
 | ||||
| echo | ||||
| echo '=== Testing drive-mirror ===' | ||||
| echo | ||||
| 
 | ||||
| # drive-mirror will send BLOCK_JOB_READY basically immediately, and cancelling | ||||
| # the job will consequently result in BLOCK_JOB_COMPLETED being emitted. | ||||
| 
 | ||||
| test_blockjob \ | ||||
|     "{'execute': 'drive-mirror', | ||||
|       'arguments': {'device': 'drv0', | ||||
|                     'target': '$TEST_DIR/o.$IMGFMT', | ||||
|                     'format': '$IMGFMT', | ||||
|                     'sync': 'none'}}" \ | ||||
|     'BLOCK_JOB_READY' \ | ||||
|     'BLOCK_JOB_COMPLETED' | ||||
| 
 | ||||
| echo | ||||
| echo '=== Testing active block-commit ===' | ||||
| echo | ||||
| 
 | ||||
| # An active block-commit will send BLOCK_JOB_READY basically immediately, and | ||||
| # cancelling the job will consequently result in BLOCK_JOB_COMPLETED being | ||||
| # emitted. | ||||
| 
 | ||||
| test_blockjob \ | ||||
|     "{'execute': 'block-commit', | ||||
|       'arguments': {'device': 'drv0'}}" \ | ||||
|     'BLOCK_JOB_READY' \ | ||||
|     'BLOCK_JOB_COMPLETED' | ||||
| 
 | ||||
| echo | ||||
| echo '=== Testing non-active block-commit ===' | ||||
| echo | ||||
| 
 | ||||
| # Give block-commit something to work on, otherwise it would be done | ||||
| # immediately, send a BLOCK_JOB_COMPLETED and ejecting the BDS would work just | ||||
| # fine without the block job still running. | ||||
| 
 | ||||
| $QEMU_IO -c 'write 0 1M' "$TEST_DIR/m.$IMGFMT" | _filter_qemu_io | ||||
| 
 | ||||
| test_blockjob \ | ||||
|     "{'execute': 'block-commit', | ||||
|       'arguments': {'device': 'drv0', | ||||
|                     'top':    '$TEST_DIR/m.$IMGFMT', | ||||
|                     'speed':  1}}" \ | ||||
|     'return' \ | ||||
|     'BLOCK_JOB_CANCELLED' | ||||
| 
 | ||||
| echo | ||||
| echo '=== Testing block-stream ===' | ||||
| echo | ||||
| 
 | ||||
| # Give block-stream something to work on, otherwise it would be done | ||||
| # immediately, send a BLOCK_JOB_COMPLETED and ejecting the BDS would work just | ||||
| # fine without the block job still running. | ||||
| 
 | ||||
| $QEMU_IO -c 'write 0 1M' "$TEST_DIR/b.$IMGFMT" | _filter_qemu_io | ||||
| 
 | ||||
| # With some data to stream (and @speed set to 1), block-stream will not complete | ||||
| # until we send the block-job-cancel command. Therefore, no event other than | ||||
| # BLOCK_JOB_CANCELLED will be emitted. | ||||
| 
 | ||||
| test_blockjob \ | ||||
|     "{'execute': 'block-stream', | ||||
|       'arguments': {'device': 'drv0', | ||||
|                     'speed': 1}}" \ | ||||
|     'return' \ | ||||
|     'BLOCK_JOB_CANCELLED' | ||||
| 
 | ||||
| _cleanup_qemu | ||||
| 
 | ||||
| # success, all done | ||||
| echo "*** done" | ||||
| rm -f $seq.full | ||||
| status=0 | ||||
							
								
								
									
										59
									
								
								tests/qemu-iotests/141.out
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								tests/qemu-iotests/141.out
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,59 @@ | ||||
| QA output created by 141 | ||||
| Formatting 'TEST_DIR/b.IMGFMT', fmt=IMGFMT size=1048576 | ||||
| Formatting 'TEST_DIR/m.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/b.IMGFMT | ||||
| Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/m.IMGFMT | ||||
| {"return": {}} | ||||
| 
 | ||||
| === Testing drive-backup === | ||||
| 
 | ||||
| {"return": {}} | ||||
| Formatting 'TEST_DIR/o.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT backing_fmt=IMGFMT | ||||
| {"return": {}} | ||||
| {"error": {"class": "GenericError", "desc": "Node 'drv0' is busy: block device is in use by block job: backup"}} | ||||
| {"return": {}} | ||||
| {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "drv0", "len": 1048576, "offset": 0, "speed": 0, "type": "backup"}} | ||||
| {"return": {}} | ||||
| 
 | ||||
| === Testing drive-mirror === | ||||
| 
 | ||||
| {"return": {}} | ||||
| Formatting 'TEST_DIR/o.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT backing_fmt=IMGFMT | ||||
| {"return": {}} | ||||
| {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "drv0", "len": 0, "offset": 0, "speed": 0, "type": "mirror"}} | ||||
| {"error": {"class": "GenericError", "desc": "Node 'drv0' is busy: block device is in use by block job: mirror"}} | ||||
| {"return": {}} | ||||
| {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "drv0", "len": 0, "offset": 0, "speed": 0, "type": "mirror"}} | ||||
| {"return": {}} | ||||
| 
 | ||||
| === Testing active block-commit === | ||||
| 
 | ||||
| {"return": {}} | ||||
| {"return": {}} | ||||
| {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "drv0", "len": 0, "offset": 0, "speed": 0, "type": "commit"}} | ||||
| {"error": {"class": "GenericError", "desc": "Node 'drv0' is busy: block device is in use by block job: commit"}} | ||||
| {"return": {}} | ||||
| {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "drv0", "len": 0, "offset": 0, "speed": 0, "type": "commit"}} | ||||
| {"return": {}} | ||||
| 
 | ||||
| === Testing non-active block-commit === | ||||
| 
 | ||||
| wrote 1048576/1048576 bytes at offset 0 | ||||
| 1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||||
| {"return": {}} | ||||
| {"return": {}} | ||||
| {"error": {"class": "GenericError", "desc": "Node 'drv0' is busy: block device is in use by block job: commit"}} | ||||
| {"return": {}} | ||||
| {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "drv0", "len": 1048576, "offset": 524288, "speed": 1, "type": "commit"}} | ||||
| {"return": {}} | ||||
| 
 | ||||
| === Testing block-stream === | ||||
| 
 | ||||
| wrote 1048576/1048576 bytes at offset 0 | ||||
| 1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||||
| {"return": {}} | ||||
| {"return": {}} | ||||
| {"error": {"class": "GenericError", "desc": "Node 'drv0' is busy: block device is in use by block job: stream"}} | ||||
| {"return": {}} | ||||
| {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "drv0", "len": 1048576, "offset": 524288, "speed": 1, "type": "stream"}} | ||||
| {"return": {}} | ||||
| *** done | ||||
							
								
								
									
										73
									
								
								tests/qemu-iotests/143
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										73
									
								
								tests/qemu-iotests/143
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,73 @@ | ||||
| #!/bin/bash | ||||
| # | ||||
| # Test case for connecting to a non-existing NBD export name | ||||
| # | ||||
| # Copyright (C) 2016 Red Hat, Inc. | ||||
| # | ||||
| # This program is free software; you can redistribute it and/or modify | ||||
| # it under the terms of the GNU General Public License as published by | ||||
| # the Free Software Foundation; either version 2 of the License, or | ||||
| # (at your option) any later version. | ||||
| # | ||||
| # This program is distributed in the hope that it will be useful, | ||||
| # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| # GNU General Public License for more details. | ||||
| # | ||||
| # You should have received a copy of the GNU General Public License | ||||
| # along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
| # | ||||
| 
 | ||||
| # creator | ||||
| owner=mreitz@redhat.com | ||||
| 
 | ||||
| seq="$(basename $0)" | ||||
| echo "QA output created by $seq" | ||||
| 
 | ||||
| here="$PWD" | ||||
| tmp=/tmp/$$ | ||||
| status=1	# failure is the default! | ||||
| 
 | ||||
| _cleanup() | ||||
| { | ||||
|     rm -f "$TEST_DIR/nbd" | ||||
| } | ||||
| trap "_cleanup; exit \$status" 0 1 2 3 15 | ||||
| 
 | ||||
| # get standard environment, filters and checks | ||||
| . ./common.rc | ||||
| . ./common.filter | ||||
| . ./common.qemu | ||||
| 
 | ||||
| _supported_fmt generic | ||||
| _supported_proto generic | ||||
| _supported_os Linux | ||||
| 
 | ||||
| keep_stderr=y \ | ||||
| _launch_qemu 2> >(_filter_nbd) | ||||
| 
 | ||||
| _send_qemu_cmd $QEMU_HANDLE \ | ||||
|     "{ 'execute': 'qmp_capabilities' }" \ | ||||
|     'return' | ||||
| 
 | ||||
| _send_qemu_cmd $QEMU_HANDLE \ | ||||
|     "{ 'execute': 'nbd-server-start', | ||||
|        'arguments': { 'addr': { 'type': 'unix', | ||||
|                                 'data': { 'path': '$TEST_DIR/nbd' }}}}" \ | ||||
|     'return' | ||||
| 
 | ||||
| # This should just result in a client error, not in the server crashing | ||||
| $QEMU_IO_PROG -f raw -c quit \ | ||||
|     "nbd+unix:///no_such_export?socket=$TEST_DIR/nbd" 2>&1 \ | ||||
|     | _filter_qemu_io | _filter_nbd | ||||
| 
 | ||||
| _send_qemu_cmd $QEMU_HANDLE \ | ||||
|     "{ 'execute': 'quit' }" \ | ||||
|     'return' | ||||
| 
 | ||||
| wait=1 _cleanup_qemu | ||||
| 
 | ||||
| # success, all done | ||||
| echo '*** done' | ||||
| rm -f $seq.full | ||||
| status=0 | ||||
							
								
								
									
										7
									
								
								tests/qemu-iotests/143.out
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								tests/qemu-iotests/143.out
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,7 @@ | ||||
| QA output created by 143 | ||||
| {"return": {}} | ||||
| {"return": {}} | ||||
| can't open device nbd+unix:///no_such_export?socket=TEST_DIR/nbd: Failed to read export length | ||||
| {"return": {}} | ||||
| {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN"} | ||||
| *** done | ||||
							
								
								
									
										114
									
								
								tests/qemu-iotests/144
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										114
									
								
								tests/qemu-iotests/144
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,114 @@ | ||||
| #!/bin/bash | ||||
| # Check live snapshot, followed by active commit, and another snapshot. | ||||
| # | ||||
| # This test is to catch the error case of BZ #1300209: | ||||
| # https://bugzilla.redhat.com/show_bug.cgi?id=1300209 | ||||
| # | ||||
| # Copyright (C) 2016 Red Hat, Inc. | ||||
| # | ||||
| # This program is free software; you can redistribute it and/or modify | ||||
| # it under the terms of the GNU General Public License as published by | ||||
| # the Free Software Foundation; either version 2 of the License, or | ||||
| # (at your option) any later version. | ||||
| # | ||||
| # This program is distributed in the hope that it will be useful, | ||||
| # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| # GNU General Public License for more details. | ||||
| # | ||||
| # You should have received a copy of the GNU General Public License | ||||
| # along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
| # | ||||
| 
 | ||||
| # creator | ||||
| owner=jcody@redhat.com | ||||
| 
 | ||||
| seq=`basename $0` | ||||
| echo "QA output created by $seq" | ||||
| 
 | ||||
| here=`pwd` | ||||
| status=1	# failure is the default! | ||||
| 
 | ||||
| TMP_SNAP1=${TEST_DIR}/tmp.qcow2 | ||||
| TMP_SNAP2=${TEST_DIR}/tmp2.qcow2 | ||||
| 
 | ||||
| _cleanup() | ||||
| { | ||||
|     _cleanup_qemu | ||||
|     rm -f "${TEST_IMG}" "${TMP_SNAP1}" "${TMP_SNAP2}" | ||||
| } | ||||
| 
 | ||||
| trap "_cleanup; exit \$status" 0 1 2 3 15 | ||||
| 
 | ||||
| # get standard environment, filters and checks | ||||
| . ./common.rc | ||||
| . ./common.filter | ||||
| . ./common.qemu | ||||
| 
 | ||||
| _supported_fmt qcow2 | ||||
| _supported_proto file | ||||
| _supported_os Linux | ||||
| 
 | ||||
| size=512M | ||||
| 
 | ||||
| _make_test_img $size | ||||
| 
 | ||||
| echo | ||||
| echo === Launching QEMU === | ||||
| echo | ||||
| 
 | ||||
| qemu_comm_method="qmp" | ||||
| _launch_qemu -drive file="${TEST_IMG}",if=virtio | ||||
| h=$QEMU_HANDLE | ||||
| 
 | ||||
| 
 | ||||
| echo | ||||
| echo === Performing Live Snapshot 1 === | ||||
| echo | ||||
| 
 | ||||
| _send_qemu_cmd $h "{ 'execute': 'qmp_capabilities' }" "return" | ||||
| 
 | ||||
| 
 | ||||
| # First live snapshot, new overlay as active layer | ||||
| _send_qemu_cmd $h "{ 'execute': 'blockdev-snapshot-sync', | ||||
|                                 'arguments': { | ||||
|                                              'device': 'virtio0', | ||||
|                                              'snapshot-file':'${TMP_SNAP1}', | ||||
|                                              'format': 'qcow2' | ||||
|                                              } | ||||
|                     }" "return" | ||||
| 
 | ||||
| echo | ||||
| echo === Performing block-commit on active layer === | ||||
| echo | ||||
| 
 | ||||
| # Block commit on active layer, push the new overlay into base | ||||
| _send_qemu_cmd $h "{ 'execute': 'block-commit', | ||||
|                                 'arguments': { | ||||
|                                                  'device': 'virtio0' | ||||
|                                               } | ||||
|                     }" "READY" | ||||
| 
 | ||||
| _send_qemu_cmd $h "{ 'execute': 'block-job-complete', | ||||
|                                 'arguments': { | ||||
|                                                 'device': 'virtio0' | ||||
|                                               } | ||||
|                    }" "COMPLETED" | ||||
| 
 | ||||
| echo | ||||
| echo === Performing Live Snapshot 2 === | ||||
| echo | ||||
| 
 | ||||
| # New live snapshot, new overlays as active layer | ||||
| _send_qemu_cmd $h "{ 'execute': 'blockdev-snapshot-sync', | ||||
|                                 'arguments': { | ||||
|                                                 'device': 'virtio0', | ||||
|                                                 'snapshot-file':'${TMP_SNAP2}', | ||||
|                                                 'format': 'qcow2' | ||||
|                                               } | ||||
|                    }" "return" | ||||
| 
 | ||||
| # success, all done | ||||
| echo "*** done" | ||||
| rm -f $seq.full | ||||
| status=0 | ||||
							
								
								
									
										24
									
								
								tests/qemu-iotests/144.out
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								tests/qemu-iotests/144.out
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,24 @@ | ||||
| QA output created by 144 | ||||
| Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=536870912 | ||||
| 
 | ||||
| === Launching QEMU === | ||||
| 
 | ||||
| 
 | ||||
| === Performing Live Snapshot 1 === | ||||
| 
 | ||||
| {"return": {}} | ||||
| Formatting 'TEST_DIR/tmp.qcow2', fmt=qcow2 size=536870912 backing_file=TEST_DIR/t.qcow2 backing_fmt=qcow2 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 | ||||
| {"return": {}} | ||||
| 
 | ||||
| === Performing block-commit on active layer === | ||||
| 
 | ||||
| {"return": {}} | ||||
| {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "virtio0", "len": 0, "offset": 0, "speed": 0, "type": "commit"}} | ||||
| {"return": {}} | ||||
| {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "virtio0", "len": 0, "offset": 0, "speed": 0, "type": "commit"}} | ||||
| 
 | ||||
| === Performing Live Snapshot 2 === | ||||
| 
 | ||||
| Formatting 'TEST_DIR/tmp2.qcow2', fmt=qcow2 size=536870912 backing_file=TEST_DIR/t.qcow2 backing_fmt=qcow2 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 | ||||
| {"return": {}} | ||||
| *** done | ||||
| @ -230,5 +230,18 @@ _filter_qemu_img_map() | ||||
|         -e 's/Mapped to *//' | _filter_testdir | _filter_imgfmt | ||||
| } | ||||
| 
 | ||||
| _filter_nbd() | ||||
| { | ||||
|     # nbd.c error messages contain function names and line numbers that are | ||||
|     # prone to change.  Message ordering depends on timing between send and | ||||
|     # receive callbacks sometimes, making them unreliable. | ||||
|     # | ||||
|     # Filter out the TCP port number since this changes between runs. | ||||
|     sed -e '/nbd\/.*\.c:/d' \ | ||||
|         -e 's#nbd:\(//\)\?127\.0\.0\.1:[0-9]*#nbd:\1127.0.0.1:PORT#g' \ | ||||
|         -e "s#?socket=$TEST_DIR#?socket=TEST_DIR#g" \ | ||||
|         -e 's#\(exportname=foo\|PORT\): Failed to .*$#\1#' | ||||
| } | ||||
| 
 | ||||
| # make sure this script returns success | ||||
| true | ||||
|  | ||||
| @ -129,6 +129,8 @@ function _send_qemu_cmd() | ||||
| # $qemu_comm_method: set this variable to 'monitor' (case insensitive) | ||||
| #                    to use the QEMU HMP monitor for communication. | ||||
| #                    Otherwise, the default of QMP is used. | ||||
| # $keep_stderr: Set this variable to 'y' to keep QEMU's stderr output on stderr. | ||||
| #               If this variable is empty, stderr will be redirected to stdout. | ||||
| # Returns: | ||||
| # $QEMU_HANDLE: set to a handle value to communicate with this QEMU instance. | ||||
| # | ||||
| @ -151,11 +153,20 @@ function _launch_qemu() | ||||
|     mkfifo "${fifo_out}" | ||||
|     mkfifo "${fifo_in}" | ||||
| 
 | ||||
|     if [ -z "$keep_stderr" ]; then | ||||
|         QEMU_NEED_PID='y'\ | ||||
|         ${QEMU} -nographic -serial none ${comm} -machine accel=qtest "${@}" \ | ||||
|                                                                 >"${fifo_out}" \ | ||||
|                                                                 2>&1 \ | ||||
|                                                                 <"${fifo_in}" & | ||||
|     elif [ "$keep_stderr" = "y" ]; then | ||||
|         QEMU_NEED_PID='y'\ | ||||
|         ${QEMU} -nographic -serial none ${comm} -machine accel=qtest "${@}" \ | ||||
|                                                                 >"${fifo_out}" \ | ||||
|                                                                 <"${fifo_in}" & | ||||
|     else | ||||
|         exit 1 | ||||
|     fi | ||||
| 
 | ||||
|     if [[ "${BASH_VERSINFO[0]}" -ge "5" || | ||||
|         ("${BASH_VERSINFO[0]}" -ge "4"  &&  "${BASH_VERSINFO[1]}" -ge "1") ]] | ||||
|  | ||||
| @ -122,6 +122,7 @@ | ||||
| 114 rw auto quick | ||||
| 115 rw auto | ||||
| 116 rw auto quick | ||||
| 117 rw auto | ||||
| 118 rw auto | ||||
| 119 rw auto quick | ||||
| 120 rw auto quick | ||||
| @ -141,4 +142,8 @@ | ||||
| 137 rw auto | ||||
| 138 rw auto quick | ||||
| 139 rw auto quick | ||||
| 140 rw auto quick | ||||
| 141 rw auto quick | ||||
| 142 auto | ||||
| 143 auto quick | ||||
| 144 rw auto quick | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Peter Maydell
						Peter Maydell