diff options
author | Peter Maydell <peter.maydell@linaro.org> | 2020-11-09 19:33:07 +0000 |
---|---|---|
committer | Peter Maydell <peter.maydell@linaro.org> | 2020-11-09 19:33:07 +0000 |
commit | 2b030ce1ed75e075d35b0d1008a0cacd73624b28 (patch) | |
tree | f339590f14578a646381692854e66a7f387c37dd | |
parent | a2547c1ba911a0c53a10fe02d94a0f539dc064cc (diff) | |
parent | d669ed6ab028497d634e1f236c74a98725f9e45f (diff) |
Merge remote-tracking branch 'remotes/maxreitz/tags/pull-block-2020-11-09-v2' into staging
Block patches for 5.2.0-rc1:
- Some nvme fixes (addressing problems spotted by Coverity)
- Fix nfs compiling on mingw (and enable it in Cirrus)
- Fix an error path in bdrv_co_invalidate_cache() (permission update
was initiated, but not aborted)
- Fix (on-error) roll back in bdrv_drop_intermediate(): Instead of
inlining bdrv_replace_node() (wrongly), call that function
- Fix for iotest 240
- Fix error handling in bdrv_getlength()
- Be more explicit about how QCowL2Meta objects are handled
- Cleanups
# gpg: Signature made Mon 09 Nov 2020 17:45:06 GMT
# gpg: using RSA key 91BEB60A30DB3E8857D11829F407DB0061D5CF40
# gpg: issuer "mreitz@redhat.com"
# gpg: Good signature from "Max Reitz <mreitz@redhat.com>" [full]
# Primary key fingerprint: 91BE B60A 30DB 3E88 57D1 1829 F407 DB00 61D5 CF40
* remotes/maxreitz/tags/pull-block-2020-11-09-v2:
block: make bdrv_drop_intermediate() less wrong
block: add bdrv_replace_node_common()
block: add forgotten bdrv_abort_perm_update() to bdrv_co_invalidate_cache()
block: Fix some code style problems, "foo* bar" should be "foo *bar"
block: Fix integer promotion error in bdrv_getlength()
block: enable libnfs on msys2/mingw in cirrus.yml
block: Fixes nfs compiling error on msys2/mingw
iotests: rewrite iotest 240 in python
iotests: add filter_qmp_virtio_scsi function
hw/block/nvme: fix free of array-typed value
hw/block/nvme: fix uint16_t use of uint32_t sgls member
hw/block/nvme: fix null ns in register namespace
qcow2: Document and enforce the QCowL2Meta invariants
block: Move bdrv_drain_all_end_quiesce() to block_int.h
block: Remove unused include
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
-rw-r--r-- | .cirrus.yml | 1 | ||||
-rw-r--r-- | block.c | 89 | ||||
-rw-r--r-- | block/blkdebug.c | 2 | ||||
-rw-r--r-- | block/dmg-lzfse.c | 1 | ||||
-rw-r--r-- | block/dmg.c | 2 | ||||
-rw-r--r-- | block/nfs.c | 13 | ||||
-rw-r--r-- | block/qcow2-cluster.c | 5 | ||||
-rw-r--r-- | block/qcow2.c | 23 | ||||
-rw-r--r-- | block/qcow2.h | 25 | ||||
-rw-r--r-- | block/vpc.c | 10 | ||||
-rw-r--r-- | hw/block/nvme.c | 6 | ||||
-rw-r--r-- | include/block/block.h | 6 | ||||
-rw-r--r-- | include/block/block_int.h | 9 | ||||
-rwxr-xr-x | tests/qemu-iotests/240 | 219 | ||||
-rw-r--r-- | tests/qemu-iotests/240.out | 76 | ||||
-rw-r--r-- | tests/qemu-iotests/iotests.py | 10 |
16 files changed, 259 insertions, 238 deletions
diff --git a/.cirrus.yml b/.cirrus.yml index 900437dd2a..f0209b7a3e 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -109,6 +109,7 @@ windows_msys2_task: mingw-w64-x86_64-cyrus-sasl \ mingw-w64-x86_64-curl \ mingw-w64-x86_64-gnutls \ + mingw-w64-x86_64-libnfs \ " bitsadmin /transfer msys_download /dynamic /download /priority FOREGROUND ` https://repo.msys2.org/mingw/x86_64/mingw-w64-x86_64-python-sphinx-2.3.1-1-any.pkg.tar.xz ` @@ -4563,8 +4563,16 @@ static bool should_update_child(BdrvChild *c, BlockDriverState *to) return ret; } -void bdrv_replace_node(BlockDriverState *from, BlockDriverState *to, - Error **errp) +/* + * With auto_skip=true bdrv_replace_node_common skips updating from parents + * if it creates a parent-child relation loop or if parent is block-job. + * + * With auto_skip=false the error is returned if from has a parent which should + * not be updated. + */ +static void bdrv_replace_node_common(BlockDriverState *from, + BlockDriverState *to, + bool auto_skip, Error **errp) { BdrvChild *c, *next; GSList *list = NULL, *p; @@ -4583,7 +4591,12 @@ void bdrv_replace_node(BlockDriverState *from, BlockDriverState *to, QLIST_FOREACH_SAFE(c, &from->parents, next_parent, next) { assert(c->bs == from); if (!should_update_child(c, to)) { - continue; + if (auto_skip) { + continue; + } + error_setg(errp, "Should not change '%s' link to '%s'", + c->name, from->node_name); + goto out; } if (c->frozen) { error_setg(errp, "Cannot change '%s' link to '%s'", @@ -4623,6 +4636,12 @@ out: bdrv_unref(from); } +void bdrv_replace_node(BlockDriverState *from, BlockDriverState *to, + Error **errp) +{ + return bdrv_replace_node_common(from, to, true, errp); +} + /* * Add new bs contents at the top of an image chain while the chain is * live, while keeping required fields on the top layer. @@ -4891,9 +4910,11 @@ int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base, { BlockDriverState *explicit_top = top; bool update_inherits_from; - BdrvChild *c, *next; + BdrvChild *c; Error *local_err = NULL; int ret = -EIO; + g_autoptr(GSList) updated_children = NULL; + GSList *p; bdrv_ref(top); bdrv_subtree_drained_begin(top); @@ -4907,14 +4928,6 @@ int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base, goto exit; } - /* This function changes all links that point to top and makes - * them point to base. Check that none of them is frozen. */ - QLIST_FOREACH(c, &top->parents, next_parent) { - if (c->frozen) { - goto exit; - } - } - /* If 'base' recursively inherits from 'top' then we should set * base->inherits_from to top->inherits_from after 'top' and all * other intermediate nodes have been dropped. @@ -4931,36 +4944,36 @@ int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base, backing_file_str = base->filename; } - QLIST_FOREACH_SAFE(c, &top->parents, next_parent, next) { - /* Check whether we are allowed to switch c from top to base */ - GSList *ignore_children = g_slist_prepend(NULL, c); - ret = bdrv_check_update_perm(base, NULL, c->perm, c->shared_perm, - ignore_children, NULL, &local_err); - g_slist_free(ignore_children); - if (ret < 0) { - error_report_err(local_err); - goto exit; - } + QLIST_FOREACH(c, &top->parents, next_parent) { + updated_children = g_slist_prepend(updated_children, c); + } + + bdrv_replace_node_common(top, base, false, &local_err); + if (local_err) { + error_report_err(local_err); + goto exit; + } + + for (p = updated_children; p; p = p->next) { + c = p->data; - /* If so, update the backing file path in the image file */ if (c->klass->update_filename) { ret = c->klass->update_filename(c, base, backing_file_str, &local_err); if (ret < 0) { - bdrv_abort_perm_update(base); + /* + * TODO: Actually, we want to rollback all previous iterations + * of this loop, and (which is almost impossible) previous + * bdrv_replace_node()... + * + * Note, that c->klass->update_filename may lead to permission + * update, so it's a bad idea to call it inside permission + * update transaction of bdrv_replace_node. + */ error_report_err(local_err); goto exit; } } - - /* - * Do the actual switch in the in-memory graph. - * Completes bdrv_check_update_perm() transaction internally. - * c->frozen is false, we have checked that above. - */ - bdrv_ref(base); - bdrv_replace_child(c, base); - bdrv_unref(top); } if (update_inherits_from) { @@ -5091,8 +5104,13 @@ int64_t bdrv_getlength(BlockDriverState *bs) { int64_t ret = bdrv_nb_sectors(bs); - ret = ret > INT64_MAX / BDRV_SECTOR_SIZE ? -EFBIG : ret; - return ret < 0 ? ret : ret * BDRV_SECTOR_SIZE; + if (ret < 0) { + return ret; + } + if (ret > INT64_MAX / BDRV_SECTOR_SIZE) { + return -EFBIG; + } + return ret * BDRV_SECTOR_SIZE; } /* return 0 as number of sectors if no device present or error */ @@ -5782,6 +5800,7 @@ int coroutine_fn bdrv_co_invalidate_cache(BlockDriverState *bs, Error **errp) bdrv_get_cumulative_perm(bs, &perm, &shared_perm); ret = bdrv_check_perm(bs, NULL, perm, shared_perm, NULL, NULL, errp); if (ret < 0) { + bdrv_abort_perm_update(bs); bs->open_flags |= BDRV_O_INACTIVE; return ret; } diff --git a/block/blkdebug.c b/block/blkdebug.c index 54da719dd1..5fe6172da9 100644 --- a/block/blkdebug.c +++ b/block/blkdebug.c @@ -173,7 +173,7 @@ static int add_rule(void *opaque, QemuOpts *opts, Error **errp) { struct add_rule_data *d = opaque; BDRVBlkdebugState *s = d->s; - const char* event_name; + const char *event_name; int event; struct BlkdebugRule *rule; int64_t sector; diff --git a/block/dmg-lzfse.c b/block/dmg-lzfse.c index 19d25bc646..6798cf4fbf 100644 --- a/block/dmg-lzfse.c +++ b/block/dmg-lzfse.c @@ -22,7 +22,6 @@ * THE SOFTWARE. */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "dmg.h" #include <lzfse.h> diff --git a/block/dmg.c b/block/dmg.c index 0d6c317296..ef35a505f2 100644 --- a/block/dmg.c +++ b/block/dmg.c @@ -559,7 +559,7 @@ static void dmg_refresh_limits(BlockDriverState *bs, Error **errp) bs->bl.request_alignment = BDRV_SECTOR_SIZE; /* No sub-sector I/O */ } -static inline int is_sector_in_chunk(BDRVDMGState* s, +static inline int is_sector_in_chunk(BDRVDMGState *s, uint32_t chunk_num, uint64_t sector_num) { if (chunk_num >= s->n_chunks || s->sectors[chunk_num] > sector_num || diff --git a/block/nfs.c b/block/nfs.c index f86e660374..77905f516d 100644 --- a/block/nfs.c +++ b/block/nfs.c @@ -24,7 +24,9 @@ #include "qemu/osdep.h" +#if !defined(_WIN32) #include <poll.h> +#endif #include "qemu/config-file.h" #include "qemu/error-report.h" #include "qapi/error.h" @@ -58,7 +60,7 @@ typedef struct NFSClient { bool has_zero_init; AioContext *aio_context; QemuMutex mutex; - blkcnt_t st_blocks; + uint64_t st_blocks; bool cache_used; NFSServer *server; char *path; @@ -545,7 +547,9 @@ static int64_t nfs_client_open(NFSClient *client, BlockdevOptionsNfs *opts, } ret = DIV_ROUND_UP(st.st_size, BDRV_SECTOR_SIZE); +#if !defined(_WIN32) client->st_blocks = st.st_blocks; +#endif client->has_zero_init = S_ISREG(st.st_mode); *strp = '/'; goto out; @@ -706,6 +710,7 @@ static int nfs_has_zero_init(BlockDriverState *bs) return client->has_zero_init; } +#if !defined(_WIN32) /* Called (via nfs_service) with QemuMutex held. */ static void nfs_get_allocated_file_size_cb(int ret, struct nfs_context *nfs, void *data, @@ -748,6 +753,7 @@ static int64_t nfs_get_allocated_file_size(BlockDriverState *bs) return (task.ret < 0 ? task.ret : st.st_blocks * 512); } +#endif static int coroutine_fn nfs_file_co_truncate(BlockDriverState *bs, int64_t offset, bool exact, @@ -800,7 +806,9 @@ static int nfs_reopen_prepare(BDRVReopenState *state, nfs_get_error(client->context)); return ret; } +#if !defined(_WIN32) client->st_blocks = st.st_blocks; +#endif } return 0; @@ -869,7 +877,10 @@ static BlockDriver bdrv_nfs = { .create_opts = &nfs_create_opts, .bdrv_has_zero_init = nfs_has_zero_init, +/* libnfs does not provide the allocated filesize of a file on win32. */ +#if !defined(_WIN32) .bdrv_get_allocated_file_size = nfs_get_allocated_file_size, +#endif .bdrv_co_truncate = nfs_file_co_truncate, .bdrv_file_open = nfs_file_open, diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c index aa87d3e99b..485b4cb92e 100644 --- a/block/qcow2-cluster.c +++ b/block/qcow2-cluster.c @@ -1049,6 +1049,8 @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m) qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_slice); assert(l2_index + m->nb_clusters <= s->l2_slice_size); + assert(m->cow_end.offset + m->cow_end.nb_bytes <= + m->nb_clusters << s->cluster_bits); for (i = 0; i < m->nb_clusters; i++) { uint64_t offset = cluster_offset + ((uint64_t)i << s->cluster_bits); /* if two concurrent writes happen to the same unallocated cluster @@ -1070,8 +1072,7 @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m) if (has_subclusters(s) && !m->prealloc) { uint64_t l2_bitmap = get_l2_bitmap(s, l2_slice, l2_index + i); unsigned written_from = m->cow_start.offset; - unsigned written_to = m->cow_end.offset + m->cow_end.nb_bytes ?: - m->nb_clusters << s->cluster_bits; + unsigned written_to = m->cow_end.offset + m->cow_end.nb_bytes; int first_sc, last_sc; /* Narrow written_from and written_to down to the current cluster */ written_from = MAX(written_from, i << s->cluster_bits); diff --git a/block/qcow2.c b/block/qcow2.c index 4274806a2a..3a90ef2786 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -269,7 +269,7 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, case QCOW2_EXT_MAGIC_FEATURE_TABLE: if (p_feature_table != NULL) { - void* feature_table = g_malloc0(ext.len + 2 * sizeof(Qcow2Feature)); + void *feature_table = g_malloc0(ext.len + 2 * sizeof(Qcow2Feature)); ret = bdrv_pread(bs->file, offset , feature_table, ext.len); if (ret < 0) { error_setg_errno(errp, -ret, "ERROR: ext_feature_table: " @@ -2361,15 +2361,26 @@ static bool merge_cow(uint64_t offset, unsigned bytes, continue; } - /* The data (middle) region must be immediately after the - * start region */ + /* + * The write request should start immediately after the first + * COW region. This does not always happen because the area + * touched by the request can be larger than the one defined + * by @m (a single request can span an area consisting of a + * mix of previously unallocated and allocated clusters, that + * is why @l2meta is a list). + */ if (l2meta_cow_start(m) + m->cow_start.nb_bytes != offset) { + /* In this case the request starts before this region */ + assert(offset < l2meta_cow_start(m)); + assert(m->cow_start.nb_bytes == 0); continue; } - /* The end region must be immediately after the data (middle) - * region */ + /* The write request should end immediately before the second + * COW region (see above for why it does not always happen) */ if (m->offset + m->cow_end.offset != offset + bytes) { + assert(offset + bytes > m->offset + m->cow_end.offset); + assert(m->cow_end.nb_bytes == 0); continue; } @@ -3377,7 +3388,7 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp) size_t cluster_size; int version; int refcount_order; - uint64_t* refcount_table; + uint64_t *refcount_table; int ret; uint8_t compression_type = QCOW2_COMPRESSION_TYPE_ZLIB; diff --git a/block/qcow2.h b/block/qcow2.h index 125ea9679b..0678073b74 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -343,8 +343,8 @@ typedef struct BDRVQcow2State { uint64_t l1_table_offset; uint64_t *l1_table; - Qcow2Cache* l2_table_cache; - Qcow2Cache* refcount_block_cache; + Qcow2Cache *l2_table_cache; + Qcow2Cache *refcount_block_cache; QEMUTimer *cache_clean_timer; unsigned cache_clean_interval; @@ -394,7 +394,7 @@ typedef struct BDRVQcow2State { uint64_t autoclear_features; size_t unknown_header_fields_size; - void* unknown_header_fields; + void *unknown_header_fields; QLIST_HEAD(, Qcow2UnknownHeaderExtension) unknown_header_ext; QTAILQ_HEAD (, Qcow2DiscardRegion) discards; bool cache_discards; @@ -435,17 +435,18 @@ typedef struct Qcow2COWRegion { /** * Describes an in-flight (part of a) write request that writes to clusters - * that are not referenced in their L2 table yet. + * that need to have their L2 table entries updated (because they are + * newly allocated or need changes in their L2 bitmaps) */ typedef struct QCowL2Meta { - /** Guest offset of the first newly allocated cluster */ + /** Guest offset of the first updated cluster */ uint64_t offset; - /** Host offset of the first newly allocated cluster */ + /** Host offset of the first updated cluster */ uint64_t alloc_offset; - /** Number of newly allocated clusters */ + /** Number of updated clusters */ int nb_clusters; /** Do not free the old clusters */ @@ -458,14 +459,16 @@ typedef struct QCowL2Meta CoQueue dependent_requests; /** - * The COW Region between the start of the first allocated cluster and the - * area the guest actually writes to. + * The COW Region immediately before the area the guest actually + * writes to. This (part of the) write request starts at + * cow_start.offset + cow_start.nb_bytes. */ Qcow2COWRegion cow_start; /** - * The COW Region between the area the guest actually writes to and the - * end of the last allocated cluster. + * The COW Region immediately after the area the guest actually + * writes to. This (part of the) write request ends at cow_end.offset + * (which must always be set even when cow_end.nb_bytes is 0). */ Qcow2COWRegion cow_end; diff --git a/block/vpc.c b/block/vpc.c index 890554277e..1ab55f9287 100644 --- a/block/vpc.c +++ b/block/vpc.c @@ -172,7 +172,7 @@ static QemuOptsList vpc_runtime_opts = { static QemuOptsList vpc_create_opts; -static uint32_t vpc_checksum(uint8_t* buf, size_t size) +static uint32_t vpc_checksum(uint8_t *buf, size_t size) { uint32_t res = 0; int i; @@ -528,7 +528,7 @@ static inline int64_t get_image_offset(BlockDriverState *bs, uint64_t offset, * * Returns 0 on success and < 0 on error */ -static int rewrite_footer(BlockDriverState* bs) +static int rewrite_footer(BlockDriverState *bs) { int ret; BDRVVPCState *s = bs->opaque; @@ -548,7 +548,7 @@ static int rewrite_footer(BlockDriverState* bs) * * Returns the sectors' offset in the image file on success and < 0 on error */ -static int64_t alloc_block(BlockDriverState* bs, int64_t offset) +static int64_t alloc_block(BlockDriverState *bs, int64_t offset) { BDRVVPCState *s = bs->opaque; int64_t bat_offset; @@ -781,8 +781,8 @@ static int coroutine_fn vpc_co_block_status(BlockDriverState *bs, * the hardware EIDE and ATA-2 limit of 16 heads (max disk size of 127 GB) * and instead allow up to 255 heads. */ -static int calculate_geometry(int64_t total_sectors, uint16_t* cyls, - uint8_t* heads, uint8_t* secs_per_cyl) +static int calculate_geometry(int64_t total_sectors, uint16_t *cyls, + uint8_t *heads, uint8_t *secs_per_cyl) { uint32_t cyls_times_heads; diff --git a/hw/block/nvme.c b/hw/block/nvme.c index fa2cba744b..01b657b1c5 100644 --- a/hw/block/nvme.c +++ b/hw/block/nvme.c @@ -452,7 +452,7 @@ static uint16_t nvme_map_sgl_data(NvmeCtrl *n, QEMUSGList *qsg, * segments and/or descriptors. The controller might accept * ignoring the rest of the SGL. */ - uint16_t sgls = le16_to_cpu(n->id_ctrl.sgls); + uint32_t sgls = le32_to_cpu(n->id_ctrl.sgls); if (sgls & NVME_CTRL_SGLS_EXCESS_LENGTH) { break; } @@ -2562,8 +2562,7 @@ int nvme_register_namespace(NvmeCtrl *n, NvmeNamespace *ns, Error **errp) if (!nsid) { for (int i = 1; i <= n->num_namespaces; i++) { - NvmeNamespace *ns = nvme_ns(n, i); - if (!ns) { + if (!nvme_ns(n, i)) { nsid = ns->params.nsid = i; break; } @@ -2800,7 +2799,6 @@ static void nvme_exit(PCIDevice *pci_dev) NvmeCtrl *n = NVME(pci_dev); nvme_clear_ctrl(n); - g_free(n->namespaces); g_free(n->cq); g_free(n->sq); g_free(n->aer_reqs); diff --git a/include/block/block.h b/include/block/block.h index 4bfe3b546b..c9d7c58765 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -782,12 +782,6 @@ void bdrv_drained_end(BlockDriverState *bs); void bdrv_drained_end_no_poll(BlockDriverState *bs, int *drained_end_counter); /** - * End all quiescent sections started by bdrv_drain_all_begin(). This is - * only needed when deleting a BDS before bdrv_drain_all_end() is called. - */ -void bdrv_drain_all_end_quiesce(BlockDriverState *bs); - -/** * End a quiescent section started by bdrv_subtree_drained_begin(). */ void bdrv_subtree_drained_end(BlockDriverState *bs); diff --git a/include/block/block_int.h b/include/block/block_int.h index 38cad9d15c..95d9333be1 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -1407,4 +1407,13 @@ static inline BlockDriverState *bdrv_primary_bs(BlockDriverState *bs) return child_bs(bdrv_primary_child(bs)); } +/** + * End all quiescent sections started by bdrv_drain_all_begin(). This is + * needed when deleting a BDS before bdrv_drain_all_end() is called. + * + * NOTE: this is an internal helper for bdrv_close() *only*. No one else + * should call it. + */ +void bdrv_drain_all_end_quiesce(BlockDriverState *bs); + #endif /* BLOCK_INT_H */ diff --git a/tests/qemu-iotests/240 b/tests/qemu-iotests/240 index 8b4337b58d..c0f71f0461 100755 --- a/tests/qemu-iotests/240 +++ b/tests/qemu-iotests/240 @@ -1,5 +1,5 @@ -#!/usr/bin/env bash -# +#!/usr/bin/env python3 + # Test hot plugging and unplugging with iothreads # # Copyright (C) 2019 Igalia, S.L. @@ -17,133 +17,90 @@ # # 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=berto@igalia.com - -seq=`basename $0` -echo "QA output created by $seq" - -status=1 # failure is the default! - -_cleanup() -{ - rm -f "$SOCK_DIR/nbd" -} -trap "_cleanup; exit \$status" 0 1 2 3 15 - -# get standard environment, filters and checks -. ./common.rc -. ./common.filter - -_supported_fmt generic -_supported_proto generic - -do_run_qemu() -{ - echo Testing: "$@" - $QEMU -nographic -qmp stdio -serial none "$@" - echo -} - -# Remove QMP events from (pretty-printed) output. Doesn't handle -# nested dicts correctly, but we don't get any of those in this test. -_filter_qmp_events() -{ - tr '\n' '\t' | sed -e \ - 's/{\s*"timestamp":\s*{[^}]*},\s*"event":[^,}]*\(,\s*"data":\s*{[^}]*}\)\?\s*}\s*//g' \ - | tr '\t' '\n' -} - -run_qemu() -{ - do_run_qemu "$@" 2>&1 | _filter_qmp | _filter_qmp_events -} - -case "$QEMU_DEFAULT_MACHINE" in - s390-ccw-virtio) - virtio_scsi=virtio-scsi-ccw - ;; - *) - virtio_scsi=virtio-scsi-pci - ;; -esac - -echo -echo === Unplug a SCSI disk and then plug it again === -echo - -run_qemu <<EOF -{ "execute": "qmp_capabilities" } -{ "execute": "blockdev-add", "arguments": {"driver": "null-co", "read-zeroes": true, "node-name": "hd0"}} -{ "execute": "object-add", "arguments": {"qom-type": "iothread", "id": "iothread0"}} -{ "execute": "device_add", "arguments": {"id": "scsi0", "driver": "${virtio_scsi}", "iothread": "iothread0"}} -{ "execute": "device_add", "arguments": {"id": "scsi-hd0", "driver": "scsi-hd", "drive": "hd0"}} -{ "execute": "device_del", "arguments": {"id": "scsi-hd0"}} -{ "execute": "device_add", "arguments": {"id": "scsi-hd0", "driver": "scsi-hd", "drive": "hd0"}} -{ "execute": "device_del", "arguments": {"id": "scsi-hd0"}} -{ "execute": "device_del", "arguments": {"id": "scsi0"}} -{ "execute": "blockdev-del", "arguments": {"node-name": "hd0"}} -{ "execute": "quit"} -EOF - -echo -echo === Attach two SCSI disks using the same block device and the same iothread === -echo - -run_qemu <<EOF -{ "execute": "qmp_capabilities" } -{ "execute": "blockdev-add", "arguments": {"driver": "null-co", "read-zeroes": true, "node-name": "hd0", "read-only": true}} -{ "execute": "object-add", "arguments": {"qom-type": "iothread", "id": "iothread0"}} -{ "execute": "device_add", "arguments": {"id": "scsi0", "driver": "${virtio_scsi}", "iothread": "iothread0"}} -{ "execute": "device_add", "arguments": {"id": "scsi-hd0", "driver": "scsi-hd", "drive": "hd0"}} -{ "execute": "device_add", "arguments": {"id": "scsi-hd1", "driver": "scsi-hd", "drive": "hd0"}} -{ "execute": "device_del", "arguments": {"id": "scsi-hd0"}} -{ "execute": "device_del", "arguments": {"id": "scsi-hd1"}} -{ "execute": "device_del", "arguments": {"id": "scsi0"}} -{ "execute": "blockdev-del", "arguments": {"node-name": "hd0"}} -{ "execute": "quit"} -EOF - -echo -echo === Attach two SCSI disks using the same block device but different iothreads === -echo - -run_qemu <<EOF -{ "execute": "qmp_capabilities" } -{ "execute": "blockdev-add", "arguments": {"driver": "null-co", "read-zeroes": true, "node-name": "hd0", "read-only": true}} -{ "execute": "object-add", "arguments": {"qom-type": "iothread", "id": "iothread0"}} -{ "execute": "object-add", "arguments": {"qom-type": "iothread", "id": "iothread1"}} -{ "execute": "device_add", "arguments": {"id": "scsi0", "driver": "${virtio_scsi}", "iothread": "iothread0"}} -{ "execute": "device_add", "arguments": {"id": "scsi1", "driver": "${virtio_scsi}", "iothread": "iothread1"}} -{ "execute": "device_add", "arguments": {"id": "scsi-hd0", "driver": "scsi-hd", "drive": "hd0", "bus": "scsi0.0"}} -{ "execute": "device_add", "arguments": {"id": "scsi-hd1", "driver": "scsi-hd", "drive": "hd0", "bus": "scsi1.0"}} -{ "execute": "device_del", "arguments": {"id": "scsi-hd0"}} -{ "execute": "device_add", "arguments": {"id": "scsi-hd1", "driver": "scsi-hd", "drive": "hd0", "bus": "scsi1.0"}} -{ "execute": "device_del", "arguments": {"id": "scsi-hd1"}} -{ "execute": "device_del", "arguments": {"id": "scsi0"}} -{ "execute": "device_del", "arguments": {"id": "scsi1"}} -{ "execute": "blockdev-del", "arguments": {"node-name": "hd0"}} -{ "execute": "quit"} -EOF - -echo -echo === Attach a SCSI disks using the same block device as a NBD server === -echo - -run_qemu <<EOF -{ "execute": "qmp_capabilities" } -{ "execute": "blockdev-add", "arguments": {"driver": "null-co", "read-zeroes": true, "node-name": "hd0", "read-only": true}} -{ "execute": "nbd-server-start", "arguments": {"addr":{"type":"unix","data":{"path":"$SOCK_DIR/nbd"}}}} -{ "execute": "nbd-server-add", "arguments": {"device":"hd0"}} -{ "execute": "object-add", "arguments": {"qom-type": "iothread", "id": "iothread0"}} -{ "execute": "device_add", "arguments": {"id": "scsi0", "driver": "${virtio_scsi}", "iothread": "iothread0"}} -{ "execute": "device_add", "arguments": {"id": "scsi-hd0", "driver": "scsi-hd", "drive": "hd0", "bus": "scsi0.0"}} -{ "execute": "quit"} -EOF - -# success, all done -echo "*** done" -rm -f $seq.full -status=0 +import iotests +import os + +nbd_sock = iotests.file_path('nbd.sock', base_dir=iotests.sock_dir) + +class TestCase(iotests.QMPTestCase): + test_driver = "null-co" + + def required_drivers(self): + return [self.test_driver] + + @iotests.skip_if_unsupported(required_drivers) + def setUp(self): + self.vm = iotests.VM() + self.vm.launch() + + def tearDown(self): + self.vm.shutdown() + + def test1(self): + iotests.log('==Unplug a SCSI disk and then plug it again==') + self.vm.qmp_log('blockdev-add', driver='null-co', read_zeroes=True, node_name='hd0') + self.vm.qmp_log('object-add', qom_type='iothread', id="iothread0") + self.vm.qmp_log('device_add', id='scsi0', driver=iotests.get_virtio_scsi_device(), iothread='iothread0', filters=[iotests.filter_qmp_virtio_scsi]) + self.vm.qmp_log('device_add', id='scsi-hd0', driver='scsi-hd', drive='hd0') + self.vm.qmp_log('device_del', id='scsi-hd0') + self.vm.event_wait('DEVICE_DELETED') + self.vm.qmp_log('device_add', id='scsi-hd0', driver='scsi-hd', drive='hd0') + self.vm.qmp_log('device_del', id='scsi-hd0') + self.vm.event_wait('DEVICE_DELETED') + self.vm.qmp_log('blockdev-del', node_name='hd0') + + def test2(self): + iotests.log('==Attach two SCSI disks using the same block device and the same iothread==') + self.vm.qmp_log('blockdev-add', driver='null-co', read_zeroes=True, node_name='hd0', read_only=True) + self.vm.qmp_log('object-add', qom_type='iothread', id="iothread0") + self.vm.qmp_log('device_add', id='scsi0', driver=iotests.get_virtio_scsi_device(), iothread='iothread0', filters=[iotests.filter_qmp_virtio_scsi]) + + self.vm.qmp_log('device_add', id='scsi-hd0', driver='scsi-hd', drive='hd0') + self.vm.qmp_log('device_add', id='scsi-hd1', driver='scsi-hd', drive='hd0') + self.vm.qmp_log('device_del', id='scsi-hd0') + self.vm.event_wait('DEVICE_DELETED') + self.vm.qmp_log('device_del', id='scsi-hd1') + self.vm.event_wait('DEVICE_DELETED') + self.vm.qmp_log('blockdev-del', node_name='hd0') + + def test3(self): + iotests.log('==Attach two SCSI disks using the same block device but different iothreads==') + + self.vm.qmp_log('blockdev-add', driver='null-co', read_zeroes=True, node_name='hd0', read_only=True) + + self.vm.qmp_log('object-add', qom_type='iothread', id="iothread0") + self.vm.qmp_log('object-add', qom_type='iothread', id="iothread1") + + self.vm.qmp_log('device_add', id='scsi0', driver=iotests.get_virtio_scsi_device(), iothread='iothread0', filters=[iotests.filter_qmp_virtio_scsi]) + self.vm.qmp_log('device_add', id='scsi1', driver=iotests.get_virtio_scsi_device(), iothread='iothread1', filters=[iotests.filter_qmp_virtio_scsi]) + + self.vm.qmp_log('device_add', id='scsi-hd0', driver='scsi-hd', drive='hd0', bus="scsi0.0") + self.vm.qmp_log('device_add', id='scsi-hd1', driver='scsi-hd', drive='hd0', bus="scsi1.0") + + self.vm.qmp_log('device_del', id='scsi-hd0') + self.vm.event_wait('DEVICE_DELETED') + self.vm.qmp_log('device_add', id='scsi-hd1', driver='scsi-hd', drive='hd0', bus="scsi1.0") + + self.vm.qmp_log('device_del', id='scsi-hd1') + self.vm.event_wait('DEVICE_DELETED') + self.vm.qmp_log('blockdev-del', node_name='hd0') + + def test4(self): + iotests.log('==Attach a SCSI disks using the same block device as a NBD server==') + + self.vm.qmp_log('blockdev-add', driver='null-co', read_zeroes=True, node_name='hd0', read_only=True) + + self.vm.qmp_log('nbd-server-start', + filters=[iotests.filter_qmp_testfiles], + addr={'type':'unix', 'data':{'path':nbd_sock}}) + + self.vm.qmp_log('nbd-server-add', device='hd0') + + self.vm.qmp_log('object-add', qom_type='iothread', id="iothread0") + self.vm.qmp_log('device_add', id='scsi0', driver=iotests.get_virtio_scsi_device(), iothread='iothread0', filters=[iotests.filter_qmp_virtio_scsi]) + self.vm.qmp_log('device_add', id='scsi-hd0', driver='scsi-hd', drive='hd0') + +if __name__ == '__main__': + iotests.activate_logging() + iotests.main() diff --git a/tests/qemu-iotests/240.out b/tests/qemu-iotests/240.out index d00df50297..e0982831ae 100644 --- a/tests/qemu-iotests/240.out +++ b/tests/qemu-iotests/240.out @@ -1,67 +1,75 @@ -QA output created by 240 - -=== Unplug a SCSI disk and then plug it again === - -Testing: -QMP_VERSION -{"return": {}} +==Unplug a SCSI disk and then plug it again== +{"execute": "blockdev-add", "arguments": {"driver": "null-co", "node-name": "hd0", "read-zeroes": true}} {"return": {}} +{"execute": "object-add", "arguments": {"id": "iothread0", "qom-type": "iothread"}} {"return": {}} +{"execute": "device_add", "arguments": {"driver": "virtio-scsi", "id": "scsi0", "iothread": "iothread0"}} {"return": {}} +{"execute": "device_add", "arguments": {"drive": "hd0", "driver": "scsi-hd", "id": "scsi-hd0"}} {"return": {}} +{"execute": "device_del", "arguments": {"id": "scsi-hd0"}} {"return": {}} +{"execute": "device_add", "arguments": {"drive": "hd0", "driver": "scsi-hd", "id": "scsi-hd0"}} {"return": {}} +{"execute": "device_del", "arguments": {"id": "scsi-hd0"}} {"return": {}} +{"execute": "blockdev-del", "arguments": {"node-name": "hd0"}} {"return": {}} +==Attach two SCSI disks using the same block device and the same iothread== +{"execute": "blockdev-add", "arguments": {"driver": "null-co", "node-name": "hd0", "read-only": true, "read-zeroes": true}} {"return": {}} +{"execute": "object-add", "arguments": {"id": "iothread0", "qom-type": "iothread"}} {"return": {}} - -=== Attach two SCSI disks using the same block device and the same iothread === - -Testing: -QMP_VERSION -{"return": {}} +{"execute": "device_add", "arguments": {"driver": "virtio-scsi", "id": "scsi0", "iothread": "iothread0"}} {"return": {}} +{"execute": "device_add", "arguments": {"drive": "hd0", "driver": "scsi-hd", "id": "scsi-hd0"}} {"return": {}} +{"execute": "device_add", "arguments": {"drive": "hd0", "driver": "scsi-hd", "id": "scsi-hd1"}} {"return": {}} +{"execute": "device_del", "arguments": {"id": "scsi-hd0"}} {"return": {}} +{"execute": "device_del", "arguments": {"id": "scsi-hd1"}} {"return": {}} +{"execute": "blockdev-del", "arguments": {"node-name": "hd0"}} {"return": {}} +==Attach two SCSI disks using the same block device but different iothreads== +{"execute": "blockdev-add", "arguments": {"driver": "null-co", "node-name": "hd0", "read-only": true, "read-zeroes": true}} {"return": {}} +{"execute": "object-add", "arguments": {"id": "iothread0", "qom-type": "iothread"}} {"return": {}} +{"execute": "object-add", "arguments": {"id": "iothread1", "qom-type": "iothread"}} {"return": {}} +{"execute": "device_add", "arguments": {"driver": "virtio-scsi", "id": "scsi0", "iothread": "iothread0"}} {"return": {}} - -=== Attach two SCSI disks using the same block device but different iothreads === - -Testing: -QMP_VERSION -{"return": {}} -{"return": {}} -{"return": {}} -{"return": {}} -{"return": {}} +{"execute": "device_add", "arguments": {"driver": "virtio-scsi", "id": "scsi1", "iothread": "iothread1"}} {"return": {}} +{"execute": "device_add", "arguments": {"bus": "scsi0.0", "drive": "hd0", "driver": "scsi-hd", "id": "scsi-hd0"}} {"return": {}} +{"execute": "device_add", "arguments": {"bus": "scsi1.0", "drive": "hd0", "driver": "scsi-hd", "id": "scsi-hd1"}} {"error": {"class": "GenericError", "desc": "Cannot change iothread of active block backend"}} +{"execute": "device_del", "arguments": {"id": "scsi-hd0"}} {"return": {}} +{"execute": "device_add", "arguments": {"bus": "scsi1.0", "drive": "hd0", "driver": "scsi-hd", "id": "scsi-hd1"}} {"return": {}} +{"execute": "device_del", "arguments": {"id": "scsi-hd1"}} {"return": {}} +{"execute": "blockdev-del", "arguments": {"node-name": "hd0"}} {"return": {}} +==Attach a SCSI disks using the same block device as a NBD server== +{"execute": "blockdev-add", "arguments": {"driver": "null-co", "node-name": "hd0", "read-only": true, "read-zeroes": true}} {"return": {}} +{"execute": "nbd-server-start", "arguments": {"addr": {"data": {"path": "SOCK_DIR/PID-nbd.sock"}, "type": "unix"}}} {"return": {}} +{"execute": "nbd-server-add", "arguments": {"device": "hd0"}} {"return": {}} - -=== Attach a SCSI disks using the same block device as a NBD server === - -Testing: -QMP_VERSION -{"return": {}} -{"return": {}} -{"return": {}} -{"return": {}} +{"execute": "object-add", "arguments": {"id": "iothread0", "qom-type": "iothread"}} {"return": {}} +{"execute": "device_add", "arguments": {"driver": "virtio-scsi", "id": "scsi0", "iothread": "iothread0"}} {"return": {}} +{"execute": "device_add", "arguments": {"drive": "hd0", "driver": "scsi-hd", "id": "scsi-hd0"}} {"return": {}} -{"return": {}} -*** done +.... +---------------------------------------------------------------------- +Ran 4 tests + +OK diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py index 814804a4c6..bcd4fe5b6f 100644 --- a/tests/qemu-iotests/iotests.py +++ b/tests/qemu-iotests/iotests.py @@ -392,6 +392,16 @@ def filter_qmp_testfiles(qmsg): return value return filter_qmp(qmsg, _filter) +def filter_virtio_scsi(output: str) -> str: + return re.sub(r'(virtio-scsi)-(ccw|pci)', r'\1', output) + +def filter_qmp_virtio_scsi(qmsg): + def _filter(_key, value): + if is_str(value): + return filter_virtio_scsi(value) + return value + return filter_qmp(qmsg, _filter) + def filter_generated_node_ids(msg): return re.sub("#block[0-9]+", "NODE_NAME", msg) |