diff options
author | Peter Maydell <peter.maydell@linaro.org> | 2019-01-31 19:26:09 +0000 |
---|---|---|
committer | Peter Maydell <peter.maydell@linaro.org> | 2019-01-31 19:26:09 +0000 |
commit | cfe6c547690b06fbce54a6d0f7b05dd7f18e36ea (patch) | |
tree | 103fb713da1c3ba222cb025f7b47899fb339596f | |
parent | e8977901b79fb678f288dd372261b640bbeccd0d (diff) | |
parent | 908b30164bbffad7430d551b2a03a8fbcaa631ef (diff) |
Merge remote-tracking branch 'remotes/xanclic/tags/pull-block-2019-01-31' into staging
Block patches:
- New debugging QMP command to explore block graphs
- Converted DPRINTF()s to trace events
- Fixed qemu-io's use of getopt() for systems with optreset
- Minor NVMe emulation fixes
- An iotest fix
# gpg: Signature made Thu 31 Jan 2019 00:51:46 GMT
# gpg: using RSA key F407DB0061D5CF40
# gpg: Good signature from "Max Reitz <mreitz@redhat.com>" [full]
# Primary key fingerprint: 91BE B60A 30DB 3E88 57D1 1829 F407 DB00 61D5 CF40
* remotes/xanclic/tags/pull-block-2019-01-31:
iotests: Allow 147 to be run concurrently
iotests: Bind qemu-nbd to localhost in 147
iotests.py: Add qemu_nbd_pipe()
nvme: use pci_dev directly in nvme_realize
nvme: ensure the num_queues is not zero
nvme: use TYPE_NVME instead of constant string
qemu-io: Add generic function for reinitializing optind.
block/sheepdog: Convert from DPRINTF() macro to trace events
block/file-posix: Convert from DPRINTF() macro to trace events
block/curl: Convert from DPRINTF() macro to trace events
block/ssh: Convert from DPRINTF() macro to trace events
scripts: add render_block_graph function for QEMUMachine
qapi: add x-debug-query-block-graph
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
-rw-r--r-- | block.c | 148 | ||||
-rw-r--r-- | block/block-backend.c | 5 | ||||
-rw-r--r-- | block/curl.c | 29 | ||||
-rw-r--r-- | block/file-posix.c | 25 | ||||
-rw-r--r-- | block/sheepdog.c | 47 | ||||
-rw-r--r-- | block/ssh.c | 46 | ||||
-rw-r--r-- | block/trace-events | 47 | ||||
-rw-r--r-- | blockdev.c | 5 | ||||
-rwxr-xr-x | configure | 14 | ||||
-rw-r--r-- | hw/block/nvme.c | 15 | ||||
-rw-r--r-- | include/block/block.h | 1 | ||||
-rw-r--r-- | include/qemu/osdep.h | 16 | ||||
-rw-r--r-- | include/sysemu/block-backend.h | 2 | ||||
-rw-r--r-- | qapi/block-core.json | 108 | ||||
-rw-r--r-- | qemu-img.c | 2 | ||||
-rw-r--r-- | qemu-io-cmds.c | 2 | ||||
-rwxr-xr-x | scripts/render_block_graph.py | 120 | ||||
-rwxr-xr-x | tests/qemu-iotests/147 | 98 | ||||
-rw-r--r-- | tests/qemu-iotests/iotests.py | 14 |
19 files changed, 608 insertions, 136 deletions
@@ -4119,6 +4119,154 @@ BlockDeviceInfoList *bdrv_named_nodes_list(Error **errp) return list; } +#define QAPI_LIST_ADD(list, element) do { \ + typeof(list) _tmp = g_new(typeof(*(list)), 1); \ + _tmp->value = (element); \ + _tmp->next = (list); \ + (list) = _tmp; \ +} while (0) + +typedef struct XDbgBlockGraphConstructor { + XDbgBlockGraph *graph; + GHashTable *graph_nodes; +} XDbgBlockGraphConstructor; + +static XDbgBlockGraphConstructor *xdbg_graph_new(void) +{ + XDbgBlockGraphConstructor *gr = g_new(XDbgBlockGraphConstructor, 1); + + gr->graph = g_new0(XDbgBlockGraph, 1); + gr->graph_nodes = g_hash_table_new(NULL, NULL); + + return gr; +} + +static XDbgBlockGraph *xdbg_graph_finalize(XDbgBlockGraphConstructor *gr) +{ + XDbgBlockGraph *graph = gr->graph; + + g_hash_table_destroy(gr->graph_nodes); + g_free(gr); + + return graph; +} + +static uintptr_t xdbg_graph_node_num(XDbgBlockGraphConstructor *gr, void *node) +{ + uintptr_t ret = (uintptr_t)g_hash_table_lookup(gr->graph_nodes, node); + + if (ret != 0) { + return ret; + } + + /* + * Start counting from 1, not 0, because 0 interferes with not-found (NULL) + * answer of g_hash_table_lookup. + */ + ret = g_hash_table_size(gr->graph_nodes) + 1; + g_hash_table_insert(gr->graph_nodes, node, (void *)ret); + + return ret; +} + +static void xdbg_graph_add_node(XDbgBlockGraphConstructor *gr, void *node, + XDbgBlockGraphNodeType type, const char *name) +{ + XDbgBlockGraphNode *n; + + n = g_new0(XDbgBlockGraphNode, 1); + + n->id = xdbg_graph_node_num(gr, node); + n->type = type; + n->name = g_strdup(name); + + QAPI_LIST_ADD(gr->graph->nodes, n); +} + +static void xdbg_graph_add_edge(XDbgBlockGraphConstructor *gr, void *parent, + const BdrvChild *child) +{ + typedef struct { + unsigned int flag; + BlockPermission num; + } PermissionMap; + + static const PermissionMap permissions[] = { + { BLK_PERM_CONSISTENT_READ, BLOCK_PERMISSION_CONSISTENT_READ }, + { BLK_PERM_WRITE, BLOCK_PERMISSION_WRITE }, + { BLK_PERM_WRITE_UNCHANGED, BLOCK_PERMISSION_WRITE_UNCHANGED }, + { BLK_PERM_RESIZE, BLOCK_PERMISSION_RESIZE }, + { BLK_PERM_GRAPH_MOD, BLOCK_PERMISSION_GRAPH_MOD }, + { 0, 0 } + }; + const PermissionMap *p; + XDbgBlockGraphEdge *edge; + + QEMU_BUILD_BUG_ON(1UL << (ARRAY_SIZE(permissions) - 1) != BLK_PERM_ALL + 1); + + edge = g_new0(XDbgBlockGraphEdge, 1); + + edge->parent = xdbg_graph_node_num(gr, parent); + edge->child = xdbg_graph_node_num(gr, child->bs); + edge->name = g_strdup(child->name); + + for (p = permissions; p->flag; p++) { + if (p->flag & child->perm) { + QAPI_LIST_ADD(edge->perm, p->num); + } + if (p->flag & child->shared_perm) { + QAPI_LIST_ADD(edge->shared_perm, p->num); + } + } + + QAPI_LIST_ADD(gr->graph->edges, edge); +} + + +XDbgBlockGraph *bdrv_get_xdbg_block_graph(Error **errp) +{ + BlockBackend *blk; + BlockJob *job; + BlockDriverState *bs; + BdrvChild *child; + XDbgBlockGraphConstructor *gr = xdbg_graph_new(); + + for (blk = blk_all_next(NULL); blk; blk = blk_all_next(blk)) { + char *allocated_name = NULL; + const char *name = blk_name(blk); + + if (!*name) { + name = allocated_name = blk_get_attached_dev_id(blk); + } + xdbg_graph_add_node(gr, blk, X_DBG_BLOCK_GRAPH_NODE_TYPE_BLOCK_BACKEND, + name); + g_free(allocated_name); + if (blk_root(blk)) { + xdbg_graph_add_edge(gr, blk, blk_root(blk)); + } + } + + for (job = block_job_next(NULL); job; job = block_job_next(job)) { + GSList *el; + + xdbg_graph_add_node(gr, job, X_DBG_BLOCK_GRAPH_NODE_TYPE_BLOCK_JOB, + job->job.id); + for (el = job->nodes; el; el = el->next) { + xdbg_graph_add_edge(gr, job, (BdrvChild *)el->data); + } + } + + QTAILQ_FOREACH(bs, &graph_bdrv_states, node_list) { + xdbg_graph_add_node(gr, bs, X_DBG_BLOCK_GRAPH_NODE_TYPE_BLOCK_DRIVER, + bs->node_name); + QLIST_FOREACH(child, &bs->children, next) { + xdbg_graph_add_edge(gr, bs, child); + } + } + + return xdbg_graph_finalize(gr); +} + BlockDriverState *bdrv_lookup_bs(const char *device, const char *node_name, Error **errp) diff --git a/block/block-backend.c b/block/block-backend.c index 60d37a0c3d..cf05abd89d 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -2249,3 +2249,8 @@ int coroutine_fn blk_co_copy_range(BlockBackend *blk_in, int64_t off_in, blk_out->root, off_out, bytes, read_flags, write_flags); } + +const BdrvChild *blk_root(BlockBackend *blk) +{ + return blk->root; +} diff --git a/block/curl.c b/block/curl.c index db5d2bd8ef..b7ac265d3a 100644 --- a/block/curl.c +++ b/block/curl.c @@ -32,22 +32,10 @@ #include "crypto/secret.h" #include <curl/curl.h> #include "qemu/cutils.h" +#include "trace.h" -// #define DEBUG_CURL // #define DEBUG_VERBOSE -#ifdef DEBUG_CURL -#define DEBUG_CURL_PRINT 1 -#else -#define DEBUG_CURL_PRINT 0 -#endif -#define DPRINTF(fmt, ...) \ - do { \ - if (DEBUG_CURL_PRINT) { \ - fprintf(stderr, fmt, ## __VA_ARGS__); \ - } \ - } while (0) - #if LIBCURL_VERSION_NUM >= 0x071000 /* The multi interface timer callback was introduced in 7.16.0 */ #define NEED_CURL_TIMER_CALLBACK @@ -154,7 +142,7 @@ static int curl_timer_cb(CURLM *multi, long timeout_ms, void *opaque) { BDRVCURLState *s = opaque; - DPRINTF("CURL: timer callback timeout_ms %ld\n", timeout_ms); + trace_curl_timer_cb(timeout_ms); if (timeout_ms == -1) { timer_del(&s->timer); } else { @@ -193,7 +181,7 @@ static int curl_sock_cb(CURL *curl, curl_socket_t fd, int action, } socket = NULL; - DPRINTF("CURL (AIO): Sock action %d on fd %d\n", action, (int)fd); + trace_curl_sock_cb(action, (int)fd); switch (action) { case CURL_POLL_IN: aio_set_fd_handler(s->aio_context, fd, false, @@ -238,7 +226,7 @@ static size_t curl_read_cb(void *ptr, size_t size, size_t nmemb, void *opaque) size_t realsize = size * nmemb; int i; - DPRINTF("CURL: Just reading %zd bytes\n", realsize); + trace_curl_read_cb(realsize); if (!s || !s->orig_buf) { goto read_end; @@ -777,7 +765,7 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags, } } - DPRINTF("CURL: Opening %s\n", file); + trace_curl_open(file); qemu_co_queue_init(&s->free_state_waitq); s->aio_context = bdrv_get_aio_context(bs); s->url = g_strdup(file); @@ -830,7 +818,7 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags, "Server does not support 'range' (byte ranges)."); goto out; } - DPRINTF("CURL: Size = %" PRIu64 "\n", s->len); + trace_curl_open_size(s->len); qemu_mutex_lock(&s->mutex); curl_clean_state(state); @@ -908,8 +896,7 @@ static void curl_setup_preadv(BlockDriverState *bs, CURLAIOCB *acb) state->acb[0] = acb; snprintf(state->range, 127, "%" PRIu64 "-%" PRIu64, start, end); - DPRINTF("CURL (AIO): Reading %" PRIu64 " at %" PRIu64 " (%s)\n", - acb->bytes, start, state->range); + trace_curl_setup_preadv(acb->bytes, start, state->range); curl_easy_setopt(state->curl, CURLOPT_RANGE, state->range); curl_multi_add_handle(s->multi, state->curl); @@ -943,7 +930,7 @@ static void curl_close(BlockDriverState *bs) { BDRVCURLState *s = bs->opaque; - DPRINTF("CURL: Close\n"); + trace_curl_close(); curl_detach_aio_context(bs); qemu_mutex_destroy(&s->mutex); diff --git a/block/file-posix.c b/block/file-posix.c index 8aee7a3fb8..ba6ab62a38 100644 --- a/block/file-posix.c +++ b/block/file-posix.c @@ -102,19 +102,7 @@ #include <xfs/xfs.h> #endif -//#define DEBUG_BLOCK - -#ifdef DEBUG_BLOCK -# define DEBUG_BLOCK_PRINT 1 -#else -# define DEBUG_BLOCK_PRINT 0 -#endif -#define DPRINTF(fmt, ...) \ -do { \ - if (DEBUG_BLOCK_PRINT) { \ - printf(fmt, ## __VA_ARGS__); \ - } \ -} while (0) +#include "trace.h" /* OS X does not have O_DSYNC */ #ifndef O_DSYNC @@ -1411,7 +1399,7 @@ static int xfs_write_zeroes(BDRVRawState *s, int64_t offset, uint64_t bytes) if (xfsctl(NULL, s->fd, XFS_IOC_ZERO_RANGE, &fl) < 0) { err = errno; - DPRINTF("cannot write zero range (%s)\n", strerror(errno)); + trace_file_xfs_write_zeroes(strerror(errno)); return -err; } @@ -1430,7 +1418,7 @@ static int xfs_discard(BDRVRawState *s, int64_t offset, uint64_t bytes) if (xfsctl(NULL, s->fd, XFS_IOC_UNRESVSP64, &fl) < 0) { err = errno; - DPRINTF("cannot punch hole (%s)\n", strerror(errno)); + trace_file_xfs_discard(strerror(errno)); return -err; } @@ -2819,7 +2807,7 @@ static char *FindEjectableOpticalMedia(io_iterator_t *mediaIterator) /* If a match was found, leave the loop */ if (*mediaIterator != 0) { - DPRINTF("Matching using %s\n", matching_array[index]); + trace_file_FindEjectableOpticalMedia(matching_array[index]); mediaType = g_strdup(matching_array[index]); break; } @@ -2879,7 +2867,7 @@ static bool setup_cdrom(char *bsd_path, Error **errp) if (partition_found == false) { error_setg(errp, "Failed to find a working partition on disc"); } else { - DPRINTF("Using %s as optical disc\n", test_partition); + trace_file_setup_cdrom(test_partition); pstrcpy(bsd_path, MAXPATHLEN, test_partition); } return partition_found; @@ -2974,8 +2962,7 @@ static bool hdev_is_sg(BlockDriverState *bs) ret = ioctl(s->fd, SG_GET_SCSI_ID, &scsiid); if (ret >= 0) { - DPRINTF("SG device found: type=%d, version=%d\n", - scsiid.scsi_type, sg_version); + trace_file_hdev_is_sg(scsiid.scsi_type, sg_version); return true; } diff --git a/block/sheepdog.c b/block/sheepdog.c index ed14f7afbe..b916ba07bf 100644 --- a/block/sheepdog.c +++ b/block/sheepdog.c @@ -28,6 +28,7 @@ #include "sysemu/block-backend.h" #include "qemu/bitops.h" #include "qemu/cutils.h" +#include "trace.h" #define SD_PROTO_VER 0x01 @@ -299,19 +300,6 @@ static inline size_t count_data_objs(const struct SheepdogInode *inode) (1UL << inode->block_size_shift)); } -#undef DPRINTF -#ifdef DEBUG_SDOG -#define DEBUG_SDOG_PRINT 1 -#else -#define DEBUG_SDOG_PRINT 0 -#endif -#define DPRINTF(fmt, args...) \ - do { \ - if (DEBUG_SDOG_PRINT) { \ - fprintf(stderr, "%s %d: " fmt, __func__, __LINE__, ##args); \ - } \ - } while (0) - typedef struct SheepdogAIOCB SheepdogAIOCB; typedef struct BDRVSheepdogState BDRVSheepdogState; @@ -750,7 +738,7 @@ static coroutine_fn void reconnect_to_sdog(void *opaque) Error *local_err = NULL; s->fd = get_sheep_fd(s, &local_err); if (s->fd < 0) { - DPRINTF("Wait for connection to be established\n"); + trace_sheepdog_reconnect_to_sdog(); error_report_err(local_err); qemu_co_sleep_ns(QEMU_CLOCK_REALTIME, 1000000000ULL); } @@ -847,7 +835,7 @@ static void coroutine_fn aio_read_response(void *opaque) break; case AIOCB_FLUSH_CACHE: if (rsp.result == SD_RES_INVALID_PARMS) { - DPRINTF("disable cache since the server doesn't support it\n"); + trace_sheepdog_aio_read_response(); s->cache_flags = SD_FLAG_CMD_DIRECT; rsp.result = SD_RES_SUCCESS; } @@ -1639,7 +1627,7 @@ static int sd_open(BlockDriverState *bs, QDict *options, int flags, s->discard_supported = true; if (snap_id || tag[0]) { - DPRINTF("%" PRIx32 " snapshot inode was open.\n", vid); + trace_sheepdog_open(vid); s->is_snapshot = true; } @@ -2252,7 +2240,7 @@ static void sd_close(BlockDriverState *bs) unsigned int wlen, rlen = 0; int fd, ret; - DPRINTF("%s\n", s->name); + trace_sheepdog_close(s->name); fd = connect_to_sdog(s, &local_err); if (fd < 0) { @@ -2429,7 +2417,7 @@ static int sd_create_branch(BDRVSheepdogState *s) char *buf; bool deleted; - DPRINTF("%" PRIx32 " is snapshot.\n", s->inode.vdi_id); + trace_sheepdog_create_branch_snapshot(s->inode.vdi_id); buf = g_malloc(SD_INODE_SIZE); @@ -2445,7 +2433,7 @@ static int sd_create_branch(BDRVSheepdogState *s) goto out; } - DPRINTF("%" PRIx32 " is created.\n", vid); + trace_sheepdog_create_branch_created(vid); fd = connect_to_sdog(s, &local_err); if (fd < 0) { @@ -2467,7 +2455,7 @@ static int sd_create_branch(BDRVSheepdogState *s) s->is_snapshot = false; ret = 0; - DPRINTF("%" PRIx32 " was newly created.\n", s->inode.vdi_id); + trace_sheepdog_create_branch_new(s->inode.vdi_id); out: g_free(buf); @@ -2561,11 +2549,11 @@ static void coroutine_fn sd_co_rw_vector(SheepdogAIOCB *acb) } if (create) { - DPRINTF("update ino (%" PRIu32 ") %" PRIu64 " %" PRIu64 " %ld\n", - inode->vdi_id, oid, - vid_to_data_oid(inode->data_vdi_id[idx], idx), idx); + trace_sheepdog_co_rw_vector_update(inode->vdi_id, oid, + vid_to_data_oid(inode->data_vdi_id[idx], idx), + idx); oid = vid_to_data_oid(inode->vdi_id, idx); - DPRINTF("new oid %" PRIx64 "\n", oid); + trace_sheepdog_co_rw_vector_new(oid); } aio_req = alloc_aio_req(s, acb, oid, len, offset, flags, create, @@ -2670,9 +2658,8 @@ static int sd_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) SheepdogInode *inode; unsigned int datalen; - DPRINTF("sn_info: name %s id_str %s s: name %s vm_state_size %" PRId64 " " - "is_snapshot %d\n", sn_info->name, sn_info->id_str, - s->name, sn_info->vm_state_size, s->is_snapshot); + trace_sheepdog_snapshot_create_info(sn_info->name, sn_info->id_str, s->name, + sn_info->vm_state_size, s->is_snapshot); if (s->is_snapshot) { error_report("You can't create a snapshot of a snapshot VDI, " @@ -2681,7 +2668,7 @@ static int sd_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) return -EINVAL; } - DPRINTF("%s %s\n", sn_info->name, sn_info->id_str); + trace_sheepdog_snapshot_create(sn_info->name, sn_info->id_str); s->inode.vm_state_size = sn_info->vm_state_size; s->inode.vm_clock_nsec = sn_info->vm_clock_nsec; @@ -2726,8 +2713,8 @@ static int sd_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) } memcpy(&s->inode, inode, datalen); - DPRINTF("s->inode: name %s snap_id %x oid %x\n", - s->inode.name, s->inode.snap_id, s->inode.vdi_id); + trace_sheepdog_snapshot_create_inode(s->inode.name, s->inode.snap_id, + s->inode.vdi_id); cleanup: g_free(inode); diff --git a/block/ssh.c b/block/ssh.c index 7fbc27abdf..bbc513e095 100644 --- a/block/ssh.c +++ b/block/ssh.c @@ -41,27 +41,17 @@ #include "qapi/qmp/qstring.h" #include "qapi/qobject-input-visitor.h" #include "qapi/qobject-output-visitor.h" +#include "trace.h" -/* DEBUG_SSH=1 enables the DPRINTF (debugging printf) statements in - * this block driver code. - * +/* * TRACE_LIBSSH2=<bitmask> enables tracing in libssh2 itself. Note * that this requires that libssh2 was specially compiled with the * `./configure --enable-debug' option, so most likely you will have * to compile it yourself. The meaning of <bitmask> is described * here: http://www.libssh2.org/libssh2_trace.html */ -#define DEBUG_SSH 0 #define TRACE_LIBSSH2 0 /* or try: LIBSSH2_TRACE_SFTP */ -#define DPRINTF(fmt, ...) \ - do { \ - if (DEBUG_SSH) { \ - fprintf(stderr, "ssh: %-15s " fmt "\n", \ - __func__, ##__VA_ARGS__); \ - } \ - } while (0) - typedef struct BDRVSSHState { /* Coroutine. */ CoMutex lock; @@ -336,7 +326,7 @@ static int check_host_key_knownhosts(BDRVSSHState *s, switch (r) { case LIBSSH2_KNOWNHOST_CHECK_MATCH: /* OK */ - DPRINTF("host key OK: %s", found->key); + trace_ssh_check_host_key_knownhosts(found->key); break; case LIBSSH2_KNOWNHOST_CHECK_MISMATCH: ret = -EINVAL; @@ -721,8 +711,7 @@ static int connect_to_ssh(BDRVSSHState *s, BlockdevOptionsSsh *opts, } /* Open the remote file. */ - DPRINTF("opening file %s flags=0x%x creat_mode=0%o", - opts->path, ssh_flags, creat_mode); + trace_ssh_connect_to_ssh(opts->path, ssh_flags, creat_mode); s->sftp_handle = libssh2_sftp_open(s->sftp, opts->path, ssh_flags, creat_mode); if (!s->sftp_handle) { @@ -890,7 +879,7 @@ static int coroutine_fn ssh_co_create_opts(const char *filename, QemuOpts *opts, /* Get desired file size. */ ssh_opts->size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), BDRV_SECTOR_SIZE); - DPRINTF("total_size=%" PRIi64, ssh_opts->size); + trace_ssh_co_create_opts(ssh_opts->size); uri_options = qdict_new(); ret = parse_uri(filename, uri_options, errp); @@ -946,7 +935,7 @@ static void restart_coroutine(void *opaque) BDRVSSHState *s = bs->opaque; AioContext *ctx = bdrv_get_aio_context(bs); - DPRINTF("co=%p", restart->co); + trace_ssh_restart_coroutine(restart->co); aio_set_fd_handler(ctx, s->sock, false, NULL, NULL, NULL, NULL); aio_co_wake(restart->co); @@ -974,13 +963,12 @@ static coroutine_fn void co_yield(BDRVSSHState *s, BlockDriverState *bs) wr_handler = restart_coroutine; } - DPRINTF("s->sock=%d rd_handler=%p wr_handler=%p", s->sock, - rd_handler, wr_handler); + trace_ssh_co_yield(s->sock, rd_handler, wr_handler); aio_set_fd_handler(bdrv_get_aio_context(bs), s->sock, false, rd_handler, wr_handler, NULL, &restart); qemu_coroutine_yield(); - DPRINTF("s->sock=%d - back", s->sock); + trace_ssh_co_yield_back(s->sock); } /* SFTP has a function `libssh2_sftp_seek64' which seeks to a position @@ -1003,7 +991,7 @@ static void ssh_seek(BDRVSSHState *s, int64_t offset, int flags) bool force = (flags & SSH_SEEK_FORCE) != 0; if (force || op_read != s->offset_op_read || offset != s->offset) { - DPRINTF("seeking to offset=%" PRIi64, offset); + trace_ssh_seek(offset); libssh2_sftp_seek64(s->sftp_handle, offset); s->offset = offset; s->offset_op_read = op_read; @@ -1019,7 +1007,7 @@ static coroutine_fn int ssh_read(BDRVSSHState *s, BlockDriverState *bs, char *buf, *end_of_vec; struct iovec *i; - DPRINTF("offset=%" PRIi64 " size=%zu", offset, size); + trace_ssh_read(offset, size); ssh_seek(s, offset, SSH_SEEK_READ); @@ -1038,9 +1026,9 @@ static coroutine_fn int ssh_read(BDRVSSHState *s, BlockDriverState *bs, */ for (got = 0; got < size; ) { again: - DPRINTF("sftp_read buf=%p size=%zu", buf, end_of_vec - buf); + trace_ssh_read_buf(buf, end_of_vec - buf); r = libssh2_sftp_read(s->sftp_handle, buf, end_of_vec - buf); - DPRINTF("sftp_read returned %zd", r); + trace_ssh_read_return(r); if (r == LIBSSH2_ERROR_EAGAIN || r == LIBSSH2_ERROR_TIMEOUT) { co_yield(s, bs); @@ -1094,7 +1082,7 @@ static int ssh_write(BDRVSSHState *s, BlockDriverState *bs, char *buf, *end_of_vec; struct iovec *i; - DPRINTF("offset=%" PRIi64 " size=%zu", offset, size); + trace_ssh_write(offset, size); ssh_seek(s, offset, SSH_SEEK_WRITE); @@ -1108,9 +1096,9 @@ static int ssh_write(BDRVSSHState *s, BlockDriverState *bs, for (written = 0; written < size; ) { again: - DPRINTF("sftp_write buf=%p size=%zu", buf, end_of_vec - buf); + trace_ssh_write_buf(buf, end_of_vec - buf); r = libssh2_sftp_write(s->sftp_handle, buf, end_of_vec - buf); - DPRINTF("sftp_write returned %zd", r); + trace_ssh_write_return(r); if (r == LIBSSH2_ERROR_EAGAIN || r == LIBSSH2_ERROR_TIMEOUT) { co_yield(s, bs); @@ -1187,7 +1175,7 @@ static coroutine_fn int ssh_flush(BDRVSSHState *s, BlockDriverState *bs) { int r; - DPRINTF("fsync"); + trace_ssh_flush(); again: r = libssh2_sftp_fsync(s->sftp_handle); if (r == LIBSSH2_ERROR_EAGAIN || r == LIBSSH2_ERROR_TIMEOUT) { @@ -1238,7 +1226,7 @@ static int64_t ssh_getlength(BlockDriverState *bs) /* Note we cannot make a libssh2 call here. */ length = (int64_t) s->attrs.filesize; - DPRINTF("length=%" PRIi64, length); + trace_ssh_getlength(length); return length; } diff --git a/block/trace-events b/block/trace-events index 693c14c443..70056eafd2 100644 --- a/block/trace-events +++ b/block/trace-events @@ -160,3 +160,50 @@ iscsi_xcopy(void *src_lun, uint64_t src_off, void *dst_lun, uint64_t dst_off, ui # block/nbd-client.c nbd_read_reply_entry_fail(int ret, const char *err) "ret = %d, err: %s" nbd_co_request_fail(uint64_t from, uint32_t len, uint64_t handle, uint16_t flags, uint16_t type, const char *name, int ret, const char *err) "Request failed { .from = %" PRIu64", .len = %" PRIu32 ", .handle = %" PRIu64 ", .flags = 0x%" PRIx16 ", .type = %" PRIu16 " (%s) } ret = %d, err: %s" + +# block/ssh.c +ssh_restart_coroutine(void *co) "co=%p" +ssh_flush(void) "fsync" +ssh_check_host_key_knownhosts(const char *key) "host key OK: %s" +ssh_connect_to_ssh(char *path, int flags, int mode) "opening file %s flags=0x%x creat_mode=0%o" +ssh_co_yield(int sock, void *rd_handler, void *wr_handler) "s->sock=%d rd_handler=%p wr_handler=%p" +ssh_co_yield_back(int sock) "s->sock=%d - back" +ssh_getlength(int64_t length) "length=%" PRIi64 +ssh_co_create_opts(uint64_t size) "total_size=%" PRIu64 +ssh_read(int64_t offset, size_t size) "offset=%" PRIi64 " size=%zu" +ssh_read_buf(void *buf, size_t size) "sftp_read buf=%p size=%zu" +ssh_read_return(ssize_t ret) "sftp_read returned %zd" +ssh_write(int64_t offset, size_t size) "offset=%" PRIi64 " size=%zu" +ssh_write_buf(void *buf, size_t size) "sftp_write buf=%p size=%zu" +ssh_write_return(ssize_t ret) "sftp_write returned %zd" +ssh_seek(int64_t offset) "seeking to offset=%" PRIi64 + +# block/curl.c +curl_timer_cb(long timeout_ms) "timer callback timeout_ms %ld" +curl_sock_cb(int action, int fd) "sock action %d on fd %d" +curl_read_cb(size_t realsize) "just reading %zu bytes" +curl_open(const char *file) "opening %s" +curl_open_size(uint64_t size) "size = %" PRIu64 +curl_setup_preadv(uint64_t bytes, uint64_t start, const char *range) "reading %" PRIu64 " at %" PRIu64 " (%s)" +curl_close(void) "close" + +# block/file-posix.c +file_xfs_write_zeroes(const char *error) "cannot write zero range (%s)" +file_xfs_discard(const char *error) "cannot punch hole (%s)" +file_FindEjectableOpticalMedia(const char *media) "Matching using %s" +file_setup_cdrom(const char *partition) "Using %s as optical disc" +file_hdev_is_sg(int type, int version) "SG device found: type=%d, version=%d" + +# block/sheepdog.c +sheepdog_reconnect_to_sdog(void) "Wait for connection to be established" +sheepdog_aio_read_response(void) "disable cache since the server doesn't support it" +sheepdog_open(uint32_t vid) "0x%" PRIx32 " snapshot inode was open" +sheepdog_close(const char *name) "%s" +sheepdog_create_branch_snapshot(uint32_t vdi) "0x%" PRIx32 " is snapshot" +sheepdog_create_branch_created(uint32_t vdi) "0x%" PRIx32 " is created" +sheepdog_create_branch_new(uint32_t vdi) "0x%" PRIx32 " was newly created" +sheepdog_co_rw_vector_update(uint32_t vdi, uint64_t oid, uint64_t data, long idx) "update ino (%" PRIu32 ") %" PRIu64 " %" PRIu64 " %ld" +sheepdog_co_rw_vector_new(uint64_t oid) "new oid 0x%" PRIx64 +sheepdog_snapshot_create_info(const char *sn_name, const char *id, const char *name, int64_t size, int is_snapshot) "sn_info: name %s id_str %s s: name %s vm_state_size %" PRId64 " " "is_snapshot %d" +sheepdog_snapshot_create(const char *sn_name, const char *id) "%s %s" +sheepdog_snapshot_create_inode(const char *name, uint32_t snap, uint32_t vdi) "s->inode: name %s snap_id 0x%" PRIx32 " vdi 0x%" PRIx32 diff --git a/blockdev.c b/blockdev.c index a8fa8748a9..fb18e9c975 100644 --- a/blockdev.c +++ b/blockdev.c @@ -3582,6 +3582,11 @@ BlockDeviceInfoList *qmp_query_named_block_nodes(Error **errp) return bdrv_named_nodes_list(errp); } +XDbgBlockGraph *qmp_x_debug_query_block_graph(Error **errp) +{ + return bdrv_get_xdbg_block_graph(errp); +} + BlockJob *do_blockdev_backup(BlockdevBackup *backup, JobTxn *txn, Error **errp) { @@ -4269,6 +4269,17 @@ if compile_prog "" "" ; then signalfd=yes fi +# check if optreset global is declared by <getopt.h> +optreset="no" +cat > $TMPC << EOF +#include <getopt.h> +int main(void) { return optreset; } +EOF + +if compile_prog "" "" ; then + optreset=yes +fi + # check if eventfd is supported eventfd=no cat > $TMPC << EOF @@ -6643,6 +6654,9 @@ fi if test "$signalfd" = "yes" ; then echo "CONFIG_SIGNALFD=y" >> $config_host_mak fi +if test "$optreset" = "yes" ; then + echo "HAVE_OPTRESET=y" >> $config_host_mak +fi if test "$tcg" = "yes"; then echo "CONFIG_TCG=y" >> $config_host_mak if test "$tcg_interpreter" = "yes" ; then diff --git a/hw/block/nvme.c b/hw/block/nvme.c index 7c8c63e8f5..8325b5e88a 100644 --- a/hw/block/nvme.c +++ b/hw/block/nvme.c @@ -1208,6 +1208,11 @@ static void nvme_realize(PCIDevice *pci_dev, Error **errp) int64_t bs_size; uint8_t *pci_conf; + if (!n->num_queues) { + error_setg(errp, "num_queues can't be zero"); + return; + } + if (!n->conf.blk) { error_setg(errp, "drive property not set"); return; @@ -1233,7 +1238,7 @@ static void nvme_realize(PCIDevice *pci_dev, Error **errp) pci_conf[PCI_INTERRUPT_PIN] = 1; pci_config_set_prog_interface(pci_dev->config, 0x2); pci_config_set_class(pci_dev->config, PCI_CLASS_STORAGE_EXPRESS); - pcie_endpoint_cap_init(&n->parent_obj, 0x80); + pcie_endpoint_cap_init(pci_dev, 0x80); n->num_namespaces = 1; n->reg_size = pow2ceil(0x1004 + 2 * (n->num_queues + 1) * 4); @@ -1245,10 +1250,10 @@ static void nvme_realize(PCIDevice *pci_dev, Error **errp) memory_region_init_io(&n->iomem, OBJECT(n), &nvme_mmio_ops, n, "nvme", n->reg_size); - pci_register_bar(&n->parent_obj, 0, + pci_register_bar(pci_dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_TYPE_64, &n->iomem); - msix_init_exclusive_bar(&n->parent_obj, n->num_queues, 4, NULL); + msix_init_exclusive_bar(pci_dev, n->num_queues, 4, NULL); id->vid = cpu_to_le16(pci_get_word(pci_conf + PCI_VENDOR_ID)); id->ssvid = cpu_to_le16(pci_get_word(pci_conf + PCI_SUBSYSTEM_VENDOR_ID)); @@ -1303,7 +1308,7 @@ static void nvme_realize(PCIDevice *pci_dev, Error **errp) n->cmbuf = g_malloc0(NVME_CMBSZ_GETSIZE(n->bar.cmbsz)); memory_region_init_io(&n->ctrl_mem, OBJECT(n), &nvme_cmb_ops, n, "nvme-cmb", NVME_CMBSZ_GETSIZE(n->bar.cmbsz)); - pci_register_bar(&n->parent_obj, NVME_CMBLOC_BIR(n->bar.cmbloc), + pci_register_bar(pci_dev, NVME_CMBLOC_BIR(n->bar.cmbloc), PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_TYPE_64 | PCI_BASE_ADDRESS_MEM_PREFETCH, &n->ctrl_mem); @@ -1381,7 +1386,7 @@ static void nvme_instance_init(Object *obj) } static const TypeInfo nvme_info = { - .name = "nvme", + .name = TYPE_NVME, .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(NvmeCtrl), .class_init = nvme_class_init, diff --git a/include/block/block.h b/include/block/block.h index f70a843b72..57233cf2c0 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -448,6 +448,7 @@ void bdrv_eject(BlockDriverState *bs, bool eject_flag); const char *bdrv_get_format_name(BlockDriverState *bs); BlockDriverState *bdrv_find_node(const char *node_name); BlockDeviceInfoList *bdrv_named_nodes_list(Error **errp); +XDbgBlockGraph *bdrv_get_xdbg_block_graph(Error **errp); BlockDriverState *bdrv_lookup_bs(const char *device, const char *node_name, Error **errp); diff --git a/include/qemu/osdep.h b/include/qemu/osdep.h index 80df7253db..840af09cb0 100644 --- a/include/qemu/osdep.h +++ b/include/qemu/osdep.h @@ -109,6 +109,7 @@ extern int daemon(int, int); #include <ctype.h> #include <errno.h> #include <fcntl.h> +#include <getopt.h> #include <sys/stat.h> #include <sys/time.h> #include <assert.h> @@ -604,4 +605,19 @@ extern int qemu_icache_linesize_log; extern int qemu_dcache_linesize; extern int qemu_dcache_linesize_log; +/* + * After using getopt or getopt_long, if you need to parse another set + * of options, then you must reset optind. Unfortunately the way to + * do this varies between implementations of getopt. + */ +static inline void qemu_reset_optind(void) +{ +#ifdef HAVE_OPTRESET + optind = 1; + optreset = 1; +#else + optind = 0; +#endif +} + #endif diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h index c96bcdee14..c8f816a200 100644 --- a/include/sysemu/block-backend.h +++ b/include/sysemu/block-backend.h @@ -237,4 +237,6 @@ int coroutine_fn blk_co_copy_range(BlockBackend *blk_in, int64_t off_in, int bytes, BdrvRequestFlags read_flags, BdrvRequestFlags write_flags); +const BdrvChild *blk_root(BlockBackend *blk); + #endif diff --git a/qapi/block-core.json b/qapi/block-core.json index 91685be6c2..cde1b53708 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -1666,6 +1666,114 @@ { 'command': 'query-named-block-nodes', 'returns': [ 'BlockDeviceInfo' ] } ## +# @XDbgBlockGraphNodeType: +# +# @block-backend: corresponds to BlockBackend +# +# @block-job: corresonds to BlockJob +# +# @block-driver: corresponds to BlockDriverState +# +# Since: 4.0 +## +{ 'enum': 'XDbgBlockGraphNodeType', + 'data': [ 'block-backend', 'block-job', 'block-driver' ] } + +## +# @XDbgBlockGraphNode: +# +# @id: Block graph node identifier. This @id is generated only for +# x-debug-query-block-graph and does not relate to any other identifiers in +# Qemu. +# +# @type: Type of graph node. Can be one of block-backend, block-job or +# block-driver-state. +# +# @name: Human readable name of the node. Corresponds to node-name for +# block-driver-state nodes; is not guaranteed to be unique in the whole +# graph (with block-jobs and block-backends). +# +# Since: 4.0 +## +{ 'struct': 'XDbgBlockGraphNode', + 'data': { 'id': 'uint64', 'type': 'XDbgBlockGraphNodeType', 'name': 'str' } } + +## +# @BlockPermission: +# +# Enum of base block permissions. +# +# @consistent-read: A user that has the "permission" of consistent reads is +# guaranteed that their view of the contents of the block +# device is complete and self-consistent, representing the +# contents of a disk at a specific point. +# For most block devices (including their backing files) this +# is true, but the property cannot be maintained in a few +# situations like for intermediate nodes of a commit block +# job. +# +# @write: This permission is required to change the visible disk contents. +# +# @write-unchanged: This permission (which is weaker than BLK_PERM_WRITE) is +# both enough and required for writes to the block node when +# the caller promises that the visible disk content doesn't +# change. +# As the BLK_PERM_WRITE permission is strictly stronger, +# either is sufficient to perform an unchanging write. +# +# @resize: This permission is required to change the size of a block node. +# +# @graph-mod: This permission is required to change the node that this +# BdrvChild points to. +# +# Since: 4.0 +## + { 'enum': 'BlockPermission', + 'data': [ 'consistent-read', 'write', 'write-unchanged', 'resize', + 'graph-mod' ] } +## +# @XDbgBlockGraphEdge: +# +# Block Graph edge description for x-debug-query-block-graph. +# +# @parent: parent id +# +# @child: child id +# +# @name: name of the relation (examples are 'file' and 'backing') +# +# @perm: granted permissions for the parent operating on the child +# +# @shared-perm: permissions that can still be granted to other users of the +# child while it is still attached to this parent +# +# Since: 4.0 +## +{ 'struct': 'XDbgBlockGraphEdge', + 'data': { 'parent': 'uint64', 'child': 'uint64', + 'name': 'str', 'perm': [ 'BlockPermission' ], + 'shared-perm': [ 'BlockPermission' ] } } + +## +# @XDbgBlockGraph: +# +# Block Graph - list of nodes and list of edges. +# +# Since: 4.0 +## +{ 'struct': 'XDbgBlockGraph', + 'data': { 'nodes': ['XDbgBlockGraphNode'], 'edges': ['XDbgBlockGraphEdge'] } } + +## +# @x-debug-query-block-graph: +# +# Get the block graph. +# +# Since: 4.0 +## +{ 'command': 'x-debug-query-block-graph', 'returns': 'XDbgBlockGraph' } + +## # @drive-mirror: # # Start mirroring a block device's writes to a new destination. target diff --git a/qemu-img.c b/qemu-img.c index ad04f59565..25288c4d18 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -4962,7 +4962,7 @@ int main(int argc, char **argv) return 0; } argv += optind; - optind = 0; + qemu_reset_optind(); if (!trace_init_backends()) { exit(1); diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c index 2c39124036..ee8f56e46a 100644 --- a/qemu-io-cmds.c +++ b/qemu-io-cmds.c @@ -114,7 +114,7 @@ static int command(BlockBackend *blk, const cmdinfo_t *ct, int argc, } } - optind = 0; + qemu_reset_optind(); return ct->cfunc(blk, argc, argv); } diff --git a/scripts/render_block_graph.py b/scripts/render_block_graph.py new file mode 100755 index 0000000000..ed7e581b4f --- /dev/null +++ b/scripts/render_block_graph.py @@ -0,0 +1,120 @@ +#!/usr/bin/env python +# +# Render Qemu Block Graph +# +# Copyright (c) 2018 Virtuozzo International GmbH. All rights reserved. +# +# 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/>. +# + +import os +import sys +import subprocess +import json +from graphviz import Digraph +from qemu import MonitorResponseError + + +def perm(arr): + s = 'w' if 'write' in arr else '_' + s += 'r' if 'consistent-read' in arr else '_' + s += 'u' if 'write-unchanged' in arr else '_' + s += 'g' if 'graph-mod' in arr else '_' + s += 's' if 'resize' in arr else '_' + return s + + +def render_block_graph(qmp, filename, format='png'): + ''' + Render graph in text (dot) representation into "@filename" and + representation in @format into "@filename.@format" + ''' + + bds_nodes = qmp.command('query-named-block-nodes') + bds_nodes = {n['node-name']: n for n in bds_nodes} + + job_nodes = qmp.command('query-block-jobs') + job_nodes = {n['device']: n for n in job_nodes} + + block_graph = qmp.command('x-debug-query-block-graph') + + graph = Digraph(comment='Block Nodes Graph') + graph.format = format + graph.node('permission symbols:\l' + ' w - Write\l' + ' r - consistent-Read\l' + ' u - write - Unchanged\l' + ' g - Graph-mod\l' + ' s - reSize\l' + 'edge label scheme:\l' + ' <child type>\l' + ' <perm>\l' + ' <shared_perm>\l', shape='none') + + for n in block_graph['nodes']: + if n['type'] == 'block-driver': + info = bds_nodes[n['name']] + label = n['name'] + ' [' + info['drv'] + ']' + if info['drv'] == 'file': + label += '\n' + os.path.basename(info['file']) + shape = 'ellipse' + elif n['type'] == 'block-job': + info = job_nodes[n['name']] + label = info['type'] + ' job (' + n['name'] + ')' + shape = 'box' + else: + assert n['type'] == 'block-backend' + label = n['name'] if n['name'] else 'unnamed blk' + shape = 'box' + + graph.node(str(n['id']), label, shape=shape) + + for e in block_graph['edges']: + label = '%s\l%s\l%s\l' % (e['name'], perm(e['perm']), + perm(e['shared-perm'])) + graph.edge(str(e['parent']), str(e['child']), label=label) + + graph.render(filename) + + +class LibvirtGuest(): + def __init__(self, name): + self.name = name + + def command(self, cmd): + # only supports qmp commands without parameters + m = {'execute': cmd} + ar = ['virsh', 'qemu-monitor-command', self.name, json.dumps(m)] + + reply = json.loads(subprocess.check_output(ar)) + + if 'error' in reply: + raise MonitorResponseError(reply) + + return reply['return'] + + +if __name__ == '__main__': + obj = sys.argv[1] + out = sys.argv[2] + + if os.path.exists(obj): + # assume unix socket + qmp = QEMUMonitorProtocol(obj) + qmp.connect() + else: + # assume libvirt guest name + qmp = LibvirtGuest(obj) + + render_block_graph(qmp, out) diff --git a/tests/qemu-iotests/147 b/tests/qemu-iotests/147 index 05b374b7d3..82513279b0 100755 --- a/tests/qemu-iotests/147 +++ b/tests/qemu-iotests/147 @@ -19,13 +19,17 @@ # import os +import random import socket import stat import time import iotests -from iotests import cachemode, imgfmt, qemu_img, qemu_nbd +from iotests import cachemode, imgfmt, qemu_img, qemu_nbd, qemu_nbd_pipe -NBD_PORT = 10811 +NBD_PORT_START = 32768 +NBD_PORT_END = NBD_PORT_START + 1024 +NBD_IPV6_PORT_START = NBD_PORT_END +NBD_IPV6_PORT_END = NBD_IPV6_PORT_START + 1024 test_img = os.path.join(iotests.test_dir, 'test.img') unix_socket = os.path.join(iotests.test_dir, 'nbd.socket') @@ -88,17 +92,29 @@ class QemuNBD(NBDBlockdevAddBase): except OSError: pass + def _try_server_up(self, *args): + status, msg = qemu_nbd_pipe('-f', imgfmt, test_img, *args) + if status == 0: + return True + if 'Address already in use' in msg: + return False + self.fail(msg) + def _server_up(self, *args): - self.assertEqual(qemu_nbd('-f', imgfmt, test_img, *args), 0) + self.assertTrue(self._try_server_up(*args)) def test_inet(self): - self._server_up('-p', str(NBD_PORT)) + while True: + nbd_port = random.randrange(NBD_PORT_START, NBD_PORT_END) + if self._try_server_up('-b', 'localhost', '-p', str(nbd_port)): + break + address = { 'type': 'inet', 'data': { 'host': 'localhost', - 'port': str(NBD_PORT) + 'port': str(nbd_port) } } - self.client_test('nbd://localhost:%i' % NBD_PORT, + self.client_test('nbd://localhost:%i' % nbd_port, flatten_sock_addr(address)) def test_unix(self): @@ -130,8 +146,13 @@ class BuiltinNBD(NBDBlockdevAddBase): except OSError: pass - def _server_up(self, address, export_name=None, export_name2=None): + # Returns False on EADDRINUSE; fails an assertion on other errors. + # Returns True on success. + def _try_server_up(self, address, export_name=None, export_name2=None): result = self.server.qmp('nbd-server-start', addr=address) + if 'error' in result and \ + 'Address already in use' in result['error']['desc']: + return False self.assert_qmp(result, 'return', {}) if export_name is None: @@ -146,20 +167,28 @@ class BuiltinNBD(NBDBlockdevAddBase): name=export_name2) self.assert_qmp(result, 'return', {}) + return True + + def _server_up(self, address, export_name=None, export_name2=None): + self.assertTrue(self._try_server_up(address, export_name, export_name2)) def _server_down(self): result = self.server.qmp('nbd-server-stop') self.assert_qmp(result, 'return', {}) def do_test_inet(self, export_name=None): - address = { 'type': 'inet', - 'data': { - 'host': 'localhost', - 'port': str(NBD_PORT) - } } - self._server_up(address, export_name) + while True: + nbd_port = random.randrange(NBD_PORT_START, NBD_PORT_END) + address = { 'type': 'inet', + 'data': { + 'host': 'localhost', + 'port': str(nbd_port) + } } + if self._try_server_up(address, export_name): + break + export_name = export_name or 'nbd-export' - self.client_test('nbd://localhost:%i/%s' % (NBD_PORT, export_name), + self.client_test('nbd://localhost:%i/%s' % (nbd_port, export_name), flatten_sock_addr(address), export_name) self._server_down() @@ -173,15 +202,19 @@ class BuiltinNBD(NBDBlockdevAddBase): self.do_test_inet('shadow') def test_inet_two_exports(self): - address = { 'type': 'inet', - 'data': { - 'host': 'localhost', - 'port': str(NBD_PORT) - } } - self._server_up(address, 'exp1', 'exp2') - self.client_test('nbd://localhost:%i/%s' % (NBD_PORT, 'exp1'), + while True: + nbd_port = random.randrange(NBD_PORT_START, NBD_PORT_END) + address = { 'type': 'inet', + 'data': { + 'host': 'localhost', + 'port': str(nbd_port) + } } + if self._try_server_up(address, 'exp1', 'exp2'): + break + + self.client_test('nbd://localhost:%i/%s' % (nbd_port, 'exp1'), flatten_sock_addr(address), 'exp1', 'node1', False) - self.client_test('nbd://localhost:%i/%s' % (NBD_PORT, 'exp2'), + self.client_test('nbd://localhost:%i/%s' % (nbd_port, 'exp2'), flatten_sock_addr(address), 'exp2', 'node2', False) result = self.vm.qmp('blockdev-del', node_name='node1') self.assert_qmp(result, 'return', {}) @@ -197,20 +230,25 @@ class BuiltinNBD(NBDBlockdevAddBase): except socket.gaierror: # IPv6 not available, skip return - address = { 'type': 'inet', - 'data': { - 'host': '::1', - 'port': str(NBD_PORT), - 'ipv4': False, - 'ipv6': True - } } + + while True: + nbd_port = random.randrange(NBD_IPV6_PORT_START, NBD_IPV6_PORT_END) + address = { 'type': 'inet', + 'data': { + 'host': '::1', + 'port': str(nbd_port), + 'ipv4': False, + 'ipv6': True + } } + if self._try_server_up(address): + break + filename = { 'driver': 'raw', 'file': { 'driver': 'nbd', 'export': 'nbd-export', 'server': flatten_sock_addr(address) } } - self._server_up(address) self.client_test(filename, flatten_sock_addr(address), 'nbd-export') self._server_down() diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py index cbedfaf1df..009c614ef7 100644 --- a/tests/qemu-iotests/iotests.py +++ b/tests/qemu-iotests/iotests.py @@ -201,6 +201,20 @@ def qemu_nbd(*args): '''Run qemu-nbd in daemon mode and return the parent's exit code''' return subprocess.call(qemu_nbd_args + ['--fork'] + list(args)) +def qemu_nbd_pipe(*args): + '''Run qemu-nbd in daemon mode and return both the parent's exit code + and its output''' + subp = subprocess.Popen(qemu_nbd_args + ['--fork'] + list(args), + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + universal_newlines=True) + exitcode = subp.wait() + if exitcode < 0: + sys.stderr.write('qemu-nbd received signal %i: %s\n' % + (-exitcode, + ' '.join(qemu_nbd_args + ['--fork'] + list(args)))) + return exitcode, subp.communicate()[0] + def compare_images(img1, img2, fmt1=imgfmt, fmt2=imgfmt): '''Return True if two image files are identical''' return qemu_img('compare', '-f', fmt1, |