summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--MAINTAINERS1
-rw-r--r--VERSION2
-rw-r--r--block/io.c3
-rw-r--r--block/nbd-client.c2
-rw-r--r--block/nvme.c11
-rw-r--r--disas/riscv.c2
-rw-r--r--gdbstub.c7
-rw-r--r--hmp.c2
-rw-r--r--hw/tpm/tpm_crb.c18
-rw-r--r--include/exec/gdbstub.h2
-rw-r--r--include/monitor/monitor.h1
-rw-r--r--include/qemu/coroutine_int.h1
-rw-r--r--include/qemu/queue.h8
-rw-r--r--io/channel-websock.c4
-rw-r--r--migration/migration.c22
-rw-r--r--migration/postcopy-ram.c1
-rw-r--r--monitor.c118
-rw-r--r--nbd/client.c30
-rw-r--r--nbd/server.c8
-rw-r--r--nbd/trace-events6
-rw-r--r--qemu-options.hx189
-rw-r--r--scripts/qapi/common.py2
-rw-r--r--target/i386/hvf/hvf.c24
-rw-r--r--target/riscv/cpu.c123
-rw-r--r--target/riscv/op_helper.c17
-rw-r--r--tcg/tcg-opc.h4
-rw-r--r--tests/Makefile.include1
-rw-r--r--tests/libqtest.c10
-rw-r--r--tests/libqtest.h7
-rw-r--r--tests/qapi-schema/doc-good.out4
-rw-r--r--tests/qapi-schema/ident-with-escape.out2
-rw-r--r--tests/qapi-schema/indented-expr.out4
-rw-r--r--tests/qapi-schema/oob-test.err1
-rw-r--r--tests/qapi-schema/oob-test.exit1
-rw-r--r--tests/qapi-schema/oob-test.json2
-rw-r--r--tests/qapi-schema/oob-test.out0
-rw-r--r--tests/qapi-schema/qapi-schema-test.json3
-rw-r--r--tests/qapi-schema/qapi-schema-test.out20
-rw-r--r--tests/qapi-schema/test-qapi.py4
-rw-r--r--tests/qmp-test.c85
-rw-r--r--tests/test-aio.c63
-rw-r--r--tests/test-qmp-cmds.c4
-rw-r--r--tests/test-visitor-serialization.c2
-rw-r--r--tests/tpm-crb-test.c74
-rw-r--r--util/qemu-coroutine-lock.c34
-rw-r--r--util/qemu-coroutine.c94
-rw-r--r--vl.c7
47 files changed, 681 insertions, 349 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index 6fa36c7a2d..24b70169bc 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1351,6 +1351,7 @@ F: util/aio-*.c
F: block/io.c
F: migration/block*
F: include/block/aio.h
+F: include/block/aio-wait.h
F: scripts/qemugdb/aio.py
T: git git://github.com/stefanha/qemu.git block
diff --git a/VERSION b/VERSION
index 9ecd15dc0d..5610569c0d 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-2.11.90
+2.11.91
diff --git a/block/io.c b/block/io.c
index 2b09c656d0..bd9a19a9c4 100644
--- a/block/io.c
+++ b/block/io.c
@@ -249,8 +249,7 @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
BdrvCoDrainData data;
/* Calling bdrv_drain() from a BH ensures the current coroutine yields and
- * other coroutines run if they were queued from
- * qemu_co_queue_run_restart(). */
+ * other coroutines run if they were queued by aio_co_enter(). */
assert(qemu_in_coroutine());
data = (BdrvCoDrainData) {
diff --git a/block/nbd-client.c b/block/nbd-client.c
index e64e346d69..e7caf49fbb 100644
--- a/block/nbd-client.c
+++ b/block/nbd-client.c
@@ -239,7 +239,7 @@ static int nbd_parse_blockstatus_payload(NBDClientSession *client,
{
uint32_t context_id;
- if (chunk->length != sizeof(context_id) + sizeof(extent)) {
+ if (chunk->length != sizeof(context_id) + sizeof(*extent)) {
error_setg(errp, "Protocol error: invalid payload for "
"NBD_REPLY_TYPE_BLOCK_STATUS");
return -EINVAL;
diff --git a/block/nvme.c b/block/nvme.c
index 8bca57aae6..c4f3a7bc94 100644
--- a/block/nvme.c
+++ b/block/nvme.c
@@ -695,12 +695,11 @@ static void nvme_parse_filename(const char *filename, QDict *options,
unsigned long ns;
const char *slash = strchr(tmp, '/');
if (!slash) {
- qdict_put(options, NVME_BLOCK_OPT_DEVICE,
- qstring_from_str(tmp));
+ qdict_put_str(options, NVME_BLOCK_OPT_DEVICE, tmp);
return;
}
device = g_strndup(tmp, slash - tmp);
- qdict_put(options, NVME_BLOCK_OPT_DEVICE, qstring_from_str(device));
+ qdict_put_str(options, NVME_BLOCK_OPT_DEVICE, device);
g_free(device);
namespace = slash + 1;
if (*namespace && qemu_strtoul(namespace, NULL, 10, &ns)) {
@@ -708,8 +707,8 @@ static void nvme_parse_filename(const char *filename, QDict *options,
namespace);
return;
}
- qdict_put(options, NVME_BLOCK_OPT_NAMESPACE,
- qstring_from_str(*namespace ? namespace : "1"));
+ qdict_put_str(options, NVME_BLOCK_OPT_NAMESPACE,
+ *namespace ? namespace : "1");
}
}
@@ -1082,7 +1081,7 @@ static void nvme_refresh_filename(BlockDriverState *bs, QDict *opts)
bs->drv->format_name);
}
- qdict_put(opts, "driver", qstring_from_str(bs->drv->format_name));
+ qdict_put_str(opts, "driver", bs->drv->format_name);
bs->full_open_options = opts;
}
diff --git a/disas/riscv.c b/disas/riscv.c
index 3c17501120..74ad16eacd 100644
--- a/disas/riscv.c
+++ b/disas/riscv.c
@@ -600,7 +600,7 @@ static const rvc_constraint rvcc_mv[] = { rvc_imm_eq_zero, rvc_end };
static const rvc_constraint rvcc_not[] = { rvc_imm_eq_n1, rvc_end };
static const rvc_constraint rvcc_neg[] = { rvc_rs1_eq_x0, rvc_end };
static const rvc_constraint rvcc_negw[] = { rvc_rs1_eq_x0, rvc_end };
-static const rvc_constraint rvcc_sext_w[] = { rvc_rs2_eq_x0, rvc_end };
+static const rvc_constraint rvcc_sext_w[] = { rvc_imm_eq_zero, rvc_end };
static const rvc_constraint rvcc_seqz[] = { rvc_imm_eq_p1, rvc_end };
static const rvc_constraint rvcc_snez[] = { rvc_rs1_eq_x0, rvc_end };
static const rvc_constraint rvcc_sltz[] = { rvc_rs2_eq_x0, rvc_end };
diff --git a/gdbstub.c b/gdbstub.c
index f1d51480f7..a76b2fa481 100644
--- a/gdbstub.c
+++ b/gdbstub.c
@@ -2052,6 +2052,13 @@ int gdbserver_start(const char *device)
return 0;
}
+void gdbserver_cleanup(void)
+{
+ if (gdbserver_state) {
+ put_packet(gdbserver_state, "W00");
+ }
+}
+
static void register_types(void)
{
type_register_static(&char_gdb_type_info);
diff --git a/hmp.c b/hmp.c
index 679467d85a..a25c7bd9a8 100644
--- a/hmp.c
+++ b/hmp.c
@@ -381,7 +381,7 @@ void hmp_info_cpus(Monitor *mon, const QDict *qdict)
monitor_printf(mon, "%c CPU #%" PRId64 ":", active,
cpu->value->cpu_index);
- monitor_printf(mon, " thread-id=%" PRId64 "\n", cpu->value->thread_id);
+ monitor_printf(mon, " thread_id=%" PRId64 "\n", cpu->value->thread_id);
}
qapi_free_CpuInfoFastList(cpu_list);
diff --git a/hw/tpm/tpm_crb.c b/hw/tpm/tpm_crb.c
index ef8b80e9aa..a92dd50437 100644
--- a/hw/tpm/tpm_crb.c
+++ b/hw/tpm/tpm_crb.c
@@ -76,6 +76,8 @@ enum crb_cancel {
CRB_CANCEL_INVOKE = BIT(0),
};
+#define TPM_CRB_NO_LOCALITY 0xff
+
static uint64_t tpm_crb_mmio_read(void *opaque, hwaddr addr,
unsigned size)
{
@@ -95,10 +97,19 @@ static uint64_t tpm_crb_mmio_read(void *opaque, hwaddr addr,
return val;
}
+static uint8_t tpm_crb_get_active_locty(CRBState *s)
+{
+ if (!ARRAY_FIELD_EX32(s->regs, CRB_LOC_STATE, locAssigned)) {
+ return TPM_CRB_NO_LOCALITY;
+ }
+ return ARRAY_FIELD_EX32(s->regs, CRB_LOC_STATE, activeLocality);
+}
+
static void tpm_crb_mmio_write(void *opaque, hwaddr addr,
uint64_t val, unsigned size)
{
CRBState *s = CRB(opaque);
+ uint8_t locty = addr >> 12;
trace_tpm_crb_mmio_write(addr, size, val);
@@ -123,7 +134,8 @@ static void tpm_crb_mmio_write(void *opaque, hwaddr addr,
break;
case A_CRB_CTRL_START:
if (val == CRB_START_INVOKE &&
- !(s->regs[R_CRB_CTRL_START] & CRB_START_INVOKE)) {
+ !(s->regs[R_CRB_CTRL_START] & CRB_START_INVOKE) &&
+ tpm_crb_get_active_locty(s) == locty) {
void *mem = memory_region_get_ram_ptr(&s->cmdmem);
s->regs[R_CRB_CTRL_START] |= CRB_START_INVOKE;
@@ -145,6 +157,8 @@ static void tpm_crb_mmio_write(void *opaque, hwaddr addr,
case CRB_LOC_CTRL_RELINQUISH:
ARRAY_FIELD_DP32(s->regs, CRB_LOC_STATE,
locAssigned, 0);
+ ARRAY_FIELD_DP32(s->regs, CRB_LOC_STS,
+ Granted, 0);
break;
case CRB_LOC_CTRL_REQUEST_ACCESS:
ARRAY_FIELD_DP32(s->regs, CRB_LOC_STS,
@@ -220,6 +234,8 @@ static void tpm_crb_reset(void *dev)
ARRAY_FIELD_DP32(s->regs, CRB_LOC_STATE,
tpmRegValidSts, 1);
+ ARRAY_FIELD_DP32(s->regs, CRB_CTRL_STS,
+ tpmIdle, 1);
ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID,
InterfaceType, CRB_INTF_TYPE_CRB_ACTIVE);
ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID,
diff --git a/include/exec/gdbstub.h b/include/exec/gdbstub.h
index 9aa7756d92..2e8a4b83b9 100644
--- a/include/exec/gdbstub.h
+++ b/include/exec/gdbstub.h
@@ -103,6 +103,8 @@ int gdbserver_start(int);
int gdbserver_start(const char *port);
#endif
+void gdbserver_cleanup(void);
+
/**
* gdb_has_xml:
* This is an ugly hack to cope with both new and old gdb.
diff --git a/include/monitor/monitor.h b/include/monitor/monitor.h
index 0cb0538a31..d6ab70cae2 100644
--- a/include/monitor/monitor.h
+++ b/include/monitor/monitor.h
@@ -13,6 +13,7 @@ extern Monitor *cur_mon;
#define MONITOR_USE_READLINE 0x02
#define MONITOR_USE_CONTROL 0x04
#define MONITOR_USE_PRETTY 0x08
+#define MONITOR_USE_OOB 0x10
bool monitor_cur_is_qmp(void);
diff --git a/include/qemu/coroutine_int.h b/include/qemu/coroutine_int.h
index 59e8406398..bd6b0468e1 100644
--- a/include/qemu/coroutine_int.h
+++ b/include/qemu/coroutine_int.h
@@ -68,6 +68,5 @@ Coroutine *qemu_coroutine_new(void);
void qemu_coroutine_delete(Coroutine *co);
CoroutineAction qemu_coroutine_switch(Coroutine *from, Coroutine *to,
CoroutineAction action);
-void coroutine_fn qemu_co_queue_run_restart(Coroutine *co);
#endif
diff --git a/include/qemu/queue.h b/include/qemu/queue.h
index aa270d2b38..59fd1203a1 100644
--- a/include/qemu/queue.h
+++ b/include/qemu/queue.h
@@ -324,6 +324,14 @@ struct { \
} \
} while (/*CONSTCOND*/0)
+#define QSIMPLEQ_PREPEND(head1, head2) do { \
+ if (!QSIMPLEQ_EMPTY((head2))) { \
+ *(head2)->sqh_last = (head1)->sqh_first; \
+ (head1)->sqh_first = (head2)->sqh_first; \
+ QSIMPLEQ_INIT((head2)); \
+ } \
+} while (/*CONSTCOND*/0)
+
#define QSIMPLEQ_LAST(head, type, field) \
(QSIMPLEQ_EMPTY((head)) ? \
NULL : \
diff --git a/io/channel-websock.c b/io/channel-websock.c
index ec48a305f0..e6608b969d 100644
--- a/io/channel-websock.c
+++ b/io/channel-websock.c
@@ -586,9 +586,7 @@ static gboolean qio_channel_websock_handshake_io(QIOChannel *ioc,
return TRUE;
}
- if (err) {
- error_propagate(&wioc->io_err, err);
- }
+ error_propagate(&wioc->io_err, err);
trace_qio_channel_websock_handshake_reply(ioc);
qio_channel_add_watch(
diff --git a/migration/migration.c b/migration/migration.c
index 52a5092add..58bd382730 100644
--- a/migration/migration.c
+++ b/migration/migration.c
@@ -306,13 +306,21 @@ static void process_incoming_migration_bh(void *opaque)
Error *local_err = NULL;
MigrationIncomingState *mis = opaque;
- /* Make sure all file formats flush their mutable metadata.
- * If we get an error here, just don't restart the VM yet. */
- bdrv_invalidate_cache_all(&local_err);
- if (local_err) {
- error_report_err(local_err);
- local_err = NULL;
- autostart = false;
+ /* Only fire up the block code now if we're going to restart the
+ * VM, else 'cont' will do it.
+ * This causes file locking to happen; so we don't want it to happen
+ * unless we really are starting the VM.
+ */
+ if (autostart && (!global_state_received() ||
+ global_state_get_runstate() == RUN_STATE_RUNNING)) {
+ /* Make sure all file formats flush their mutable metadata.
+ * If we get an error here, just don't restart the VM yet. */
+ bdrv_invalidate_cache_all(&local_err);
+ if (local_err) {
+ error_report_err(local_err);
+ local_err = NULL;
+ autostart = false;
+ }
}
/*
diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c
index efd77939af..4a0b33b373 100644
--- a/migration/postcopy-ram.c
+++ b/migration/postcopy-ram.c
@@ -754,6 +754,7 @@ static void *postcopy_ram_fault_thread(void *opaque)
}
}
trace_postcopy_ram_fault_thread_exit();
+ g_free(pfd);
return NULL;
}
diff --git a/monitor.c b/monitor.c
index 77f4c41cfa..51f4cf480f 100644
--- a/monitor.c
+++ b/monitor.c
@@ -36,6 +36,7 @@
#include "net/slirp.h"
#include "chardev/char-fe.h"
#include "chardev/char-io.h"
+#include "chardev/char-mux.h"
#include "ui/qemu-spice.h"
#include "sysemu/numa.h"
#include "monitor/monitor.h"
@@ -234,6 +235,22 @@ static struct {
QEMUBH *qmp_respond_bh;
} mon_global;
+struct QMPRequest {
+ /* Owner of the request */
+ Monitor *mon;
+ /* "id" field of the request */
+ QObject *id;
+ /* Request object to be handled */
+ QObject *req;
+ /*
+ * Whether we need to resume the monitor afterward. This flag is
+ * used to emulate the old QMP server behavior that the current
+ * command must be completed before execution of the next one.
+ */
+ bool need_resume;
+};
+typedef struct QMPRequest QMPRequest;
+
/* QMP checker flags */
#define QMP_ACCEPT_UNKNOWNS 1
@@ -310,6 +327,38 @@ int monitor_read_password(Monitor *mon, ReadLineFunc *readline_func,
}
}
+static void qmp_request_free(QMPRequest *req)
+{
+ qobject_decref(req->id);
+ qobject_decref(req->req);
+ g_free(req);
+}
+
+/* Must with the mon->qmp.qmp_queue_lock held */
+static void monitor_qmp_cleanup_req_queue_locked(Monitor *mon)
+{
+ while (!g_queue_is_empty(mon->qmp.qmp_requests)) {
+ qmp_request_free(g_queue_pop_head(mon->qmp.qmp_requests));
+ }
+}
+
+/* Must with the mon->qmp.qmp_queue_lock held */
+static void monitor_qmp_cleanup_resp_queue_locked(Monitor *mon)
+{
+ while (!g_queue_is_empty(mon->qmp.qmp_responses)) {
+ qobject_decref(g_queue_pop_head(mon->qmp.qmp_responses));
+ }
+}
+
+static void monitor_qmp_cleanup_queues(Monitor *mon)
+{
+ qemu_mutex_lock(&mon->qmp.qmp_queue_lock);
+ monitor_qmp_cleanup_req_queue_locked(mon);
+ monitor_qmp_cleanup_resp_queue_locked(mon);
+ qemu_mutex_unlock(&mon->qmp.qmp_queue_lock);
+}
+
+
static void monitor_flush_locked(Monitor *mon);
static gboolean monitor_unblocked(GIOChannel *chan, GIOCondition cond,
@@ -701,6 +750,8 @@ static void monitor_data_destroy(Monitor *mon)
QDECREF(mon->outbuf);
qemu_mutex_destroy(&mon->out_lock);
qemu_mutex_destroy(&mon->qmp.qmp_queue_lock);
+ monitor_qmp_cleanup_req_queue_locked(mon);
+ monitor_qmp_cleanup_resp_queue_locked(mon);
g_queue_free(mon->qmp.qmp_requests);
g_queue_free(mon->qmp.qmp_responses);
}
@@ -1203,8 +1254,14 @@ static bool qmp_cmd_oob_check(Monitor *mon, QDict *req, Error **errp)
cmd = qmp_find_command(mon->qmp.commands, command);
if (!cmd) {
- error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND,
- "The command %s has not been found", command);
+ if (mon->qmp.commands == &qmp_cap_negotiation_commands) {
+ error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND,
+ "Expecting capabilities negotiation "
+ "with 'qmp_capabilities'");
+ } else {
+ error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND,
+ "The command %s has not been found", command);
+ }
return false;
}
@@ -4003,22 +4060,6 @@ static void monitor_qmp_respond(Monitor *mon, QObject *rsp,
qobject_decref(rsp);
}
-struct QMPRequest {
- /* Owner of the request */
- Monitor *mon;
- /* "id" field of the request */
- QObject *id;
- /* Request object to be handled */
- QObject *req;
- /*
- * Whether we need to resume the monitor afterward. This flag is
- * used to emulate the old QMP server behavior that the current
- * command must be completed before execution of the next one.
- */
- bool need_resume;
-};
-typedef struct QMPRequest QMPRequest;
-
/*
* Dispatch one single QMP request. The function will free the req_obj
* and objects inside it before return.
@@ -4027,7 +4068,6 @@ static void monitor_qmp_dispatch_one(QMPRequest *req_obj)
{
Monitor *mon, *old_mon;
QObject *req, *rsp = NULL, *id;
- QDict *qdict = NULL;
bool need_resume;
req = req_obj->req;
@@ -4050,18 +4090,6 @@ static void monitor_qmp_dispatch_one(QMPRequest *req_obj)
cur_mon = old_mon;
- if (mon->qmp.commands == &qmp_cap_negotiation_commands) {
- qdict = qdict_get_qdict(qobject_to(QDict, rsp), "error");
- if (qdict
- && !g_strcmp0(qdict_get_try_str(qdict, "class"),
- QapiErrorClass_str(ERROR_CLASS_COMMAND_NOT_FOUND))) {
- /* Provide a more useful error message */
- qdict_del(qdict, "desc");
- qdict_put_str(qdict, "desc", "Expecting capabilities negotiation"
- " with 'qmp_capabilities'");
- }
- }
-
/* Respond if necessary */
monitor_qmp_respond(mon, rsp, NULL, id);
@@ -4198,9 +4226,7 @@ static void handle_qmp_command(JSONMessageParser *parser, GQueue *tokens)
qapi_event_send_command_dropped(id,
COMMAND_DROP_REASON_QUEUE_FULL,
&error_abort);
- qobject_decref(id);
- qobject_decref(req);
- g_free(req_obj);
+ qmp_request_free(req_obj);
return;
}
}
@@ -4315,7 +4341,7 @@ static QObject *get_qmp_greeting(Monitor *mon)
/* Monitors that are not using IOThread won't support OOB */
continue;
}
- qlist_append(cap_list, qstring_from_str(QMPCapability_str(cap)));
+ qlist_append_str(cap_list, QMPCapability_str(cap));
}
return qobject_from_jsonf("{'QMP': {'version': %p, 'capabilities': %p}}",
@@ -4342,6 +4368,7 @@ static void monitor_qmp_event(void *opaque, int event)
mon_refcount++;
break;
case CHR_EVENT_CLOSED:
+ monitor_qmp_cleanup_queues(mon);
json_message_parser_destroy(&mon->qmp.parser);
json_message_parser_init(&mon->qmp.parser, handle_qmp_command);
mon_refcount--;
@@ -4536,12 +4563,26 @@ static void monitor_qmp_setup_handlers_bh(void *opaque)
void monitor_init(Chardev *chr, int flags)
{
Monitor *mon = g_malloc(sizeof(*mon));
+ bool use_readline = flags & MONITOR_USE_READLINE;
+ bool use_oob = flags & MONITOR_USE_OOB;
+
+ if (use_oob) {
+ if (CHARDEV_IS_MUX(chr)) {
+ error_report("Monitor Out-Of-Band is not supported with "
+ "MUX typed chardev backend");
+ exit(1);
+ }
+ if (use_readline) {
+ error_report("Monitor Out-Of-band is only supported by QMP");
+ exit(1);
+ }
+ }
- monitor_data_init(mon, false, false);
+ monitor_data_init(mon, false, use_oob);
qemu_chr_fe_init(&mon->chr, chr, &error_abort);
mon->flags = flags;
- if (flags & MONITOR_USE_READLINE) {
+ if (use_readline) {
mon->rs = readline_init(monitor_readline_printf,
monitor_readline_flush,
mon,
@@ -4637,6 +4678,9 @@ QemuOptsList qemu_mon_opts = {
},{
.name = "pretty",
.type = QEMU_OPT_BOOL,
+ },{
+ .name = "x-oob",
+ .type = QEMU_OPT_BOOL,
},
{ /* end of list */ }
},
diff --git a/nbd/client.c b/nbd/client.c
index 9b9b7f0ea2..b9e175d1c2 100644
--- a/nbd/client.c
+++ b/nbd/client.c
@@ -599,8 +599,8 @@ static QIOChannel *nbd_receive_starttls(QIOChannel *ioc,
* Set one meta context. Simple means that reply must contain zero (not
* negotiated) or one (negotiated) contexts. More contexts would be considered
* as a protocol error. It's also implied that meta-data query equals queried
- * context name, so, if server replies with something different then @context,
- * it considered as error too.
+ * context name, so, if server replies with something different than @context,
+ * it is considered an error too.
* return 1 for successful negotiation, context_id is set
* 0 if operation is unsupported,
* -1 with errp set for any other error
@@ -623,6 +623,7 @@ static int nbd_negotiate_simple_meta_context(QIOChannel *ioc,
char *data = g_malloc(data_len);
char *p = data;
+ trace_nbd_opt_meta_request(context, export);
stl_be_p(p, export_len);
memcpy(p += sizeof(export_len), export, export_len);
stl_be_p(p += export_len, 1);
@@ -649,29 +650,38 @@ static int nbd_negotiate_simple_meta_context(QIOChannel *ioc,
if (reply.type == NBD_REP_META_CONTEXT) {
char *name;
- size_t len;
+
+ if (reply.length != sizeof(received_id) + context_len) {
+ error_setg(errp, "Failed to negotiate meta context '%s', server "
+ "answered with unexpected length %" PRIu32, context,
+ reply.length);
+ nbd_send_opt_abort(ioc);
+ return -1;
+ }
if (nbd_read(ioc, &received_id, sizeof(received_id), errp) < 0) {
return -1;
}
be32_to_cpus(&received_id);
- len = reply.length - sizeof(received_id);
- name = g_malloc(len + 1);
- if (nbd_read(ioc, name, len, errp) < 0) {
+ reply.length -= sizeof(received_id);
+ name = g_malloc(reply.length + 1);
+ if (nbd_read(ioc, name, reply.length, errp) < 0) {
g_free(name);
return -1;
}
- name[len] = '\0';
+ name[reply.length] = '\0';
if (strcmp(context, name)) {
error_setg(errp, "Failed to negotiate meta context '%s', server "
"answered with different context '%s'", context,
name);
g_free(name);
+ nbd_send_opt_abort(ioc);
return -1;
}
g_free(name);
+ trace_nbd_opt_meta_reply(context, received_id);
received = true;
/* receive NBD_REP_ACK */
@@ -690,6 +700,12 @@ static int nbd_negotiate_simple_meta_context(QIOChannel *ioc,
if (reply.type != NBD_REP_ACK) {
error_setg(errp, "Unexpected reply type %" PRIx32 " expected %x",
reply.type, NBD_REP_ACK);
+ nbd_send_opt_abort(ioc);
+ return -1;
+ }
+ if (reply.length) {
+ error_setg(errp, "Unexpected length to ACK response");
+ nbd_send_opt_abort(ioc);
return -1;
}
diff --git a/nbd/server.c b/nbd/server.c
index cea158913b..9e1f227178 100644
--- a/nbd/server.c
+++ b/nbd/server.c
@@ -726,6 +726,7 @@ static int nbd_negotiate_send_meta_context(NBDClient *client,
context_id = 0;
}
+ trace_nbd_negotiate_meta_query_reply(context, context_id);
set_be_option_rep(&opt.h, client->opt, NBD_REP_META_CONTEXT,
sizeof(opt) - sizeof(opt.h) + iov[1].iov_len);
stl_be_p(&opt.context_id, context_id);
@@ -752,10 +753,12 @@ static int nbd_meta_base_query(NBDClient *client, NBDExportMetaContexts *meta,
if (client->opt == NBD_OPT_LIST_META_CONTEXT) {
meta->base_allocation = true;
}
+ trace_nbd_negotiate_meta_query_parse("base:");
return 1;
}
if (len != alen) {
+ trace_nbd_negotiate_meta_query_skip("not base:allocation");
return nbd_opt_skip(client, len, errp);
}
@@ -768,6 +771,7 @@ static int nbd_meta_base_query(NBDClient *client, NBDExportMetaContexts *meta,
meta->base_allocation = true;
}
+ trace_nbd_negotiate_meta_query_parse("base:allocation");
return 1;
}
@@ -800,6 +804,7 @@ static int nbd_negotiate_meta_query(NBDClient *client,
/* The only supported namespace for now is 'base'. So query should start
* with 'base:'. Otherwise, we can ignore it and skip the remainder. */
if (len < baselen) {
+ trace_nbd_negotiate_meta_query_skip("length too short");
return nbd_opt_skip(client, len, errp);
}
@@ -809,6 +814,7 @@ static int nbd_negotiate_meta_query(NBDClient *client,
return ret;
}
if (strncmp(query, "base:", baselen) != 0) {
+ trace_nbd_negotiate_meta_query_skip("not for base: namespace");
return nbd_opt_skip(client, len, errp);
}
@@ -858,6 +864,8 @@ static int nbd_negotiate_meta_queries(NBDClient *client,
return ret;
}
cpu_to_be32s(&nb_queries);
+ trace_nbd_negotiate_meta_context(nbd_opt_lookup(client->opt),
+ meta->export_name, nb_queries);
if (client->opt == NBD_OPT_LIST_META_CONTEXT && !nb_queries) {
/* enable all known contexts */
diff --git a/nbd/trace-events b/nbd/trace-events
index 0d03edc967..dee081e775 100644
--- a/nbd/trace-events
+++ b/nbd/trace-events
@@ -10,6 +10,8 @@ nbd_receive_query_exports_start(const char *wantname) "Querying export list for
nbd_receive_query_exports_success(const char *wantname) "Found desired export name '%s'"
nbd_receive_starttls_new_client(void) "Setting up TLS"
nbd_receive_starttls_tls_handshake(void) "Starting TLS handshake"
+nbd_opt_meta_request(const char *context, const char *export) "Requesting to set meta context %s for export %s"
+nbd_opt_meta_reply(const char *context, uint32_t id) "Received mapping of context %s to id %" PRIu32
nbd_receive_negotiate(void *tlscreds, const char *hostname) "Receiving negotiation tlscreds=%p hostname=%s"
nbd_receive_negotiate_magic(uint64_t magic) "Magic is 0x%" PRIx64
nbd_receive_negotiate_server_flags(uint32_t globalflags) "Global flags are 0x%" PRIx32
@@ -44,6 +46,10 @@ nbd_negotiate_handle_info_request(int request, const char *name) "Client request
nbd_negotiate_handle_info_block_size(uint32_t minimum, uint32_t preferred, uint32_t maximum) "advertising minimum 0x%" PRIx32 ", preferred 0x%" PRIx32 ", maximum 0x%" PRIx32
nbd_negotiate_handle_starttls(void) "Setting up TLS"
nbd_negotiate_handle_starttls_handshake(void) "Starting TLS handshake"
+nbd_negotiate_meta_context(const char *optname, const char *export, uint32_t queries) "Client requested %s for export %s, with %" PRIu32 " queries"
+nbd_negotiate_meta_query_skip(const char *reason) "Skipping meta query: %s"
+nbd_negotiate_meta_query_parse(const char *query) "Parsed meta query '%s'"
+nbd_negotiate_meta_query_reply(const char *context, uint32_t id) "Replying with meta context '%s' id %" PRIu32
nbd_negotiate_options_flags(uint32_t flags) "Received client flags 0x%" PRIx32
nbd_negotiate_options_check_magic(uint64_t magic) "Checking opts magic 0x%" PRIx64
nbd_negotiate_options_check_option(uint32_t option, const char *name) "Checking option %" PRIu32 " (%s)"
diff --git a/qemu-options.hx b/qemu-options.hx
index 74158e7493..3ece30d216 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -2048,41 +2048,40 @@ DEF("net", HAS_ARG, QEMU_OPTION_net,
" old way to initialize a host network interface\n"
" (use the -netdev option if possible instead)\n", QEMU_ARCH_ALL)
STEXI
-@item -net nic[,vlan=@var{n}][,netdev=@var{nd}][,macaddr=@var{mac}][,model=@var{type}] [,name=@var{name}][,addr=@var{addr}][,vectors=@var{v}]
-@findex -net
-Configure or create an on-board (or machine default) Network Interface Card
-(NIC) and connect it either to VLAN @var{n} (@var{n} = 0 is the default), or
-to the netdev @var{nd}. The NIC is an e1000 by default on the PC
-target. Optionally, the MAC address can be changed to @var{mac}, the
-device address set to @var{addr} (PCI cards only),
-and a @var{name} can be assigned for use in monitor commands.
-Optionally, for PCI cards, you can specify the number @var{v} of MSI-X vectors
-that the card should have; this option currently only affects virtio cards; set
-@var{v} = 0 to disable MSI-X. If no @option{-net} option is specified, a single
-NIC is created. QEMU can emulate several different models of network card.
-Valid values for @var{type} are
-@code{virtio}, @code{i82551}, @code{i82557b}, @code{i82559er},
-@code{ne2k_pci}, @code{ne2k_isa}, @code{pcnet}, @code{rtl8139},
-@code{e1000}, @code{smc91c111}, @code{lance} and @code{mcf_fec}.
-Not all devices are supported on all targets. Use @code{-net nic,model=help}
-for a list of available devices for your target.
+@item -nic [tap|bridge|user|l2tpv3|vde|netmap|vhost-user|socket][,...][,mac=macaddr][,model=mn]
+@findex -nic
+This option is a shortcut for configuring both the on-board (default) guest
+NIC hardware and the host network backend in one go. The host backend options
+are the same as with the corresponding @option{-netdev} options below.
+The guest NIC model can be set with @option{model=@var{modelname}}.
+Use @option{model=help} to list the available device types.
+The hardware MAC address can be set with @option{mac=@var{macaddr}}.
+
+The following two example do exactly the same, to show how @option{-nic} can
+be used to shorten the command line length (note that the e1000 is the default
+on i386, so the @option{model=e1000} parameter could even be omitted here, too):
+@example
+qemu-system-i386 -netdev user,id=n1,ipv6=off -device e1000,netdev=n1,mac=52:54:98:76:54:32
+qemu-system-i386 -nic user,ipv6=off,model=e1000,mac=52:54:98:76:54:32
+@end example
+
+@item -nic none
+Indicate that no network devices should be configured. It is used to override
+the default configuration (default NIC with ``user'' host network backend)
+which is activated if no other networking options are provided.
@item -netdev user,id=@var{id}[,@var{option}][,@var{option}][,...]
@findex -netdev
-@item -net user[,@var{option}][,@var{option}][,...]
-Use the user mode network stack which requires no administrator
+Configure user mode host network backend which requires no administrator
privilege to run. Valid options are:
@table @option
-@item vlan=@var{n}
-Connect user mode stack to VLAN @var{n} (@var{n} = 0 is the default).
-
@item id=@var{id}
-@itemx name=@var{name}
Assign symbolic name for use in monitor commands.
-@option{ipv4} and @option{ipv6} specify that either IPv4 or IPv6 must
-be enabled. If neither is specified both protocols are enabled.
+@item ipv4=on|off and ipv6=on|off
+Specify that either IPv4 or IPv6 must be enabled. If neither is specified
+both protocols are enabled.
@item net=@var{addr}[/@var{mask}]
Set IP network address the guest will see. Optionally specify the netmask,
@@ -2134,7 +2133,7 @@ can not be resolved.
Example:
@example
-qemu -net user,dnssearch=mgmt.example.org,dnssearch=example.org [...]
+qemu-system-i386 -nic user,dnssearch=mgmt.example.org,dnssearch=example.org
@end example
@item tftp=@var{dir}
@@ -2150,7 +2149,8 @@ a guest from a local directory.
Example (using pxelinux):
@example
-qemu-system-i386 -hda linux.img -boot n -net user,tftp=/path/to/tftp/files,bootfile=/pxelinux.0
+qemu-system-i386 -hda linux.img -boot n -device e1000,netdev=n1 \
+ -netdev user,id=n1,tftp=/path/to/tftp/files,bootfile=/pxelinux.0
@end example
@item smb=@var{dir}[,smbserver=@var{addr}]
@@ -2169,8 +2169,6 @@ or @file{C:\WINNT\SYSTEM32\DRIVERS\ETC\LMHOSTS} (Windows NT/2000).
Then @file{@var{dir}} can be accessed in @file{\\smbserver\qemu}.
Note that a SAMBA server must be installed on the host OS.
-QEMU was tested successfully with smbd versions from Red Hat 9,
-Fedora Core 3 and OpenSUSE 11.x.
@item hostfwd=[tcp|udp]:[@var{hostaddr}]:@var{hostport}-[@var{guestaddr}]:@var{guestport}
Redirect incoming TCP or UDP connections to the host port @var{hostport} to
@@ -2185,7 +2183,7 @@ screen 0, use the following:
@example
# on the host
-qemu-system-i386 -net user,hostfwd=tcp:127.0.0.1:6001-:6000 [...]
+qemu-system-i386 -nic user,hostfwd=tcp:127.0.0.1:6001-:6000
# this host xterm should open in the guest X11 server
xterm -display :1
@end example
@@ -2195,7 +2193,7 @@ the guest, use the following:
@example
# on the host
-qemu-system-i386 -net user,hostfwd=tcp::5555-:23 [...]
+qemu-system-i386 -nic user,hostfwd=tcp::5555-:23
telnet localhost 5555
@end example
@@ -2214,7 +2212,7 @@ lifetime, like in the following example:
@example
# open 10.10.1.1:4321 on bootup, connect 10.0.2.100:1234 to it whenever
# the guest accesses it
-qemu -net user,guestfwd=tcp:10.0.2.100:1234-tcp:10.10.1.1:4321 [...]
+qemu-system-i386 -nic user,guestfwd=tcp:10.0.2.100:1234-tcp:10.10.1.1:4321
@end example
Or you can execute a command on every TCP connection established by the guest,
@@ -2223,7 +2221,7 @@ so that QEMU behaves similar to an inetd process for that virtual server:
@example
# call "netcat 10.10.1.1 4321" on every TCP connection to 10.0.2.100:1234
# and connect the TCP stream to its stdin/stdout
-qemu -net 'user,guestfwd=tcp:10.0.2.100:1234-cmd:netcat 10.10.1.1 4321'
+qemu-system-i386 -nic 'user,id=n1,guestfwd=tcp:10.0.2.100:1234-cmd:netcat 10.10.1.1 4321'
@end example
@end table
@@ -2234,8 +2232,7 @@ syntax gives undefined results. Their use for new applications is discouraged
as they will be removed from future versions.
@item -netdev tap,id=@var{id}[,fd=@var{h}][,ifname=@var{name}][,script=@var{file}][,downscript=@var{dfile}][,br=@var{bridge}][,helper=@var{helper}]
-@itemx -net tap[,vlan=@var{n}][,name=@var{name}][,fd=@var{h}][,ifname=@var{name}][,script=@var{file}][,downscript=@var{dfile}][,br=@var{bridge}][,helper=@var{helper}]
-Connect the host TAP network interface @var{name} to VLAN @var{n}.
+Configure a host TAP network backend with ID @var{id}.
Use the network script @var{file} to configure it and the network script
@var{dfile} to deconfigure it. If @var{name} is not provided, the OS
@@ -2256,7 +2253,7 @@ Examples:
@example
#launch a QEMU instance with the default network script
-qemu-system-i386 linux.img -net nic -net tap
+qemu-system-i386 linux.img -nic tap
@end example
@example
@@ -2270,12 +2267,11 @@ qemu-system-i386 linux.img \
@example
#launch a QEMU instance with the default network helper to
#connect a TAP device to bridge br0
-qemu-system-i386 linux.img \
- -net nic -net tap,"helper=/path/to/qemu-bridge-helper"
+qemu-system-i386 linux.img -device virtio-net-pci,netdev=n1 \
+ -netdev tap,id=n1,"helper=/path/to/qemu-bridge-helper"
@end example
@item -netdev bridge,id=@var{id}[,br=@var{bridge}][,helper=@var{helper}]
-@itemx -net bridge[,vlan=@var{n}][,name=@var{name}][,br=@var{bridge}][,helper=@var{helper}]
Connect a host TAP network interface to a host bridge device.
Use the network helper @var{helper} to configure the TAP interface and
@@ -2288,21 +2284,20 @@ Examples:
@example
#launch a QEMU instance with the default network helper to
#connect a TAP device to bridge br0
-qemu-system-i386 linux.img -net bridge -net nic,model=virtio
+qemu-system-i386 linux.img -netdev bridge,id=n1 -device virtio-net,netdev=n1
@end example
@example
#launch a QEMU instance with the default network helper to
#connect a TAP device to bridge qemubr0
-qemu-system-i386 linux.img -net bridge,br=qemubr0 -net nic,model=virtio
+qemu-system-i386 linux.img -netdev bridge,br=qemubr0,id=n1 -device virtio-net,netdev=n1
@end example
@item -netdev socket,id=@var{id}[,fd=@var{h}][,listen=[@var{host}]:@var{port}][,connect=@var{host}:@var{port}]
-@itemx -net socket[,vlan=@var{n}][,name=@var{name}][,fd=@var{h}] [,listen=[@var{host}]:@var{port}][,connect=@var{host}:@var{port}]
-Connect the VLAN @var{n} to a remote VLAN in another QEMU virtual
-machine using a TCP socket connection. If @option{listen} is
-specified, QEMU waits for incoming connections on @var{port}
+This host network backend can be used to connect the guest's network to
+another QEMU virtual machine using a TCP socket connection. If @option{listen}
+is specified, QEMU waits for incoming connections on @var{port}
(@var{host} is optional). @option{connect} is used to connect to
another QEMU instance using the @option{listen} option. @option{fd}=@var{h}
specifies an already opened TCP socket.
@@ -2311,21 +2306,19 @@ Example:
@example
# launch a first QEMU instance
qemu-system-i386 linux.img \
- -net nic,macaddr=52:54:00:12:34:56 \
- -net socket,listen=:1234
-# connect the VLAN 0 of this instance to the VLAN 0
-# of the first instance
+ -device e1000,netdev=n1,mac=52:54:00:12:34:56 \
+ -netdev socket,id=n1,listen=:1234
+# connect the network of this instance to the network of the first instance
qemu-system-i386 linux.img \
- -net nic,macaddr=52:54:00:12:34:57 \
- -net socket,connect=127.0.0.1:1234
+ -device e1000,netdev=n2,mac=52:54:00:12:34:57 \
+ -netdev socket,id=n2,connect=127.0.0.1:1234
@end example
@item -netdev socket,id=@var{id}[,fd=@var{h}][,mcast=@var{maddr}:@var{port}[,localaddr=@var{addr}]]
-@itemx -net socket[,vlan=@var{n}][,name=@var{name}][,fd=@var{h}][,mcast=@var{maddr}:@var{port}[,localaddr=@var{addr}]]
-Create a VLAN @var{n} shared with another QEMU virtual
-machines using a UDP multicast socket, effectively making a bus for
-every QEMU with same multicast address @var{maddr} and @var{port}.
+Configure a socket host network backend to share the guest's network traffic
+with another QEMU virtual machines using a UDP multicast socket, effectively
+making a bus for every QEMU with same multicast address @var{maddr} and @var{port}.
NOTES:
@enumerate
@item
@@ -2342,25 +2335,24 @@ Example:
@example
# launch one QEMU instance
qemu-system-i386 linux.img \
- -net nic,macaddr=52:54:00:12:34:56 \
- -net socket,mcast=230.0.0.1:1234
+ -device e1000,netdev=n1,mac=52:54:00:12:34:56 \
+ -netdev socket,id=n1,mcast=230.0.0.1:1234
# launch another QEMU instance on same "bus"
qemu-system-i386 linux.img \
- -net nic,macaddr=52:54:00:12:34:57 \
- -net socket,mcast=230.0.0.1:1234
+ -device e1000,netdev=n2,mac=52:54:00:12:34:57 \
+ -netdev socket,id=n2,mcast=230.0.0.1:1234
# launch yet another QEMU instance on same "bus"
qemu-system-i386 linux.img \
- -net nic,macaddr=52:54:00:12:34:58 \
- -net socket,mcast=230.0.0.1:1234
+ -device e1000,netdev=n3,macaddr=52:54:00:12:34:58 \
+ -netdev socket,id=n3,mcast=230.0.0.1:1234
@end example
Example (User Mode Linux compat.):
@example
-# launch QEMU instance (note mcast address selected
-# is UML's default)
+# launch QEMU instance (note mcast address selected is UML's default)
qemu-system-i386 linux.img \
- -net nic,macaddr=52:54:00:12:34:56 \
- -net socket,mcast=239.192.168.1:1102
+ -device e1000,netdev=n1,mac=52:54:00:12:34:56 \
+ -netdev socket,id=n1,mcast=239.192.168.1:1102
# launch UML
/path/to/linux ubd0=/path/to/root_fs eth0=mcast
@end example
@@ -2368,14 +2360,13 @@ qemu-system-i386 linux.img \
Example (send packets from host's 1.2.3.4):
@example
qemu-system-i386 linux.img \
- -net nic,macaddr=52:54:00:12:34:56 \
- -net socket,mcast=239.192.168.1:1102,localaddr=1.2.3.4
+ -device e1000,netdev=n1,mac=52:54:00:12:34:56 \
+ -netdev socket,id=n1,mcast=239.192.168.1:1102,localaddr=1.2.3.4
@end example
@item -netdev l2tpv3,id=@var{id},src=@var{srcaddr},dst=@var{dstaddr}[,srcport=@var{srcport}][,dstport=@var{dstport}],txsession=@var{txsession}[,rxsession=@var{rxsession}][,ipv6][,udp][,cookie64][,counter][,pincounter][,txcookie=@var{txcookie}][,rxcookie=@var{rxcookie}][,offset=@var{offset}]
-@itemx -net l2tpv3[,vlan=@var{n}][,name=@var{name}],src=@var{srcaddr},dst=@var{dstaddr}[,srcport=@var{srcport}][,dstport=@var{dstport}],txsession=@var{txsession}[,rxsession=@var{rxsession}][,ipv6][,udp][,cookie64][,counter][,pincounter][,txcookie=@var{txcookie}][,rxcookie=@var{rxcookie}][,offset=@var{offset}]
-Connect VLAN @var{n} to L2TPv3 pseudowire. L2TPv3 (RFC3391) is a popular
-protocol to transport Ethernet (and other Layer 2) data frames between
+Configure a L2TPv3 pseudowire host network backend. L2TPv3 (RFC3391) is a
+popular protocol to transport Ethernet (and other Layer 2) data frames between
two systems. It is present in routers, firewalls and the Linux kernel
(from version 3.3 onwards).
@@ -2428,14 +2419,13 @@ brctl addif br-lan vmtunnel0
# on 4.3.2.1
# launch QEMU instance - if your network has reorder or is very lossy add ,pincounter
-qemu-system-i386 linux.img -net nic -net l2tpv3,src=4.2.3.1,dst=1.2.3.4,udp,srcport=16384,dstport=16384,rxsession=0xffffffff,txsession=0xffffffff,counter
-
+qemu-system-i386 linux.img -device e1000,netdev=n1 \
+ -netdev l2tpv3,id=n1,src=4.2.3.1,dst=1.2.3.4,udp,srcport=16384,dstport=16384,rxsession=0xffffffff,txsession=0xffffffff,counter
@end example
@item -netdev vde,id=@var{id}[,sock=@var{socketpath}][,port=@var{n}][,group=@var{groupname}][,mode=@var{octalmode}]
-@itemx -net vde[,vlan=@var{n}][,name=@var{name}][,sock=@var{socketpath}] [,port=@var{n}][,group=@var{groupname}][,mode=@var{octalmode}]
-Connect VLAN @var{n} to PORT @var{n} of a vde switch running on host and
+Configure VDE backend to connect to PORT @var{n} of a vde switch running on host and
listening for incoming connections on @var{socketpath}. Use GROUP @var{groupname}
and MODE @var{octalmode} to change default ownership and permissions for
communication port. This option is only available if QEMU has been compiled
@@ -2446,19 +2436,9 @@ Example:
# launch vde switch
vde_switch -F -sock /tmp/myswitch
# launch QEMU instance
-qemu-system-i386 linux.img -net nic -net vde,sock=/tmp/myswitch
+qemu-system-i386 linux.img -nic vde,sock=/tmp/myswitch
@end example
-@item -netdev hubport,id=@var{id},hubid=@var{hubid}[,netdev=@var{nd}]
-
-Create a hub port on QEMU "vlan" @var{hubid}.
-
-The hubport netdev lets you connect a NIC to a QEMU "vlan" instead of a single
-netdev. @code{-net} and @code{-device} with parameter @option{vlan} create the
-required hub automatically. Alternatively, you can also connect the hubport
-to another netdev with ID @var{nd} by using the @option{netdev=@var{nd}}
-option.
-
@item -netdev vhost-user,chardev=@var{id}[,vhostforce=on|off][,queues=n]
Establish a vhost-user netdev, backed by a chardev @var{id}. The chardev should
@@ -2477,17 +2457,36 @@ qemu -m 512 -object memory-backend-file,id=mem,size=512M,mem-path=/hugetlbfs,sha
-device virtio-net-pci,netdev=net0
@end example
-@item --nic [tap|bridge|user|l2tpv3|vde|netmap|vhost-user|socket][,...][,mac=macaddr]
+@item -netdev hubport,id=@var{id},hubid=@var{hubid}[,netdev=@var{nd}]
-This option is a shortcut for setting both, the on-board (default) guest NIC
-hardware and the host network backend in one go. The host backend options are
-the same as with the corresponding @option{--netdev} option. The guest NIC
-hardware MAC address can be set with @option{mac=@var{macaddr}}.
+Create a hub port on the emulated hub with ID @var{hubid}.
-@item --nic none
-Indicate that no network devices should be configured. It is used to override
-the default configuration (default NIC with @option{--net user} backend) which
-is activated if no other networking options are provided.
+The hubport netdev lets you connect a NIC to a QEMU emulated hub instead of a
+single netdev. @code{-net} and @code{-device} with the parameter @option{vlan}
+(deprecated), or @code{-nic hubport} can also be used to connect a
+network device or a NIC to a hub. Alternatively, you can also connect the
+hubport to another netdev with ID @var{nd} by using the @option{netdev=@var{nd}}
+option.
+
+@item -net nic[,vlan=@var{n}][,netdev=@var{nd}][,macaddr=@var{mac}][,model=@var{type}] [,name=@var{name}][,addr=@var{addr}][,vectors=@var{v}]
+@findex -net
+Legacy option to configure or create an on-board (or machine default) Network
+Interface Card(NIC) and connect it either to the emulated hub port ("vlan")
+with number @var{n} (@var{n} = 0 is the default), or to the netdev @var{nd}.
+The NIC is an e1000 by default on the PC target. Optionally, the MAC address
+can be changed to @var{mac}, the device address set to @var{addr} (PCI cards
+only), and a @var{name} can be assigned for use in monitor commands.
+Optionally, for PCI cards, you can specify the number @var{v} of MSI-X vectors
+that the card should have; this option currently only affects virtio cards; set
+@var{v} = 0 to disable MSI-X. If no @option{-net} option is specified, a single
+NIC is created. QEMU can emulate several different models of network card.
+Use @code{-net nic,model=help} for a list of available devices for your target.
+
+@item -net user|tap|bridge|socket|l2tpv3|vde[,...][,vlan=@var{n}][,name=@var{name}]
+Configure a host network backend (with the options corresponding to the same
+@option{-netdev} option) and connect it to the emulated hub ("vlan") with the
+number @var{n} (default is number 0). Use @var{name} to specify the name of the
+hub port.
ETEXI
STEXI
diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py
index 2c05e3c284..3e14bc41f2 100644
--- a/scripts/qapi/common.py
+++ b/scripts/qapi/common.py
@@ -872,7 +872,7 @@ def check_keys(expr_elem, meta, required, optional=[]):
raise QAPISemError(info,
"'%s' of %s '%s' should only use false value"
% (key, meta, name))
- if key == 'boxed' and value is not True:
+ if (key == 'boxed' or key == 'allow-oob') and value is not True:
raise QAPISemError(info,
"'%s' of %s '%s' should only use true value"
% (key, meta, name))
diff --git a/target/i386/hvf/hvf.c b/target/i386/hvf/hvf.c
index 15870a4f36..c36753954b 100644
--- a/target/i386/hvf/hvf.c
+++ b/target/i386/hvf/hvf.c
@@ -86,25 +86,25 @@ static void assert_hvf_ok(hv_return_t ret)
switch (ret) {
case HV_ERROR:
- error_report("Error: HV_ERROR\n");
+ error_report("Error: HV_ERROR");
break;
case HV_BUSY:
- error_report("Error: HV_BUSY\n");
+ error_report("Error: HV_BUSY");
break;
case HV_BAD_ARGUMENT:
- error_report("Error: HV_BAD_ARGUMENT\n");
+ error_report("Error: HV_BAD_ARGUMENT");
break;
case HV_NO_RESOURCES:
- error_report("Error: HV_NO_RESOURCES\n");
+ error_report("Error: HV_NO_RESOURCES");
break;
case HV_NO_DEVICE:
- error_report("Error: HV_NO_DEVICE\n");
+ error_report("Error: HV_NO_DEVICE");
break;
case HV_UNSUPPORTED:
- error_report("Error: HV_UNSUPPORTED\n");
+ error_report("Error: HV_UNSUPPORTED");
break;
default:
- error_report("Unknown Error\n");
+ error_report("Unknown Error");
}
abort();
@@ -191,7 +191,7 @@ void hvf_set_phys_mem(MemoryRegionSection *section, bool add)
if (mem) {
mem->size = 0;
if (do_hvf_set_memory(mem)) {
- error_report("Failed to reset overlapping slot\n");
+ error_report("Failed to reset overlapping slot");
abort();
}
}
@@ -211,7 +211,7 @@ void hvf_set_phys_mem(MemoryRegionSection *section, bool add)
}
if (x == hvf_state->num_slots) {
- error_report("No free slots\n");
+ error_report("No free slots");
abort();
}
@@ -221,7 +221,7 @@ void hvf_set_phys_mem(MemoryRegionSection *section, bool add)
mem->region = area;
if (do_hvf_set_memory(mem)) {
- error_report("Error registering new memory slot\n");
+ error_report("Error registering new memory slot");
abort();
}
}
@@ -884,7 +884,7 @@ int hvf_vcpu_exec(CPUState *cpu)
break;
}
default:
- error_report("Unrecognized CR %d\n", cr);
+ error_report("Unrecognized CR %d", cr);
abort();
}
RIP(env) += ins_len;
@@ -930,7 +930,7 @@ int hvf_vcpu_exec(CPUState *cpu)
env->error_code = 0;
break;
default:
- error_report("%llx: unhandled exit %llx\n", rip, exit_reason);
+ error_report("%llx: unhandled exit %llx", rip, exit_reason);
}
} while (ret == 0);
diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
index 9de34d7099..5a527fbba0 100644
--- a/target/riscv/cpu.c
+++ b/target/riscv/cpu.c
@@ -115,6 +115,8 @@ static void riscv_any_cpu_init(Object *obj)
set_resetvec(env, DEFAULT_RSTVEC);
}
+#if defined(TARGET_RISCV32)
+
static void rv32gcsu_priv1_09_1_cpu_init(Object *obj)
{
CPURISCVState *env = &RISCV_CPU(obj)->env;
@@ -141,6 +143,8 @@ static void rv32imacu_nommu_cpu_init(Object *obj)
set_resetvec(env, DEFAULT_RSTVEC);
}
+#elif defined(TARGET_RISCV64)
+
static void rv64gcsu_priv1_09_1_cpu_init(Object *obj)
{
CPURISCVState *env = &RISCV_CPU(obj)->env;
@@ -167,20 +171,7 @@ static void rv64imacu_nommu_cpu_init(Object *obj)
set_resetvec(env, DEFAULT_RSTVEC);
}
-static const RISCVCPUInfo riscv_cpus[] = {
- { 96, TYPE_RISCV_CPU_ANY, riscv_any_cpu_init },
- { 32, TYPE_RISCV_CPU_RV32GCSU_V1_09_1, rv32gcsu_priv1_09_1_cpu_init },
- { 32, TYPE_RISCV_CPU_RV32GCSU_V1_10_0, rv32gcsu_priv1_10_0_cpu_init },
- { 32, TYPE_RISCV_CPU_RV32IMACU_NOMMU, rv32imacu_nommu_cpu_init },
- { 32, TYPE_RISCV_CPU_SIFIVE_E31, rv32imacu_nommu_cpu_init },
- { 32, TYPE_RISCV_CPU_SIFIVE_U34, rv32gcsu_priv1_10_0_cpu_init },
- { 64, TYPE_RISCV_CPU_RV64GCSU_V1_09_1, rv64gcsu_priv1_09_1_cpu_init },
- { 64, TYPE_RISCV_CPU_RV64GCSU_V1_10_0, rv64gcsu_priv1_10_0_cpu_init },
- { 64, TYPE_RISCV_CPU_RV64IMACU_NOMMU, rv64imacu_nommu_cpu_init },
- { 64, TYPE_RISCV_CPU_SIFIVE_E51, rv64imacu_nommu_cpu_init },
- { 64, TYPE_RISCV_CPU_SIFIVE_U54, rv64gcsu_priv1_10_0_cpu_init },
- { 0, NULL, NULL }
-};
+#endif
static ObjectClass *riscv_cpu_class_by_name(const char *cpu_model)
{
@@ -366,28 +357,6 @@ static void riscv_cpu_class_init(ObjectClass *c, void *data)
cc->vmsd = &vmstate_riscv_cpu;
}
-static void cpu_register(const RISCVCPUInfo *info)
-{
- TypeInfo type_info = {
- .name = info->name,
- .parent = TYPE_RISCV_CPU,
- .instance_size = sizeof(RISCVCPU),
- .instance_init = info->initfn,
- };
-
- type_register(&type_info);
-}
-
-static const TypeInfo riscv_cpu_type_info = {
- .name = TYPE_RISCV_CPU,
- .parent = TYPE_CPU,
- .instance_size = sizeof(RISCVCPU),
- .instance_init = riscv_cpu_init,
- .abstract = false,
- .class_size = sizeof(RISCVCPUClass),
- .class_init = riscv_cpu_class_init,
-};
-
char *riscv_isa_string(RISCVCPU *cpu)
{
int i;
@@ -403,30 +372,76 @@ char *riscv_isa_string(RISCVCPU *cpu)
return isa_str;
}
-void riscv_cpu_list(FILE *f, fprintf_function cpu_fprintf)
+typedef struct RISCVCPUListState {
+ fprintf_function cpu_fprintf;
+ FILE *file;
+} RISCVCPUListState;
+
+static gint riscv_cpu_list_compare(gconstpointer a, gconstpointer b)
{
- const RISCVCPUInfo *info = riscv_cpus;
+ ObjectClass *class_a = (ObjectClass *)a;
+ ObjectClass *class_b = (ObjectClass *)b;
+ const char *name_a, *name_b;
- while (info->name) {
- if (info->bit_widths & TARGET_LONG_BITS) {
- (*cpu_fprintf)(f, "%s\n", info->name);
- }
- info++;
- }
+ name_a = object_class_get_name(class_a);
+ name_b = object_class_get_name(class_b);
+ return strcmp(name_a, name_b);
}
-static void riscv_cpu_register_types(void)
+static void riscv_cpu_list_entry(gpointer data, gpointer user_data)
{
- const RISCVCPUInfo *info = riscv_cpus;
+ RISCVCPUListState *s = user_data;
+ const char *typename = object_class_get_name(OBJECT_CLASS(data));
+ int len = strlen(typename) - strlen(RISCV_CPU_TYPE_SUFFIX);
- type_register_static(&riscv_cpu_type_info);
+ (*s->cpu_fprintf)(s->file, "%.*s\n", len, typename);
+}
- while (info->name) {
- if (info->bit_widths & TARGET_LONG_BITS) {
- cpu_register(info);
- }
- info++;
- }
+void riscv_cpu_list(FILE *f, fprintf_function cpu_fprintf)
+{
+ RISCVCPUListState s = {
+ .cpu_fprintf = cpu_fprintf,
+ .file = f,
+ };
+ GSList *list;
+
+ list = object_class_get_list(TYPE_RISCV_CPU, false);
+ list = g_slist_sort(list, riscv_cpu_list_compare);
+ g_slist_foreach(list, riscv_cpu_list_entry, &s);
+ g_slist_free(list);
}
-type_init(riscv_cpu_register_types)
+#define DEFINE_CPU(type_name, initfn) \
+ { \
+ .name = type_name, \
+ .parent = TYPE_RISCV_CPU, \
+ .instance_init = initfn \
+ }
+
+static const TypeInfo riscv_cpu_type_infos[] = {
+ {
+ .name = TYPE_RISCV_CPU,
+ .parent = TYPE_CPU,
+ .instance_size = sizeof(RISCVCPU),
+ .instance_init = riscv_cpu_init,
+ .abstract = true,
+ .class_size = sizeof(RISCVCPUClass),
+ .class_init = riscv_cpu_class_init,
+ },
+ DEFINE_CPU(TYPE_RISCV_CPU_ANY, riscv_any_cpu_init),
+#if defined(TARGET_RISCV32)
+ DEFINE_CPU(TYPE_RISCV_CPU_RV32GCSU_V1_09_1, rv32gcsu_priv1_09_1_cpu_init),
+ DEFINE_CPU(TYPE_RISCV_CPU_RV32GCSU_V1_10_0, rv32gcsu_priv1_10_0_cpu_init),
+ DEFINE_CPU(TYPE_RISCV_CPU_RV32IMACU_NOMMU, rv32imacu_nommu_cpu_init),
+ DEFINE_CPU(TYPE_RISCV_CPU_SIFIVE_E31, rv32imacu_nommu_cpu_init),
+ DEFINE_CPU(TYPE_RISCV_CPU_SIFIVE_U34, rv32gcsu_priv1_10_0_cpu_init)
+#elif defined(TARGET_RISCV64)
+ DEFINE_CPU(TYPE_RISCV_CPU_RV64GCSU_V1_09_1, rv64gcsu_priv1_09_1_cpu_init),
+ DEFINE_CPU(TYPE_RISCV_CPU_RV64GCSU_V1_10_0, rv64gcsu_priv1_10_0_cpu_init),
+ DEFINE_CPU(TYPE_RISCV_CPU_RV64IMACU_NOMMU, rv64imacu_nommu_cpu_init),
+ DEFINE_CPU(TYPE_RISCV_CPU_SIFIVE_E51, rv64imacu_nommu_cpu_init),
+ DEFINE_CPU(TYPE_RISCV_CPU_SIFIVE_U54, rv64gcsu_priv1_10_0_cpu_init)
+#endif
+};
+
+DEFINE_TYPES(riscv_cpu_type_infos)
diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c
index e34715df4e..7c6068bac9 100644
--- a/target/riscv/op_helper.c
+++ b/target/riscv/op_helper.c
@@ -144,8 +144,21 @@ void csr_write_helper(CPURISCVState *env, target_ulong val_to_write,
}
mstatus = (mstatus & ~mask) | (val_to_write & mask);
- int dirty = (mstatus & MSTATUS_FS) == MSTATUS_FS;
- dirty |= (mstatus & MSTATUS_XS) == MSTATUS_XS;
+
+ /* Note: this is a workaround for an issue where mstatus.FS
+ does not report dirty after floating point operations
+ that modify floating point state. This workaround is
+ technically compliant with the RISC-V Privileged
+ specification as it is legal to return only off, or dirty.
+ at the expense of extra floating point save/restore. */
+
+ /* FP is always dirty or off */
+ if (mstatus & MSTATUS_FS) {
+ mstatus |= MSTATUS_FS;
+ }
+
+ int dirty = ((mstatus & MSTATUS_FS) == MSTATUS_FS) |
+ ((mstatus & MSTATUS_XS) == MSTATUS_XS);
mstatus = set_field(mstatus, MSTATUS_SD, dirty);
env->mstatus = mstatus;
break;
diff --git a/tcg/tcg-opc.h b/tcg/tcg-opc.h
index d81a6c4535..e3a43aabb6 100644
--- a/tcg/tcg-opc.h
+++ b/tcg/tcg-opc.h
@@ -182,8 +182,8 @@ DEF(add2_i64, 2, 4, 0, IMPL64 | IMPL(TCG_TARGET_HAS_add2_i64))
DEF(sub2_i64, 2, 4, 0, IMPL64 | IMPL(TCG_TARGET_HAS_sub2_i64))
DEF(mulu2_i64, 2, 2, 0, IMPL64 | IMPL(TCG_TARGET_HAS_mulu2_i64))
DEF(muls2_i64, 2, 2, 0, IMPL64 | IMPL(TCG_TARGET_HAS_muls2_i64))
-DEF(muluh_i64, 1, 2, 0, IMPL(TCG_TARGET_HAS_muluh_i64))
-DEF(mulsh_i64, 1, 2, 0, IMPL(TCG_TARGET_HAS_mulsh_i64))
+DEF(muluh_i64, 1, 2, 0, IMPL64 | IMPL(TCG_TARGET_HAS_muluh_i64))
+DEF(mulsh_i64, 1, 2, 0, IMPL64 | IMPL(TCG_TARGET_HAS_mulsh_i64))
#define TLADDR_ARGS (TARGET_LONG_BITS <= TCG_TARGET_REG_BITS ? 1 : 2)
#define DATA64_ARGS (TCG_TARGET_REG_BITS == 64 ? 1 : 2)
diff --git a/tests/Makefile.include b/tests/Makefile.include
index eb218a9539..3b9a5e31a2 100644
--- a/tests/Makefile.include
+++ b/tests/Makefile.include
@@ -523,6 +523,7 @@ qapi-schema += missing-comma-object.json
qapi-schema += missing-type.json
qapi-schema += nested-struct-data.json
qapi-schema += non-objects.json
+qapi-schema += oob-test.json
qapi-schema += pragma-doc-required-crap.json
qapi-schema += pragma-extra-junk.json
qapi-schema += pragma-name-case-whitelist-crap.json
diff --git a/tests/libqtest.c b/tests/libqtest.c
index 200b2b9e92..6f33a37667 100644
--- a/tests/libqtest.c
+++ b/tests/libqtest.c
@@ -166,7 +166,8 @@ static const char *qtest_qemu_binary(void)
return qemu_bin;
}
-QTestState *qtest_init_without_qmp_handshake(const char *extra_args)
+QTestState *qtest_init_without_qmp_handshake(bool use_oob,
+ const char *extra_args)
{
QTestState *s;
int sock, qmpsock, i;
@@ -199,12 +200,13 @@ QTestState *qtest_init_without_qmp_handshake(const char *extra_args)
command = g_strdup_printf("exec %s "
"-qtest unix:%s,nowait "
"-qtest-log %s "
- "-qmp unix:%s,nowait "
+ "-chardev socket,path=%s,nowait,id=char0 "
+ "-mon chardev=char0,mode=control%s "
"-machine accel=qtest "
"-display none "
"%s", qemu_binary, socket_path,
getenv("QTEST_LOG") ? "/dev/fd/2" : "/dev/null",
- qmp_socket_path,
+ qmp_socket_path, use_oob ? ",x-oob=on" : "",
extra_args ?: "");
execlp("/bin/sh", "sh", "-c", command, NULL);
exit(1);
@@ -239,7 +241,7 @@ QTestState *qtest_init_without_qmp_handshake(const char *extra_args)
QTestState *qtest_init(const char *extra_args)
{
- QTestState *s = qtest_init_without_qmp_handshake(extra_args);
+ QTestState *s = qtest_init_without_qmp_handshake(false, extra_args);
/* Read the QMP greeting and then do the handshake */
qtest_qmp_discard_response(s, "");
diff --git a/tests/libqtest.h b/tests/libqtest.h
index 811169453a..cbe8df4473 100644
--- a/tests/libqtest.h
+++ b/tests/libqtest.h
@@ -56,11 +56,14 @@ QTestState *qtest_init(const char *extra_args);
/**
* qtest_init_without_qmp_handshake:
- * @extra_args: other arguments to pass to QEMU.
+ * @use_oob: true to have the server advertise OOB support
+ * @extra_args: other arguments to pass to QEMU. CAUTION: these
+ * arguments are subject to word splitting and shell evaluation.
*
* Returns: #QTestState instance.
*/
-QTestState *qtest_init_without_qmp_handshake(const char *extra_args);
+QTestState *qtest_init_without_qmp_handshake(bool use_oob,
+ const char *extra_args);
/**
* qtest_quit:
diff --git a/tests/qapi-schema/doc-good.out b/tests/qapi-schema/doc-good.out
index 430b5a87db..63058b1590 100644
--- a/tests/qapi-schema/doc-good.out
+++ b/tests/qapi-schema/doc-good.out
@@ -28,9 +28,9 @@ object q_obj_cmd-arg
member arg2: str optional=True
member arg3: bool optional=False
command cmd q_obj_cmd-arg -> Object
- gen=True success_response=True boxed=False
+ gen=True success_response=True boxed=False oob=False
command cmd-boxed Object -> None
- gen=True success_response=True boxed=True
+ gen=True success_response=True boxed=True oob=False
doc freeform
body=
= Section
diff --git a/tests/qapi-schema/ident-with-escape.out b/tests/qapi-schema/ident-with-escape.out
index ee3b34e623..82213aa51d 100644
--- a/tests/qapi-schema/ident-with-escape.out
+++ b/tests/qapi-schema/ident-with-escape.out
@@ -5,4 +5,4 @@ module ident-with-escape.json
object q_obj_fooA-arg
member bar1: str optional=False
command fooA q_obj_fooA-arg -> None
- gen=True success_response=True boxed=False
+ gen=True success_response=True boxed=False oob=False
diff --git a/tests/qapi-schema/indented-expr.out b/tests/qapi-schema/indented-expr.out
index a79935e8c3..862678f8f4 100644
--- a/tests/qapi-schema/indented-expr.out
+++ b/tests/qapi-schema/indented-expr.out
@@ -3,6 +3,6 @@ enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool']
prefix QTYPE
module indented-expr.json
command eins None -> None
- gen=True success_response=True boxed=False
+ gen=True success_response=True boxed=False oob=False
command zwei None -> None
- gen=True success_response=True boxed=False
+ gen=True success_response=True boxed=False oob=False
diff --git a/tests/qapi-schema/oob-test.err b/tests/qapi-schema/oob-test.err
new file mode 100644
index 0000000000..35b60f7480
--- /dev/null
+++ b/tests/qapi-schema/oob-test.err
@@ -0,0 +1 @@
+tests/qapi-schema/oob-test.json:2: 'allow-oob' of command 'oob-command-1' should only use true value
diff --git a/tests/qapi-schema/oob-test.exit b/tests/qapi-schema/oob-test.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/oob-test.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/oob-test.json b/tests/qapi-schema/oob-test.json
new file mode 100644
index 0000000000..da9635920f
--- /dev/null
+++ b/tests/qapi-schema/oob-test.json
@@ -0,0 +1,2 @@
+# Check against oob illegal value
+{ 'command': 'oob-command-1', 'allow-oob': 'some-string' }
diff --git a/tests/qapi-schema/oob-test.out b/tests/qapi-schema/oob-test.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/oob-test.out
diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json
index c72dbd8050..06e30f452e 100644
--- a/tests/qapi-schema/qapi-schema-test.json
+++ b/tests/qapi-schema/qapi-schema-test.json
@@ -139,6 +139,9 @@
{ 'command': 'boxed-struct', 'boxed': true, 'data': 'UserDefZero' }
{ 'command': 'boxed-union', 'data': 'UserDefNativeListUnion', 'boxed': true }
+# Smoke test on Out-Of-Band
+{ 'command': 'an-oob-command', 'allow-oob': true }
+
# For testing integer range flattening in opts-visitor. The following schema
# corresponds to the option format:
#
diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out
index 012e7fc06a..467577d770 100644
--- a/tests/qapi-schema/qapi-schema-test.out
+++ b/tests/qapi-schema/qapi-schema-test.out
@@ -16,7 +16,7 @@ object Empty1
object Empty2
base Empty1
command user_def_cmd0 Empty2 -> Empty2
- gen=True success_response=True boxed=False
+ gen=True success_response=True boxed=False oob=False
enum QEnumTwo ['value1', 'value2']
prefix QENUM_TWO
object UserDefOne
@@ -143,29 +143,31 @@ object UserDefNativeListUnion
case sizes: q_obj_sizeList-wrapper
case any: q_obj_anyList-wrapper
command user_def_cmd None -> None
- gen=True success_response=True boxed=False
+ gen=True success_response=True boxed=False oob=False
object q_obj_user_def_cmd1-arg
member ud1a: UserDefOne optional=False
command user_def_cmd1 q_obj_user_def_cmd1-arg -> None
- gen=True success_response=True boxed=False
+ gen=True success_response=True boxed=False oob=False
object q_obj_user_def_cmd2-arg
member ud1a: UserDefOne optional=False
member ud1b: UserDefOne optional=True
command user_def_cmd2 q_obj_user_def_cmd2-arg -> UserDefTwo
- gen=True success_response=True boxed=False
+ gen=True success_response=True boxed=False oob=False
object q_obj_guest-get-time-arg
member a: int optional=False
member b: int optional=True
command guest-get-time q_obj_guest-get-time-arg -> int
- gen=True success_response=True boxed=False
+ gen=True success_response=True boxed=False oob=False
object q_obj_guest-sync-arg
member arg: any optional=False
command guest-sync q_obj_guest-sync-arg -> any
- gen=True success_response=True boxed=False
+ gen=True success_response=True boxed=False oob=False
command boxed-struct UserDefZero -> None
- gen=True success_response=True boxed=True
+ gen=True success_response=True boxed=True oob=False
command boxed-union UserDefNativeListUnion -> None
- gen=True success_response=True boxed=True
+ gen=True success_response=True boxed=True oob=False
+command an-oob-command None -> None
+ gen=True success_response=True boxed=False oob=True
object UserDefOptions
member i64: intList optional=True
member u64: uint64List optional=True
@@ -229,4 +231,4 @@ object q_obj___org.qemu_x-command-arg
member c: __org.qemu_x-Union2 optional=False
member d: __org.qemu_x-Alt optional=False
command __org.qemu_x-command q_obj___org.qemu_x-command-arg -> __org.qemu_x-Union1
- gen=True success_response=True boxed=False
+ gen=True success_response=True boxed=False oob=False
diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py
index 10e68b01d9..c1a144ba29 100644
--- a/tests/qapi-schema/test-qapi.py
+++ b/tests/qapi-schema/test-qapi.py
@@ -45,8 +45,8 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor):
gen, success_response, boxed, allow_oob):
print('command %s %s -> %s' % \
(name, arg_type and arg_type.name, ret_type and ret_type.name))
- print(' gen=%s success_response=%s boxed=%s' % \
- (gen, success_response, boxed))
+ print(' gen=%s success_response=%s boxed=%s oob=%s' % \
+ (gen, success_response, boxed, allow_oob))
def visit_event(self, name, info, arg_type, boxed):
print('event %s %s' % (name, arg_type and arg_type.name))
diff --git a/tests/qmp-test.c b/tests/qmp-test.c
index 558e83540c..772058fc4c 100644
--- a/tests/qmp-test.c
+++ b/tests/qmp-test.c
@@ -81,7 +81,7 @@ static void test_qmp_protocol(void)
QList *capabilities;
QTestState *qts;
- qts = qtest_init_without_qmp_handshake(common_args);
+ qts = qtest_init_without_qmp_handshake(false, common_args);
/* Test greeting */
resp = qtest_qmp_receive(qts);
@@ -90,6 +90,7 @@ static void test_qmp_protocol(void)
test_version(qdict_get(q, "version"));
capabilities = qdict_get_qlist(q, "capabilities");
g_assert(capabilities && qlist_empty(capabilities));
+ QDECREF(resp);
/* Test valid command before handshake */
resp = qtest_qmp(qts, "{ 'execute': 'query-version' }");
@@ -134,6 +135,87 @@ static void test_qmp_protocol(void)
qtest_quit(qts);
}
+/* Tests for Out-Of-Band support. */
+static void test_qmp_oob(void)
+{
+ QTestState *qts;
+ QDict *resp, *q;
+ int acks = 0;
+ const QListEntry *entry;
+ QList *capabilities;
+ QString *qstr;
+ const char *cmd_id;
+
+ qts = qtest_init_without_qmp_handshake(true, common_args);
+
+ /* Check the greeting message. */
+ resp = qtest_qmp_receive(qts);
+ q = qdict_get_qdict(resp, "QMP");
+ g_assert(q);
+ capabilities = qdict_get_qlist(q, "capabilities");
+ g_assert(capabilities && !qlist_empty(capabilities));
+ entry = qlist_first(capabilities);
+ g_assert(entry);
+ qstr = qobject_to(QString, entry->value);
+ g_assert(qstr);
+ g_assert_cmpstr(qstring_get_str(qstr), ==, "oob");
+ QDECREF(resp);
+
+ /* Try a fake capability, it should fail. */
+ resp = qtest_qmp(qts,
+ "{ 'execute': 'qmp_capabilities', "
+ " 'arguments': { 'enable': [ 'cap-does-not-exist' ] } }");
+ g_assert(qdict_haskey(resp, "error"));
+ QDECREF(resp);
+
+ /* Now, enable OOB in current QMP session, it should succeed. */
+ resp = qtest_qmp(qts,
+ "{ 'execute': 'qmp_capabilities', "
+ " 'arguments': { 'enable': [ 'oob' ] } }");
+ g_assert(qdict_haskey(resp, "return"));
+ QDECREF(resp);
+
+ /*
+ * Try any command that does not support OOB but with OOB flag. We
+ * should get failure.
+ */
+ resp = qtest_qmp(qts,
+ "{ 'execute': 'query-cpus',"
+ " 'control': { 'run-oob': true } }");
+ g_assert(qdict_haskey(resp, "error"));
+ QDECREF(resp);
+
+ /*
+ * First send the "x-oob-test" command with lock=true and
+ * oob=false, it should hang the dispatcher and main thread;
+ * later, we send another lock=false with oob=true to continue
+ * that thread processing. Finally we should receive replies from
+ * both commands.
+ */
+ qtest_async_qmp(qts,
+ "{ 'execute': 'x-oob-test',"
+ " 'arguments': { 'lock': true }, "
+ " 'id': 'lock-cmd'}");
+ qtest_async_qmp(qts,
+ "{ 'execute': 'x-oob-test', "
+ " 'arguments': { 'lock': false }, "
+ " 'control': { 'run-oob': true }, "
+ " 'id': 'unlock-cmd' }");
+
+ /* Ignore all events. Wait for 2 acks */
+ while (acks < 2) {
+ resp = qtest_qmp_receive(qts);
+ cmd_id = qdict_get_str(resp, "id");
+ if (!g_strcmp0(cmd_id, "lock-cmd") ||
+ !g_strcmp0(cmd_id, "unlock-cmd")) {
+ acks++;
+ }
+ QDECREF(resp);
+ }
+
+ qtest_quit(qts);
+}
+
static int query_error_class(const char *cmd)
{
static struct {
@@ -318,6 +400,7 @@ int main(int argc, char *argv[])
g_test_init(&argc, &argv, NULL);
qtest_add_func("qmp/protocol", test_qmp_protocol);
+ qtest_add_func("qmp/oob", test_qmp_oob);
qmp_schema_init(&schema);
add_query_tests(&schema);
diff --git a/tests/test-aio.c b/tests/test-aio.c
index 54e20d6ab1..86fb73b3d5 100644
--- a/tests/test-aio.c
+++ b/tests/test-aio.c
@@ -16,6 +16,8 @@
#include "qemu/timer.h"
#include "qemu/sockets.h"
#include "qemu/error-report.h"
+#include "qemu/coroutine.h"
+#include "qemu/main-loop.h"
static AioContext *ctx;
@@ -827,24 +829,59 @@ static void test_source_timer_schedule(void)
timer_del(&data.timer);
}
+/*
+ * Check that aio_co_enter() can chain many times
+ *
+ * Two coroutines should be able to invoke each other via aio_co_enter() many
+ * times without hitting a limit like stack exhaustion. In other words, the
+ * calls should be chained instead of nested.
+ */
-/* End of tests. */
+typedef struct {
+ Coroutine *other;
+ unsigned i;
+ unsigned max;
+} ChainData;
-int main(int argc, char **argv)
+static void coroutine_fn chain(void *opaque)
{
- Error *local_error = NULL;
- GSource *src;
+ ChainData *data = opaque;
- init_clocks(NULL);
+ for (data->i = 0; data->i < data->max; data->i++) {
+ /* Queue up the other coroutine... */
+ aio_co_enter(ctx, data->other);
- ctx = aio_context_new(&local_error);
- if (!ctx) {
- error_reportf_err(local_error, "Failed to create AIO Context: ");
- exit(1);
+ /* ...and give control to it */
+ qemu_coroutine_yield();
}
- src = aio_get_g_source(ctx);
- g_source_attach(src, NULL);
- g_source_unref(src);
+}
+
+static void test_queue_chaining(void)
+{
+ /* This number of iterations hit stack exhaustion in the past: */
+ ChainData data_a = { .max = 25000 };
+ ChainData data_b = { .max = 25000 };
+
+ data_b.other = qemu_coroutine_create(chain, &data_a);
+ data_a.other = qemu_coroutine_create(chain, &data_b);
+
+ qemu_coroutine_enter(data_b.other);
+
+ g_assert_cmpint(data_a.i, ==, data_a.max);
+ g_assert_cmpint(data_b.i, ==, data_b.max - 1);
+
+ /* Allow the second coroutine to terminate */
+ qemu_coroutine_enter(data_a.other);
+
+ g_assert_cmpint(data_b.i, ==, data_b.max);
+}
+
+/* End of tests. */
+
+int main(int argc, char **argv)
+{
+ qemu_init_main_loop(&error_fatal);
+ ctx = qemu_get_aio_context();
while (g_main_context_iteration(NULL, false));
@@ -864,6 +901,8 @@ int main(int argc, char **argv)
g_test_add_func("/aio/external-client", test_aio_external_client);
g_test_add_func("/aio/timer/schedule", test_timer_schedule);
+ g_test_add_func("/aio/coroutine/queue-chaining", test_queue_chaining);
+
g_test_add_func("/aio-gsource/flush", test_source_flush);
g_test_add_func("/aio-gsource/bh/schedule", test_source_bh_schedule);
g_test_add_func("/aio-gsource/bh/schedule10", test_source_bh_schedule10);
diff --git a/tests/test-qmp-cmds.c b/tests/test-qmp-cmds.c
index 93fbbb1b73..db690cc5ae 100644
--- a/tests/test-qmp-cmds.c
+++ b/tests/test-qmp-cmds.c
@@ -16,6 +16,10 @@ void qmp_user_def_cmd(Error **errp)
{
}
+void qmp_an_oob_command(Error **errp)
+{
+}
+
Empty2 *qmp_user_def_cmd0(Error **errp)
{
return g_new0(Empty2, 1);
diff --git a/tests/test-visitor-serialization.c b/tests/test-visitor-serialization.c
index 438c18a0d6..d18d90db2c 100644
--- a/tests/test-visitor-serialization.c
+++ b/tests/test-visitor-serialization.c
@@ -1115,7 +1115,7 @@ static const SerializeOps visitors[] = {
static void add_visitor_type(const SerializeOps *ops)
{
- char testname_prefix[128];
+ char testname_prefix[32];
char testname[128];
TestArgs *args;
int i = 0;
diff --git a/tests/tpm-crb-test.c b/tests/tpm-crb-test.c
index e1513cb69e..d8f9569203 100644
--- a/tests/tpm-crb-test.c
+++ b/tests/tpm-crb-test.c
@@ -28,6 +28,10 @@ static void tpm_crb_test(const void *data)
uint64_t caddr = readq(TPM_CRB_ADDR_BASE + A_CRB_CTRL_CMD_LADDR);
uint32_t rsize = readl(TPM_CRB_ADDR_BASE + A_CRB_CTRL_RSP_SIZE);
uint64_t raddr = readq(TPM_CRB_ADDR_BASE + A_CRB_CTRL_RSP_ADDR);
+ uint8_t locstate = readb(TPM_CRB_ADDR_BASE + A_CRB_LOC_STATE);
+ uint32_t locctrl = readl(TPM_CRB_ADDR_BASE + A_CRB_LOC_CTRL);
+ uint32_t locsts = readl(TPM_CRB_ADDR_BASE + A_CRB_LOC_STS);
+ uint32_t sts = readl(TPM_CRB_ADDR_BASE + A_CRB_CTRL_STS);
g_assert_cmpint(FIELD_EX32(intfid, CRB_INTF_ID, InterfaceType), ==, 1);
g_assert_cmpint(FIELD_EX32(intfid, CRB_INTF_ID, InterfaceVersion), ==, 1);
@@ -45,9 +49,47 @@ static void tpm_crb_test(const void *data)
g_assert_cmpint(caddr, >, TPM_CRB_ADDR_BASE);
g_assert_cmpint(raddr, >, TPM_CRB_ADDR_BASE);
+ g_assert_cmpint(FIELD_EX32(locstate, CRB_LOC_STATE, tpmEstablished), ==, 1);
+ g_assert_cmpint(FIELD_EX32(locstate, CRB_LOC_STATE, locAssigned), ==, 0);
+ g_assert_cmpint(FIELD_EX32(locstate, CRB_LOC_STATE, activeLocality), ==, 0);
+ g_assert_cmpint(FIELD_EX32(locstate, CRB_LOC_STATE, reserved), ==, 0);
+ g_assert_cmpint(FIELD_EX32(locstate, CRB_LOC_STATE, tpmRegValidSts), ==, 1);
+
+ g_assert_cmpint(locctrl, ==, 0);
+
+ g_assert_cmpint(FIELD_EX32(locsts, CRB_LOC_STS, Granted), ==, 0);
+ g_assert_cmpint(FIELD_EX32(locsts, CRB_LOC_STS, beenSeized), ==, 0);
+
+ g_assert_cmpint(FIELD_EX32(sts, CRB_CTRL_STS, tpmIdle), ==, 1);
+ g_assert_cmpint(FIELD_EX32(sts, CRB_CTRL_STS, tpmSts), ==, 0);
+
+ /* request access to locality 0 */
+ writeb(TPM_CRB_ADDR_BASE + A_CRB_LOC_CTRL, 1);
+
+ /* granted bit must be set now */
+ locsts = readl(TPM_CRB_ADDR_BASE + A_CRB_LOC_STS);
+ g_assert_cmpint(FIELD_EX32(locsts, CRB_LOC_STS, Granted), ==, 1);
+ g_assert_cmpint(FIELD_EX32(locsts, CRB_LOC_STS, beenSeized), ==, 0);
+
+ /* we must have an assigned locality */
+ locstate = readb(TPM_CRB_ADDR_BASE + A_CRB_LOC_STATE);
+ g_assert_cmpint(FIELD_EX32(locstate, CRB_LOC_STATE, tpmEstablished), ==, 1);
+ g_assert_cmpint(FIELD_EX32(locstate, CRB_LOC_STATE, locAssigned), ==, 1);
+ g_assert_cmpint(FIELD_EX32(locstate, CRB_LOC_STATE, activeLocality), ==, 0);
+ g_assert_cmpint(FIELD_EX32(locstate, CRB_LOC_STATE, reserved), ==, 0);
+ g_assert_cmpint(FIELD_EX32(locstate, CRB_LOC_STATE, tpmRegValidSts), ==, 1);
+
+ /* set into ready state */
+ writel(TPM_CRB_ADDR_BASE + A_CRB_CTRL_REQ, 1);
+
+ /* TPM must not be in the idle state */
+ sts = readl(TPM_CRB_ADDR_BASE + A_CRB_CTRL_STS);
+ g_assert_cmpint(FIELD_EX32(sts, CRB_CTRL_STS, tpmIdle), ==, 0);
+ g_assert_cmpint(FIELD_EX32(sts, CRB_CTRL_STS, tpmSts), ==, 0);
+
memwrite(caddr, TPM_CMD, sizeof(TPM_CMD));
- uint32_t sts, start = 1;
+ uint32_t start = 1;
uint64_t end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND;
writel(TPM_CRB_ADDR_BASE + A_CRB_CTRL_START, start);
do {
@@ -58,12 +100,40 @@ static void tpm_crb_test(const void *data)
} while (g_get_monotonic_time() < end_time);
start = readl(TPM_CRB_ADDR_BASE + A_CRB_CTRL_START);
g_assert_cmpint(start & 1, ==, 0);
+
+ /* TPM must still not be in the idle state */
sts = readl(TPM_CRB_ADDR_BASE + A_CRB_CTRL_STS);
- g_assert_cmpint(sts & 1, ==, 0);
+ g_assert_cmpint(FIELD_EX32(sts, CRB_CTRL_STS, tpmIdle), ==, 0);
+ g_assert_cmpint(FIELD_EX32(sts, CRB_CTRL_STS, tpmSts), ==, 0);
struct tpm_hdr tpm_msg;
memread(raddr, &tpm_msg, sizeof(tpm_msg));
g_assert_cmpmem(&tpm_msg, sizeof(tpm_msg), s->tpm_msg, sizeof(*s->tpm_msg));
+
+ /* set TPM into idle state */
+ writel(TPM_CRB_ADDR_BASE + A_CRB_CTRL_REQ, 2);
+
+ /* idle state must be indicated now */
+ sts = readl(TPM_CRB_ADDR_BASE + A_CRB_CTRL_STS);
+ g_assert_cmpint(FIELD_EX32(sts, CRB_CTRL_STS, tpmIdle), ==, 1);
+ g_assert_cmpint(FIELD_EX32(sts, CRB_CTRL_STS, tpmSts), ==, 0);
+
+ /* relinquish locality */
+ writel(TPM_CRB_ADDR_BASE + A_CRB_LOC_CTRL, 2);
+
+ /* Granted flag must be cleared */
+ sts = readl(TPM_CRB_ADDR_BASE + A_CRB_LOC_STS);
+ g_assert_cmpint(FIELD_EX32(sts, CRB_LOC_STS, Granted), ==, 0);
+ g_assert_cmpint(FIELD_EX32(sts, CRB_LOC_STS, beenSeized), ==, 0);
+
+ /* no locality may be assigned */
+ locstate = readb(TPM_CRB_ADDR_BASE + A_CRB_LOC_STATE);
+ g_assert_cmpint(FIELD_EX32(locstate, CRB_LOC_STATE, tpmEstablished), ==, 1);
+ g_assert_cmpint(FIELD_EX32(locstate, CRB_LOC_STATE, locAssigned), ==, 0);
+ g_assert_cmpint(FIELD_EX32(locstate, CRB_LOC_STATE, activeLocality), ==, 0);
+ g_assert_cmpint(FIELD_EX32(locstate, CRB_LOC_STATE, reserved), ==, 0);
+ g_assert_cmpint(FIELD_EX32(locstate, CRB_LOC_STATE, tpmRegValidSts), ==, 1);
+
}
int main(int argc, char **argv)
diff --git a/util/qemu-coroutine-lock.c b/util/qemu-coroutine-lock.c
index 5a80c10690..27438a1858 100644
--- a/util/qemu-coroutine-lock.c
+++ b/util/qemu-coroutine-lock.c
@@ -68,40 +68,6 @@ void coroutine_fn qemu_co_queue_wait_impl(CoQueue *queue, QemuLockable *lock)
}
}
-/**
- * qemu_co_queue_run_restart:
- *
- * Enter each coroutine that was previously marked for restart by
- * qemu_co_queue_next() or qemu_co_queue_restart_all(). This function is
- * invoked by the core coroutine code when the current coroutine yields or
- * terminates.
- */
-void qemu_co_queue_run_restart(Coroutine *co)
-{
- Coroutine *next;
- QSIMPLEQ_HEAD(, Coroutine) tmp_queue_wakeup =
- QSIMPLEQ_HEAD_INITIALIZER(tmp_queue_wakeup);
-
- trace_qemu_co_queue_run_restart(co);
-
- /* Because "co" has yielded, any coroutine that we wakeup can resume it.
- * If this happens and "co" terminates, co->co_queue_wakeup becomes
- * invalid memory. Therefore, use a temporary queue and do not touch
- * the "co" coroutine as soon as you enter another one.
- *
- * In its turn resumed "co" can populate "co_queue_wakeup" queue with
- * new coroutines to be woken up. The caller, who has resumed "co",
- * will be responsible for traversing the same queue, which may cause
- * a different wakeup order but not any missing wakeups.
- */
- QSIMPLEQ_CONCAT(&tmp_queue_wakeup, &co->co_queue_wakeup);
-
- while ((next = QSIMPLEQ_FIRST(&tmp_queue_wakeup))) {
- QSIMPLEQ_REMOVE_HEAD(&tmp_queue_wakeup, co_queue_next);
- qemu_coroutine_enter(next);
- }
-}
-
static bool qemu_co_queue_do_restart(CoQueue *queue, bool single)
{
Coroutine *next;
diff --git a/util/qemu-coroutine.c b/util/qemu-coroutine.c
index 9eff7fd450..1ba4191b84 100644
--- a/util/qemu-coroutine.c
+++ b/util/qemu-coroutine.c
@@ -104,57 +104,65 @@ static void coroutine_delete(Coroutine *co)
void qemu_aio_coroutine_enter(AioContext *ctx, Coroutine *co)
{
- Coroutine *self = qemu_coroutine_self();
- CoroutineAction ret;
-
- /* Cannot rely on the read barrier for co in aio_co_wake(), as there are
- * callers outside of aio_co_wake() */
- const char *scheduled = atomic_mb_read(&co->scheduled);
+ QSIMPLEQ_HEAD(, Coroutine) pending = QSIMPLEQ_HEAD_INITIALIZER(pending);
+ Coroutine *from = qemu_coroutine_self();
- trace_qemu_aio_coroutine_enter(ctx, self, co, co->entry_arg);
+ QSIMPLEQ_INSERT_TAIL(&pending, co, co_queue_next);
- /* if the Coroutine has already been scheduled, entering it again will
- * cause us to enter it twice, potentially even after the coroutine has
- * been deleted */
- if (scheduled) {
- fprintf(stderr,
- "%s: Co-routine was already scheduled in '%s'\n",
- __func__, scheduled);
- abort();
- }
+ /* Run co and any queued coroutines */
+ while (!QSIMPLEQ_EMPTY(&pending)) {
+ Coroutine *to = QSIMPLEQ_FIRST(&pending);
+ CoroutineAction ret;
- if (co->caller) {
- fprintf(stderr, "Co-routine re-entered recursively\n");
- abort();
- }
+ /* Cannot rely on the read barrier for to in aio_co_wake(), as there are
+ * callers outside of aio_co_wake() */
+ const char *scheduled = atomic_mb_read(&to->scheduled);
- co->caller = self;
- co->ctx = ctx;
+ QSIMPLEQ_REMOVE_HEAD(&pending, co_queue_next);
- /* Store co->ctx before anything that stores co. Matches
- * barrier in aio_co_wake and qemu_co_mutex_wake.
- */
- smp_wmb();
+ trace_qemu_aio_coroutine_enter(ctx, from, to, to->entry_arg);
- ret = qemu_coroutine_switch(self, co, COROUTINE_ENTER);
-
- qemu_co_queue_run_restart(co);
+ /* if the Coroutine has already been scheduled, entering it again will
+ * cause us to enter it twice, potentially even after the coroutine has
+ * been deleted */
+ if (scheduled) {
+ fprintf(stderr,
+ "%s: Co-routine was already scheduled in '%s'\n",
+ __func__, scheduled);
+ abort();
+ }
- /* Beware, if ret == COROUTINE_YIELD and qemu_co_queue_run_restart()
- * has started any other coroutine, "co" might have been reentered
- * and even freed by now! So be careful and do not touch it.
- */
+ if (to->caller) {
+ fprintf(stderr, "Co-routine re-entered recursively\n");
+ abort();
+ }
- switch (ret) {
- case COROUTINE_YIELD:
- return;
- case COROUTINE_TERMINATE:
- assert(!co->locks_held);
- trace_qemu_coroutine_terminate(co);
- coroutine_delete(co);
- return;
- default:
- abort();
+ to->caller = from;
+ to->ctx = ctx;
+
+ /* Store to->ctx before anything that stores to. Matches
+ * barrier in aio_co_wake and qemu_co_mutex_wake.
+ */
+ smp_wmb();
+
+ ret = qemu_coroutine_switch(from, to, COROUTINE_ENTER);
+
+ /* Queued coroutines are run depth-first; previously pending coroutines
+ * run after those queued more recently.
+ */
+ QSIMPLEQ_PREPEND(&pending, &to->co_queue_wakeup);
+
+ switch (ret) {
+ case COROUTINE_YIELD:
+ break;
+ case COROUTINE_TERMINATE:
+ assert(!to->locks_held);
+ trace_qemu_coroutine_terminate(to);
+ coroutine_delete(to);
+ break;
+ default:
+ abort();
+ }
}
}
diff --git a/vl.c b/vl.c
index c81cc86607..fce1fd12d8 100644
--- a/vl.c
+++ b/vl.c
@@ -2404,6 +2404,11 @@ static int mon_init_func(void *opaque, QemuOpts *opts, Error **errp)
if (qemu_opt_get_bool(opts, "pretty", 0))
flags |= MONITOR_USE_PRETTY;
+ /* OOB is off by default */
+ if (qemu_opt_get_bool(opts, "x-oob", 0)) {
+ flags |= MONITOR_USE_OOB;
+ }
+
chardev = qemu_opt_get(opts, "chardev");
chr = qemu_chr_find(chardev);
if (chr == NULL) {
@@ -4728,6 +4733,8 @@ int main(int argc, char **argv, char **envp)
main_loop();
+ gdbserver_cleanup();
+
/* No more vcpu or device emulation activity beyond this point */
vm_shutdown();