diff options
44 files changed, 468 insertions, 1091 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 3480d79db3..52d65d6c04 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -42,7 +42,7 @@ include: image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:latest script: - scripts/git-submodule.sh update - $(grep GIT_SUBMODULES build/config-host.mak | sed 's/GIT_SUBMODULES=//') + $(sed -n '/GIT_SUBMODULES=/ s/.*=// p' build/config-host.mak) - cd build - find . -type f -exec touch {} + # Avoid recompiling by hiding ninja with NINJA=":" @@ -223,7 +223,7 @@ build-system-centos: variables: IMAGE: centos8 CONFIGURE_ARGS: --disable-nettle --enable-gcrypt --enable-fdt=system - --enable-modules + --enable-modules --enable-trace-backends=dtrace TARGETS: ppc64-softmmu or1k-softmmu s390x-softmmu x86_64-softmmu rx-softmmu sh4-softmmu nios2-softmmu MAKE_CHECK_ARGS: check-build @@ -6299,7 +6299,7 @@ done (for i in $cross_cc_vars; do export $i done -export target_list source_path use_containers +export target_list source_path use_containers ARCH $source_path/tests/tcg/configure.sh) # temporary config to build submodules diff --git a/docs/system/gdb.rst b/docs/system/gdb.rst index 72b1e68f4e..144d083df3 100644 --- a/docs/system/gdb.rst +++ b/docs/system/gdb.rst @@ -45,7 +45,66 @@ Here are some useful tips in order to use gdb on system code: 3. Use ``set architecture i8086`` to dump 16 bit code. Then use ``x/10i $cs*16+$eip`` to dump the code at the PC position. -Advanced debugging options: +Debugging multicore machines +============================ + +GDB's abstraction for debugging targets with multiple possible +parallel flows of execution is a two layer one: it supports multiple +"inferiors", each of which can have multiple "threads". When the QEMU +machine has more than one CPU, QEMU exposes each CPU cluster as a +separate "inferior", where each CPU within the cluster is a separate +"thread". Most QEMU machine types have identical CPUs, so there is a +single cluster which has all the CPUs in it. A few machine types are +heterogenous and have multiple clusters: for example the ``sifive_u`` +machine has a cluster with one E51 core and a second cluster with four +U54 cores. Here the E51 is the only thread in the first inferior, and +the U54 cores are all threads in the second inferior. + +When you connect gdb to the gdbstub, it will automatically +connect to the first inferior; you can display the CPUs in this +cluster using the gdb ``info thread`` command, and switch between +them using gdb's usual thread-management commands. + +For multi-cluster machines, unfortunately gdb does not by default +handle multiple inferiors, and so you have to explicitly connect +to them. First, you must connect with the ``extended-remote`` +protocol, not ``remote``:: + + (gdb) target extended-remote localhost:1234 + +Once connected, gdb will have a single inferior, for the +first cluster. You need to create inferiors for the other +clusters and attach to them, like this:: + + (gdb) add-inferior + Added inferior 2 + (gdb) inferior 2 + [Switching to inferior 2 [<null>] (<noexec>)] + (gdb) attach 2 + Attaching to process 2 + warning: No executable has been specified and target does not support + determining executable automatically. Try using the "file" command. + 0x00000000 in ?? () + +Once you've done this, ``info threads`` will show CPUs in +all the clusters you have attached to:: + + (gdb) info threads + Id Target Id Frame + 1.1 Thread 1.1 (cortex-m33-arm-cpu cpu [running]) 0x00000000 in ?? () + * 2.1 Thread 2.2 (cortex-m33-arm-cpu cpu [halted ]) 0x00000000 in ?? () + +You probably also want to set gdb to ``schedule-multiple`` mode, +so that when you tell gdb to ``continue`` it resumes all CPUs, +not just those in the cluster you are currently working on:: + + (gdb) set schedule-multiple on + +Advanced debugging options +========================== + +Changing single-stepping behaviour +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The default single stepping behavior is step with the IRQs and timer service routines off. It is set this way because when gdb executes a @@ -88,6 +147,8 @@ three commands you can query and set the single step behavior: sending: "qemu.sstep=0x5" received: "OK" +Examining physical memory +^^^^^^^^^^^^^^^^^^^^^^^^^ Another feature that QEMU gdbstub provides is to toggle the memory GDB works with, by default GDB will show the current process memory respecting diff --git a/hw/block/nvme-dif.c b/hw/block/nvme-dif.c index e6f04faafb..81b0a4cb13 100644 --- a/hw/block/nvme-dif.c +++ b/hw/block/nvme-dif.c @@ -1,3 +1,13 @@ +/* + * QEMU NVM Express End-to-End Data Protection support + * + * Copyright (c) 2021 Samsung Electronics Co., Ltd. + * + * Authors: + * Klaus Jensen <k.jensen@samsung.com> + * Gollu Appalanaidu <anaidu.gollu@samsung.com> + */ + #include "qemu/osdep.h" #include "hw/block/block.h" #include "sysemu/dma.h" diff --git a/hw/block/nvme-dif.h b/hw/block/nvme-dif.h index 5a8e37c852..524faffbd7 100644 --- a/hw/block/nvme-dif.h +++ b/hw/block/nvme-dif.h @@ -1,3 +1,13 @@ +/* + * QEMU NVM Express End-to-End Data Protection support + * + * Copyright (c) 2021 Samsung Electronics Co., Ltd. + * + * Authors: + * Klaus Jensen <k.jensen@samsung.com> + * Gollu Appalanaidu <anaidu.gollu@samsung.com> + */ + #ifndef HW_NVME_DIF_H #define HW_NVME_DIF_H diff --git a/hw/block/nvme-ns.c b/hw/block/nvme-ns.c index 7f8d139a86..7bb618f182 100644 --- a/hw/block/nvme-ns.c +++ b/hw/block/nvme-ns.c @@ -73,7 +73,7 @@ static int nvme_ns_init(NvmeNamespace *ns, Error **errp) /* support DULBE and I/O optimization fields */ id_ns->nsfeat |= (0x4 | 0x10); - if (nvme_ns_shared(ns)) { + if (ns->params.shared) { id_ns->nmic |= NVME_NMIC_NS_SHARED; } @@ -387,25 +387,46 @@ static void nvme_zoned_ns_shutdown(NvmeNamespace *ns) assert(ns->nr_open_zones == 0); } -static int nvme_ns_check_constraints(NvmeNamespace *ns, Error **errp) +static int nvme_ns_check_constraints(NvmeCtrl *n, NvmeNamespace *ns, + Error **errp) { if (!ns->blkconf.blk) { error_setg(errp, "block backend not configured"); return -1; } - if (ns->params.pi && !ns->params.ms) { + if (ns->params.pi && ns->params.ms < 8) { error_setg(errp, "at least 8 bytes of metadata required to enable " "protection information"); return -1; } + if (ns->params.nsid > NVME_MAX_NAMESPACES) { + error_setg(errp, "invalid namespace id (must be between 0 and %d)", + NVME_MAX_NAMESPACES); + return -1; + } + + if (!n->subsys) { + if (ns->params.detached) { + error_setg(errp, "detached requires that the nvme device is " + "linked to an nvme-subsys device"); + return -1; + } + + if (ns->params.shared) { + error_setg(errp, "shared requires that the nvme device is " + "linked to an nvme-subsys device"); + return -1; + } + } + return 0; } -int nvme_ns_setup(NvmeNamespace *ns, Error **errp) +int nvme_ns_setup(NvmeCtrl *n, NvmeNamespace *ns, Error **errp) { - if (nvme_ns_check_constraints(ns, errp)) { + if (nvme_ns_check_constraints(n, ns, errp)) { return -1; } @@ -453,27 +474,62 @@ static void nvme_ns_realize(DeviceState *dev, Error **errp) NvmeNamespace *ns = NVME_NS(dev); BusState *s = qdev_get_parent_bus(dev); NvmeCtrl *n = NVME(s->parent); + NvmeSubsystem *subsys = n->subsys; + uint32_t nsid = ns->params.nsid; + int i; - if (nvme_ns_setup(ns, errp)) { + if (nvme_ns_setup(n, ns, errp)) { return; } - if (ns->subsys) { - if (nvme_subsys_register_ns(ns, errp)) { + if (!nsid) { + for (i = 1; i <= NVME_MAX_NAMESPACES; i++) { + if (nvme_ns(n, i) || nvme_subsys_ns(subsys, i)) { + continue; + } + + nsid = ns->params.nsid = i; + break; + } + + if (!nsid) { + error_setg(errp, "no free namespace id"); return; } } else { - if (nvme_register_namespace(n, ns, errp)) { + if (nvme_ns(n, nsid) || nvme_subsys_ns(subsys, nsid)) { + error_setg(errp, "namespace id '%d' already allocated", nsid); return; } } + + if (subsys) { + subsys->namespaces[nsid] = ns; + + if (ns->params.detached) { + return; + } + + if (ns->params.shared) { + for (i = 0; i < ARRAY_SIZE(subsys->ctrls); i++) { + NvmeCtrl *ctrl = subsys->ctrls[i]; + + if (ctrl) { + nvme_attach_ns(ctrl, ns); + } + } + + return; + } + } + + nvme_attach_ns(n, ns); } static Property nvme_ns_props[] = { DEFINE_BLOCK_PROPERTIES(NvmeNamespace, blkconf), - DEFINE_PROP_LINK("subsys", NvmeNamespace, subsys, TYPE_NVME_SUBSYS, - NvmeSubsystem *), DEFINE_PROP_BOOL("detached", NvmeNamespace, params.detached, false), + DEFINE_PROP_BOOL("shared", NvmeNamespace, params.shared, false), DEFINE_PROP_UINT32("nsid", NvmeNamespace, params.nsid, 0), DEFINE_PROP_UUID("uuid", NvmeNamespace, params.uuid), DEFINE_PROP_UINT16("ms", NvmeNamespace, params.ms, 0), diff --git a/hw/block/nvme-ns.h b/hw/block/nvme-ns.h index 9ab7894fc8..fb0a41f912 100644 --- a/hw/block/nvme-ns.h +++ b/hw/block/nvme-ns.h @@ -29,6 +29,7 @@ typedef struct NvmeZone { typedef struct NvmeNamespaceParams { bool detached; + bool shared; uint32_t nsid; QemuUUID uuid; @@ -60,8 +61,8 @@ typedef struct NvmeNamespace { const uint32_t *iocs; uint8_t csi; uint16_t status; + int attached; - NvmeSubsystem *subsys; QTAILQ_ENTRY(NvmeNamespace) entry; NvmeIdNsZoned *id_ns_zoned; @@ -96,12 +97,7 @@ static inline uint32_t nvme_nsid(NvmeNamespace *ns) return ns->params.nsid; } - return -1; -} - -static inline bool nvme_ns_shared(NvmeNamespace *ns) -{ - return !!ns->subsys; + return 0; } static inline NvmeLBAF *nvme_ns_lbaf(NvmeNamespace *ns) @@ -225,7 +221,7 @@ static inline void nvme_aor_dec_active(NvmeNamespace *ns) } void nvme_ns_init_format(NvmeNamespace *ns); -int nvme_ns_setup(NvmeNamespace *ns, Error **errp); +int nvme_ns_setup(NvmeCtrl *n, NvmeNamespace *ns, Error **errp); void nvme_ns_drain(NvmeNamespace *ns); void nvme_ns_shutdown(NvmeNamespace *ns); void nvme_ns_cleanup(NvmeNamespace *ns); diff --git a/hw/block/nvme-subsys.c b/hw/block/nvme-subsys.c index 9fadef8cec..283a97b79d 100644 --- a/hw/block/nvme-subsys.c +++ b/hw/block/nvme-subsys.c @@ -43,34 +43,6 @@ int nvme_subsys_register_ctrl(NvmeCtrl *n, Error **errp) return cntlid; } -int nvme_subsys_register_ns(NvmeNamespace *ns, Error **errp) -{ - NvmeSubsystem *subsys = ns->subsys; - NvmeCtrl *n; - uint32_t nsid = nvme_nsid(ns); - int i; - - assert(nsid && nsid <= NVME_SUBSYS_MAX_NAMESPACES); - - if (subsys->namespaces[nsid]) { - error_setg(errp, "namespace %d already registerd to subsy %s", - nvme_nsid(ns), subsys->parent_obj.id); - return -1; - } - - subsys->namespaces[nsid] = ns; - - for (i = 0; i < ARRAY_SIZE(subsys->ctrls); i++) { - n = subsys->ctrls[i]; - - if (n && nvme_register_namespace(n, ns, errp)) { - return -1; - } - } - - return 0; -} - static void nvme_subsys_setup(NvmeSubsystem *subsys) { const char *nqn = subsys->params.nqn ? diff --git a/hw/block/nvme-subsys.h b/hw/block/nvme-subsys.h index aafa04b848..7d7ef5f7f1 100644 --- a/hw/block/nvme-subsys.h +++ b/hw/block/nvme-subsys.h @@ -14,7 +14,7 @@ OBJECT_CHECK(NvmeSubsystem, (obj), TYPE_NVME_SUBSYS) #define NVME_SUBSYS_MAX_CTRLS 32 -#define NVME_SUBSYS_MAX_NAMESPACES 256 +#define NVME_MAX_NAMESPACES 256 typedef struct NvmeCtrl NvmeCtrl; typedef struct NvmeNamespace NvmeNamespace; @@ -24,7 +24,7 @@ typedef struct NvmeSubsystem { NvmeCtrl *ctrls[NVME_SUBSYS_MAX_CTRLS]; /* Allocated namespaces for this subsystem */ - NvmeNamespace *namespaces[NVME_SUBSYS_MAX_NAMESPACES + 1]; + NvmeNamespace *namespaces[NVME_MAX_NAMESPACES + 1]; struct { char *nqn; @@ -32,12 +32,11 @@ typedef struct NvmeSubsystem { } NvmeSubsystem; int nvme_subsys_register_ctrl(NvmeCtrl *n, Error **errp); -int nvme_subsys_register_ns(NvmeNamespace *ns, Error **errp); static inline NvmeCtrl *nvme_subsys_ctrl(NvmeSubsystem *subsys, uint32_t cntlid) { - if (!subsys) { + if (!subsys || cntlid >= NVME_SUBSYS_MAX_CTRLS) { return NULL; } @@ -50,12 +49,10 @@ static inline NvmeCtrl *nvme_subsys_ctrl(NvmeSubsystem *subsys, static inline NvmeNamespace *nvme_subsys_ns(NvmeSubsystem *subsys, uint32_t nsid) { - if (!subsys) { + if (!subsys || !nsid || nsid > NVME_MAX_NAMESPACES) { return NULL; } - assert(nsid && nsid <= NVME_SUBSYS_MAX_NAMESPACES); - return subsys->namespaces[nsid]; } diff --git a/hw/block/nvme.c b/hw/block/nvme.c index 7244534a89..6b1f056a0e 100644 --- a/hw/block/nvme.c +++ b/hw/block/nvme.c @@ -93,10 +93,13 @@ * * nvme namespace device parameters * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - `subsys` - * If given, the namespace will be attached to all controllers in the - * subsystem. Otherwise, `bus` must be given to attach this namespace to a - * specific controller as a non-shared namespace. + * - `shared` + * When the parent nvme device (as defined explicitly by the 'bus' parameter + * or implicitly by the most recently defined NvmeBus) is linked to an + * nvme-subsys device, the namespace will be attached to all controllers in + * the subsystem. If set to 'off' (the default), the namespace will remain a + * private namespace and may only be attached to a single controller at a + * time. * * - `detached` * This parameter is only valid together with the `subsys` parameter. If left @@ -4242,7 +4245,7 @@ static uint16_t nvme_identify_ns_attached_list(NvmeCtrl *n, NvmeRequest *req) continue; } - if (!nvme_ns_is_attached(ctrl, ns)) { + if (!nvme_ns(ctrl, c->nsid)) { continue; } @@ -4868,6 +4871,21 @@ static uint16_t nvme_aer(NvmeCtrl *n, NvmeRequest *req) return NVME_NO_COMPLETE; } +static void nvme_update_dmrsl(NvmeCtrl *n) +{ + int nsid; + + for (nsid = 1; nsid <= NVME_MAX_NAMESPACES; nsid++) { + NvmeNamespace *ns = nvme_ns(n, nsid); + if (!ns) { + continue; + } + + n->dmrsl = MIN_NON_ZERO(n->dmrsl, + BDRV_REQUEST_MAX_BYTES / nvme_l2b(ns, 1)); + } +} + static void __nvme_select_ns_iocs(NvmeCtrl *n, NvmeNamespace *ns); static uint16_t nvme_ns_attachment(NvmeCtrl *n, NvmeRequest *req) { @@ -4884,6 +4902,10 @@ static uint16_t nvme_ns_attachment(NvmeCtrl *n, NvmeRequest *req) trace_pci_nvme_ns_attachment(nvme_cid(req), dw10 & 0xf); + if (!nvme_nsid_valid(n, nsid)) { + return NVME_INVALID_NSID | NVME_DNR; + } + ns = nvme_subsys_ns(n->subsys, nsid); if (!ns) { return NVME_INVALID_FIELD | NVME_DNR; @@ -4898,6 +4920,7 @@ static uint16_t nvme_ns_attachment(NvmeCtrl *n, NvmeRequest *req) return NVME_NS_CTRL_LIST_INVALID | NVME_DNR; } + *nr_ids = MIN(*nr_ids, NVME_CONTROLLER_LIST_SIZE - 1); for (i = 0; i < *nr_ids; i++) { ctrl = nvme_subsys_ctrl(n->subsys, ids[i]); if (!ctrl) { @@ -4905,18 +4928,25 @@ static uint16_t nvme_ns_attachment(NvmeCtrl *n, NvmeRequest *req) } if (attach) { - if (nvme_ns_is_attached(ctrl, ns)) { + if (nvme_ns(ctrl, nsid)) { return NVME_NS_ALREADY_ATTACHED | NVME_DNR; } - nvme_ns_attach(ctrl, ns); + if (ns->attached && !ns->params.shared) { + return NVME_NS_PRIVATE | NVME_DNR; + } + + nvme_attach_ns(ctrl, ns); __nvme_select_ns_iocs(ctrl, ns); } else { - if (!nvme_ns_is_attached(ctrl, ns)) { + if (!nvme_ns(ctrl, nsid)) { return NVME_NS_NOT_ATTACHED | NVME_DNR; } - nvme_ns_detach(ctrl, ns); + ctrl->namespaces[nsid - 1] = NULL; + ns->attached--; + + nvme_update_dmrsl(ctrl); } /* @@ -5805,9 +5835,10 @@ static void nvme_check_constraints(NvmeCtrl *n, Error **errp) params->max_ioqpairs = params->num_queues - 1; } - if (n->conf.blk) { - warn_report("drive property is deprecated; " - "please use an nvme-ns device instead"); + if (n->namespace.blkconf.blk && n->subsys) { + error_setg(errp, "subsystem support is unavailable with legacy " + "namespace ('drive' property)"); + return; } if (params->max_ioqpairs < 1 || @@ -5870,75 +5901,6 @@ static void nvme_init_state(NvmeCtrl *n) n->aer_reqs = g_new0(NvmeRequest *, n->params.aerl + 1); } -static int nvme_attach_namespace(NvmeCtrl *n, NvmeNamespace *ns, Error **errp) -{ - if (nvme_ns_is_attached(n, ns)) { - error_setg(errp, - "namespace %d is already attached to controller %d", - nvme_nsid(ns), n->cntlid); - return -1; - } - - nvme_ns_attach(n, ns); - - return 0; -} - -int nvme_register_namespace(NvmeCtrl *n, NvmeNamespace *ns, Error **errp) -{ - uint32_t nsid = nvme_nsid(ns); - - if (nsid > NVME_MAX_NAMESPACES) { - error_setg(errp, "invalid namespace id (must be between 0 and %d)", - NVME_MAX_NAMESPACES); - return -1; - } - - if (!nsid) { - for (int i = 1; i <= n->num_namespaces; i++) { - if (!nvme_ns(n, i)) { - nsid = ns->params.nsid = i; - break; - } - } - - if (!nsid) { - error_setg(errp, "no free namespace id"); - return -1; - } - } else { - if (n->namespaces[nsid - 1]) { - error_setg(errp, "namespace id '%d' is already in use", nsid); - return -1; - } - } - - trace_pci_nvme_register_namespace(nsid); - - /* - * If subsys is not given, namespae is always attached to the controller - * because there's no subsystem to manage namespace allocation. - */ - if (!n->subsys) { - if (ns->params.detached) { - error_setg(errp, - "detached needs nvme-subsys specified nvme or nvme-ns"); - return -1; - } - - return nvme_attach_namespace(n, ns, errp); - } else { - if (!ns->params.detached) { - return nvme_attach_namespace(n, ns, errp); - } - } - - n->dmrsl = MIN_NON_ZERO(n->dmrsl, - BDRV_REQUEST_MAX_BYTES / nvme_l2b(ns, 1)); - - return 0; -} - static void nvme_init_cmb(NvmeCtrl *n, PCIDevice *pci_dev) { uint64_t cmb_size = n->params.cmb_size_mb * MiB; @@ -6168,6 +6130,18 @@ static int nvme_init_subsys(NvmeCtrl *n, Error **errp) return 0; } +void nvme_attach_ns(NvmeCtrl *n, NvmeNamespace *ns) +{ + uint32_t nsid = ns->params.nsid; + assert(nsid && nsid <= NVME_MAX_NAMESPACES); + + n->namespaces[nsid - 1] = ns; + ns->attached++; + + n->dmrsl = MIN_NON_ZERO(n->dmrsl, + BDRV_REQUEST_MAX_BYTES / nvme_l2b(ns, 1)); +} + static void nvme_realize(PCIDevice *pci_dev, Error **errp) { NvmeCtrl *n = NVME(pci_dev); @@ -6199,13 +6173,11 @@ static void nvme_realize(PCIDevice *pci_dev, Error **errp) ns = &n->namespace; ns->params.nsid = 1; - if (nvme_ns_setup(ns, errp)) { + if (nvme_ns_setup(n, ns, errp)) { return; } - if (nvme_register_namespace(n, ns, errp)) { - return; - } + nvme_attach_ns(n, ns); } } diff --git a/hw/block/nvme.h b/hw/block/nvme.h index 5b0031b11d..5d05ec368f 100644 --- a/hw/block/nvme.h +++ b/hw/block/nvme.h @@ -6,17 +6,9 @@ #include "nvme-subsys.h" #include "nvme-ns.h" -#define NVME_MAX_NAMESPACES 256 - #define NVME_DEFAULT_ZONE_SIZE (128 * MiB) #define NVME_DEFAULT_MAX_ZA_SIZE (128 * KiB) -/* - * Subsystem namespace list for allocated namespaces should be larger than - * attached namespace list in a controller. - */ -QEMU_BUILD_BUG_ON(NVME_MAX_NAMESPACES > NVME_SUBSYS_MAX_NAMESPACES); - typedef struct NvmeParams { char *serial; uint32_t num_queues; /* deprecated since 5.1 */ @@ -86,6 +78,7 @@ static inline const char *nvme_adm_opc_str(uint8_t opc) case NVME_ADM_CMD_SET_FEATURES: return "NVME_ADM_CMD_SET_FEATURES"; case NVME_ADM_CMD_GET_FEATURES: return "NVME_ADM_CMD_GET_FEATURES"; case NVME_ADM_CMD_ASYNC_EV_REQ: return "NVME_ADM_CMD_ASYNC_EV_REQ"; + case NVME_ADM_CMD_NS_ATTACHMENT: return "NVME_ADM_CMD_NS_ATTACHMENT"; case NVME_ADM_CMD_FORMAT_NVM: return "NVME_ADM_CMD_FORMAT_NVM"; default: return "NVME_ADM_CMD_UNKNOWN"; } @@ -165,7 +158,6 @@ typedef struct NvmeCtrl { NvmeBar bar; NvmeParams params; NvmeBus bus; - BlockConf conf; uint16_t cntlid; bool qs_created; @@ -234,35 +226,6 @@ static inline NvmeNamespace *nvme_ns(NvmeCtrl *n, uint32_t nsid) return n->namespaces[nsid - 1]; } -static inline bool nvme_ns_is_attached(NvmeCtrl *n, NvmeNamespace *ns) -{ - int nsid; - - for (nsid = 1; nsid <= n->num_namespaces; nsid++) { - if (nvme_ns(n, nsid) == ns) { - return true; - } - } - - return false; -} - -static inline void nvme_ns_attach(NvmeCtrl *n, NvmeNamespace *ns) -{ - uint32_t nsid = nvme_nsid(ns); - assert(nsid && nsid <= NVME_MAX_NAMESPACES); - - n->namespaces[nsid - 1] = ns; -} - -static inline void nvme_ns_detach(NvmeCtrl *n, NvmeNamespace *ns) -{ - uint32_t nsid = nvme_nsid(ns); - assert(nsid && nsid <= NVME_MAX_NAMESPACES); - - n->namespaces[nsid - 1] = NULL; -} - static inline NvmeCQueue *nvme_cq(NvmeRequest *req) { NvmeSQueue *sq = req->sq; @@ -291,7 +254,7 @@ typedef enum NvmeTxDirection { NVME_TX_DIRECTION_FROM_DEVICE = 1, } NvmeTxDirection; -int nvme_register_namespace(NvmeCtrl *n, NvmeNamespace *ns, Error **errp); +void nvme_attach_ns(NvmeCtrl *n, NvmeNamespace *ns); uint16_t nvme_bounce_data(NvmeCtrl *n, uint8_t *ptr, uint32_t len, NvmeTxDirection dir, NvmeRequest *req); uint16_t nvme_bounce_mdata(NvmeCtrl *n, uint8_t *ptr, uint32_t len, diff --git a/hw/block/trace-events b/hw/block/trace-events index 22da06986d..fa12e3a67a 100644 --- a/hw/block/trace-events +++ b/hw/block/trace-events @@ -51,7 +51,6 @@ hd_geometry_guess(void *blk, uint32_t cyls, uint32_t heads, uint32_t secs, int t # nvme.c # nvme traces for successful events -pci_nvme_register_namespace(uint32_t nsid) "nsid %"PRIu32"" pci_nvme_irq_msix(uint32_t vector) "raising MSI-X IRQ vector %u" pci_nvme_irq_pin(void) "pulsing IRQ pin" pci_nvme_irq_masked(void) "IRQ is masked" diff --git a/hw/net/xen_nic.c b/hw/net/xen_nic.c index 8431808ea0..5c815b4f0c 100644 --- a/hw/net/xen_nic.c +++ b/hw/net/xen_nic.c @@ -296,8 +296,9 @@ static int net_init(struct XenLegacyDevice *xendev) netdev->nic = qemu_new_nic(&net_xen_info, &netdev->conf, "xen", NULL, netdev); - qemu_get_queue(netdev->nic)->info_str = g_strdup_printf( - "nic: xenbus vif macaddr=%s", netdev->mac); + snprintf(qemu_get_queue(netdev->nic)->info_str, + sizeof(qemu_get_queue(netdev->nic)->info_str), + "nic: xenbus vif macaddr=%s", netdev->mac); /* fill info */ xenstore_write_be_int(&netdev->xendev, "feature-rx-copy", 1); diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c index e770955176..d120bf8f43 100644 --- a/hw/virtio/virtio-balloon.c +++ b/hw/virtio/virtio-balloon.c @@ -66,8 +66,12 @@ static bool virtio_balloon_pbp_matches(PartiallyBalloonedPage *pbp, static bool virtio_balloon_inhibited(void) { - /* Postcopy cannot deal with concurrent discards, so it's special. */ - return ram_block_discard_is_disabled() || migration_in_incoming_postcopy(); + /* + * Postcopy cannot deal with concurrent discards, + * so it's special, as well as background snapshots. + */ + return ram_block_discard_is_disabled() || migration_in_incoming_postcopy() || + migration_in_bg_snapshot(); } static void balloon_inflate_page(VirtIOBalloon *balloon, diff --git a/include/block/nvme.h b/include/block/nvme.h index b0a4e42916..4ac926fbc6 100644 --- a/include/block/nvme.h +++ b/include/block/nvme.h @@ -847,6 +847,7 @@ enum NvmeStatusCodes { NVME_FEAT_NOT_NS_SPEC = 0x010f, NVME_FW_REQ_SUSYSTEM_RESET = 0x0110, NVME_NS_ALREADY_ATTACHED = 0x0118, + NVME_NS_PRIVATE = 0x0119, NVME_NS_NOT_ATTACHED = 0x011A, NVME_NS_CTRL_LIST_INVALID = 0x011C, NVME_CONFLICTING_ATTRS = 0x0180, diff --git a/include/migration/misc.h b/include/migration/misc.h index bccc1b6b44..738675ef52 100644 --- a/include/migration/misc.h +++ b/include/migration/misc.h @@ -70,6 +70,8 @@ bool migration_in_postcopy_after_devices(MigrationState *); void migration_global_dump(Monitor *mon); /* True if incomming migration entered POSTCOPY_INCOMING_DISCARD */ bool migration_in_incoming_postcopy(void); +/* True if background snapshot is active */ +bool migration_in_bg_snapshot(void); /* migration/block-dirty-bitmap.c */ void dirty_bitmap_mig_init(void); diff --git a/include/net/net.h b/include/net/net.h index 3559f3ca19..eff24519d2 100644 --- a/include/net/net.h +++ b/include/net/net.h @@ -5,8 +5,6 @@ #include "qapi/qapi-types-net.h" #include "net/queue.h" #include "hw/qdev-properties-system.h" -#include "qapi/clone-visitor.h" -#include "qapi/qapi-visit-net.h" #define MAC_FMT "%02X:%02X:%02X:%02X:%02X:%02X" #define MAC_ARG(x) ((uint8_t *)(x))[0], ((uint8_t *)(x))[1], \ @@ -94,8 +92,7 @@ struct NetClientState { NetQueue *incoming_queue; char *model; char *name; - char *info_str; - NetdevInfo *stored_config; + char info_str[256]; unsigned receive_disabled : 1; NetClientDestructor *destructor; unsigned int queue_index; diff --git a/include/qapi/hmp-output-visitor.h b/include/qapi/hmp-output-visitor.h deleted file mode 100644 index 541e4002e3..0000000000 --- a/include/qapi/hmp-output-visitor.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * HMP string output Visitor - * - * Copyright Yandex N.V., 2021 - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * - */ - -#ifndef HMP_OUTPUT_VISITOR_H -#define HMP_OUTPUT_VISITOR_H - -#include "qapi/visitor.h" - -typedef struct HMPOutputVisitor HMPOutputVisitor; - -/** - * Create a HMP string output visitor for @obj - * - * Flattens dicts/structures, only shows arrays borders. - * - * Errors are not expected to happen. - * - * The caller is responsible for freeing the visitor with - * visit_free(). - */ -Visitor *hmp_output_visitor_new(char **result); - -#endif diff --git a/migration/migration.c b/migration/migration.c index ca8b97baa5..8ca034136b 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -1976,6 +1976,14 @@ bool migration_in_incoming_postcopy(void) return ps >= POSTCOPY_INCOMING_DISCARD && ps < POSTCOPY_INCOMING_END; } +bool migration_in_bg_snapshot(void) +{ + MigrationState *s = migrate_get_current(); + + return migrate_background_snapshot() && + migration_is_setup_or_active(s->state); +} + bool migration_is_idle(void) { MigrationState *s = current_migration; @@ -3812,13 +3820,21 @@ static void *bg_migration_thread(void *opaque) * with vCPUs running and, finally, write stashed non-RAM part of * the vmstate from the buffer to the migration stream. */ - s->bioc = qio_channel_buffer_new(128 * 1024); + s->bioc = qio_channel_buffer_new(512 * 1024); qio_channel_set_name(QIO_CHANNEL(s->bioc), "vmstate-buffer"); fb = qemu_fopen_channel_output(QIO_CHANNEL(s->bioc)); object_unref(OBJECT(s->bioc)); update_iteration_initial_status(s); + /* + * Prepare for tracking memory writes with UFFD-WP - populate + * RAM pages before protecting. + */ +#ifdef __linux__ + ram_write_tracking_prepare(); +#endif + qemu_savevm_state_header(s->to_dst_file); qemu_savevm_state_setup(s->to_dst_file); @@ -3866,6 +3882,12 @@ static void *bg_migration_thread(void *opaque) if (qemu_savevm_state_complete_precopy_non_iterable(fb, false, false)) { goto fail; } + /* + * Since we are going to get non-iterable state data directly + * from s->bioc->data, explicit flush is needed here. + */ + qemu_fflush(fb); + /* Now initialize UFFD context and start tracking RAM writes */ if (ram_write_tracking_start()) { goto fail; diff --git a/migration/ram.c b/migration/ram.c index 40e78952ad..4682f3625c 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -1455,7 +1455,7 @@ static RAMBlock *poll_fault_page(RAMState *rs, ram_addr_t *offset) { struct uffd_msg uffd_msg; void *page_address; - RAMBlock *bs; + RAMBlock *block; int res; if (!migrate_background_snapshot()) { @@ -1468,9 +1468,9 @@ static RAMBlock *poll_fault_page(RAMState *rs, ram_addr_t *offset) } page_address = (void *)(uintptr_t) uffd_msg.arg.pagefault.address; - bs = qemu_ram_block_from_host(page_address, false, offset); - assert(bs && (bs->flags & RAM_UF_WRITEPROTECT) != 0); - return bs; + block = qemu_ram_block_from_host(page_address, false, offset); + assert(block && (block->flags & RAM_UF_WRITEPROTECT) != 0); + return block; } /** @@ -1526,7 +1526,7 @@ bool ram_write_tracking_compatible(void) { const uint64_t uffd_ioctls_mask = BIT(_UFFDIO_WRITEPROTECT); int uffd_fd; - RAMBlock *bs; + RAMBlock *block; bool ret = false; /* Open UFFD file descriptor */ @@ -1537,15 +1537,15 @@ bool ram_write_tracking_compatible(void) RCU_READ_LOCK_GUARD(); - RAMBLOCK_FOREACH_NOT_IGNORED(bs) { + RAMBLOCK_FOREACH_NOT_IGNORED(block) { uint64_t uffd_ioctls; /* Nothing to do with read-only and MMIO-writable regions */ - if (bs->mr->readonly || bs->mr->rom_device) { + if (block->mr->readonly || block->mr->rom_device) { continue; } /* Try to register block memory via UFFD-IO to track writes */ - if (uffd_register_memory(uffd_fd, bs->host, bs->max_length, + if (uffd_register_memory(uffd_fd, block->host, block->max_length, UFFDIO_REGISTER_MODE_WP, &uffd_ioctls)) { goto out; } @@ -1561,6 +1561,55 @@ out: } /* + * ram_block_populate_pages: populate memory in the RAM block by reading + * an integer from the beginning of each page. + * + * Since it's solely used for userfault_fd WP feature, here we just + * hardcode page size to qemu_real_host_page_size. + * + * @block: RAM block to populate + */ +static void ram_block_populate_pages(RAMBlock *block) +{ + char *ptr = (char *) block->host; + + for (ram_addr_t offset = 0; offset < block->used_length; + offset += qemu_real_host_page_size) { + char tmp = *(ptr + offset); + + /* Don't optimize the read out */ + asm volatile("" : "+r" (tmp)); + } +} + +/* + * ram_write_tracking_prepare: prepare for UFFD-WP memory tracking + */ +void ram_write_tracking_prepare(void) +{ + RAMBlock *block; + + RCU_READ_LOCK_GUARD(); + + RAMBLOCK_FOREACH_NOT_IGNORED(block) { + /* Nothing to do with read-only and MMIO-writable regions */ + if (block->mr->readonly || block->mr->rom_device) { + continue; + } + + /* + * Populate pages of the RAM block before enabling userfault_fd + * write protection. + * + * This stage is required since ioctl(UFFDIO_WRITEPROTECT) with + * UFFDIO_WRITEPROTECT_MODE_WP mode setting would silently skip + * pages with pte_none() entries in page table. + */ + ram_block_populate_pages(block); + } +} + +/* * ram_write_tracking_start: start UFFD-WP memory tracking * * Returns 0 for success or negative value in case of error @@ -1569,7 +1618,7 @@ int ram_write_tracking_start(void) { int uffd_fd; RAMState *rs = ram_state; - RAMBlock *bs; + RAMBlock *block; /* Open UFFD file descriptor */ uffd_fd = uffd_create_fd(UFFD_FEATURE_PAGEFAULT_FLAG_WP, true); @@ -1580,27 +1629,27 @@ int ram_write_tracking_start(void) RCU_READ_LOCK_GUARD(); - RAMBLOCK_FOREACH_NOT_IGNORED(bs) { + RAMBLOCK_FOREACH_NOT_IGNORED(block) { /* Nothing to do with read-only and MMIO-writable regions */ - if (bs->mr->readonly || bs->mr->rom_device) { + if (block->mr->readonly || block->mr->rom_device) { continue; } /* Register block memory with UFFD to track writes */ - if (uffd_register_memory(rs->uffdio_fd, bs->host, - bs->max_length, UFFDIO_REGISTER_MODE_WP, NULL)) { + if (uffd_register_memory(rs->uffdio_fd, block->host, + block->max_length, UFFDIO_REGISTER_MODE_WP, NULL)) { goto fail; } /* Apply UFFD write protection to the block memory range */ - if (uffd_change_protection(rs->uffdio_fd, bs->host, - bs->max_length, true, false)) { + if (uffd_change_protection(rs->uffdio_fd, block->host, + block->max_length, true, false)) { goto fail; } - bs->flags |= RAM_UF_WRITEPROTECT; - memory_region_ref(bs->mr); + block->flags |= RAM_UF_WRITEPROTECT; + memory_region_ref(block->mr); - trace_ram_write_tracking_ramblock_start(bs->idstr, bs->page_size, - bs->host, bs->max_length); + trace_ram_write_tracking_ramblock_start(block->idstr, block->page_size, + block->host, block->max_length); } return 0; @@ -1608,19 +1657,20 @@ int ram_write_tracking_start(void) fail: error_report("ram_write_tracking_start() failed: restoring initial memory state"); - RAMBLOCK_FOREACH_NOT_IGNORED(bs) { - if ((bs->flags & RAM_UF_WRITEPROTECT) == 0) { + RAMBLOCK_FOREACH_NOT_IGNORED(block) { + if ((block->flags & RAM_UF_WRITEPROTECT) == 0) { continue; } /* * In case some memory block failed to be write-protected * remove protection and unregister all succeeded RAM blocks */ - uffd_change_protection(rs->uffdio_fd, bs->host, bs->max_length, false, false); - uffd_unregister_memory(rs->uffdio_fd, bs->host, bs->max_length); + uffd_change_protection(rs->uffdio_fd, block->host, block->max_length, + false, false); + uffd_unregister_memory(rs->uffdio_fd, block->host, block->max_length); /* Cleanup flags and remove reference */ - bs->flags &= ~RAM_UF_WRITEPROTECT; - memory_region_unref(bs->mr); + block->flags &= ~RAM_UF_WRITEPROTECT; + memory_region_unref(block->mr); } uffd_close_fd(uffd_fd); @@ -1634,24 +1684,25 @@ fail: void ram_write_tracking_stop(void) { RAMState *rs = ram_state; - RAMBlock *bs; + RAMBlock *block; RCU_READ_LOCK_GUARD(); - RAMBLOCK_FOREACH_NOT_IGNORED(bs) { - if ((bs->flags & RAM_UF_WRITEPROTECT) == 0) { + RAMBLOCK_FOREACH_NOT_IGNORED(block) { + if ((block->flags & RAM_UF_WRITEPROTECT) == 0) { continue; } /* Remove protection and unregister all affected RAM blocks */ - uffd_change_protection(rs->uffdio_fd, bs->host, bs->max_length, false, false); - uffd_unregister_memory(rs->uffdio_fd, bs->host, bs->max_length); + uffd_change_protection(rs->uffdio_fd, block->host, block->max_length, + false, false); + uffd_unregister_memory(rs->uffdio_fd, block->host, block->max_length); - trace_ram_write_tracking_ramblock_stop(bs->idstr, bs->page_size, - bs->host, bs->max_length); + trace_ram_write_tracking_ramblock_stop(block->idstr, block->page_size, + block->host, block->max_length); /* Cleanup flags and remove reference */ - bs->flags &= ~RAM_UF_WRITEPROTECT; - memory_region_unref(bs->mr); + block->flags &= ~RAM_UF_WRITEPROTECT; + memory_region_unref(block->mr); } /* Finally close UFFD file descriptor */ diff --git a/migration/ram.h b/migration/ram.h index 6378bb3ebc..4833e9fd5b 100644 --- a/migration/ram.h +++ b/migration/ram.h @@ -82,6 +82,7 @@ void colo_incoming_start_dirty_log(void); /* Background snapshot */ bool ram_write_tracking_available(void); bool ram_write_tracking_compatible(void); +void ram_write_tracking_prepare(void); int ram_write_tracking_start(void); void ram_write_tracking_stop(void); diff --git a/net/l2tpv3.c b/net/l2tpv3.c index b7e1d84674..e4d4218db6 100644 --- a/net/l2tpv3.c +++ b/net/l2tpv3.c @@ -723,12 +723,8 @@ int net_init_l2tpv3(const Netdev *netdev, l2tpv3_read_poll(s, true); - /* Store startup parameters */ - nc->stored_config = g_new0(NetdevInfo, 1); - nc->stored_config->type = NET_BACKEND_L2TPV3; - - QAPI_CLONE_MEMBERS(NetdevL2TPv3Options, - &nc->stored_config->u.l2tpv3, l2tpv3); + snprintf(s->nc.info_str, sizeof(s->nc.info_str), + "l2tpv3: connected"); return 0; outerr: qemu_del_net_client(nc); @@ -36,6 +36,7 @@ #include "monitor/monitor.h" #include "qemu/help_option.h" #include "qapi/qapi-commands-net.h" +#include "qapi/qapi-visit-net.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qerror.h" #include "qemu/error-report.h" @@ -55,7 +56,6 @@ #include "sysemu/sysemu.h" #include "net/filter.h" #include "qapi/string-output-visitor.h" -#include "qapi/hmp-output-visitor.h" /* Net bridge is currently not supported for W32. */ #if !defined(_WIN32) @@ -130,12 +130,11 @@ char *qemu_mac_strdup_printf(const uint8_t *macaddr) void qemu_format_nic_info_str(NetClientState *nc, uint8_t macaddr[6]) { - g_free(nc->info_str); - nc->info_str = g_strdup_printf( - "model=%s,macaddr=%02x:%02x:%02x:%02x:%02x:%02x", - nc->model, - macaddr[0], macaddr[1], macaddr[2], - macaddr[3], macaddr[4], macaddr[5]); + snprintf(nc->info_str, sizeof(nc->info_str), + "model=%s,macaddr=%02x:%02x:%02x:%02x:%02x:%02x", + nc->model, + macaddr[0], macaddr[1], macaddr[2], + macaddr[3], macaddr[4], macaddr[5]); } static int mac_table[256] = {0}; @@ -354,8 +353,6 @@ static void qemu_free_net_client(NetClientState *nc) } g_free(nc->name); g_free(nc->model); - g_free(nc->info_str); - qapi_free_NetdevInfo(nc->stored_config); if (nc->destructor) { nc->destructor(nc); } @@ -1222,42 +1219,14 @@ static void netfilter_print_info(Monitor *mon, NetFilterState *nf) monitor_printf(mon, "\n"); } -static char *generate_info_str(NetClientState *nc) -{ - NetdevInfo *ni = nc->stored_config; - char *ret_out = NULL; - Visitor *v; - - /* Use legacy field info_str for NIC and hubports */ - if ((nc->info->type == NET_CLIENT_DRIVER_NIC) || - (nc->info->type == NET_CLIENT_DRIVER_HUBPORT)) { - return g_strdup(nc->info_str ? nc->info_str : ""); - } - - if (!ni) { - return g_malloc0(1); - } - - v = hmp_output_visitor_new(&ret_out); - if (visit_type_NetdevInfo(v, "", &ni, NULL)) { - visit_complete(v, &ret_out); - } - visit_free(v); - - return ret_out; -} - void print_net_client(Monitor *mon, NetClientState *nc) { NetFilterState *nf; - char *info_str = generate_info_str(nc); monitor_printf(mon, "%s: index=%d,type=%s,%s\n", nc->name, nc->queue_index, NetClientDriver_str(nc->info->type), - info_str); - g_free(info_str); - + nc->info_str); if (!QTAILQ_EMPTY(&nc->filters)) { monitor_printf(mon, "filters:\n"); } @@ -1320,34 +1289,6 @@ RxFilterInfoList *qmp_query_rx_filter(bool has_name, const char *name, return filter_list; } -NetdevInfoList *qmp_query_netdev(Error **errp) -{ - NetdevInfoList *list = NULL; - NetClientState *nc; - - QTAILQ_FOREACH(nc, &net_clients, next) { - /* - * Only look at netdevs (backend network devices), not for each queue - * or NIC / hubport - */ - if (nc->stored_config) { - NetdevInfo *element = QAPI_CLONE(NetdevInfo, nc->stored_config); - - g_free(element->id); /* Need to dealloc empty id after clone */ - element->id = g_strdup(nc->name); - - element->has_peer_id = nc->peer != NULL; - if (element->has_peer_id) { - element->peer_id = g_strdup(nc->peer->name); - } - - QAPI_LIST_PREPEND(list, element); - } - } - - return list; -} - void hmp_info_network(Monitor *mon, const QDict *qdict) { NetClientState *nc, *peer; diff --git a/net/netmap.c b/net/netmap.c index ad59d4ade4..350f097f91 100644 --- a/net/netmap.c +++ b/net/netmap.c @@ -427,13 +427,6 @@ int net_init_netmap(const Netdev *netdev, pstrcpy(s->ifname, sizeof(s->ifname), netmap_opts->ifname); netmap_read_poll(s, true); /* Initially only poll for reads. */ - /* Store startup parameters */ - nc->stored_config = g_new0(NetdevInfo, 1); - nc->stored_config->type = NET_BACKEND_NETMAP; - - QAPI_CLONE_MEMBERS(NetdevNetmapOptions, - &nc->stored_config->u.netmap, netmap_opts); - return 0; } diff --git a/net/slirp.c b/net/slirp.c index a9fdc7a08f..a01a0fccd3 100644 --- a/net/slirp.c +++ b/net/slirp.c @@ -387,9 +387,6 @@ static int net_slirp_init(NetClientState *peer, const char *model, int shift; char *end; struct slirp_config_str *config; - NetdevUserOptions *stored; - StringList **stored_hostfwd; - StringList **stored_guestfwd; if (!ipv4 && (vnetwork || vhost || vnameserver)) { error_setg(errp, "IPv4 disabled but netmask/host/dns provided"); @@ -565,114 +562,9 @@ static int net_slirp_init(NetClientState *peer, const char *model, nc = qemu_new_net_client(&net_slirp_info, peer, model, name); - /* Store startup parameters */ - nc->stored_config = g_new0(NetdevInfo, 1); - nc->stored_config->type = NET_BACKEND_USER; - stored = &nc->stored_config->u.user; - - if (vhostname) { - stored->has_hostname = true; - stored->hostname = g_strdup(vhostname); - } - - stored->has_q_restrict = true; - stored->q_restrict = restricted; - - stored->has_ipv4 = true; - stored->ipv4 = ipv4; - - stored->has_ipv6 = true; - stored->ipv6 = ipv6; - - if (ipv4) { - uint8_t *net_bytes = (uint8_t *)&net; - uint8_t *mask_bytes = (uint8_t *)&mask; - - stored->has_net = true; - stored->net = g_strdup_printf("%d.%d.%d.%d/%d.%d.%d.%d", - net_bytes[0], net_bytes[1], - net_bytes[2], net_bytes[3], - mask_bytes[0], mask_bytes[1], - mask_bytes[2], mask_bytes[3]); - - stored->has_host = true; - stored->host = g_strdup(inet_ntoa(host)); - } - - if (tftp_export) { - stored->has_tftp = true; - stored->tftp = g_strdup(tftp_export); - } - - if (bootfile) { - stored->has_bootfile = true; - stored->bootfile = g_strdup(bootfile); - } - - if (vdhcp_start) { - stored->has_dhcpstart = true; - stored->dhcpstart = g_strdup(vdhcp_start); - } - - if (ipv4) { - stored->has_dns = true; - stored->dns = g_strdup(inet_ntoa(dns)); - } - - if (dnssearch) { - stored->has_dnssearch = true; - StringList **stored_list = &stored->dnssearch; - - for (int i = 0; dnssearch[i]; i++) { - String *element = g_new0(String, 1); - - element->str = g_strdup(dnssearch[i]); - QAPI_LIST_APPEND(stored_list, element); - } - } - - if (vdomainname) { - stored->has_domainname = true; - stored->domainname = g_strdup(vdomainname); - } - - if (ipv6) { - char addrstr[INET6_ADDRSTRLEN]; - const char *res; - - stored->has_ipv6_prefix = true; - stored->ipv6_prefix = g_strdup(vprefix6); - - stored->has_ipv6_prefixlen = true; - stored->ipv6_prefixlen = vprefix6_len; - - res = inet_ntop(AF_INET6, &ip6_host, - addrstr, sizeof(addrstr)); - - stored->has_ipv6_host = true; - stored->ipv6_host = g_strdup(res); - - res = inet_ntop(AF_INET6, &ip6_dns, - addrstr, sizeof(addrstr)); - - stored->has_ipv6_dns = true; - stored->ipv6_dns = g_strdup(res); - } - - if (smb_export) { - stored->has_smb = true; - stored->smb = g_strdup(smb_export); - } - - if (vsmbserver) { - stored->has_smbserver = true; - stored->smbserver = g_strdup(vsmbserver); - } - - if (tftp_server_name) { - stored->has_tftp_server_name = true; - stored->tftp_server_name = g_strdup(tftp_server_name); - } + snprintf(nc->info_str, sizeof(nc->info_str), + "net=%s,restrict=%s", inet_ntoa(net), + restricted ? "on" : "off"); s = DO_UPCAST(SlirpState, nc, nc); @@ -699,25 +591,15 @@ static int net_slirp_init(NetClientState *peer, const char *model, s->poll_notifier.notify = net_slirp_poll_notify; main_loop_poll_add_notifier(&s->poll_notifier); - stored_hostfwd = &stored->hostfwd; - stored_guestfwd = &stored->guestfwd; - for (config = slirp_configs; config; config = config->next) { - String *element = g_new0(String, 1); - - element->str = g_strdup(config->str); if (config->flags & SLIRP_CFG_HOSTFWD) { if (slirp_hostfwd(s, config->str, errp) < 0) { goto error; } - stored->has_hostfwd = true; - QAPI_LIST_APPEND(stored_hostfwd, element); } else { if (slirp_guestfwd(s, config->str, errp) < 0) { goto error; } - stored->has_guestfwd = true; - QAPI_LIST_APPEND(stored_guestfwd, element); } } #ifndef _WIN32 diff --git a/net/socket.c b/net/socket.c index c0de10c0c0..15b410e8d8 100644 --- a/net/socket.c +++ b/net/socket.c @@ -180,6 +180,7 @@ static void net_socket_send(void *opaque) s->fd = -1; net_socket_rs_init(&s->rs, net_socket_rs_finalize, false); s->nc.link_down = true; + memset(s->nc.info_str, 0, sizeof(s->nc.info_str)); return; } @@ -341,7 +342,6 @@ static NetSocketState *net_socket_fd_init_dgram(NetClientState *peer, NetSocketState *s; SocketAddress *sa; SocketAddressType sa_type; - NetdevSocketOptions *stored; sa = socket_local_address(fd, errp); if (!sa) { @@ -385,24 +385,19 @@ static NetSocketState *net_socket_fd_init_dgram(NetClientState *peer, net_socket_rs_init(&s->rs, net_socket_rs_finalize, false); net_socket_read_poll(s, true); - /* Store startup parameters */ - nc->stored_config = g_new0(NetdevInfo, 1); - nc->stored_config->type = NET_BACKEND_SOCKET; - stored = &nc->stored_config->u.socket; - - stored->has_fd = true; - stored->fd = g_strdup_printf("%d", fd); - /* mcast: save bound address as dst */ if (is_connected && mcast != NULL) { - stored->has_mcast = true; - stored->mcast = g_strdup(mcast); - s->dgram_dst = saddr; + snprintf(nc->info_str, sizeof(nc->info_str), + "socket: fd=%d (cloned mcast=%s:%d)", + fd, inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port)); } else { if (sa_type == SOCKET_ADDRESS_TYPE_UNIX) { s->dgram_dst.sin_family = AF_UNIX; } + + snprintf(nc->info_str, sizeof(nc->info_str), + "socket: fd=%d %s", fd, SocketAddressType_str(sa_type)); } return s; @@ -433,10 +428,11 @@ static NetSocketState *net_socket_fd_init_stream(NetClientState *peer, { NetClientState *nc; NetSocketState *s; - NetdevSocketOptions *stored; nc = qemu_new_net_client(&net_socket_info, peer, model, name); + snprintf(nc->info_str, sizeof(nc->info_str), "socket: fd=%d", fd); + s = DO_UPCAST(NetSocketState, nc, nc); s->fd = fd; @@ -451,15 +447,6 @@ static NetSocketState *net_socket_fd_init_stream(NetClientState *peer, } else { qemu_set_fd_handler(s->fd, NULL, net_socket_connect, s); } - - /* Store startup parameters */ - nc->stored_config = g_new0(NetdevInfo, 1); - nc->stored_config->type = NET_BACKEND_SOCKET; - stored = &nc->stored_config->u.socket; - - stored->has_fd = true; - stored->fd = g_strdup_printf("%d", fd); - return s; } @@ -496,7 +483,6 @@ static void net_socket_accept(void *opaque) struct sockaddr_in saddr; socklen_t len; int fd; - NetdevSocketOptions *stored; for(;;) { len = sizeof(saddr); @@ -512,12 +498,9 @@ static void net_socket_accept(void *opaque) s->fd = fd; s->nc.link_down = false; net_socket_connect(s); - - /* Store additional startup parameters (extend net_socket_listen_init) */ - stored = &s->nc.stored_config->u.socket; - - stored->has_fd = true; - stored->fd = g_strdup_printf("%d", fd); + snprintf(s->nc.info_str, sizeof(s->nc.info_str), + "socket: connection from %s:%d", + inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port)); } static int net_socket_listen_init(NetClientState *peer, @@ -530,7 +513,6 @@ static int net_socket_listen_init(NetClientState *peer, NetSocketState *s; struct sockaddr_in saddr; int fd, ret; - NetdevSocketOptions *stored; if (parse_host_port(&saddr, host_str, errp) < 0) { return -1; @@ -567,15 +549,6 @@ static int net_socket_listen_init(NetClientState *peer, net_socket_rs_init(&s->rs, net_socket_rs_finalize, false); qemu_set_fd_handler(s->listen_fd, net_socket_accept, NULL, s); - - /* Store startup parameters */ - nc->stored_config = g_new0(NetdevInfo, 1); - nc->stored_config->type = NET_BACKEND_SOCKET; - stored = &nc->stored_config->u.socket; - - stored->has_listen = true; - stored->listen = g_strdup(host_str); - return 0; } @@ -588,7 +561,6 @@ static int net_socket_connect_init(NetClientState *peer, NetSocketState *s; int fd, connected, ret; struct sockaddr_in saddr; - NetdevSocketOptions *stored; if (parse_host_port(&saddr, host_str, errp) < 0) { return -1; @@ -626,12 +598,9 @@ static int net_socket_connect_init(NetClientState *peer, return -1; } - /* Store additional startup parameters (extend net_socket_fd_init) */ - stored = &s->nc.stored_config->u.socket; - - stored->has_connect = true; - stored->connect = g_strdup(host_str); - + snprintf(s->nc.info_str, sizeof(s->nc.info_str), + "socket: connect to %s:%d", + inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port)); return 0; } @@ -646,7 +615,6 @@ static int net_socket_mcast_init(NetClientState *peer, int fd; struct sockaddr_in saddr; struct in_addr localaddr, *param_localaddr; - NetdevSocketOptions *stored; if (parse_host_port(&saddr, host_str, errp) < 0) { return -1; @@ -675,20 +643,11 @@ static int net_socket_mcast_init(NetClientState *peer, s->dgram_dst = saddr; - /* Store additional startup parameters (extend net_socket_fd_init) */ - stored = &s->nc.stored_config->u.socket; - - if (!stored->has_mcast) { - stored->has_mcast = true; - stored->mcast = g_strdup(host_str); - } - - if (localaddr_str) { - stored->has_localaddr = true; - stored->localaddr = g_strdup(localaddr_str); - } - + snprintf(s->nc.info_str, sizeof(s->nc.info_str), + "socket: mcast=%s:%d", + inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port)); return 0; + } static int net_socket_udp_init(NetClientState *peer, @@ -701,7 +660,6 @@ static int net_socket_udp_init(NetClientState *peer, NetSocketState *s; int fd, ret; struct sockaddr_in laddr, raddr; - NetdevSocketOptions *stored; if (parse_host_port(&laddr, lhost, errp) < 0) { return -1; @@ -740,15 +698,9 @@ static int net_socket_udp_init(NetClientState *peer, s->dgram_dst = raddr; - /* Store additional startup parameters (extend net_socket_fd_init) */ - stored = &s->nc.stored_config->u.socket; - - stored->has_localaddr = true; - stored->localaddr = g_strdup(lhost); - - stored->has_udp = true; - stored->udp = g_strdup(rhost); - + snprintf(s->nc.info_str, sizeof(s->nc.info_str), + "socket: udp=%s:%d", + inet_ntoa(raddr.sin_addr), ntohs(raddr.sin_port)); return 0; } diff --git a/net/tap-win32.c b/net/tap-win32.c index d7c2a8759c..897bd18e32 100644 --- a/net/tap-win32.c +++ b/net/tap-win32.c @@ -686,7 +686,7 @@ static ssize_t tap_receive(NetClientState *nc, const uint8_t *buf, size_t size) static void tap_win32_send(void *opaque) { TAPState *s = opaque; - uint8_t *buf; + uint8_t *buf, *orig_buf; int max_size = 4096; int size; uint8_t min_pkt[ETH_ZLEN]; @@ -694,6 +694,8 @@ static void tap_win32_send(void *opaque) size = tap_win32_read(s->handle, &buf, max_size); if (size > 0) { + orig_buf = buf; + if (!s->nc.peer->do_not_pad) { if (eth_pad_short_frame(min_pkt, &min_pktsz, buf, size)) { buf = min_pkt; @@ -702,7 +704,7 @@ static void tap_win32_send(void *opaque) } qemu_send_packet(&s->nc, buf, size); - tap_win32_free_buffer(s->handle, buf); + tap_win32_free_buffer(s->handle, orig_buf); } } @@ -778,7 +780,6 @@ static int tap_win32_init(NetClientState *peer, const char *model, NetClientState *nc; TAPState *s; tap_win32_overlapped_t *handle; - NetdevTapOptions *stored; if (tap_win32_open(&handle, ifname) < 0) { printf("tap: Could not open '%s'\n", ifname); @@ -789,13 +790,8 @@ static int tap_win32_init(NetClientState *peer, const char *model, s = DO_UPCAST(TAPState, nc, nc); - /* Store startup parameters */ - nc->stored_config = g_new0(NetdevInfo, 1); - nc->stored_config->type = NET_BACKEND_TAP; - stored = &nc->stored_config->u.tap; - - stored->has_ifname = true; - stored->ifname = g_strdup(ifname); + snprintf(s->nc.info_str, sizeof(s->nc.info_str), + "tap: ifname=%s", ifname); s->handle = handle; @@ -600,7 +600,6 @@ int net_init_bridge(const Netdev *netdev, const char *name, const char *helper, *br; TAPState *s; int fd, vnet_hdr; - NetdevBridgeOptions *stored; assert(netdev->type == NET_CLIENT_DRIVER_BRIDGE); bridge = &netdev->u.bridge; @@ -620,20 +619,8 @@ int net_init_bridge(const Netdev *netdev, const char *name, } s = net_tap_fd_init(peer, "bridge", name, fd, vnet_hdr); - /* Store startup parameters */ - s->nc.stored_config = g_new0(NetdevInfo, 1); - s->nc.stored_config->type = NET_BACKEND_BRIDGE; - stored = &s->nc.stored_config->u.bridge; - - if (br) { - stored->has_br = true; - stored->br = g_strdup(br); - } - - if (helper) { - stored->has_helper = true; - stored->helper = g_strdup(helper); - } + snprintf(s->nc.info_str, sizeof(s->nc.info_str), "helper=%s,br=%s", helper, + br); return 0; } @@ -679,13 +666,11 @@ static void net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer, const char *model, const char *name, const char *ifname, const char *script, const char *downscript, const char *vhostfdname, - int vnet_hdr, int fd, NetdevInfo **common_stored, - Error **errp) + int vnet_hdr, int fd, Error **errp) { Error *err = NULL; TAPState *s = net_tap_fd_init(peer, model, name, fd, vnet_hdr); int vhostfd; - NetdevTapOptions *stored; tap_set_sndbuf(s->fd, tap, &err); if (err) { @@ -693,59 +678,15 @@ static void net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer, return; } - /* Store startup parameters */ - if (!*common_stored) { - *common_stored = g_new0(NetdevInfo, 1); - (*common_stored)->type = NET_BACKEND_TAP; - s->nc.stored_config = *common_stored; - } - stored = &(*common_stored)->u.tap; - - if (tap->has_sndbuf && !stored->has_sndbuf) { - stored->has_sndbuf = true; - stored->sndbuf = tap->sndbuf; - } - - if (vnet_hdr && !stored->has_vnet_hdr) { - stored->has_vnet_hdr = true; - stored->vnet_hdr = true; - } - if (tap->has_fd || tap->has_fds) { - if (!stored->has_fds) { - stored->has_fds = true; - stored->fds = g_strdup_printf("%d", fd); - } else { - char *tmp_s = stored->fds; - stored->fds = g_strdup_printf("%s:%d", stored->fds, fd); - g_free(tmp_s); - } + snprintf(s->nc.info_str, sizeof(s->nc.info_str), "fd=%d", fd); } else if (tap->has_helper) { - if (!stored->has_helper) { - stored->has_helper = true; - stored->helper = g_strdup(tap->helper); - } - - if (!stored->has_br) { - stored->has_br = true; - stored->br = tap->has_br ? g_strdup(tap->br) : - g_strdup(DEFAULT_BRIDGE_INTERFACE); - } + snprintf(s->nc.info_str, sizeof(s->nc.info_str), "helper=%s", + tap->helper); } else { - if (ifname && !stored->has_ifname) { - stored->has_ifname = true; - stored->ifname = g_strdup(ifname); - } - - if (script && !stored->has_script) { - stored->has_script = true; - stored->script = g_strdup(script); - } - - if (downscript && !stored->has_downscript) { - stored->has_downscript = true; - stored->downscript = g_strdup(downscript); - } + snprintf(s->nc.info_str, sizeof(s->nc.info_str), + "ifname=%s,script=%s,downscript=%s", ifname, script, + downscript); if (strcmp(downscript, "no") != 0) { snprintf(s->down_script, sizeof(s->down_script), "%s", downscript); @@ -758,20 +699,9 @@ static void net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer, vhostfdname || (tap->has_vhostforce && tap->vhostforce)) { VhostNetOptions options; - stored->has_vhost = true; - stored->vhost = true; - - if (tap->has_vhostforce && tap->vhostforce) { - stored->has_vhostforce = true; - stored->vhostforce = true; - } - options.backend_type = VHOST_BACKEND_TYPE_KERNEL; options.net_backend = &s->nc; if (tap->has_poll_us) { - stored->has_poll_us = true; - stored->poll_us = tap->poll_us; - options.busyloop_timeout = tap->poll_us; } else { options.busyloop_timeout = 0; @@ -811,15 +741,6 @@ static void net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer, } options.opaque = (void *)(uintptr_t)vhostfd; - if (!stored->has_vhostfds) { - stored->has_vhostfds = true; - stored->vhostfds = g_strdup_printf("%d", vhostfd); - } else { - char *tmp_s = stored->vhostfds; - stored->vhostfds = g_strdup_printf("%s:%d", stored->fds, vhostfd); - g_free(tmp_s); - } - s->vhost_net = vhost_net_init(&options); if (!s->vhost_net) { if (tap->has_vhostforce && tap->vhostforce) { @@ -872,7 +793,6 @@ int net_init_tap(const Netdev *netdev, const char *name, const char *vhostfdname; char ifname[128]; int ret = 0; - NetdevInfo *common_stored = NULL; /* will store configuration */ assert(netdev->type == NET_CLIENT_DRIVER_TAP); tap = &netdev->u.tap; @@ -919,7 +839,7 @@ int net_init_tap(const Netdev *netdev, const char *name, net_init_tap_one(tap, peer, "tap", name, NULL, script, downscript, - vhostfdname, vnet_hdr, fd, &common_stored, &err); + vhostfdname, vnet_hdr, fd, &err); if (err) { error_propagate(errp, err); close(fd); @@ -982,7 +902,7 @@ int net_init_tap(const Netdev *netdev, const char *name, net_init_tap_one(tap, peer, "tap", name, ifname, script, downscript, tap->has_vhostfds ? vhost_fds[i] : NULL, - vnet_hdr, fd, &common_stored, &err); + vnet_hdr, fd, &err); if (err) { error_propagate(errp, err); ret = -1; @@ -1025,7 +945,7 @@ free_fail: net_init_tap_one(tap, peer, "bridge", name, ifname, script, downscript, vhostfdname, - vnet_hdr, fd, &common_stored, &err); + vnet_hdr, fd, &err); if (err) { error_propagate(errp, err); close(fd); @@ -1071,8 +991,7 @@ free_fail: net_init_tap_one(tap, peer, "tap", name, ifname, i >= 1 ? "no" : script, i >= 1 ? "no" : downscript, - vhostfdname, vnet_hdr, fd, - &common_stored, &err); + vhostfdname, vnet_hdr, fd, &err); if (err) { error_propagate(errp, err); close(fd); @@ -84,7 +84,6 @@ static int net_vde_init(NetClientState *peer, const char *model, VDECONN *vde; char *init_group = (char *)group; char *init_sock = (char *)sock; - NetdevVdeOptions *stored; struct vde_open_args args = { .port = port, @@ -100,33 +99,15 @@ static int net_vde_init(NetClientState *peer, const char *model, nc = qemu_new_net_client(&net_vde_info, peer, model, name); + snprintf(nc->info_str, sizeof(nc->info_str), "sock=%s,fd=%d", + sock, vde_datafd(vde)); + s = DO_UPCAST(VDEState, nc, nc); s->vde = vde; qemu_set_fd_handler(vde_datafd(s->vde), vde_to_qemu, NULL, s); - /* Store startup parameters */ - nc->stored_config = g_new0(NetdevInfo, 1); - nc->stored_config->type = NET_BACKEND_VDE; - stored = &nc->stored_config->u.vde; - - if (sock) { - stored->has_sock = true; - stored->sock = g_strdup(sock); - } - - stored->has_port = true; - stored->port = port; - - if (group) { - stored->has_group = true; - stored->group = g_strdup(group); - } - - stored->has_mode = true; - stored->mode = mode; - return 0; } diff --git a/net/vhost-user.c b/net/vhost-user.c index e443c4b2b5..ffbd94d944 100644 --- a/net/vhost-user.c +++ b/net/vhost-user.c @@ -311,15 +311,14 @@ static void net_vhost_user_event(void *opaque, QEMUChrEvent event) } static int net_vhost_user_init(NetClientState *peer, const char *device, - const char *name, const char *chardev, - Chardev *chr, int queues) + const char *name, Chardev *chr, + int queues) { Error *err = NULL; NetClientState *nc, *nc0 = NULL; NetVhostUserState *s = NULL; VhostUserState *user; int i; - NetdevVhostUserOptions *stored; assert(name); assert(queues > 0); @@ -327,6 +326,8 @@ static int net_vhost_user_init(NetClientState *peer, const char *device, user = g_new0(struct VhostUserState, 1); for (i = 0; i < queues; i++) { nc = qemu_new_net_client(&net_vhost_user_info, peer, device, name); + snprintf(nc->info_str, sizeof(nc->info_str), "vhost-user%d to %s", + i, chr->label); nc->queue_index = i; if (!nc0) { nc0 = nc; @@ -354,16 +355,6 @@ static int net_vhost_user_init(NetClientState *peer, const char *device, assert(s->vhost_net); - /* Store startup parameters */ - nc0->stored_config = g_new0(NetdevInfo, 1); - nc0->stored_config->type = NET_BACKEND_VHOST_USER; - stored = &nc0->stored_config->u.vhost_user; - - stored->chardev = g_strdup(chardev); - - stored->has_queues = true; - stored->queues = queues; - return 0; err: @@ -455,6 +446,5 @@ int net_init_vhost_user(const Netdev *netdev, const char *name, return -1; } - return net_vhost_user_init(peer, "vhost_user", name, - vhost_user_opts->chardev, chr, queues); + return net_vhost_user_init(peer, "vhost_user", name, chr, queues); } diff --git a/net/vhost-vdpa.c b/net/vhost-vdpa.c index 5a28bbcd7b..fe659ec9e2 100644 --- a/net/vhost-vdpa.c +++ b/net/vhost-vdpa.c @@ -184,22 +184,9 @@ static int net_vhost_vdpa_init(NetClientState *peer, const char *device, VhostVDPAState *s; int vdpa_device_fd = -1; int ret = 0; - NetdevVhostVDPAOptions *stored; - assert(name); nc = qemu_new_net_client(&net_vhost_vdpa_info, peer, device, name); - - /* Store startup parameters */ - nc->stored_config = g_new0(NetdevInfo, 1); - nc->stored_config->type = NET_BACKEND_VHOST_VDPA; - stored = &nc->stored_config->u.vhost_vdpa; - - stored->has_vhostdev = true; - stored->vhostdev = g_strdup(vhostdev); - - stored->has_queues = true; - stored->queues = 1; /* TODO: change when support multiqueue */ - + snprintf(nc->info_str, sizeof(nc->info_str), TYPE_VHOST_VDPA); nc->queue_index = 0; s = DO_UPCAST(VhostVDPAState, nc, nc); vdpa_device_fd = qemu_open_old(vhostdev, O_RDWR); diff --git a/qapi/hmp-output-visitor.c b/qapi/hmp-output-visitor.c deleted file mode 100644 index 8036605f97..0000000000 --- a/qapi/hmp-output-visitor.c +++ /dev/null @@ -1,193 +0,0 @@ -/* - * HMP string output Visitor - * - * Copyright Yandex N.V., 2021 - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * - */ - -#include "qemu/osdep.h" -#include "qemu/cutils.h" -#include "qapi/hmp-output-visitor.h" -#include "qapi/visitor-impl.h" - -struct HMPOutputVisitor { - Visitor visitor; - char **result; - GString *buffer; - bool is_continue; -}; - -static HMPOutputVisitor *to_hov(Visitor *v) -{ - return container_of(v, HMPOutputVisitor, visitor); -} - -static void hmp_output_append_formatted(Visitor *v, const char *fmt, ...) -{ - HMPOutputVisitor *ov = to_hov(v); - va_list args; - - if (ov->is_continue) { - g_string_append(ov->buffer, ","); - } else { - ov->is_continue = true; - } - - va_start(args, fmt); - g_string_append_vprintf(ov->buffer, fmt, args); - va_end(args); -} - -static void hmp_output_skip_comma(Visitor *v) -{ - HMPOutputVisitor *ov = to_hov(v); - - ov->is_continue = false; -} - -static bool hmp_output_start_struct(Visitor *v, const char *name, - void **obj, size_t unused, Error **errp) -{ - return true; -} - -static void hmp_output_end_struct(Visitor *v, void **obj) {} - -static bool hmp_output_start_list(Visitor *v, const char *name, - GenericList **listp, size_t size, - Error **errp) -{ - hmp_output_append_formatted(v, "%s=[", name); - /* First element in array without comma before it */ - hmp_output_skip_comma(v); - - return true; -} - -static GenericList *hmp_output_next_list(Visitor *v, GenericList *tail, - size_t size) -{ - return tail->next; -} - -static void hmp_output_end_list(Visitor *v, void **obj) -{ - /* Don't need comma after last array element */ - hmp_output_skip_comma(v); - hmp_output_append_formatted(v, "]"); -} - -static bool hmp_output_type_int64(Visitor *v, const char *name, - int64_t *obj, Error **errp) -{ - hmp_output_append_formatted(v, "%s=%" PRId64, name, *obj); - - return true; -} - -static bool hmp_output_type_uint64(Visitor *v, const char *name, - uint64_t *obj, Error **errp) -{ - hmp_output_append_formatted(v, "%s=%" PRIu64, name, *obj); - - return true; -} - -static bool hmp_output_type_bool(Visitor *v, const char *name, bool *obj, - Error **errp) -{ - hmp_output_append_formatted(v, "%s=%s", name, *obj ? "true" : "false"); - - return true; -} - -static bool hmp_output_type_str(Visitor *v, const char *name, char **obj, - Error **errp) -{ - /* Skip already printed or unused fields */ - if (!*obj || g_str_equal(name, "id") || g_str_equal(name, "type")) { - return true; - } - - /* Do not print stub name for StringList elements */ - if (g_str_equal(name, "str")) { - hmp_output_append_formatted(v, "%s", *obj); - } else { - hmp_output_append_formatted(v, "%s=%s", name, *obj); - } - - return true; -} - -static bool hmp_output_type_number(Visitor *v, const char *name, - double *obj, Error **errp) -{ - hmp_output_append_formatted(v, "%s=%.17g", name, *obj); - - return true; -} - -/* TODO: remove this function? */ -static bool hmp_output_type_any(Visitor *v, const char *name, - QObject **obj, Error **errp) -{ - return true; -} - -static bool hmp_output_type_null(Visitor *v, const char *name, - QNull **obj, Error **errp) -{ - hmp_output_append_formatted(v, "%s=NULL", name); - - return true; -} - -static void hmp_output_complete(Visitor *v, void *opaque) -{ - HMPOutputVisitor *ov = to_hov(v); - - *ov->result = g_string_free(ov->buffer, false); - ov->buffer = NULL; -} - -static void hmp_output_free(Visitor *v) -{ - HMPOutputVisitor *ov = to_hov(v); - - if (ov->buffer) { - g_string_free(ov->buffer, true); - } - g_free(v); -} - -Visitor *hmp_output_visitor_new(char **result) -{ - HMPOutputVisitor *v; - - v = g_malloc0(sizeof(*v)); - - v->visitor.type = VISITOR_OUTPUT; - v->visitor.start_struct = hmp_output_start_struct; - v->visitor.end_struct = hmp_output_end_struct; - v->visitor.start_list = hmp_output_start_list; - v->visitor.next_list = hmp_output_next_list; - v->visitor.end_list = hmp_output_end_list; - v->visitor.type_int64 = hmp_output_type_int64; - v->visitor.type_uint64 = hmp_output_type_uint64; - v->visitor.type_bool = hmp_output_type_bool; - v->visitor.type_str = hmp_output_type_str; - v->visitor.type_number = hmp_output_type_number; - v->visitor.type_any = hmp_output_type_any; - v->visitor.type_null = hmp_output_type_null; - v->visitor.complete = hmp_output_complete; - v->visitor.free = hmp_output_free; - - v->result = result; - v->buffer = g_string_new(""); - v->is_continue = false; - - return &v->visitor; -} diff --git a/qapi/meson.build b/qapi/meson.build index 0d20226fa3..376f4ceafe 100644 --- a/qapi/meson.build +++ b/qapi/meson.build @@ -8,7 +8,6 @@ util_ss.add(files( 'qobject-output-visitor.c', 'string-input-visitor.c', 'string-output-visitor.c', - 'hmp-output-visitor.c', )) if have_system or have_tools util_ss.add(files( diff --git a/qapi/net.json b/qapi/net.json index b86d053ad6..af3f5b0fda 100644 --- a/qapi/net.json +++ b/qapi/net.json @@ -694,83 +694,3 @@ ## { 'event': 'FAILOVER_NEGOTIATED', 'data': {'device-id': 'str'} } - -## -# @NetBackend: -# -# Available netdev backend drivers. -# -# Since: 6.0 -## -{ 'enum': 'NetBackend', - 'data': [ 'bridge', 'l2tpv3', 'netmap', 'socket', 'tap', 'user', 'vde', - 'vhost-user', 'vhost-vdpa' ] } - -## -# @NetdevInfo: -# -# Configuration of a network backend device (netdev). -# -# @id: Device identifier. -# -# @type: Specify the driver used for interpreting remaining arguments. -# -# @peer-id: The connected frontend network device name (absent if no frontend -# is connected). -# -# Since: 6.0 -## -{ 'union': 'NetdevInfo', - 'base': { 'id': 'str', - 'type': 'NetBackend', - '*peer-id': 'str' }, - 'discriminator': 'type', - 'data': { - 'bridge': 'NetdevBridgeOptions', - 'l2tpv3': 'NetdevL2TPv3Options', - 'netmap': 'NetdevNetmapOptions', - 'socket': 'NetdevSocketOptions', - 'tap': 'NetdevTapOptions', - 'user': 'NetdevUserOptions', - 'vde': 'NetdevVdeOptions', - 'vhost-user': 'NetdevVhostUserOptions', - 'vhost-vdpa': 'NetdevVhostVDPAOptions' } } - -## -# @query-netdev: -# -# Get a list of @NetdevInfo for all virtual network backend devices (netdevs). -# -# Returns: a list of @NetdevInfo describing each netdev. -# -# Since: 6.0 -# -# Example: -# -# -> { "execute": "query-netdev" } -# <- { "return": [ -# { -# "ipv6": true, -# "ipv4": true, -# "host": "10.0.2.2", -# "ipv6-dns": "fec0::3", -# "ipv6-prefix": "fec0::", -# "net": "10.0.2.0/255.255.255.0", -# "ipv6-host": "fec0::2", -# "type": "user", -# "peer-id": "net0", -# "dns": "10.0.2.3", -# "hostfwd": [ -# { -# "str": "tcp::20004-:22" -# } -# ], -# "ipv6-prefixlen": 64, -# "id": "netdev0", -# "restrict": false -# } -# ] -# } -# -## -{ 'command': 'query-netdev', 'returns': ['NetdevInfo'] } diff --git a/tests/docker/Makefile.include b/tests/docker/Makefile.include index 7cab761bf5..9f464cb92c 100644 --- a/tests/docker/Makefile.include +++ b/tests/docker/Makefile.include @@ -16,7 +16,10 @@ DOCKER_IMAGES := $(sort $(notdir $(basename $(wildcard $(DOCKER_FILES_DIR)/*.doc DOCKER_TARGETS := $(patsubst %,docker-image-%,$(DOCKER_IMAGES)) # Use a global constant ccache directory to speed up repetitive builds DOCKER_CCACHE_DIR := $$HOME/.cache/qemu-docker-ccache -DOCKER_REGISTRY := $(if $(REGISTRY),$(REGISTRY),registry.gitlab.com/qemu-project/qemu) +ifeq ($(HOST_ARCH),x86_64) +DOCKER_DEFAULT_REGISTRY := registry.gitlab.com/qemu-project/qemu +endif +DOCKER_REGISTRY := $(if $(REGISTRY),$(REGISTRY),$(DOCKER_DEFAULT_REGISTRY)) DOCKER_TESTS := $(notdir $(shell \ find $(SRC_PATH)/tests/docker/ -name 'test-*' -type f)) diff --git a/tests/docker/dockerfiles/centos8.docker b/tests/docker/dockerfiles/centos8.docker index a763d55730..a8c6c528b0 100644 --- a/tests/docker/dockerfiles/centos8.docker +++ b/tests/docker/dockerfiles/centos8.docker @@ -29,6 +29,7 @@ ENV PACKAGES \ rdma-core-devel \ spice-glib-devel \ spice-server \ + systemtap-sdt-devel \ tar \ zlib-devel diff --git a/tests/migration/guestperf/engine.py b/tests/migration/guestperf/engine.py index e399447940..6b49aed579 100644 --- a/tests/migration/guestperf/engine.py +++ b/tests/migration/guestperf/engine.py @@ -102,7 +102,7 @@ class Engine(object): info.get("downtime", 0), info.get("expected-downtime", 0), info.get("setup-time", 0), - info.get("x-cpu-throttle-percentage", 0), + info.get("cpu-throttle-percentage", 0), ) def _migrate(self, hardware, scenario, src, dst, connect_uri): @@ -135,7 +135,7 @@ class Engine(object): "state": True } ]) resp = src.command("migrate-set-parameters", - x_cpu_throttle_increment=scenario._auto_converge_step) + cpu_throttle_increment=scenario._auto_converge_step) if scenario._post_copy: resp = src.command("migrate-set-capabilities", diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 902cfef7cb..420cd9986e 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -33,9 +33,6 @@ qtests_generic = \ if config_host.has_key('CONFIG_MODULES') qtests_generic += [ 'modules-test' ] endif -if slirp.found() - qtests_generic += [ 'test-query-netdev' ] -endif qtests_pci = \ (config_all_devices.has_key('CONFIG_VGA') ? ['display-vga-test'] : []) + \ diff --git a/tests/qtest/test-query-netdev.c b/tests/qtest/test-query-netdev.c deleted file mode 100644 index 1118537a9f..0000000000 --- a/tests/qtest/test-query-netdev.c +++ /dev/null @@ -1,120 +0,0 @@ -/* - * QTest testcase for the query-netdev - * - * Copyright Yandex N.V., 2019 - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * - */ - -#include "qemu/osdep.h" - -#include "libqos/libqtest.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qlist.h" - -/* - * Events can get in the way of responses we are actually waiting for. - */ -GCC_FMT_ATTR(2, 3) -static QObject *wait_command(QTestState *who, const char *command, ...) -{ - va_list ap; - QDict *response; - QObject *result; - - va_start(ap, command); - qtest_qmp_vsend(who, command, ap); - va_end(ap); - - response = qtest_qmp_receive(who); - - result = qdict_get(response, "return"); - g_assert(result); - qobject_ref(result); - qobject_unref(response); - - return result; -} - -static void qmp_query_netdev_no_error(QTestState *qts, size_t netdevs_count) -{ - QObject *resp; - QList *netdevs; - - resp = wait_command(qts, "{'execute': 'query-netdev'}"); - - netdevs = qobject_to(QList, resp); - g_assert(netdevs); - g_assert(qlist_size(netdevs) == netdevs_count); - - qobject_unref(resp); -} - -static void test_query_netdev(void) -{ - const char *arch = qtest_get_arch(); - QObject *resp; - QTestState *state; - - /* Choosing machine for platforms without default one */ - if (g_str_equal(arch, "arm") || - g_str_equal(arch, "aarch64")) { - state = qtest_init( - "-nodefaults " - "-M virt " - "-netdev user,id=slirp0"); - } else if (g_str_equal(arch, "tricore")) { - state = qtest_init( - "-nodefaults " - "-M tricore_testboard " - "-netdev user,id=slirp0"); - } else if (g_str_equal(arch, "avr")) { - state = qtest_init( - "-nodefaults " - "-M mega2560 " - "-netdev user,id=slirp0"); - } else if (g_str_equal(arch, "rx")) { - state = qtest_init( - "-nodefaults " - "-M gdbsim-r5f562n8 " - "-netdev user,id=slirp0"); - } else { - state = qtest_init( - "-nodefaults " - "-netdev user,id=slirp0"); - } - g_assert(state); - - qmp_query_netdev_no_error(state, 1); - - resp = wait_command(state, - "{'execute': 'netdev_add', 'arguments': {" - " 'id': 'slirp1'," - " 'type': 'user'}}"); - qobject_unref(resp); - - qmp_query_netdev_no_error(state, 2); - - resp = wait_command(state, - "{'execute': 'netdev_del', 'arguments': {" - " 'id': 'slirp1'}}"); - qobject_unref(resp); - - qmp_query_netdev_no_error(state, 1); - - qtest_quit(state); -} - -int main(int argc, char **argv) -{ - int ret = 0; - g_test_init(&argc, &argv, NULL); - - qtest_add_func("/net/qapi/query_netdev", test_query_netdev); - - ret = g_test_run(); - - return ret; -} diff --git a/tests/tcg/configure.sh b/tests/tcg/configure.sh index ce304f4933..fa1a4261a4 100755 --- a/tests/tcg/configure.sh +++ b/tests/tcg/configure.sh @@ -52,7 +52,7 @@ fi : ${cross_cc_hexagon="hexagon-unknown-linux-musl-clang"} : ${cross_cc_cflags_hexagon="-mv67 -O2 -static"} : ${cross_cc_hppa="hppa-linux-gnu-gcc"} -: ${cross_cc_i386="i386-pc-linux-gnu-gcc"} +: ${cross_cc_i386="i686-linux-gnu-gcc"} : ${cross_cc_cflags_i386="-m32"} : ${cross_cc_m68k="m68k-linux-gnu-gcc"} : $(cross_cc_mips64el="mips64el-linux-gnuabi64-gcc") @@ -69,7 +69,7 @@ fi : ${cross_cc_cflags_sparc="-m32 -mv8plus -mcpu=ultrasparc"} : ${cross_cc_sparc64="sparc64-linux-gnu-gcc"} : ${cross_cc_cflags_sparc64="-m64 -mcpu=ultrasparc"} -: ${cross_cc_x86_64="x86_64-pc-linux-gnu-gcc"} +: ${cross_cc_x86_64="x86_64-linux-gnu-gcc"} : ${cross_cc_cflags_x86_64="-m64"} for target in $target_list; do @@ -108,79 +108,103 @@ for target in $target_list; do case $target in aarch64-*) # We don't have any bigendian build tools so we only use this for AArch64 + container_hosts="x86_64 aarch64" container_image=debian-arm64-test-cross container_cross_cc=aarch64-linux-gnu-gcc-10 ;; alpha-*) + container_hosts=x86_64 container_image=debian-alpha-cross container_cross_cc=alpha-linux-gnu-gcc ;; arm-*) # We don't have any bigendian build tools so we only use this for ARM + container_hosts="x86_64 aarch64" container_image=debian-armhf-cross container_cross_cc=arm-linux-gnueabihf-gcc ;; cris-*) + container_hosts=x86_64 container_image=fedora-cris-cross container_cross_cc=cris-linux-gnu-gcc ;; hppa-*) + container_hosts=x86_64 container_image=debian-hppa-cross container_cross_cc=hppa-linux-gnu-gcc ;; i386-*) + container_hosts=x86_64 container_image=fedora-i386-cross container_cross_cc=gcc ;; m68k-*) + container_hosts=x86_64 container_image=debian-m68k-cross container_cross_cc=m68k-linux-gnu-gcc ;; mips64el-*) + container_hosts=x86_64 container_image=debian-mips64el-cross container_cross_cc=mips64el-linux-gnuabi64-gcc ;; mips64-*) + container_hosts=x86_64 container_image=debian-mips64-cross container_cross_cc=mips64-linux-gnuabi64-gcc ;; mipsel-*) + container_hosts=x86_64 container_image=debian-mipsel-cross container_cross_cc=mipsel-linux-gnu-gcc ;; mips-*) + container_hosts=x86_64 container_image=debian-mips-cross container_cross_cc=mips-linux-gnu-gcc ;; ppc-*|ppc64abi32-*) + container_hosts=x86_64 container_image=debian-powerpc-cross container_cross_cc=powerpc-linux-gnu-gcc ;; ppc64-*) + container_hosts=x86_64 container_image=debian-ppc64-cross container_cross_cc=powerpc64-linux-gnu-gcc ;; ppc64le-*) + container_hosts=x86_64 container_image=debian-ppc64el-cross container_cross_cc=powerpc64le-linux-gnu-gcc ;; riscv64-*) + container_hosts=x86_64 container_image=debian-riscv64-cross container_cross_cc=riscv64-linux-gnu-gcc ;; s390x-*) + container_hosts=x86_64 container_image=debian-s390x-cross container_cross_cc=s390x-linux-gnu-gcc ;; sh4-*) + container_hosts=x86_64 container_image=debian-sh4-cross container_cross_cc=sh4-linux-gnu-gcc ;; sparc64-*) + container_hosts=x86_64 container_image=debian-sparc64-cross container_cross_cc=sparc64-linux-gnu-gcc ;; + x86_64-*) + container_hosts="aarch64 ppc64el x86_64" + container_image=debian-amd64-cross + container_cross_cc=x86_64-linux-gnu-gcc + ;; xtensa*-softmmu) + container_hosts=x86_64 container_image=debian-xtensa-cross # default to the dc232b cpu @@ -257,6 +281,12 @@ for target in $target_list; do echo "CROSS_CC_HAS_POWER8_VECTOR=y" >> $config_target_mak fi ;; + i386-linux-user) + if do_compiler "$target_compiler" $target_compiler_cflags \ + -Werror -fno-pie -o $TMPE $TMPC; then + echo "CROSS_CC_HAS_I386_NOPIE=y" >> $config_target_mak + fi + ;; esac enabled_cross_compilers="$enabled_cross_compilers $target_compiler" @@ -265,7 +295,11 @@ for target in $target_list; do done if test $got_cross_cc = no && test "$container" != no && test -n "$container_image"; then - echo "DOCKER_IMAGE=$container_image" >> $config_target_mak - echo "DOCKER_CROSS_CC_GUEST=$container_cross_cc" >> $config_target_mak + for host in $container_hosts; do + if test "$host" = "$ARCH"; then + echo "DOCKER_IMAGE=$container_image" >> $config_target_mak + echo "DOCKER_CROSS_CC_GUEST=$container_cross_cc" >> $config_target_mak + fi + done fi done diff --git a/tests/tcg/i386/Makefile.target b/tests/tcg/i386/Makefile.target index c4a6f91966..f7efaab918 100644 --- a/tests/tcg/i386/Makefile.target +++ b/tests/tcg/i386/Makefile.target @@ -27,13 +27,23 @@ run-plugin-test-i386-bmi2-%: QEMU_OPTS += -cpu max hello-i386: CFLAGS+=-ffreestanding hello-i386: LDFLAGS+=-nostdlib -# -# test-386 includes a couple of additional objects that need to be linked together -# +# test-386 includes a couple of additional objects that need to be +# linked together, we also need a no-pie capable compiler due to the +# non-pic calls into 16-bit mode +ifneq ($(DOCKER_IMAGE)$(CROSS_CC_HAS_I386_NOPIE),) +test-i386: CFLAGS += -fno-pie test-i386: test-i386.c test-i386-code16.S test-i386-vm86.S test-i386.h test-i386-shift.h test-i386-muldiv.h $(CC) $(CFLAGS) $(LDFLAGS) $(EXTRA_CFLAGS) -o $@ \ $(<D)/test-i386.c $(<D)/test-i386-code16.S $(<D)/test-i386-vm86.S -lm +else +test-i386: + $(call skip-test, "BUILD of $@", "missing -no-pie compiler support") +run-test-i386: + $(call skip-test, "RUN of test-i386", "not built") +run-plugin-test-i386-with-%: + $(call skip-test, "RUN of test-i386 ($*)", "not built") +endif ifeq ($(SPEED), slow) diff --git a/tests/tcg/i386/system/kernel.ld b/tests/tcg/i386/system/kernel.ld index 92de525e93..27ea5bbe04 100644 --- a/tests/tcg/i386/system/kernel.ld +++ b/tests/tcg/i386/system/kernel.ld @@ -12,7 +12,7 @@ SECTIONS { } .data : { - *(.data) + *(.data*) __load_en = .; } diff --git a/tests/tcg/multiarch/gdbstub/sha1.py b/tests/tcg/multiarch/gdbstub/sha1.py index 2bfde49633..423b720e6d 100644 --- a/tests/tcg/multiarch/gdbstub/sha1.py +++ b/tests/tcg/multiarch/gdbstub/sha1.py @@ -40,7 +40,10 @@ def run_test(): check_break("SHA1Init") - # check step and inspect values + # Check step and inspect values. We do a double next after the + # breakpoint as depending on the version of gdb we may step the + # preamble and not the first actual line of source. + gdb.execute("next") gdb.execute("next") val_ctx = gdb.parse_and_eval("context->state[0]") exp_ctx = 0x67452301 diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c index b144320e48..1553d2ef45 100644 --- a/tools/virtiofsd/passthrough_ll.c +++ b/tools/virtiofsd/passthrough_ll.c @@ -2636,7 +2636,8 @@ static void parse_xattrmap(struct lo_data *lo) strerror(ret)); exit(1); } - if (!strcmp(lo->xattr_security_capability, "security.capability")) { + if (!lo->xattr_security_capability || + !strcmp(lo->xattr_security_capability, "security.capability")) { /* 1-1 mapping, don't need to do anything */ free(lo->xattr_security_capability); lo->xattr_security_capability = NULL; |