mirror of https://github.com/xemu-project/xemu.git
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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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):
|
||||
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()
|
||||
|
||||
|
|
|
@ -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…
Reference in New Issue