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 -----BEGIN PGP SIGNATURE----- iQEcBAABAgAGBQJcUkaiAAoJEPQH2wBh1c9AHsEIAIU0+FNjtdz7lNgyeBCSFCFa /qWNk4+w6QBfhTTx/N0hGwh5/FvNYQhby8VHtZitE4/QcLbJwHYgWf14pwce3tP3 3qNB87AdQpKMpbajQM2x2Xy8lnlPeM7fe21Q/12vuX7AlEDT3gH+W9rg94bw2oFN r+xBk6H5F2aVElw3CwMM7eary4+dPnnCQwAnoqM+g5hdpL+0scrIyARGw7v0hmSn LDWESCM4a55lEYmwj1wS3J3uj6Fj00yzBvcEuCcT1GO+lXlV8/ciO9r2HqxVKwgz 4GAi/BERoMKjfn+/77/yI5flprPx2voNGgkyBY4C3z9ncnN6u02QBZSusBIWpSg= =Kt4r -----END PGP SIGNATURE----- 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>
This commit is contained in:
		
						commit
						cfe6c54769
					
				
							
								
								
									
										148
									
								
								block.c
									
									
									
									
									
								
							
							
						
						
									
										148
									
								
								block.c
									
									
									
									
									
								
							@ -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)
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										29
									
								
								block/curl.c
									
									
									
									
									
								
							
							
						
						
									
										29
									
								
								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);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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);
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										46
									
								
								block/ssh.c
									
									
									
									
									
								
							
							
						
						
									
										46
									
								
								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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
@ -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)
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										14
									
								
								configure
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										14
									
								
								configure
									
									
									
									
										vendored
									
									
								
							@ -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
 | 
			
		||||
 | 
			
		||||
@ -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,
 | 
			
		||||
 | 
			
		||||
@ -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);
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
@ -1665,6 +1665,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:
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
@ -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);
 | 
			
		||||
 | 
			
		||||
@ -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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										120
									
								
								scripts/render_block_graph.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										120
									
								
								scripts/render_block_graph.py
									
									
									
									
									
										Executable file
									
								
							@ -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)
 | 
			
		||||
@ -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):
 | 
			
		||||
        while True:
 | 
			
		||||
            nbd_port = random.randrange(NBD_PORT_START, NBD_PORT_END)
 | 
			
		||||
            address = { 'type': 'inet',
 | 
			
		||||
                        'data': {
 | 
			
		||||
                            'host': 'localhost',
 | 
			
		||||
                        'port': str(NBD_PORT)
 | 
			
		||||
                            'port': str(nbd_port)
 | 
			
		||||
                        } }
 | 
			
		||||
        self._server_up(address, export_name)
 | 
			
		||||
            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):
 | 
			
		||||
        while True:
 | 
			
		||||
            nbd_port = random.randrange(NBD_PORT_START, NBD_PORT_END)
 | 
			
		||||
            address = { 'type': 'inet',
 | 
			
		||||
                        'data': {
 | 
			
		||||
                            'host': 'localhost',
 | 
			
		||||
                        'port': str(NBD_PORT)
 | 
			
		||||
                            'port': str(nbd_port)
 | 
			
		||||
                        } }
 | 
			
		||||
        self._server_up(address, 'exp1', 'exp2')
 | 
			
		||||
        self.client_test('nbd://localhost:%i/%s' % (NBD_PORT, 'exp1'),
 | 
			
		||||
            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
 | 
			
		||||
 | 
			
		||||
        while True:
 | 
			
		||||
            nbd_port = random.randrange(NBD_IPV6_PORT_START, NBD_IPV6_PORT_END)
 | 
			
		||||
            address = { 'type': 'inet',
 | 
			
		||||
                        'data': {
 | 
			
		||||
                            'host': '::1',
 | 
			
		||||
                        'port': str(NBD_PORT),
 | 
			
		||||
                            '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()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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,
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user