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:
Peter Maydell 2016-02-02 18:04:04 +00:00
commit c65db7705b
52 changed files with 1302 additions and 323 deletions

114
block.c
View File

@ -79,6 +79,9 @@ struct BdrvStates bdrv_states = QTAILQ_HEAD_INITIALIZER(bdrv_states);
static QTAILQ_HEAD(, BlockDriverState) graph_bdrv_states = static QTAILQ_HEAD(, BlockDriverState) graph_bdrv_states =
QTAILQ_HEAD_INITIALIZER(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 = static QLIST_HEAD(, BlockDriver) bdrv_drivers =
QLIST_HEAD_INITIALIZER(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); const BdrvChildRole *child_role, Error **errp);
static void bdrv_dirty_bitmap_truncate(BlockDriverState *bs); 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 */ /* If non-zero, use only whitelisted block drivers */
static int use_bdrv_whitelist; static int use_bdrv_whitelist;
static void bdrv_close(BlockDriverState *bs);
#ifdef _WIN32 #ifdef _WIN32
static int is_windows_drive_prefix(const char *filename) 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++) { for (i = 0; i < BLOCK_OP_TYPE_MAX; i++) {
QLIST_INIT(&bs->op_blockers[i]); QLIST_INIT(&bs->op_blockers[i]);
} }
notifier_list_init(&bs->close_notifiers);
notifier_with_return_list_init(&bs->before_write_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[0]);
qemu_co_queue_init(&bs->throttled_reqs[1]); qemu_co_queue_init(&bs->throttled_reqs[1]);
bs->refcnt = 1; bs->refcnt = 1;
bs->aio_context = qemu_get_aio_context(); 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) return bs;
{
notifier_list_add(&bs->close_notifiers, notify);
} }
BlockDriver *bdrv_find_format(const char *format_name) 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; BdrvAioNotifier *ban, *ban_next;
if (bs->job) { assert(!bs->job);
block_job_cancel_sync(bs->job);
}
/* Disable I/O limits and drain all pending throttled requests */ /* Disable I/O limits and drain all pending throttled requests */
if (bs->throttle_state) { if (bs->throttle_state) {
@ -2155,7 +2156,8 @@ void bdrv_close(BlockDriverState *bs)
bdrv_flush(bs); bdrv_flush(bs);
bdrv_drain(bs); /* in case flush left pending I/O */ 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) { if (bs->blk) {
blk_dev_change_media_cb(bs->blk, false); blk_dev_change_media_cb(bs->blk, false);
@ -2210,31 +2212,55 @@ void bdrv_close(BlockDriverState *bs)
void bdrv_close_all(void) void bdrv_close_all(void)
{ {
BlockDriverState *bs; BlockDriverState *bs;
AioContext *aio_context;
QTAILQ_FOREACH(bs, &bdrv_states, device_list) { /* Drop references from requests still in flight, such as canceled block
AioContext *aio_context = bdrv_get_aio_context(bs); * jobs whose AIO context has not been polled yet */
bdrv_drain_all();
aio_context_acquire(aio_context); blk_remove_all_bs();
bdrv_close(bs); blockdev_close_all_bdrv_states();
aio_context_release(aio_context);
/* 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);
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 /* make a BlockDriverState anonymous by removing from bdrv_state and
* graph_bdrv_state list. * graph_bdrv_state list.
Also, NULL terminate the device_name to prevent double remove */ Also, NULL terminate the device_name to prevent double remove */
void bdrv_make_anon(BlockDriverState *bs) void bdrv_make_anon(BlockDriverState *bs)
{ {
/* /* Take care to remove bs from bdrv_states only when it's actually
* Take care to remove bs from bdrv_states only when it's actually * in it. */
* 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.
*/
if (bs->device_list.tqe_prev) { if (bs->device_list.tqe_prev) {
QTAILQ_REMOVE(&bdrv_states, bs, device_list); bdrv_device_remove(bs);
bs->device_list.tqe_prev = NULL;
} }
if (bs->node_name[0] != '\0') { if (bs->node_name[0] != '\0') {
QTAILQ_REMOVE(&graph_bdrv_states, bs, node_list); 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) { if (!to->device_list.tqe_prev) {
QTAILQ_INSERT_BEFORE(from, to, device_list); 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(!bs->job);
assert(bdrv_op_blocker_is_empty(bs)); assert(bdrv_op_blocker_is_empty(bs));
assert(!bs->refcnt); assert(!bs->refcnt);
assert(QLIST_EMPTY(&bs->dirty_bitmaps));
bdrv_close(bs); bdrv_close(bs);
/* remove from list, if necessary */ /* remove from list, if necessary */
bdrv_make_anon(bs); bdrv_make_anon(bs);
QTAILQ_REMOVE(&all_bdrv_states, bs, bs_list);
g_free(bs); g_free(bs);
} }
@ -3582,21 +3609,40 @@ 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; BdrvDirtyBitmap *bm, *next;
QLIST_FOREACH_SAFE(bm, &bs->dirty_bitmaps, list, 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)); assert(!bdrv_dirty_bitmap_frozen(bm));
QLIST_REMOVE(bitmap, list); QLIST_REMOVE(bm, list);
hbitmap_free(bitmap->bitmap); hbitmap_free(bm->bitmap);
g_free(bitmap->name); g_free(bm->name);
g_free(bitmap); g_free(bm);
return;
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) void bdrv_disable_dirty_bitmap(BdrvDirtyBitmap *bitmap)
{ {
assert(!bdrv_dirty_bitmap_frozen(bitmap)); assert(!bdrv_dirty_bitmap_frozen(bitmap));

View File

@ -49,6 +49,8 @@ struct BlockBackend {
BlockdevOnError on_read_error, on_write_error; BlockdevOnError on_read_error, on_write_error;
bool iostatus_enabled; bool iostatus_enabled;
BlockDeviceIoStatus iostatus; BlockDeviceIoStatus iostatus;
NotifierList remove_bs_notifiers, insert_bs_notifiers;
}; };
typedef struct BlockBackendAIOCB { typedef struct BlockBackendAIOCB {
@ -99,6 +101,8 @@ BlockBackend *blk_new(const char *name, Error **errp)
blk = g_new0(BlockBackend, 1); blk = g_new0(BlockBackend, 1);
blk->name = g_strdup(name); blk->name = g_strdup(name);
blk->refcnt = 1; blk->refcnt = 1;
notifier_list_init(&blk->remove_bs_notifiers);
notifier_list_init(&blk->insert_bs_notifiers);
QTAILQ_INSERT_TAIL(&blk_backends, blk, link); QTAILQ_INSERT_TAIL(&blk_backends, blk, link);
return blk; return blk;
} }
@ -162,11 +166,10 @@ static void blk_delete(BlockBackend *blk)
assert(!blk->refcnt); assert(!blk->refcnt);
assert(!blk->dev); assert(!blk->dev);
if (blk->bs) { if (blk->bs) {
assert(blk->bs->blk == blk); blk_remove_bs(blk);
blk->bs->blk = NULL;
bdrv_unref(blk->bs);
blk->bs = NULL;
} }
assert(QLIST_EMPTY(&blk->remove_bs_notifiers.notifiers));
assert(QLIST_EMPTY(&blk->insert_bs_notifiers.notifiers));
if (blk->root_state.throttle_state) { if (blk->root_state.throttle_state) {
g_free(blk->root_state.throttle_group); g_free(blk->root_state.throttle_group);
throttle_group_unref(blk->root_state.throttle_state); 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. * Return the BlockBackend after @blk.
* If @blk is null, return the first one. * 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) 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_update_root_state(blk);
blk->bs->blk = NULL; blk->bs->blk = NULL;
@ -361,6 +383,8 @@ void blk_insert_bs(BlockBackend *blk, BlockDriverState *bs)
bdrv_ref(bs); bdrv_ref(bs);
blk->bs = bs; blk->bs = bs;
bs->blk = blk; 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); 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. * Notify @blk's attached device model of a media eject request.
* If @force is true, the medium is about to be yanked out forcefully. * 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) 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 blk->dev_ops->is_tray_open(blk->dev_opaque);
} }
return false; 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) { notifier_list_add(&blk->remove_bs_notifiers, notify);
bdrv_add_close_notifier(blk->bs, 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) void blk_io_plug(BlockBackend *blk)

View File

@ -664,6 +664,7 @@ int bdrv_write_zeroes(BlockDriverState *bs, int64_t sector_num,
int bdrv_make_zero(BlockDriverState *bs, BdrvRequestFlags flags) int bdrv_make_zero(BlockDriverState *bs, BdrvRequestFlags flags)
{ {
int64_t target_sectors, ret, nb_sectors, sector_num = 0; int64_t target_sectors, ret, nb_sectors, sector_num = 0;
BlockDriverState *file;
int n; int n;
target_sectors = bdrv_nb_sectors(bs); target_sectors = bdrv_nb_sectors(bs);
@ -676,7 +677,7 @@ int bdrv_make_zero(BlockDriverState *bs, BdrvRequestFlags flags)
if (nb_sectors <= 0) { if (nb_sectors <= 0) {
return 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) { if (ret < 0) {
error_report("error getting block status at sector %" PRId64 ": %s", error_report("error getting block status at sector %" PRId64 ": %s",
sector_num, strerror(-ret)); sector_num, strerror(-ret));
@ -1466,6 +1467,7 @@ int bdrv_flush_all(void)
typedef struct BdrvCoGetBlockStatusData { typedef struct BdrvCoGetBlockStatusData {
BlockDriverState *bs; BlockDriverState *bs;
BlockDriverState *base; BlockDriverState *base;
BlockDriverState **file;
int64_t sector_num; int64_t sector_num;
int nb_sectors; int nb_sectors;
int *pnum; int *pnum;
@ -1487,10 +1489,14 @@ typedef struct BdrvCoGetBlockStatusData {
* *
* 'nb_sectors' is the max value 'pnum' should be set to. If nb_sectors goes * '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. * 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, static int64_t coroutine_fn bdrv_co_get_block_status(BlockDriverState *bs,
int64_t sector_num, int64_t sector_num,
int nb_sectors, int *pnum) int nb_sectors, int *pnum,
BlockDriverState **file)
{ {
int64_t total_sectors; int64_t total_sectors;
int64_t n; int64_t n;
@ -1520,7 +1526,9 @@ static int64_t coroutine_fn bdrv_co_get_block_status(BlockDriverState *bs,
return ret; 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) { if (ret < 0) {
*pnum = 0; *pnum = 0;
return ret; return ret;
@ -1529,7 +1537,7 @@ static int64_t coroutine_fn bdrv_co_get_block_status(BlockDriverState *bs,
if (ret & BDRV_BLOCK_RAW) { if (ret & BDRV_BLOCK_RAW) {
assert(ret & BDRV_BLOCK_OFFSET_VALID); assert(ret & BDRV_BLOCK_OFFSET_VALID);
return bdrv_get_block_status(bs->file->bs, ret >> BDRV_SECTOR_BITS, 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)) { 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_DATA) && !(ret & BDRV_BLOCK_ZERO) &&
(ret & BDRV_BLOCK_OFFSET_VALID)) { (ret & BDRV_BLOCK_OFFSET_VALID)) {
BlockDriverState *file2;
int file_pnum; int file_pnum;
ret2 = bdrv_co_get_block_status(bs->file->bs, ret >> BDRV_SECTOR_BITS, ret2 = bdrv_co_get_block_status(*file, ret >> BDRV_SECTOR_BITS,
*pnum, &file_pnum); *pnum, &file_pnum, &file2);
if (ret2 >= 0) { if (ret2 >= 0) {
/* Ignore errors. This is just providing extra information, it /* Ignore errors. This is just providing extra information, it
* is useful but not necessary. * is useful but not necessary.
@ -1577,14 +1586,15 @@ static int64_t coroutine_fn bdrv_co_get_block_status_above(BlockDriverState *bs,
BlockDriverState *base, BlockDriverState *base,
int64_t sector_num, int64_t sector_num,
int nb_sectors, int nb_sectors,
int *pnum) int *pnum,
BlockDriverState **file)
{ {
BlockDriverState *p; BlockDriverState *p;
int64_t ret = 0; int64_t ret = 0;
assert(bs != base); assert(bs != base);
for (p = bs; p != base; p = backing_bs(p)) { 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) { if (ret < 0 || ret & BDRV_BLOCK_ALLOCATED) {
break; 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->ret = bdrv_co_get_block_status_above(data->bs, data->base,
data->sector_num, data->sector_num,
data->nb_sectors, data->nb_sectors,
data->pnum); data->pnum,
data->file);
data->done = true; 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, int64_t bdrv_get_block_status_above(BlockDriverState *bs,
BlockDriverState *base, BlockDriverState *base,
int64_t sector_num, int64_t sector_num,
int nb_sectors, int *pnum) int nb_sectors, int *pnum,
BlockDriverState **file)
{ {
Coroutine *co; Coroutine *co;
BdrvCoGetBlockStatusData data = { BdrvCoGetBlockStatusData data = {
.bs = bs, .bs = bs,
.base = base, .base = base,
.file = file,
.sector_num = sector_num, .sector_num = sector_num,
.nb_sectors = nb_sectors, .nb_sectors = nb_sectors,
.pnum = pnum, .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 bdrv_get_block_status(BlockDriverState *bs,
int64_t sector_num, 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), 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 coroutine_fn bdrv_is_allocated(BlockDriverState *bs, int64_t sector_num,
int nb_sectors, int *pnum) 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) { if (ret < 0) {
return ret; return ret;
} }

View File

@ -532,7 +532,8 @@ static bool iscsi_allocationmap_is_allocated(IscsiLun *iscsilun,
static int64_t coroutine_fn iscsi_co_get_block_status(BlockDriverState *bs, static int64_t coroutine_fn iscsi_co_get_block_status(BlockDriverState *bs,
int64_t sector_num, int64_t sector_num,
int nb_sectors, int *pnum) int nb_sectors, int *pnum,
BlockDriverState **file)
{ {
IscsiLun *iscsilun = bs->opaque; IscsiLun *iscsilun = bs->opaque;
struct scsi_get_lba_status *lbas = NULL; struct scsi_get_lba_status *lbas = NULL;
@ -624,6 +625,9 @@ out:
if (iTask.task != NULL) { if (iTask.task != NULL) {
scsi_free_scsi_task(iTask.task); scsi_free_scsi_task(iTask.task);
} }
if (ret > 0 && ret & BDRV_BLOCK_OFFSET_VALID) {
*file = bs;
}
return ret; return ret;
} }
@ -650,7 +654,8 @@ static int coroutine_fn iscsi_co_readv(BlockDriverState *bs,
!iscsi_allocationmap_is_allocated(iscsilun, sector_num, nb_sectors)) { !iscsi_allocationmap_is_allocated(iscsilun, sector_num, nb_sectors)) {
int64_t ret; int64_t ret;
int pnum; 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) { if (ret < 0) {
return ret; return ret;
} }

View File

@ -168,6 +168,7 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
MirrorOp *op; MirrorOp *op;
int pnum; int pnum;
int64_t ret; int64_t ret;
BlockDriverState *file;
max_iov = MIN(source->bl.max_iov, s->target->bl.max_iov); 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); trace_mirror_one_iteration(s, sector_num, nb_sectors);
ret = bdrv_get_block_status_above(source, NULL, sector_num, ret = bdrv_get_block_status_above(source, NULL, sector_num,
nb_sectors, &pnum); nb_sectors, &pnum, &file);
if (ret < 0 || pnum < nb_sectors || if (ret < 0 || pnum < nb_sectors ||
(ret & BDRV_BLOCK_DATA && !(ret & BDRV_BLOCK_ZERO))) { (ret & BDRV_BLOCK_DATA && !(ret & BDRV_BLOCK_ZERO))) {
bdrv_aio_readv(source, sector_num, &op->qiov, nb_sectors, bdrv_aio_readv(source, sector_num, &op->qiov, nb_sectors,

View File

@ -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, 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; BDRVParallelsState *s = bs->opaque;
int64_t offset; int64_t offset;
@ -274,6 +274,7 @@ static int64_t coroutine_fn parallels_co_get_block_status(BlockDriverState *bs,
return 0; return 0;
} }
*file = bs->file->bs;
return (offset << BDRV_SECTOR_BITS) | return (offset << BDRV_SECTOR_BITS) |
BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID; BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID;
} }

View File

@ -211,11 +211,13 @@ void bdrv_query_image_info(BlockDriverState *bs,
Error *err = NULL; Error *err = NULL;
ImageInfo *info; ImageInfo *info;
aio_context_acquire(bdrv_get_aio_context(bs));
size = bdrv_getlength(bs); size = bdrv_getlength(bs);
if (size < 0) { if (size < 0) {
error_setg_errno(errp, -size, "Can't get size of device '%s'", error_setg_errno(errp, -size, "Can't get size of device '%s'",
bdrv_get_device_name(bs)); bdrv_get_device_name(bs));
return; goto out;
} }
info = g_new0(ImageInfo, 1); info = g_new0(ImageInfo, 1);
@ -283,10 +285,13 @@ void bdrv_query_image_info(BlockDriverState *bs,
default: default:
error_propagate(errp, err); error_propagate(errp, err);
qapi_free_ImageInfo(info); qapi_free_ImageInfo(info);
return; goto out;
} }
*p_info = info; *p_info = info;
out:
aio_context_release(bdrv_get_aio_context(bs));
} }
/* @p_info will be set only on success. */ /* @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->locked = blk_dev_is_medium_locked(blk);
info->removable = blk_dev_has_removable_media(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->has_tray_open = true;
info->tray_open = blk_dev_is_tray_open(blk); info->tray_open = blk_dev_is_tray_open(blk);
} }

View File

@ -489,7 +489,7 @@ static uint64_t get_cluster_offset(BlockDriverState *bs,
} }
static int64_t coroutine_fn qcow_co_get_block_status(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; BDRVQcowState *s = bs->opaque;
int index_in_cluster, n; 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; return BDRV_BLOCK_DATA;
} }
cluster_offset |= (index_in_cluster << BDRV_SECTOR_BITS); cluster_offset |= (index_in_cluster << BDRV_SECTOR_BITS);
*file = bs->file->bs;
return BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | cluster_offset; return BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | cluster_offset;
} }

View File

@ -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, 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; BDRVQcow2State *s = bs->opaque;
uint64_t cluster_offset; uint64_t cluster_offset;
@ -1349,6 +1349,7 @@ static int64_t coroutine_fn qcow2_co_get_block_status(BlockDriverState *bs,
!s->cipher) { !s->cipher) {
index_in_cluster = sector_num & (s->cluster_sectors - 1); index_in_cluster = sector_num & (s->cluster_sectors - 1);
cluster_offset |= (index_in_cluster << BDRV_SECTOR_BITS); cluster_offset |= (index_in_cluster << BDRV_SECTOR_BITS);
*file = bs->file->bs;
status |= BDRV_BLOCK_OFFSET_VALID | cluster_offset; status |= BDRV_BLOCK_OFFSET_VALID | cluster_offset;
} }
if (ret == QCOW2_CLUSTER_ZERO) { if (ret == QCOW2_CLUSTER_ZERO) {

View File

@ -693,6 +693,7 @@ typedef struct {
uint64_t pos; uint64_t pos;
int64_t status; int64_t status;
int *pnum; int *pnum;
BlockDriverState **file;
} QEDIsAllocatedCB; } QEDIsAllocatedCB;
static void qed_is_allocated_cb(void *opaque, int ret, uint64_t offset, size_t len) 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: case QED_CLUSTER_FOUND:
offset |= qed_offset_into_cluster(s, cb->pos); offset |= qed_offset_into_cluster(s, cb->pos);
cb->status = BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | offset; cb->status = BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | offset;
*cb->file = cb->bs->file->bs;
break; break;
case QED_CLUSTER_ZERO: case QED_CLUSTER_ZERO:
cb->status = BDRV_BLOCK_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, static int64_t coroutine_fn bdrv_qed_co_get_block_status(BlockDriverState *bs,
int64_t sector_num, int64_t sector_num,
int nb_sectors, int *pnum) int nb_sectors, int *pnum,
BlockDriverState **file)
{ {
BDRVQEDState *s = bs->opaque; BDRVQEDState *s = bs->opaque;
size_t len = (size_t)nb_sectors * BDRV_SECTOR_SIZE; 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, .pos = (uint64_t)sector_num * BDRV_SECTOR_SIZE,
.status = BDRV_BLOCK_OFFSET_MASK, .status = BDRV_BLOCK_OFFSET_MASK,
.pnum = pnum, .pnum = pnum,
.file = file,
}; };
QEDRequest request = { .l2_table = NULL }; QEDRequest request = { .l2_table = NULL };

View File

@ -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, static int64_t coroutine_fn raw_co_get_block_status(BlockDriverState *bs,
int64_t sector_num, int64_t sector_num,
int nb_sectors, int *pnum) int nb_sectors, int *pnum,
BlockDriverState **file)
{ {
off_t start, data = 0, hole = 0; off_t start, data = 0, hole = 0;
int64_t total_size; 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); *pnum = MIN(nb_sectors, (data - start) / BDRV_SECTOR_SIZE);
ret = BDRV_BLOCK_ZERO; ret = BDRV_BLOCK_ZERO;
} }
*file = bs;
return ret | BDRV_BLOCK_OFFSET_VALID | start; return ret | BDRV_BLOCK_OFFSET_VALID | start;
} }

View File

@ -115,9 +115,11 @@ fail:
static int64_t coroutine_fn raw_co_get_block_status(BlockDriverState *bs, static int64_t coroutine_fn raw_co_get_block_status(BlockDriverState *bs,
int64_t sector_num, int64_t sector_num,
int nb_sectors, int *pnum) int nb_sectors, int *pnum,
BlockDriverState **file)
{ {
*pnum = nb_sectors; *pnum = nb_sectors;
*file = bs->file->bs;
return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID | BDRV_BLOCK_DATA | return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID | BDRV_BLOCK_DATA |
(sector_num << BDRV_SECTOR_BITS); (sector_num << BDRV_SECTOR_BITS);
} }

View File

@ -2708,7 +2708,7 @@ retry:
static coroutine_fn int64_t static coroutine_fn int64_t
sd_co_get_block_status(BlockDriverState *bs, int64_t sector_num, int nb_sectors, sd_co_get_block_status(BlockDriverState *bs, int64_t sector_num, int nb_sectors,
int *pnum) int *pnum, BlockDriverState **file)
{ {
BDRVSheepdogState *s = bs->opaque; BDRVSheepdogState *s = bs->opaque;
SheepdogInode *inode = &s->inode; 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) { if (*pnum > nb_sectors) {
*pnum = nb_sectors; *pnum = nb_sectors;
} }
if (ret > 0 && ret & BDRV_BLOCK_OFFSET_VALID) {
*file = bs;
}
return ret; return ret;
} }

View File

@ -527,7 +527,7 @@ static int vdi_reopen_prepare(BDRVReopenState *state,
} }
static int64_t coroutine_fn vdi_co_get_block_status(BlockDriverState *bs, 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). */ /* TODO: Check for too large sector_num (in bdrv_is_allocated or here). */
BDRVVdiState *s = (BDRVVdiState *)bs->opaque; 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 + offset = s->header.offset_data +
(uint64_t)bmap_entry * s->block_size + (uint64_t)bmap_entry * s->block_size +
sector_in_block * SECTOR_SIZE; sector_in_block * SECTOR_SIZE;
*file = bs->file->bs;
return BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | offset; return BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | offset;
} }

View File

@ -571,6 +571,7 @@ static int vmdk_open_vmdk4(BlockDriverState *bs,
VmdkExtent *extent; VmdkExtent *extent;
BDRVVmdkState *s = bs->opaque; BDRVVmdkState *s = bs->opaque;
int64_t l1_backup_offset = 0; int64_t l1_backup_offset = 0;
bool compressed;
ret = bdrv_pread(file->bs, sizeof(magic), &header, sizeof(header)); ret = bdrv_pread(file->bs, sizeof(magic), &header, sizeof(header));
if (ret < 0) { if (ret < 0) {
@ -645,6 +646,8 @@ static int vmdk_open_vmdk4(BlockDriverState *bs,
header = footer.header; header = footer.header;
} }
compressed =
le16_to_cpu(header.compressAlgorithm) == VMDK4_COMPRESSION_DEFLATE;
if (le32_to_cpu(header.version) > 3) { if (le32_to_cpu(header.version) > 3) {
char buf[64]; char buf[64];
snprintf(buf, sizeof(buf), "VMDK version %" PRId32, 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, error_setg(errp, QERR_UNKNOWN_BLOCK_FORMAT_FEATURE,
bdrv_get_device_or_node_name(bs), "vmdk", buf); bdrv_get_device_or_node_name(bs), "vmdk", buf);
return -ENOTSUP; 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 /* VMware KB 2064959 explains that version 3 added support for
* persistent changed block tracking (CBT), and backup software can * persistent changed block tracking (CBT), and backup software can
* read it as version=1 if it doesn't care about the changed area * 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, 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; BDRVVmdkState *s = bs->opaque;
int64_t index_in_cluster, n, ret; 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); 0, 0);
qemu_co_mutex_unlock(&s->lock); qemu_co_mutex_unlock(&s->lock);
index_in_cluster = vmdk_find_index_in_cluster(extent, sector_num);
switch (ret) { switch (ret) {
case VMDK_ERROR: case VMDK_ERROR:
ret = -EIO; ret = -EIO;
@ -1286,14 +1291,15 @@ static int64_t coroutine_fn vmdk_co_get_block_status(BlockDriverState *bs,
break; break;
case VMDK_OK: case VMDK_OK:
ret = BDRV_BLOCK_DATA; ret = BDRV_BLOCK_DATA;
if (extent->file == bs->file && !extent->compressed) { if (!extent->compressed) {
ret |= BDRV_BLOCK_OFFSET_VALID | offset; ret |= BDRV_BLOCK_OFFSET_VALID;
ret |= (offset + (index_in_cluster << BDRV_SECTOR_BITS))
& BDRV_BLOCK_OFFSET_MASK;
} }
*file = extent->file->bs;
break; break;
} }
index_in_cluster = vmdk_find_index_in_cluster(extent, sector_num);
n = extent->cluster_sectors - index_in_cluster; n = extent->cluster_sectors - index_in_cluster;
if (n > nb_sectors) { if (n > nb_sectors) {
n = nb_sectors; n = nb_sectors;

View File

@ -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, 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; BDRVVPCState *s = bs->opaque;
VHDFooter *footer = (VHDFooter*) s->footer_buf; 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) { if (be32_to_cpu(footer->type) == VHD_FIXED) {
*pnum = nb_sectors; *pnum = nb_sectors;
*file = bs->file->bs;
return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID | BDRV_BLOCK_DATA | return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID | BDRV_BLOCK_DATA |
(sector_num << BDRV_SECTOR_BITS); (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 /* *pnum can't be greater than one block for allocated
* sectors since there is always a bitmap in between. */ * sectors since there is always a bitmap in between. */
if (allocated) { if (allocated) {
*file = bs->file->bs;
return BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | start; return BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | start;
} }
if (nb_sectors == 0) { if (nb_sectors == 0) {

View File

@ -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, 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; BDRVVVFATState* s = bs->opaque;
*n = s->sector_count - sector_num; *n = s->sector_count - sector_num;

View File

@ -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, void qmp_nbd_server_add(const char *device, bool has_writable, bool writable,
Error **errp) Error **errp)
{ {
BlockBackend *blk; BlockBackend *blk;
NBDExport *exp; NBDExport *exp;
NBDCloseNotifier *n;
if (server_fd == -1) { if (server_fd == -1) {
error_setg(errp, "NBD server not running"); 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); nbd_export_set_name(exp, device);
n = g_new0(NBDCloseNotifier, 1); /* The list of named exports has a strong reference to this export now and
n->n.notify = nbd_close_notifier; * our only way of accessing it is through nbd_export_find(), so we can drop
n->exp = exp; * the strong reference that is @exp. */
blk_add_close_notifier(blk, &n->n); nbd_export_put(exp);
QTAILQ_INSERT_TAIL(&close_notifiers, n, next);
} }
void qmp_nbd_server_stop(Error **errp) void qmp_nbd_server_stop(Error **errp)
{ {
while (!QTAILQ_EMPTY(&close_notifiers)) { nbd_export_close_all();
NBDCloseNotifier *cn = QTAILQ_FIRST(&close_notifiers);
nbd_close_notifier(&cn->n, nbd_export_get_blockdev(cn->exp));
}
if (server_fd != -1) { if (server_fd != -1) {
qemu_set_fd_handler(server_fd, NULL, NULL, NULL); qemu_set_fd_handler(server_fd, NULL, NULL, NULL);

View File

@ -50,6 +50,9 @@
#include "trace.h" #include "trace.h"
#include "sysemu/arch_init.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] = { static const char *const if_name[IF_COUNT] = {
[IF_NONE] = "none", [IF_NONE] = "none",
[IF_IDE] = "ide", [IF_IDE] = "ide",
@ -702,6 +705,19 @@ fail:
return NULL; 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, static void qemu_opt_rename(QemuOpts *opts, const char *from, const char *to,
Error **errp) Error **errp)
{ {
@ -2304,6 +2320,11 @@ void qmp_blockdev_open_tray(const char *device, bool has_force, bool force,
return; return;
} }
if (!blk_dev_has_tray(blk)) {
/* Ignore this command on tray-less devices */
return;
}
if (blk_dev_is_tray_open(blk)) { if (blk_dev_is_tray_open(blk)) {
return; return;
} }
@ -2334,6 +2355,11 @@ void qmp_blockdev_close_tray(const char *device, Error **errp)
return; return;
} }
if (!blk_dev_has_tray(blk)) {
/* Ignore this command on tray-less devices */
return;
}
if (!blk_dev_is_tray_open(blk)) { if (!blk_dev_is_tray_open(blk)) {
return; return;
} }
@ -2363,7 +2389,7 @@ void qmp_x_blockdev_remove_medium(const char *device, Error **errp)
return; 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); error_setg(errp, "Tray of device '%s' is not open", device);
return; 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() */ /* This follows the convention established by bdrv_make_anon() */
if (bs->device_list.tqe_prev) { if (bs->device_list.tqe_prev) {
QTAILQ_REMOVE(&bdrv_states, bs, device_list); bdrv_device_remove(bs);
bs->device_list.tqe_prev = NULL;
} }
blk_remove_bs(blk); 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: out:
aio_context_release(aio_context); aio_context_release(aio_context);
} }
@ -2413,7 +2446,7 @@ static void qmp_blockdev_insert_anon_medium(const char *device,
return; 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); error_setg(errp, "Tray of device '%s' is not open", device);
return; return;
} }
@ -2426,6 +2459,15 @@ static void qmp_blockdev_insert_anon_medium(const char *device,
blk_insert_bs(blk, bs); blk_insert_bs(blk, bs);
QTAILQ_INSERT_TAIL(&bdrv_states, bs, device_list); 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, 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; return;
} }
bdrv_close(bs); blk_remove_bs(blk);
} }
/* if we have a device attached to this BlockDriverState /* if we have a device attached to this BlockDriverState
@ -3848,12 +3890,15 @@ void qmp_blockdev_add(BlockdevOptions *options, Error **errp)
if (!bs) { if (!bs) {
goto fail; goto fail;
} }
QTAILQ_INSERT_TAIL(&monitor_bdrv_states, bs, monitor_list);
} }
if (bs && bdrv_key_required(bs)) { if (bs && bdrv_key_required(bs)) {
if (blk) { if (blk) {
blk_unref(blk); blk_unref(blk);
} else { } else {
QTAILQ_REMOVE(&monitor_bdrv_states, bs, monitor_list);
bdrv_unref(bs); bdrv_unref(bs);
} }
error_setg(errp, "blockdev-add doesn't support encrypted devices"); 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; 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", error_setg(errp, "Block device %s is in use",
bdrv_get_device_or_node_name(bs)); bdrv_get_device_or_node_name(bs));
goto out; goto out;
@ -3923,6 +3974,7 @@ void qmp_x_blockdev_del(bool has_id, const char *id,
if (blk) { if (blk) {
blk_unref(blk); blk_unref(blk);
} else { } else {
QTAILQ_REMOVE(&monitor_bdrv_states, bs, monitor_list);
bdrv_unref(bs); bdrv_unref(bs);
} }

View File

@ -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, static int block_job_finish_sync(BlockJob *job,
void (*finish)(BlockJob *, Error **errp), void (*finish)(BlockJob *, Error **errp),
Error **errp) Error **errp)

View File

@ -40,6 +40,8 @@ struct VirtIOBlockDataPlane {
EventNotifier *guest_notifier; /* irq */ EventNotifier *guest_notifier; /* irq */
QEMUBH *bh; /* bh for guest notification */ QEMUBH *bh; /* bh for guest notification */
Notifier insert_notifier, remove_notifier;
/* Note that these EventNotifiers are assigned by value. This is /* Note that these EventNotifiers are assigned by value. This is
* fine as long as you do not call event_notifier_cleanup on them * 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 * (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); 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 */ /* Context: QEMU global mutex held */
void virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *conf, void virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *conf,
VirtIOBlockDataPlane **dataplane, 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->ctx = iothread_get_aio_context(s->iothread);
s->bh = aio_bh_new(s->ctx, notify_guest_bh, s); s->bh = aio_bh_new(s->ctx, notify_guest_bh, s);
error_setg(&s->blocker, "block device is in use by data plane"); s->insert_notifier.notify = data_plane_blk_insert_notifier;
blk_op_block_all(conf->conf.blk, s->blocker); s->remove_notifier.notify = data_plane_blk_remove_notifier;
blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_RESIZE, s->blocker); blk_add_insert_bs_notifier(conf->conf.blk, &s->insert_notifier);
blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_DRIVE_DEL, s->blocker); blk_add_remove_bs_notifier(conf->conf.blk, &s->remove_notifier);
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); data_plane_set_up_op_blockers(s);
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);
*dataplane = s; *dataplane = s;
} }
@ -207,8 +247,9 @@ void virtio_blk_data_plane_destroy(VirtIOBlockDataPlane *s)
} }
virtio_blk_data_plane_stop(s); virtio_blk_data_plane_stop(s);
blk_op_unblock_all(s->conf->conf.blk, s->blocker); data_plane_remove_op_blockers(s);
error_free(s->blocker); notifier_remove(&s->insert_notifier);
notifier_remove(&s->remove_notifier);
qemu_bh_delete(s->bh); qemu_bh_delete(s->bh);
object_unref(OBJECT(s->iothread)); object_unref(OBJECT(s->iothread));
g_free(s); g_free(s);

View File

@ -173,7 +173,6 @@ typedef struct FDrive {
uint8_t media_changed; /* Is media changed */ uint8_t media_changed; /* Is media changed */
uint8_t media_rate; /* Data rate of medium */ 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? */ bool media_validated; /* Have we validated the media? */
} FDrive; } FDrive;
@ -249,7 +248,7 @@ static int fd_seek(FDrive *drv, uint8_t head, uint8_t track, uint8_t sect,
#endif #endif
drv->head = head; drv->head = head;
if (drv->track != track) { if (drv->track != track) {
if (drv->media_inserted) { if (drv->blk != NULL && blk_is_inserted(drv->blk)) {
drv->media_changed = 0; drv->media_changed = 0;
} }
ret = 1; ret = 1;
@ -258,7 +257,7 @@ static int fd_seek(FDrive *drv, uint8_t head, uint8_t track, uint8_t sect,
drv->sect = sect; drv->sect = sect;
} }
if (!drv->media_inserted) { if (drv->blk == NULL || !blk_is_inserted(drv->blk)) {
ret = 2; ret = 2;
} }
@ -288,7 +287,9 @@ static int pick_geometry(FDrive *drv)
bool magic = drv->drive == FLOPPY_DRIVE_TYPE_AUTO; bool magic = drv->drive == FLOPPY_DRIVE_TYPE_AUTO;
/* We can only pick a geometry if we have a diskette. */ /* 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; return -1;
} }
@ -390,7 +391,7 @@ static void fd_revalidate(FDrive *drv)
FLOPPY_DPRINTF("revalidate\n"); FLOPPY_DPRINTF("revalidate\n");
if (drv->blk != NULL) { if (drv->blk != NULL) {
drv->ro = blk_is_read_only(drv->blk); 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"); FLOPPY_DPRINTF("No disk in drive\n");
drv->disk = FLOPPY_DRIVE_TYPE_NONE; drv->disk = FLOPPY_DRIVE_TYPE_NONE;
} else if (!drv->media_validated) { } else if (!drv->media_validated) {
@ -793,7 +794,7 @@ static bool fdrive_media_changed_needed(void *opaque)
{ {
FDrive *drive = 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 = { static const VMStateDescription vmstate_fdrive_media_changed = {
@ -2285,22 +2286,13 @@ static void fdctrl_change_cb(void *opaque, bool load)
{ {
FDrive *drive = opaque; FDrive *drive = opaque;
drive->media_inserted = load && drive->blk && blk_is_inserted(drive->blk);
drive->media_changed = 1; drive->media_changed = 1;
drive->media_validated = false; drive->media_validated = false;
fd_revalidate(drive); fd_revalidate(drive);
} }
static bool fdctrl_is_tray_open(void *opaque)
{
FDrive *drive = opaque;
return !drive->media_inserted;
}
static const BlockDevOps fdctrl_block_ops = { static const BlockDevOps fdctrl_block_ops = {
.change_media_cb = fdctrl_change_cb, .change_media_cb = fdctrl_change_cb,
.is_tray_open = fdctrl_is_tray_open,
}; };
/* Init functions */ /* Init functions */
@ -2327,7 +2319,6 @@ static void fdctrl_connect_drives(FDCtrl *fdctrl, Error **errp)
fd_init(drive); fd_init(drive);
if (drive->blk) { if (drive->blk) {
blk_set_dev_ops(drive->blk, &fdctrl_block_ops, drive); blk_set_dev_ops(drive->blk, &fdctrl_block_ops, drive);
drive->media_inserted = blk_is_inserted(drive->blk);
pick_drive_type(drive); pick_drive_type(drive);
} }
fd_revalidate(drive); fd_revalidate(drive);

View File

@ -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, static void virtio_scsi_hotplug(HotplugHandler *hotplug_dev, DeviceState *dev,
Error **errp) Error **errp)
{ {
@ -766,6 +782,8 @@ static void virtio_scsi_hotplug(HotplugHandler *hotplug_dev, DeviceState *dev,
SCSIDevice *sd = SCSI_DEVICE(dev); SCSIDevice *sd = SCSI_DEVICE(dev);
if (s->ctx && !s->dataplane_disabled) { if (s->ctx && !s->dataplane_disabled) {
VirtIOSCSIBlkChangeNotifier *insert_notifier, *remove_notifier;
if (blk_op_is_blocked(sd->conf.blk, BLOCK_OP_TYPE_DATAPLANE, errp)) { if (blk_op_is_blocked(sd->conf.blk, BLOCK_OP_TYPE_DATAPLANE, errp)) {
return; return;
} }
@ -773,6 +791,20 @@ static void virtio_scsi_hotplug(HotplugHandler *hotplug_dev, DeviceState *dev,
aio_context_acquire(s->ctx); aio_context_acquire(s->ctx);
blk_set_aio_context(sd->conf.blk, s->ctx); blk_set_aio_context(sd->conf.blk, s->ctx);
aio_context_release(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)) { 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); VirtIODevice *vdev = VIRTIO_DEVICE(hotplug_dev);
VirtIOSCSI *s = VIRTIO_SCSI(vdev); VirtIOSCSI *s = VIRTIO_SCSI(vdev);
SCSIDevice *sd = SCSI_DEVICE(dev); SCSIDevice *sd = SCSI_DEVICE(dev);
VirtIOSCSIBlkChangeNotifier *insert_notifier, *remove_notifier;
if (virtio_vdev_has_feature(vdev, VIRTIO_SCSI_F_HOTPLUG)) { if (virtio_vdev_has_feature(vdev, VIRTIO_SCSI_F_HOTPLUG)) {
virtio_scsi_push_event(s, sd, virtio_scsi_push_event(s, sd,
@ -798,6 +831,25 @@ static void virtio_scsi_hotunplug(HotplugHandler *hotplug_dev, DeviceState *dev,
if (s->ctx) { if (s->ctx) {
blk_op_unblock_all(sd->conf.blk, s->blocker); 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); 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); add_migration_state_change_notifier(&s->migration_state_notifier);
error_setg(&s->blocker, "block device is in use by data plane"); 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) static void virtio_scsi_instance_init(Object *obj)

View File

@ -111,9 +111,10 @@ typedef struct HDGeometry {
/* /*
* Allocation status flags * 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_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 * BDRV_BLOCK_ALLOCATED: the content of the block is determined by this
* layer (as opposed to the backing file) * layer (as opposed to the backing file)
* BDRV_BLOCK_RAW: used internally to indicate that the request * 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); int bdrv_create_file(const char *filename, QemuOpts *opts, Error **errp);
BlockDriverState *bdrv_new_root(void); BlockDriverState *bdrv_new_root(void);
BlockDriverState *bdrv_new(void); BlockDriverState *bdrv_new(void);
void bdrv_device_remove(BlockDriverState *bs);
void bdrv_make_anon(BlockDriverState *bs); void bdrv_make_anon(BlockDriverState *bs);
void bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top); void bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top);
void bdrv_replace_in_backing_chain(BlockDriverState *old, void bdrv_replace_in_backing_chain(BlockDriverState *old,
@ -225,8 +227,6 @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state,
BlockReopenQueue *queue, Error **errp); BlockReopenQueue *queue, Error **errp);
void bdrv_reopen_commit(BDRVReopenState *reopen_state); void bdrv_reopen_commit(BDRVReopenState *reopen_state);
void bdrv_reopen_abort(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, int bdrv_read(BlockDriverState *bs, int64_t sector_num,
uint8_t *buf, int nb_sectors); uint8_t *buf, int nb_sectors);
int bdrv_read_unthrottled(BlockDriverState *bs, int64_t sector_num, 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_unallocated_blocks_are_zero(BlockDriverState *bs);
bool bdrv_can_write_zeroes_with_unmap(BlockDriverState *bs); bool bdrv_can_write_zeroes_with_unmap(BlockDriverState *bs);
int64_t bdrv_get_block_status(BlockDriverState *bs, int64_t sector_num, 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, int64_t bdrv_get_block_status_above(BlockDriverState *bs,
BlockDriverState *base, BlockDriverState *base,
int64_t sector_num, 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 bdrv_is_allocated(BlockDriverState *bs, int64_t sector_num, int nb_sectors,
int *pnum); int *pnum);
int bdrv_is_allocated_above(BlockDriverState *top, BlockDriverState *base, int bdrv_is_allocated_above(BlockDriverState *top, BlockDriverState *base,

View File

@ -166,7 +166,8 @@ struct BlockDriver {
int coroutine_fn (*bdrv_co_discard)(BlockDriverState *bs, int coroutine_fn (*bdrv_co_discard)(BlockDriverState *bs,
int64_t sector_num, int nb_sectors); int64_t sector_num, int nb_sectors);
int64_t coroutine_fn (*bdrv_co_get_block_status)(BlockDriverState *bs, 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. * Invalidate any cached meta-data.
@ -403,8 +404,6 @@ struct BlockDriverState {
BdrvChild *backing; BdrvChild *backing;
BdrvChild *file; BdrvChild *file;
NotifierList close_notifiers;
/* Callback before write request is processed */ /* Callback before write request is processed */
NotifierWithReturnList before_write_notifiers; NotifierWithReturnList before_write_notifiers;
@ -445,6 +444,10 @@ struct BlockDriverState {
QTAILQ_ENTRY(BlockDriverState) node_list; QTAILQ_ENTRY(BlockDriverState) node_list;
/* element of the list of "drives" the guest sees */ /* element of the list of "drives" the guest sees */
QTAILQ_ENTRY(BlockDriverState) device_list; 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; QLIST_HEAD(, BdrvDirtyBitmap) dirty_bitmaps;
int refcnt; int refcnt;
@ -695,6 +698,7 @@ void blk_set_bs(BlockBackend *blk, BlockDriverState *bs);
void blk_dev_change_media_cb(BlockBackend *blk, bool load); void blk_dev_change_media_cb(BlockBackend *blk, bool load);
bool blk_dev_has_removable_media(BlockBackend *blk); bool blk_dev_has_removable_media(BlockBackend *blk);
bool blk_dev_has_tray(BlockBackend *blk);
void blk_dev_eject_request(BlockBackend *blk, bool force); void blk_dev_eject_request(BlockBackend *blk, bool force);
bool blk_dev_is_tray_open(BlockBackend *blk); bool blk_dev_is_tray_open(BlockBackend *blk);
bool blk_dev_is_medium_locked(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_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out);
void bdrv_undo_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap *in); void bdrv_undo_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap *in);
void blockdev_close_all_bdrv_states(void);
#endif /* BLOCK_INT_H */ #endif /* BLOCK_INT_H */

View File

@ -76,6 +76,13 @@ typedef struct VirtIOSCSICommon {
VirtQueue **cmd_vqs; VirtQueue **cmd_vqs;
} VirtIOSCSICommon; } VirtIOSCSICommon;
typedef struct VirtIOSCSIBlkChangeNotifier {
Notifier n;
struct VirtIOSCSI *s;
SCSIDevice *sd;
QTAILQ_ENTRY(VirtIOSCSIBlkChangeNotifier) next;
} VirtIOSCSIBlkChangeNotifier;
typedef struct VirtIOSCSI { typedef struct VirtIOSCSI {
VirtIOSCSICommon parent_obj; VirtIOSCSICommon parent_obj;
@ -86,6 +93,9 @@ typedef struct VirtIOSCSI {
/* Fields for dataplane below */ /* Fields for dataplane below */
AioContext *ctx; /* one iothread per virtio-scsi-pci for now */ 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 /* Vring is used instead of vq in dataplane code, because of the underlying
* memory layer thread safety */ * memory layer thread safety */
VirtIOSCSIVring *ctrl_vring; VirtIOSCSIVring *ctrl_vring;

View File

@ -68,6 +68,7 @@ BlockBackend *blk_new_open(const char *name, const char *filename,
int blk_get_refcnt(BlockBackend *blk); int blk_get_refcnt(BlockBackend *blk);
void blk_ref(BlockBackend *blk); void blk_ref(BlockBackend *blk);
void blk_unref(BlockBackend *blk); void blk_unref(BlockBackend *blk);
void blk_remove_all_bs(void);
const char *blk_name(BlockBackend *blk); const char *blk_name(BlockBackend *blk);
BlockBackend *blk_by_name(const char *name); BlockBackend *blk_by_name(const char *name);
BlockBackend *blk_next(BlockBackend *blk); BlockBackend *blk_next(BlockBackend *blk);
@ -164,7 +165,8 @@ void blk_remove_aio_context_notifier(BlockBackend *blk,
void *), void *),
void (*detach_aio_context)(void *), void (*detach_aio_context)(void *),
void *opaque); 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_plug(BlockBackend *blk);
void blk_io_unplug(BlockBackend *blk); void blk_io_unplug(BlockBackend *blk);
BlockAcctStats *blk_get_stats(BlockBackend *blk); BlockAcctStats *blk_get_stats(BlockBackend *blk);

View File

@ -64,6 +64,8 @@ struct NBDExport {
QTAILQ_ENTRY(NBDExport) next; QTAILQ_ENTRY(NBDExport) next;
AioContext *ctx; AioContext *ctx;
Notifier eject_notifier;
}; };
static QTAILQ_HEAD(, NBDExport) exports = QTAILQ_HEAD_INITIALIZER(exports); static QTAILQ_HEAD(, NBDExport) exports = QTAILQ_HEAD_INITIALIZER(exports);
@ -644,6 +646,12 @@ static void blk_aio_detach(void *opaque)
exp->ctx = NULL; 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, NBDExport *nbd_export_new(BlockBackend *blk, off_t dev_offset, off_t size,
uint32_t nbdflags, void (*close)(NBDExport *), uint32_t nbdflags, void (*close)(NBDExport *),
Error **errp) 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); exp->ctx = blk_get_aio_context(blk);
blk_ref(blk); blk_ref(blk);
blk_add_aio_context_notifier(blk, blk_aio_attached, blk_aio_detach, exp); 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 * NBD exports are used for non-shared storage migration. Make sure
* that BDRV_O_INACTIVE is cleared and the image is ready for write * 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) { if (exp->blk) {
notifier_remove(&exp->eject_notifier);
blk_remove_aio_context_notifier(exp->blk, blk_aio_attached, blk_remove_aio_context_notifier(exp->blk, blk_aio_attached,
blk_aio_detach, exp); blk_aio_detach, exp);
blk_unref(exp->blk); blk_unref(exp->blk);
@ -1082,8 +1095,7 @@ static coroutine_fn void nbd_co_client_start(void *opaque)
nbd_export_get(exp); nbd_export_get(exp);
} }
if (nbd_negotiate(data)) { if (nbd_negotiate(data)) {
shutdown(client->sock, 2); client_close(client);
client->close(client);
goto out; goto out;
} }
qemu_co_mutex_init(&client->send_lock); qemu_co_mutex_init(&client->send_lock);

View File

@ -185,6 +185,33 @@
'*total-clusters': 'int', '*allocated-clusters': 'int', '*total-clusters': 'int', '*allocated-clusters': 'int',
'*fragmented-clusters': 'int', '*compressed-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 # @BlockdevCacheInfo
# #
@ -382,8 +409,8 @@
# @locked: True if the guest has locked this device from having its media # @locked: True if the guest has locked this device from having its media
# removed # removed
# #
# @tray_open: #optional True if the device has a tray and it is open # @tray_open: #optional True if the device's tray is open
# (only present if removable is true) # (only present if it has a tray)
# #
# @dirty-bitmaps: #optional dirty bitmaps information (only present if the # @dirty-bitmaps: #optional dirty bitmaps information (only present if the
# driver has one or more dirty bitmaps) (Since 2.0) # driver has one or more dirty bitmaps) (Since 2.0)
@ -2098,8 +2125,7 @@
# respond to the eject request # respond to the eject request
# - if the BlockBackend denoted by @device does not have a guest device attached # - if the BlockBackend denoted by @device does not have a guest device attached
# to it # to it
# - if the guest device does not have an actual tray and is empty, for instance # - if the guest device does not have an actual tray
# for floppy disk drives
# #
# @device: block device name # @device: block device name
# #

View File

@ -1072,13 +1072,15 @@ static int img_compare(int argc, char **argv)
for (;;) { for (;;) {
int64_t status1, status2; int64_t status1, status2;
BlockDriverState *file;
nb_sectors = sectors_to_process(total_sectors, sector_num); nb_sectors = sectors_to_process(total_sectors, sector_num);
if (nb_sectors <= 0) { if (nb_sectors <= 0) {
break; break;
} }
status1 = bdrv_get_block_status_above(bs1, NULL, sector_num, status1 = bdrv_get_block_status_above(bs1, NULL, sector_num,
total_sectors1 - sector_num, total_sectors1 - sector_num,
&pnum1); &pnum1, &file);
if (status1 < 0) { if (status1 < 0) {
ret = 3; ret = 3;
error_report("Sector allocation test failed for %s", filename1); 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, status2 = bdrv_get_block_status_above(bs2, NULL, sector_num,
total_sectors2 - sector_num, total_sectors2 - sector_num,
&pnum2); &pnum2, &file);
if (status2 < 0) { if (status2 < 0) {
ret = 3; ret = 3;
error_report("Sector allocation test failed for %s", filename2); 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); n = MIN(s->total_sectors - sector_num, BDRV_REQUEST_MAX_SECTORS);
if (s->sector_next_status <= sector_num) { if (s->sector_next_status <= sector_num) {
BlockDriverState *file;
ret = bdrv_get_block_status(blk_bs(s->src[s->src_cur]), ret = bdrv_get_block_status(blk_bs(s->src[s->src_cur]),
sector_num - s->src_cur_offset, sector_num - s->src_cur_offset,
n, &n); n, &n, &file);
if (ret < 0) { if (ret < 0) {
return ret; return ret;
} }
@ -2144,47 +2147,37 @@ static int img_info(int argc, char **argv)
return 0; 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, static void dump_map_entry(OutputFormat output_format, MapEntry *e,
MapEntry *next) MapEntry *next)
{ {
switch (output_format) { switch (output_format) {
case OFORMAT_HUMAN: case OFORMAT_HUMAN:
if ((e->flags & BDRV_BLOCK_DATA) && if (e->data && !e->has_offset) {
!(e->flags & BDRV_BLOCK_OFFSET_VALID)) {
error_report("File contains external, encrypted or compressed clusters."); error_report("File contains external, encrypted or compressed clusters.");
exit(1); 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", 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. /* This format ignores the distinction between 0, ZERO and ZERO|DATA.
* Modify the flags here to allow more coalescing. * Modify the flags here to allow more coalescing.
*/ */
if (next && if (next && (!next->data || next->zero)) {
(next->flags & (BDRV_BLOCK_DATA|BDRV_BLOCK_ZERO)) != BDRV_BLOCK_DATA) { next->data = false;
next->flags &= ~BDRV_BLOCK_DATA; next->zero = true;
next->flags |= BDRV_BLOCK_ZERO;
} }
break; break;
case OFORMAT_JSON: case OFORMAT_JSON:
printf("%s{ \"start\": %"PRId64", \"length\": %"PRId64", \"depth\": %d," printf("%s{ \"start\": %"PRId64", \"length\": %"PRId64","
" \"zero\": %s, \"data\": %s", " \"depth\": %"PRId64", \"zero\": %s, \"data\": %s",
(e->start == 0 ? "[" : ",\n"), (e->start == 0 ? "[" : ",\n"),
e->start, e->length, e->depth, e->start, e->length, e->depth,
(e->flags & BDRV_BLOCK_ZERO) ? "true" : "false", e->zero ? "true" : "false",
(e->flags & BDRV_BLOCK_DATA) ? "true" : "false"); e->data ? "true" : "false");
if (e->flags & BDRV_BLOCK_OFFSET_VALID) { if (e->has_offset) {
printf(", \"offset\": %"PRId64"", e->offset); printf(", \"offset\": %"PRId64"", e->offset);
} }
putchar('}'); putchar('}');
@ -2201,6 +2194,7 @@ static int get_block_status(BlockDriverState *bs, int64_t sector_num,
{ {
int64_t ret; int64_t ret;
int depth; int depth;
BlockDriverState *file;
/* As an optimization, we could cache the current range of unallocated /* As an optimization, we could cache the current range of unallocated
* clusters in each file of the chain, and avoid querying the same * 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; depth = 0;
for (;;) { 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) { if (ret < 0) {
return ret; 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->start = sector_num * BDRV_SECTOR_SIZE;
e->length = nb_sectors * 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->offset = ret & BDRV_BLOCK_OFFSET_MASK;
e->has_offset = !!(ret & BDRV_BLOCK_OFFSET_VALID);
e->depth = depth; e->depth = depth;
e->bs = bs; if (file && e->has_offset) {
e->has_filename = true;
e->filename = file->filename;
}
return 0; 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) static int img_map(int argc, char **argv)
{ {
int c; int c;
@ -2316,10 +2337,7 @@ static int img_map(int argc, char **argv)
goto out; goto out;
} }
if (curr.length != 0 && curr.flags == next.flags && if (entry_mergeable(&curr, &next)) {
curr.depth == next.depth &&
((curr.flags & BDRV_BLOCK_OFFSET_VALID) == 0 ||
curr.offset + curr.length == next.offset)) {
curr.length += next.length; curr.length += next.length;
continue; continue;
} }

View File

@ -1,5 +1,6 @@
stub-obj-y += arch-query-cpu-def.o stub-obj-y += arch-query-cpu-def.o
stub-obj-y += bdrv-commit-all.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 += clock-warp.o
stub-obj-y += cpu-get-clock.o stub-obj-y += cpu-get-clock.o
stub-obj-y += cpu-get-icount.o stub-obj-y += cpu-get-icount.o

View File

@ -0,0 +1,5 @@
#include "block/block_int.h"
void blockdev_close_all_bdrv_states(void)
{
}

View File

@ -304,7 +304,6 @@ static void test_media_insert(void)
qmp_discard_response("{'execute':'change', 'arguments':{" qmp_discard_response("{'execute':'change', 'arguments':{"
" 'device':'floppy0', 'target': %s, 'arg': 'raw' }}", " 'device':'floppy0', 'target': %s, 'arg': 'raw' }}",
test_image); test_image);
qmp_discard_response(""); /* ignore event (open -> close) */
dir = inb(FLOPPY_BASE + reg_dir); dir = inb(FLOPPY_BASE + reg_dir);
assert_bit_set(dir, DSKCHG); assert_bit_set(dir, DSKCHG);
@ -335,7 +334,6 @@ static void test_media_change(void)
* reset the bit. */ * reset the bit. */
qmp_discard_response("{'execute':'eject', 'arguments':{" qmp_discard_response("{'execute':'eject', 'arguments':{"
" 'device':'floppy0' }}"); " 'device':'floppy0' }}");
qmp_discard_response(""); /* ignore event */
dir = inb(FLOPPY_BASE + reg_dir); dir = inb(FLOPPY_BASE + reg_dir);
assert_bit_set(dir, DSKCHG); assert_bit_set(dir, DSKCHG);

View File

@ -132,6 +132,16 @@ _img_info
$QEMU_IO -c "write -P 0xa 900G 512" "$TEST_IMG" | _filter_qemu_io $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 $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
echo "=== Testing afl image with a very large capacity ===" echo "=== Testing afl image with a very large capacity ==="
_use_sample_img afl9.vmdk.bz2 _use_sample_img afl9.vmdk.bz2

View File

@ -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 read 1024/1024 bytes at offset 966367641600
1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) 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 === === Testing afl image with a very large capacity ===
qemu-img: Can't get size of device 'image': File too large qemu-img: Can't get size of device 'image': File too large
*** done *** done

View File

@ -169,7 +169,6 @@ Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,if=none,id=disk
"file": "TEST_DIR/t.qcow2", "file": "TEST_DIR/t.qcow2",
"encryption_key_missing": false "encryption_key_missing": false
}, },
"tray_open": false,
"type": "unknown" "type": "unknown"
} }
] ]
@ -289,7 +288,6 @@ Testing:
"file": "TEST_DIR/t.qcow2", "file": "TEST_DIR/t.qcow2",
"encryption_key_missing": false "encryption_key_missing": false
}, },
"tray_open": false,
"type": "unknown" "type": "unknown"
} }
] ]
@ -410,7 +408,6 @@ Testing:
"file": "TEST_DIR/t.qcow2", "file": "TEST_DIR/t.qcow2",
"encryption_key_missing": false "encryption_key_missing": false
}, },
"tray_open": false,
"type": "unknown" "type": "unknown"
} }
] ]
@ -501,7 +498,6 @@ Testing:
"file": "TEST_DIR/t.qcow2", "file": "TEST_DIR/t.qcow2",
"encryption_key_missing": false "encryption_key_missing": false
}, },
"tray_open": false,
"type": "unknown" "type": "unknown"
} }
] ]

View File

@ -49,17 +49,6 @@ wait_for_tcp_port() {
done 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() { check_disconnect() {
event=$1 event=$1
when=$2 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 & $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" 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 echo
} }

View File

@ -51,7 +51,6 @@ no file open, try 'help open'
=== Check disconnect after neg2 === === Check disconnect after neg2 ===
read failed: Input/output error read failed: Input/output error
=== Check disconnect 8 neg2 === === Check disconnect 8 neg2 ===
@ -66,42 +65,34 @@ no file open, try 'help open'
=== Check disconnect before request === === Check disconnect before request ===
read failed: Input/output error read failed: Input/output error
=== Check disconnect after request === === Check disconnect after request ===
read failed: Input/output error read failed: Input/output error
=== Check disconnect before reply === === Check disconnect before reply ===
read failed: Input/output error read failed: Input/output error
=== Check disconnect after reply === === Check disconnect after reply ===
read failed: Input/output error read failed: Input/output error
=== Check disconnect 4 reply === === Check disconnect 4 reply ===
read failed: Input/output error read failed: Input/output error
=== Check disconnect 8 reply === === Check disconnect 8 reply ===
read failed: Input/output error read failed: Input/output error
=== Check disconnect before data === === Check disconnect before data ===
read failed: Input/output error read failed: Input/output error
=== Check disconnect after data === === Check disconnect after data ===
read 512/512 bytes at offset 0 read 512/512 bytes at offset 0
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) 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 === === Check disconnect after neg-classic ===
read failed: Input/output error read failed: Input/output error
*** done *** done

86
tests/qemu-iotests/117 Executable file
View 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

View 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

View File

@ -42,6 +42,9 @@ class ChangeBaseClass(iotests.QMPTestCase):
self.has_opened = True self.has_opened = True
def wait_for_open(self): def wait_for_open(self):
if not self.has_real_tray:
return
timeout = time.clock() + 3 timeout = time.clock() + 3
while not self.has_opened and time.clock() < timeout: while not self.has_opened and time.clock() < timeout:
self.process_events() self.process_events()
@ -49,6 +52,9 @@ class ChangeBaseClass(iotests.QMPTestCase):
self.fail('Timeout while waiting for the tray to open') self.fail('Timeout while waiting for the tray to open')
def wait_for_close(self): def wait_for_close(self):
if not self.has_real_tray:
return
timeout = time.clock() + 3 timeout = time.clock() + 3
while not self.has_closed and time.clock() < timeout: while not self.has_closed and time.clock() < timeout:
self.process_events() self.process_events()
@ -65,7 +71,8 @@ class GeneralChangeTestsBaseClass(ChangeBaseClass):
self.wait_for_close() self.wait_for_close()
result = self.vm.qmp('query-block') result = self.vm.qmp('query-block')
self.assert_qmp(result, 'return[0]/tray_open', False) 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) self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
def test_blockdev_change_medium(self): def test_blockdev_change_medium(self):
@ -78,7 +85,8 @@ class GeneralChangeTestsBaseClass(ChangeBaseClass):
self.wait_for_close() self.wait_for_close()
result = self.vm.qmp('query-block') result = self.vm.qmp('query-block')
self.assert_qmp(result, 'return[0]/tray_open', False) 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) self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
def test_eject(self): def test_eject(self):
@ -88,7 +96,8 @@ class GeneralChangeTestsBaseClass(ChangeBaseClass):
self.wait_for_open() self.wait_for_open()
result = self.vm.qmp('query-block') result = self.vm.qmp('query-block')
self.assert_qmp(result, 'return[0]/tray_open', True) if self.has_real_tray:
self.assert_qmp(result, 'return[0]/tray_open', True)
self.assert_qmp_absent(result, 'return[0]/inserted') self.assert_qmp_absent(result, 'return[0]/inserted')
def test_tray_eject_change(self): def test_tray_eject_change(self):
@ -98,7 +107,8 @@ class GeneralChangeTestsBaseClass(ChangeBaseClass):
self.wait_for_open() self.wait_for_open()
result = self.vm.qmp('query-block') result = self.vm.qmp('query-block')
self.assert_qmp(result, 'return[0]/tray_open', True) if self.has_real_tray:
self.assert_qmp(result, 'return[0]/tray_open', True)
self.assert_qmp_absent(result, 'return[0]/inserted') self.assert_qmp_absent(result, 'return[0]/inserted')
result = self.vm.qmp('blockdev-change-medium', device='drive0', result = self.vm.qmp('blockdev-change-medium', device='drive0',
@ -109,7 +119,8 @@ class GeneralChangeTestsBaseClass(ChangeBaseClass):
self.wait_for_close() self.wait_for_close()
result = self.vm.qmp('query-block') result = self.vm.qmp('query-block')
self.assert_qmp(result, 'return[0]/tray_open', False) 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) self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
def test_tray_open_close(self): def test_tray_open_close(self):
@ -119,7 +130,8 @@ class GeneralChangeTestsBaseClass(ChangeBaseClass):
self.wait_for_open() self.wait_for_open()
result = self.vm.qmp('query-block') result = self.vm.qmp('query-block')
self.assert_qmp(result, 'return[0]/tray_open', True) if self.has_real_tray:
self.assert_qmp(result, 'return[0]/tray_open', True)
if self.was_empty == True: if self.was_empty == True:
self.assert_qmp_absent(result, 'return[0]/inserted') self.assert_qmp_absent(result, 'return[0]/inserted')
else: else:
@ -132,10 +144,8 @@ class GeneralChangeTestsBaseClass(ChangeBaseClass):
self.wait_for_close() self.wait_for_close()
result = self.vm.qmp('query-block') 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) self.assert_qmp(result, 'return[0]/tray_open', False)
else:
self.assert_qmp(result, 'return[0]/tray_open', True)
if self.was_empty == True: if self.was_empty == True:
self.assert_qmp_absent(result, 'return[0]/inserted') self.assert_qmp_absent(result, 'return[0]/inserted')
else: else:
@ -148,20 +158,18 @@ class GeneralChangeTestsBaseClass(ChangeBaseClass):
self.wait_for_open() self.wait_for_open()
result = self.vm.qmp('query-block') result = self.vm.qmp('query-block')
self.assert_qmp(result, 'return[0]/tray_open', True) if self.has_real_tray:
self.assert_qmp(result, 'return[0]/tray_open', True)
self.assert_qmp_absent(result, 'return[0]/inserted') self.assert_qmp_absent(result, 'return[0]/inserted')
result = self.vm.qmp('blockdev-close-tray', device='drive0') result = self.vm.qmp('blockdev-close-tray', device='drive0')
self.assert_qmp(result, 'return', {}) self.assert_qmp(result, 'return', {})
if self.has_real_tray: self.wait_for_close()
self.wait_for_close()
result = self.vm.qmp('query-block') result = self.vm.qmp('query-block')
if self.has_real_tray: if self.has_real_tray:
self.assert_qmp(result, 'return[0]/tray_open', False) 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') self.assert_qmp_absent(result, 'return[0]/inserted')
def test_tray_open_change(self): def test_tray_open_change(self):
@ -171,7 +179,8 @@ class GeneralChangeTestsBaseClass(ChangeBaseClass):
self.wait_for_open() self.wait_for_open()
result = self.vm.qmp('query-block') result = self.vm.qmp('query-block')
self.assert_qmp(result, 'return[0]/tray_open', True) if self.has_real_tray:
self.assert_qmp(result, 'return[0]/tray_open', True)
if self.was_empty == True: if self.was_empty == True:
self.assert_qmp_absent(result, 'return[0]/inserted') self.assert_qmp_absent(result, 'return[0]/inserted')
else: else:
@ -185,7 +194,8 @@ class GeneralChangeTestsBaseClass(ChangeBaseClass):
self.wait_for_close() self.wait_for_close()
result = self.vm.qmp('query-block') result = self.vm.qmp('query-block')
self.assert_qmp(result, 'return[0]/tray_open', False) 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) self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
def test_cycle(self): def test_cycle(self):
@ -202,7 +212,8 @@ class GeneralChangeTestsBaseClass(ChangeBaseClass):
self.wait_for_open() self.wait_for_open()
result = self.vm.qmp('query-block') result = self.vm.qmp('query-block')
self.assert_qmp(result, 'return[0]/tray_open', True) if self.has_real_tray:
self.assert_qmp(result, 'return[0]/tray_open', True)
if self.was_empty == True: if self.was_empty == True:
self.assert_qmp_absent(result, 'return[0]/inserted') self.assert_qmp_absent(result, 'return[0]/inserted')
else: else:
@ -212,7 +223,8 @@ class GeneralChangeTestsBaseClass(ChangeBaseClass):
self.assert_qmp(result, 'return', {}) self.assert_qmp(result, 'return', {})
result = self.vm.qmp('query-block') result = self.vm.qmp('query-block')
self.assert_qmp(result, 'return[0]/tray_open', True) if self.has_real_tray:
self.assert_qmp(result, 'return[0]/tray_open', True)
self.assert_qmp_absent(result, 'return[0]/inserted') self.assert_qmp_absent(result, 'return[0]/inserted')
result = self.vm.qmp('x-blockdev-insert-medium', device='drive0', result = self.vm.qmp('x-blockdev-insert-medium', device='drive0',
@ -220,7 +232,8 @@ class GeneralChangeTestsBaseClass(ChangeBaseClass):
self.assert_qmp(result, 'return', {}) self.assert_qmp(result, 'return', {})
result = self.vm.qmp('query-block') result = self.vm.qmp('query-block')
self.assert_qmp(result, 'return[0]/tray_open', True) 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) self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
result = self.vm.qmp('blockdev-close-tray', device='drive0') result = self.vm.qmp('blockdev-close-tray', device='drive0')
@ -229,7 +242,8 @@ class GeneralChangeTestsBaseClass(ChangeBaseClass):
self.wait_for_close() self.wait_for_close()
result = self.vm.qmp('query-block') result = self.vm.qmp('query-block')
self.assert_qmp(result, 'return[0]/tray_open', False) 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) self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
def test_close_on_closed(self): def test_close_on_closed(self):
@ -239,16 +253,14 @@ class GeneralChangeTestsBaseClass(ChangeBaseClass):
self.assertEquals(self.vm.get_qmp_events(wait=False), []) self.assertEquals(self.vm.get_qmp_events(wait=False), [])
def test_remove_on_closed(self): def test_remove_on_closed(self):
if self.has_opened: if not self.has_real_tray:
# Empty floppy drive
return return
result = self.vm.qmp('x-blockdev-remove-medium', device='drive0') result = self.vm.qmp('x-blockdev-remove-medium', device='drive0')
self.assert_qmp(result, 'error/class', 'GenericError') self.assert_qmp(result, 'error/class', 'GenericError')
def test_insert_on_closed(self): def test_insert_on_closed(self):
if self.has_opened: if not self.has_real_tray:
# Empty floppy drive
return return
result = self.vm.qmp('blockdev-add', result = self.vm.qmp('blockdev-add',
@ -366,7 +378,6 @@ class TestChangeReadOnly(ChangeBaseClass):
self.vm.launch() self.vm.launch()
result = self.vm.qmp('query-block') 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/ro', True)
self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img) self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
@ -376,11 +387,7 @@ class TestChangeReadOnly(ChangeBaseClass):
read_only_mode='retain') read_only_mode='retain')
self.assert_qmp(result, 'return', {}) self.assert_qmp(result, 'return', {})
self.wait_for_open()
self.wait_for_close()
result = self.vm.qmp('query-block') 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/ro', True)
self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img) self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
@ -390,7 +397,6 @@ class TestChangeReadOnly(ChangeBaseClass):
self.vm.launch() self.vm.launch()
result = self.vm.qmp('query-block') 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/ro', True)
self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img) self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
@ -400,11 +406,7 @@ class TestChangeReadOnly(ChangeBaseClass):
read_only_mode='retain') read_only_mode='retain')
self.assert_qmp(result, 'return', {}) self.assert_qmp(result, 'return', {})
self.wait_for_open()
self.wait_for_close()
result = self.vm.qmp('query-block') 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/ro', True)
self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img) self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
@ -414,7 +416,6 @@ class TestChangeReadOnly(ChangeBaseClass):
self.vm.launch() self.vm.launch()
result = self.vm.qmp('query-block') 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/ro', False)
self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img) 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), []) self.assertEquals(self.vm.get_qmp_events(wait=False), [])
result = self.vm.qmp('query-block') 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/ro', False)
self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img) self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
@ -437,7 +437,6 @@ class TestChangeReadOnly(ChangeBaseClass):
self.vm.launch() self.vm.launch()
result = self.vm.qmp('query-block') 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/ro', True)
self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img) self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
@ -448,11 +447,7 @@ class TestChangeReadOnly(ChangeBaseClass):
read_only_mode='read-write') read_only_mode='read-write')
self.assert_qmp(result, 'return', {}) self.assert_qmp(result, 'return', {})
self.wait_for_open()
self.wait_for_close()
result = self.vm.qmp('query-block') 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/ro', False)
self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img) self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
@ -462,7 +457,6 @@ class TestChangeReadOnly(ChangeBaseClass):
self.vm.launch() self.vm.launch()
result = self.vm.qmp('query-block') 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/ro', False)
self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img) self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
@ -473,11 +467,7 @@ class TestChangeReadOnly(ChangeBaseClass):
read_only_mode='read-only') read_only_mode='read-only')
self.assert_qmp(result, 'return', {}) self.assert_qmp(result, 'return', {})
self.wait_for_open()
self.wait_for_close()
result = self.vm.qmp('query-block') 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/ro', True)
self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img) self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
@ -486,7 +476,6 @@ class TestChangeReadOnly(ChangeBaseClass):
self.vm.launch() self.vm.launch()
result = self.vm.qmp('query-block') 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/ro', False)
self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img) self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
@ -497,11 +486,7 @@ class TestChangeReadOnly(ChangeBaseClass):
read_only_mode='read-only') read_only_mode='read-only')
self.assert_qmp(result, 'return', {}) self.assert_qmp(result, 'return', {})
self.wait_for_open()
self.wait_for_close()
result = self.vm.qmp('query-block') 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/ro', True)
self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img) self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
@ -511,7 +496,6 @@ class TestChangeReadOnly(ChangeBaseClass):
self.vm.launch() self.vm.launch()
result = self.vm.qmp('query-block') 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/ro', False)
self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img) self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
@ -522,10 +506,7 @@ class TestChangeReadOnly(ChangeBaseClass):
read_only_mode='read-write') read_only_mode='read-write')
self.assert_qmp(result, 'error/class', 'GenericError') self.assert_qmp(result, 'error/class', 'GenericError')
self.assertEquals(self.vm.get_qmp_events(wait=False), [])
result = self.vm.qmp('query-block') 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/ro', False)
self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img) self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
@ -535,7 +516,6 @@ class TestChangeReadOnly(ChangeBaseClass):
self.vm.launch() self.vm.launch()
result = self.vm.qmp('query-block') 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/ro', True)
self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img) self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
@ -545,11 +525,7 @@ class TestChangeReadOnly(ChangeBaseClass):
read_only_mode='retain') read_only_mode='retain')
self.assert_qmp(result, 'return', {}) self.assert_qmp(result, 'return', {})
self.wait_for_open()
self.wait_for_close()
result = self.vm.qmp('query-block') 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/ro', True)
self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img) self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
@ -559,7 +535,6 @@ class TestChangeReadOnly(ChangeBaseClass):
self.vm.launch() self.vm.launch()
result = self.vm.qmp('query-block') 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/ro', False)
self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img) self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
@ -569,10 +544,7 @@ class TestChangeReadOnly(ChangeBaseClass):
read_only_mode='retain') read_only_mode='retain')
self.assert_qmp(result, 'error/class', 'GenericError') self.assert_qmp(result, 'error/class', 'GenericError')
self.assertEquals(self.vm.get_qmp_events(wait=False), [])
result = self.vm.qmp('query-block') 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/ro', False)
self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img) self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
@ -582,7 +554,6 @@ class TestChangeReadOnly(ChangeBaseClass):
self.vm.launch() self.vm.launch()
result = self.vm.qmp('query-block') 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/ro', False)
self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img) self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
@ -594,13 +565,7 @@ class TestChangeReadOnly(ChangeBaseClass):
'driver': 'file'}}) 'driver': 'file'}})
self.assert_qmp(result, 'return', {}) 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') 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/ro', False)
self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img) self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
@ -608,7 +573,6 @@ class TestChangeReadOnly(ChangeBaseClass):
self.assert_qmp(result, 'return', {}) self.assert_qmp(result, 'return', {})
result = self.vm.qmp('query-block') result = self.vm.qmp('query-block')
self.assert_qmp(result, 'return[0]/tray_open', True)
self.assert_qmp_absent(result, 'return[0]/inserted') self.assert_qmp_absent(result, 'return[0]/inserted')
result = self.vm.qmp('x-blockdev-insert-medium', device='drive0', result = self.vm.qmp('x-blockdev-insert-medium', device='drive0',
@ -616,17 +580,10 @@ class TestChangeReadOnly(ChangeBaseClass):
self.assert_qmp(result, 'return', {}) self.assert_qmp(result, 'return', {})
result = self.vm.qmp('query-block') 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/ro', True)
self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img) 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') 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/ro', True)
self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img) self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
@ -648,7 +605,6 @@ class TestBlockJobsAfterCycle(ChangeBaseClass):
self.assert_qmp(result, 'return', {}) self.assert_qmp(result, 'return', {})
result = self.vm.qmp('query-block') 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') self.assert_qmp(result, 'return[0]/inserted/image/format', 'null-co')
# For device-less BBs, calling blockdev-open-tray or blockdev-close-tray # For device-less BBs, calling blockdev-open-tray or blockdev-close-tray
@ -671,7 +627,6 @@ class TestBlockJobsAfterCycle(ChangeBaseClass):
self.assert_qmp(result, 'return', {}) self.assert_qmp(result, 'return', {})
result = self.vm.qmp('query-block') 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) self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
def tearDown(self): def tearDown(self):
@ -717,4 +672,6 @@ if __name__ == '__main__':
# We need floppy and IDE CD-ROM # We need floppy and IDE CD-ROM
iotests.notrun('not suitable for this machine type: %s' % iotests.notrun('not suitable for this machine type: %s' %
iotests.qemu_default_machine) 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
View 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

View 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
View 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

View 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
View 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

View 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
View 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

View 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

View File

@ -230,5 +230,18 @@ _filter_qemu_img_map()
-e 's/Mapped to *//' | _filter_testdir | _filter_imgfmt -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 # make sure this script returns success
true true

View File

@ -129,6 +129,8 @@ function _send_qemu_cmd()
# $qemu_comm_method: set this variable to 'monitor' (case insensitive) # $qemu_comm_method: set this variable to 'monitor' (case insensitive)
# to use the QEMU HMP monitor for communication. # to use the QEMU HMP monitor for communication.
# Otherwise, the default of QMP is used. # 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: # Returns:
# $QEMU_HANDLE: set to a handle value to communicate with this QEMU instance. # $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_out}"
mkfifo "${fifo_in}" mkfifo "${fifo_in}"
QEMU_NEED_PID='y'\ if [ -z "$keep_stderr" ]; then
${QEMU} -nographic -serial none ${comm} -machine accel=qtest "${@}" \ QEMU_NEED_PID='y'\
${QEMU} -nographic -serial none ${comm} -machine accel=qtest "${@}" \
>"${fifo_out}" \ >"${fifo_out}" \
2>&1 \ 2>&1 \
<"${fifo_in}" & <"${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" || if [[ "${BASH_VERSINFO[0]}" -ge "5" ||
("${BASH_VERSINFO[0]}" -ge "4" && "${BASH_VERSINFO[1]}" -ge "1") ]] ("${BASH_VERSINFO[0]}" -ge "4" && "${BASH_VERSINFO[1]}" -ge "1") ]]

View File

@ -122,6 +122,7 @@
114 rw auto quick 114 rw auto quick
115 rw auto 115 rw auto
116 rw auto quick 116 rw auto quick
117 rw auto
118 rw auto 118 rw auto
119 rw auto quick 119 rw auto quick
120 rw auto quick 120 rw auto quick
@ -141,4 +142,8 @@
137 rw auto 137 rw auto
138 rw auto quick 138 rw auto quick
139 rw auto quick 139 rw auto quick
140 rw auto quick
141 rw auto quick
142 auto 142 auto
143 auto quick
144 rw auto quick