aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2021-06-09 16:40:21 +0100
committerPeter Maydell <peter.maydell@linaro.org>2021-06-09 16:40:21 +0100
commit7fe7fae8b48e3f9c647fd685e5155ebc8e6fb84d (patch)
tree1610dad29b044c9e4fbedaa14faab8421d5c50b3
parenta4716fd8d7c877185652f5f8e25032dc7699d51b (diff)
parenta4a571d97866d056787d7a654be5792765be8a60 (diff)
downloadqemu-7fe7fae8b48e3f9c647fd685e5155ebc8e6fb84d.zip
qemu-7fe7fae8b48e3f9c647fd685e5155ebc8e6fb84d.tar.gz
qemu-7fe7fae8b48e3f9c647fd685e5155ebc8e6fb84d.tar.bz2
Merge remote-tracking branch 'remotes/dgilbert-gitlab/tags/pull-migration-20210609a' into staging
Migration pull for 2021-06-09 Yank crash fix from Leo RDMA fix from Li mptcp support from me dirty-rate changes from Hyman and Peter (Note I've switched to the gitlab I've been using for virtiofs pulls) Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com> # gpg: Signature made Wed 09 Jun 2021 15:40:01 BST # gpg: using RSA key 45F5C71B4A0CB7FB977A9FA90516331EBC5BFDE7 # gpg: Good signature from "Dr. David Alan Gilbert (RH2) <dgilbert@redhat.com>" [full] # Primary key fingerprint: 45F5 C71B 4A0C B7FB 977A 9FA9 0516 331E BC5B FDE7 * remotes/dgilbert-gitlab/tags/pull-migration-20210609a: hmp: Add "calc_dirty_rate" and "info dirty_rate" cmds migration/dirtyrate: make sample page count configurable sockets: Support multipath TCP migration/socket: Close the listener at the end migration: Add cleanup hook for inwards migration io/net-listener: Call the notifier during finalize channel-socket: Only set CLOEXEC if we have space for fds migration/rdma: Fix cm event use after free yank: Unregister function when using TLS migration Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
-rw-r--r--hmp-commands-info.hx13
-rw-r--r--hmp-commands.hx14
-rw-r--r--include/monitor/hmp.h2
-rw-r--r--io/channel-socket.c8
-rw-r--r--io/dns-resolver.c4
-rw-r--r--io/net-listener.c3
-rw-r--r--migration/channel.c26
-rw-r--r--migration/dirtyrate.c78
-rw-r--r--migration/dirtyrate.h8
-rw-r--r--migration/migration.c3
-rw-r--r--migration/migration.h4
-rw-r--r--migration/multifd.c8
-rw-r--r--migration/qemu-file-channel.c4
-rw-r--r--migration/rdma.c11
-rw-r--r--migration/socket.c24
-rw-r--r--qapi/migration.json15
-rw-r--r--qapi/sockets.json5
-rw-r--r--util/qemu-sockets.c23
18 files changed, 217 insertions, 36 deletions
diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx
index b2347a6..fb59c27 100644
--- a/hmp-commands-info.hx
+++ b/hmp-commands-info.hx
@@ -867,3 +867,16 @@ SRST
``info replay``
Display the record/replay information: mode and the current icount.
ERST
+
+ {
+ .name = "dirty_rate",
+ .args_type = "",
+ .params = "",
+ .help = "show dirty rate information",
+ .cmd = hmp_info_dirty_rate,
+ },
+
+SRST
+ ``info dirty_rate``
+ Display the vcpu dirty rate information.
+ERST
diff --git a/hmp-commands.hx b/hmp-commands.hx
index 2d21fe5..84dcc3a 100644
--- a/hmp-commands.hx
+++ b/hmp-commands.hx
@@ -1727,3 +1727,17 @@ ERST
.flags = "p",
},
+SRST
+``calc_dirty_rate`` *second*
+ Start a round of dirty rate measurement with the period specified in *second*.
+ The result of the dirty rate measurement may be observed with ``info
+ dirty_rate`` command.
+ERST
+
+ {
+ .name = "calc_dirty_rate",
+ .args_type = "second:l,sample_pages_per_GB:l?",
+ .params = "second [sample_pages_per_GB]",
+ .help = "start a round of guest dirty rate measurement",
+ .cmd = hmp_calc_dirty_rate,
+ },
diff --git a/include/monitor/hmp.h b/include/monitor/hmp.h
index 605d572..3baa105 100644
--- a/include/monitor/hmp.h
+++ b/include/monitor/hmp.h
@@ -129,5 +129,7 @@ void hmp_info_replay(Monitor *mon, const QDict *qdict);
void hmp_replay_break(Monitor *mon, const QDict *qdict);
void hmp_replay_delete_break(Monitor *mon, const QDict *qdict);
void hmp_replay_seek(Monitor *mon, const QDict *qdict);
+void hmp_info_dirty_rate(Monitor *mon, const QDict *qdict);
+void hmp_calc_dirty_rate(Monitor *mon, const QDict *qdict);
#endif
diff --git a/io/channel-socket.c b/io/channel-socket.c
index de259f7..606ec97 100644
--- a/io/channel-socket.c
+++ b/io/channel-socket.c
@@ -487,15 +487,15 @@ static ssize_t qio_channel_socket_readv(QIOChannel *ioc,
memset(control, 0, CMSG_SPACE(sizeof(int) * SOCKET_MAX_FDS));
-#ifdef MSG_CMSG_CLOEXEC
- sflags |= MSG_CMSG_CLOEXEC;
-#endif
-
msg.msg_iov = (struct iovec *)iov;
msg.msg_iovlen = niov;
if (fds && nfds) {
msg.msg_control = control;
msg.msg_controllen = sizeof(control);
+#ifdef MSG_CMSG_CLOEXEC
+ sflags |= MSG_CMSG_CLOEXEC;
+#endif
+
}
retry:
diff --git a/io/dns-resolver.c b/io/dns-resolver.c
index 743a0ef..a5946a9 100644
--- a/io/dns-resolver.c
+++ b/io/dns-resolver.c
@@ -122,6 +122,10 @@ static int qio_dns_resolver_lookup_sync_inet(QIODNSResolver *resolver,
.ipv4 = iaddr->ipv4,
.has_ipv6 = iaddr->has_ipv6,
.ipv6 = iaddr->ipv6,
+#ifdef IPPROTO_MPTCP
+ .has_mptcp = iaddr->has_mptcp,
+ .mptcp = iaddr->mptcp,
+#endif
};
(*addrs)[i] = newaddr;
diff --git a/io/net-listener.c b/io/net-listener.c
index 46c2643..1c984d6 100644
--- a/io/net-listener.c
+++ b/io/net-listener.c
@@ -292,6 +292,9 @@ static void qio_net_listener_finalize(Object *obj)
QIONetListener *listener = QIO_NET_LISTENER(obj);
size_t i;
+ if (listener->io_notify) {
+ listener->io_notify(listener->io_data);
+ }
qio_net_listener_disconnect(listener);
for (i = 0; i < listener->nsioc; i++) {
diff --git a/migration/channel.c b/migration/channel.c
index c9ee902..01275a9 100644
--- a/migration/channel.c
+++ b/migration/channel.c
@@ -38,18 +38,19 @@ void migration_channel_process_incoming(QIOChannel *ioc)
trace_migration_set_incoming_channel(
ioc, object_get_typename(OBJECT(ioc)));
- if (object_dynamic_cast(OBJECT(ioc), TYPE_QIO_CHANNEL_SOCKET)) {
- yank_register_function(MIGRATION_YANK_INSTANCE,
- migration_yank_iochannel,
- QIO_CHANNEL(ioc));
- }
-
if (s->parameters.tls_creds &&
*s->parameters.tls_creds &&
!object_dynamic_cast(OBJECT(ioc),
TYPE_QIO_CHANNEL_TLS)) {
migration_tls_channel_process_incoming(s, ioc, &local_err);
} else {
+ if (object_dynamic_cast(OBJECT(ioc), TYPE_QIO_CHANNEL_SOCKET) ||
+ object_dynamic_cast(OBJECT(ioc), TYPE_QIO_CHANNEL_TLS)) {
+ yank_register_function(MIGRATION_YANK_INSTANCE,
+ migration_yank_iochannel,
+ QIO_CHANNEL(ioc));
+ }
+
migration_ioc_process_incoming(ioc, &local_err);
}
@@ -76,12 +77,6 @@ void migration_channel_connect(MigrationState *s,
ioc, object_get_typename(OBJECT(ioc)), hostname, error);
if (!error) {
- if (object_dynamic_cast(OBJECT(ioc), TYPE_QIO_CHANNEL_SOCKET)) {
- yank_register_function(MIGRATION_YANK_INSTANCE,
- migration_yank_iochannel,
- QIO_CHANNEL(ioc));
- }
-
if (s->parameters.tls_creds &&
*s->parameters.tls_creds &&
!object_dynamic_cast(OBJECT(ioc),
@@ -99,6 +94,13 @@ void migration_channel_connect(MigrationState *s,
} else {
QEMUFile *f = qemu_fopen_channel_output(ioc);
+ if (object_dynamic_cast(OBJECT(ioc), TYPE_QIO_CHANNEL_SOCKET) ||
+ object_dynamic_cast(OBJECT(ioc), TYPE_QIO_CHANNEL_TLS)) {
+ yank_register_function(MIGRATION_YANK_INSTANCE,
+ migration_yank_iochannel,
+ QIO_CHANNEL(ioc));
+ }
+
qemu_mutex_lock(&s->qemu_file_lock);
s->to_dst_file = f;
qemu_mutex_unlock(&s->qemu_file_lock);
diff --git a/migration/dirtyrate.c b/migration/dirtyrate.c
index ccb9814..320c56b 100644
--- a/migration/dirtyrate.c
+++ b/migration/dirtyrate.c
@@ -20,6 +20,9 @@
#include "ram.h"
#include "trace.h"
#include "dirtyrate.h"
+#include "monitor/hmp.h"
+#include "monitor/monitor.h"
+#include "qapi/qmp/qdict.h"
static int CalculatingState = DIRTY_RATE_STATUS_UNSTARTED;
static struct DirtyRateStat DirtyStat;
@@ -48,6 +51,12 @@ static bool is_sample_period_valid(int64_t sec)
return true;
}
+static bool is_sample_pages_valid(int64_t pages)
+{
+ return pages >= MIN_SAMPLE_PAGE_COUNT &&
+ pages <= MAX_SAMPLE_PAGE_COUNT;
+}
+
static int dirtyrate_set_state(int *state, int old_state, int new_state)
{
assert(new_state < DIRTY_RATE_STATUS__MAX);
@@ -72,13 +81,15 @@ static struct DirtyRateInfo *query_dirty_rate_info(void)
info->status = CalculatingState;
info->start_time = DirtyStat.start_time;
info->calc_time = DirtyStat.calc_time;
+ info->sample_pages = DirtyStat.sample_pages;
trace_query_dirty_rate_info(DirtyRateStatus_str(CalculatingState));
return info;
}
-static void init_dirtyrate_stat(int64_t start_time, int64_t calc_time)
+static void init_dirtyrate_stat(int64_t start_time, int64_t calc_time,
+ uint64_t sample_pages)
{
DirtyStat.total_dirty_samples = 0;
DirtyStat.total_sample_count = 0;
@@ -86,6 +97,7 @@ static void init_dirtyrate_stat(int64_t start_time, int64_t calc_time)
DirtyStat.dirty_rate = -1;
DirtyStat.start_time = start_time;
DirtyStat.calc_time = calc_time;
+ DirtyStat.sample_pages = sample_pages;
}
static void update_dirtyrate_stat(struct RamblockDirtyInfo *info)
@@ -361,6 +373,7 @@ void *get_dirtyrate_thread(void *arg)
int ret;
int64_t start_time;
int64_t calc_time;
+ uint64_t sample_pages;
ret = dirtyrate_set_state(&CalculatingState, DIRTY_RATE_STATUS_UNSTARTED,
DIRTY_RATE_STATUS_MEASURING);
@@ -371,7 +384,8 @@ void *get_dirtyrate_thread(void *arg)
start_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME) / 1000;
calc_time = config.sample_period_seconds;
- init_dirtyrate_stat(start_time, calc_time);
+ sample_pages = config.sample_pages_per_gigabytes;
+ init_dirtyrate_stat(start_time, calc_time, sample_pages);
calculate_dirtyrate(config);
@@ -383,7 +397,8 @@ void *get_dirtyrate_thread(void *arg)
return NULL;
}
-void qmp_calc_dirty_rate(int64_t calc_time, Error **errp)
+void qmp_calc_dirty_rate(int64_t calc_time, bool has_sample_pages,
+ int64_t sample_pages, Error **errp)
{
static struct DirtyRateConfig config;
QemuThread thread;
@@ -404,6 +419,17 @@ void qmp_calc_dirty_rate(int64_t calc_time, Error **errp)
return;
}
+ if (has_sample_pages) {
+ if (!is_sample_pages_valid(sample_pages)) {
+ error_setg(errp, "sample-pages is out of range[%d, %d].",
+ MIN_SAMPLE_PAGE_COUNT,
+ MAX_SAMPLE_PAGE_COUNT);
+ return;
+ }
+ } else {
+ sample_pages = DIRTYRATE_DEFAULT_SAMPLE_PAGES;
+ }
+
/*
* Init calculation state as unstarted.
*/
@@ -415,7 +441,7 @@ void qmp_calc_dirty_rate(int64_t calc_time, Error **errp)
}
config.sample_period_seconds = calc_time;
- config.sample_pages_per_gigabytes = DIRTYRATE_DEFAULT_SAMPLE_PAGES;
+ config.sample_pages_per_gigabytes = sample_pages;
qemu_thread_create(&thread, "get_dirtyrate", get_dirtyrate_thread,
(void *)&config, QEMU_THREAD_DETACHED);
}
@@ -424,3 +450,47 @@ struct DirtyRateInfo *qmp_query_dirty_rate(Error **errp)
{
return query_dirty_rate_info();
}
+
+void hmp_info_dirty_rate(Monitor *mon, const QDict *qdict)
+{
+ DirtyRateInfo *info = query_dirty_rate_info();
+
+ monitor_printf(mon, "Status: %s\n",
+ DirtyRateStatus_str(info->status));
+ monitor_printf(mon, "Start Time: %"PRIi64" (ms)\n",
+ info->start_time);
+ monitor_printf(mon, "Sample Pages: %"PRIu64" (per GB)\n",
+ info->sample_pages);
+ monitor_printf(mon, "Period: %"PRIi64" (sec)\n",
+ info->calc_time);
+ monitor_printf(mon, "Dirty rate: ");
+ if (info->has_dirty_rate) {
+ monitor_printf(mon, "%"PRIi64" (MB/s)\n", info->dirty_rate);
+ } else {
+ monitor_printf(mon, "(not ready)\n");
+ }
+ g_free(info);
+}
+
+void hmp_calc_dirty_rate(Monitor *mon, const QDict *qdict)
+{
+ int64_t sec = qdict_get_try_int(qdict, "second", 0);
+ int64_t sample_pages = qdict_get_try_int(qdict, "sample_pages_per_GB", -1);
+ bool has_sample_pages = (sample_pages != -1);
+ Error *err = NULL;
+
+ if (!sec) {
+ monitor_printf(mon, "Incorrect period length specified!\n");
+ return;
+ }
+
+ qmp_calc_dirty_rate(sec, has_sample_pages, sample_pages, &err);
+ if (err) {
+ hmp_handle_error(mon, err);
+ return;
+ }
+
+ monitor_printf(mon, "Starting dirty rate measurement with period %"PRIi64
+ " seconds\n", sec);
+ monitor_printf(mon, "[Please use 'info dirty_rate' to check results]\n");
+}
diff --git a/migration/dirtyrate.h b/migration/dirtyrate.h
index 6ec4295..e1fd290 100644
--- a/migration/dirtyrate.h
+++ b/migration/dirtyrate.h
@@ -15,7 +15,6 @@
/*
* Sample 512 pages per GB as default.
- * TODO: Make it configurable.
*/
#define DIRTYRATE_DEFAULT_SAMPLE_PAGES 512
@@ -35,6 +34,12 @@
#define MIN_FETCH_DIRTYRATE_TIME_SEC 1
#define MAX_FETCH_DIRTYRATE_TIME_SEC 60
+/*
+ * Take 1/16 pages in 1G as the maxmum sample page count
+ */
+#define MIN_SAMPLE_PAGE_COUNT 128
+#define MAX_SAMPLE_PAGE_COUNT 16384
+
struct DirtyRateConfig {
uint64_t sample_pages_per_gigabytes; /* sample pages per GB */
int64_t sample_period_seconds; /* time duration between two sampling */
@@ -63,6 +68,7 @@ struct DirtyRateStat {
int64_t dirty_rate; /* dirty rate in MB/s */
int64_t start_time; /* calculation start time in units of second */
int64_t calc_time; /* time duration of two sampling in units of second */
+ uint64_t sample_pages; /* sample pages per GB */
};
void *get_dirtyrate_thread(void *arg);
diff --git a/migration/migration.c b/migration/migration.c
index 1885860..393299e 100644
--- a/migration/migration.c
+++ b/migration/migration.c
@@ -280,6 +280,9 @@ void migration_incoming_state_destroy(void)
g_array_free(mis->postcopy_remote_fds, TRUE);
mis->postcopy_remote_fds = NULL;
}
+ if (mis->transport_cleanup) {
+ mis->transport_cleanup(mis->transport_data);
+ }
qemu_event_reset(&mis->main_thread_load_event);
diff --git a/migration/migration.h b/migration/migration.h
index b88bd8f..2ebb740 100644
--- a/migration/migration.h
+++ b/migration/migration.h
@@ -49,6 +49,10 @@ struct PostcopyBlocktimeContext;
struct MigrationIncomingState {
QEMUFile *from_src_file;
+ /* A hook to allow cleanup at the end of incoming migration */
+ void *transport_data;
+ void (*transport_cleanup)(void *data);
+
/*
* Free at the start of the main state load, set as the main thread finishes
* loading state.
diff --git a/migration/multifd.c b/migration/multifd.c
index 0a4803c..ab41590 100644
--- a/migration/multifd.c
+++ b/migration/multifd.c
@@ -987,7 +987,8 @@ int multifd_load_cleanup(Error **errp)
for (i = 0; i < migrate_multifd_channels(); i++) {
MultiFDRecvParams *p = &multifd_recv_state->params[i];
- if (object_dynamic_cast(OBJECT(p->c), TYPE_QIO_CHANNEL_SOCKET)
+ if ((object_dynamic_cast(OBJECT(p->c), TYPE_QIO_CHANNEL_SOCKET) ||
+ object_dynamic_cast(OBJECT(p->c), TYPE_QIO_CHANNEL_TLS))
&& OBJECT(p->c)->ref == 1) {
yank_unregister_function(MIGRATION_YANK_INSTANCE,
migration_yank_iochannel,
@@ -1165,6 +1166,11 @@ bool multifd_recv_all_channels_created(void)
return true;
}
+ if (!multifd_recv_state) {
+ /* Called before any connections created */
+ return false;
+ }
+
return thread_count == qatomic_read(&multifd_recv_state->count);
}
diff --git a/migration/qemu-file-channel.c b/migration/qemu-file-channel.c
index 876d05a..fad340e 100644
--- a/migration/qemu-file-channel.c
+++ b/migration/qemu-file-channel.c
@@ -26,6 +26,7 @@
#include "qemu-file-channel.h"
#include "qemu-file.h"
#include "io/channel-socket.h"
+#include "io/channel-tls.h"
#include "qemu/iov.h"
#include "qemu/yank.h"
#include "yank_functions.h"
@@ -106,7 +107,8 @@ static int channel_close(void *opaque, Error **errp)
int ret;
QIOChannel *ioc = QIO_CHANNEL(opaque);
ret = qio_channel_close(ioc, errp);
- if (object_dynamic_cast(OBJECT(ioc), TYPE_QIO_CHANNEL_SOCKET)
+ if ((object_dynamic_cast(OBJECT(ioc), TYPE_QIO_CHANNEL_SOCKET) ||
+ object_dynamic_cast(OBJECT(ioc), TYPE_QIO_CHANNEL_TLS))
&& OBJECT(ioc)->ref == 1) {
yank_unregister_function(MIGRATION_YANK_INSTANCE,
migration_yank_iochannel,
diff --git a/migration/rdma.c b/migration/rdma.c
index 1cdb456..d90b29a 100644
--- a/migration/rdma.c
+++ b/migration/rdma.c
@@ -1539,16 +1539,20 @@ static int qemu_rdma_wait_comp_channel(RDMAContext *rdma)
if (pfds[1].revents) {
ret = rdma_get_cm_event(rdma->channel, &cm_event);
- if (!ret) {
- rdma_ack_cm_event(cm_event);
+ if (ret) {
+ error_report("failed to get cm event while wait "
+ "completion channel");
+ return -EPIPE;
}
error_report("receive cm event while wait comp channel,"
"cm event is %d", cm_event->event);
if (cm_event->event == RDMA_CM_EVENT_DISCONNECTED ||
cm_event->event == RDMA_CM_EVENT_DEVICE_REMOVAL) {
+ rdma_ack_cm_event(cm_event);
return -EPIPE;
}
+ rdma_ack_cm_event(cm_event);
}
break;
@@ -3285,7 +3289,6 @@ static void rdma_cm_poll_handler(void *opaque)
error_report("get_cm_event failed %d", errno);
return;
}
- rdma_ack_cm_event(cm_event);
if (cm_event->event == RDMA_CM_EVENT_DISCONNECTED ||
cm_event->event == RDMA_CM_EVENT_DEVICE_REMOVAL) {
@@ -3298,12 +3301,14 @@ static void rdma_cm_poll_handler(void *opaque)
rdma->return_path->error_state = -EPIPE;
}
}
+ rdma_ack_cm_event(cm_event);
if (mis->migration_incoming_co) {
qemu_coroutine_enter(mis->migration_incoming_co);
}
return;
}
+ rdma_ack_cm_event(cm_event);
}
static int qemu_rdma_accept(RDMAContext *rdma)
diff --git a/migration/socket.c b/migration/socket.c
index 6016642..05705a3 100644
--- a/migration/socket.c
+++ b/migration/socket.c
@@ -126,22 +126,31 @@ static void socket_accept_incoming_migration(QIONetListener *listener,
{
trace_migration_socket_incoming_accepted();
- qio_channel_set_name(QIO_CHANNEL(cioc), "migration-socket-incoming");
- migration_channel_process_incoming(QIO_CHANNEL(cioc));
-
if (migration_has_all_channels()) {
- /* Close listening socket as its no longer needed */
- qio_net_listener_disconnect(listener);
- object_unref(OBJECT(listener));
+ error_report("%s: Extra incoming migration connection; ignoring",
+ __func__);
+ return;
}
+
+ qio_channel_set_name(QIO_CHANNEL(cioc), "migration-socket-incoming");
+ migration_channel_process_incoming(QIO_CHANNEL(cioc));
}
+static void
+socket_incoming_migration_end(void *opaque)
+{
+ QIONetListener *listener = opaque;
+
+ qio_net_listener_disconnect(listener);
+ object_unref(OBJECT(listener));
+}
static void
socket_start_incoming_migration_internal(SocketAddress *saddr,
Error **errp)
{
QIONetListener *listener = qio_net_listener_new();
+ MigrationIncomingState *mis = migration_incoming_get_current();
size_t i;
int num = 1;
@@ -156,6 +165,9 @@ socket_start_incoming_migration_internal(SocketAddress *saddr,
return;
}
+ mis->transport_data = listener;
+ mis->transport_cleanup = socket_incoming_migration_end;
+
qio_net_listener_set_client_func_full(listener,
socket_accept_incoming_migration,
NULL, NULL,
diff --git a/qapi/migration.json b/qapi/migration.json
index 7a5bdf9..1124a2d 100644
--- a/qapi/migration.json
+++ b/qapi/migration.json
@@ -1740,6 +1740,9 @@
#
# @calc-time: time in units of second for sample dirty pages
#
+# @sample-pages: page count per GB for sample dirty pages
+# the default value is 512 (since 6.1)
+#
# Since: 5.2
#
##
@@ -1747,7 +1750,8 @@
'data': {'*dirty-rate': 'int64',
'status': 'DirtyRateStatus',
'start-time': 'int64',
- 'calc-time': 'int64'} }
+ 'calc-time': 'int64',
+ 'sample-pages': 'uint64'} }
##
# @calc-dirty-rate:
@@ -1756,13 +1760,18 @@
#
# @calc-time: time in units of second for sample dirty pages
#
+# @sample-pages: page count per GB for sample dirty pages
+# the default value is 512 (since 6.1)
+#
# Since: 5.2
#
# Example:
-# {"command": "calc-dirty-rate", "data": {"calc-time": 1} }
+# {"command": "calc-dirty-rate", "data": {"calc-time": 1,
+# 'sample-pages': 512} }
#
##
-{ 'command': 'calc-dirty-rate', 'data': {'calc-time': 'int64'} }
+{ 'command': 'calc-dirty-rate', 'data': {'calc-time': 'int64',
+ '*sample-pages': 'int'} }
##
# @query-dirty-rate:
diff --git a/qapi/sockets.json b/qapi/sockets.json
index 2e83452..735eb4a 100644
--- a/qapi/sockets.json
+++ b/qapi/sockets.json
@@ -57,6 +57,8 @@
# @keep-alive: enable keep-alive when connecting to this socket. Not supported
# for passive sockets. (Since 4.2)
#
+# @mptcp: enable multi-path TCP. (Since 6.1)
+#
# Since: 1.3
##
{ 'struct': 'InetSocketAddress',
@@ -66,7 +68,8 @@
'*to': 'uint16',
'*ipv4': 'bool',
'*ipv6': 'bool',
- '*keep-alive': 'bool' } }
+ '*keep-alive': 'bool',
+ '*mptcp': { 'type': 'bool', 'if': 'defined(IPPROTO_MPTCP)' } } }
##
# @UnixSocketAddress:
diff --git a/util/qemu-sockets.c b/util/qemu-sockets.c
index 2463c49..c415c34 100644
--- a/util/qemu-sockets.c
+++ b/util/qemu-sockets.c
@@ -278,6 +278,11 @@ static int inet_listen_saddr(InetSocketAddress *saddr,
/* create socket + bind/listen */
for (e = res; e != NULL; e = e->ai_next) {
+#ifdef IPPROTO_MPTCP
+ if (saddr->has_mptcp && saddr->mptcp) {
+ e->ai_protocol = IPPROTO_MPTCP;
+ }
+#endif
getnameinfo((struct sockaddr*)e->ai_addr,e->ai_addrlen,
uaddr,INET6_ADDRSTRLEN,uport,32,
NI_NUMERICHOST | NI_NUMERICSERV);
@@ -456,6 +461,13 @@ int inet_connect_saddr(InetSocketAddress *saddr, Error **errp)
for (e = res; e != NULL; e = e->ai_next) {
error_free(local_err);
local_err = NULL;
+
+#ifdef IPPROTO_MPTCP
+ if (saddr->has_mptcp && saddr->mptcp) {
+ e->ai_protocol = IPPROTO_MPTCP;
+ }
+#endif
+
sock = inet_connect_addr(saddr, e, &local_err);
if (sock >= 0) {
break;
@@ -687,6 +699,17 @@ int inet_parse(InetSocketAddress *addr, const char *str, Error **errp)
}
addr->has_keep_alive = true;
}
+#ifdef IPPROTO_MPTCP
+ begin = strstr(optstr, ",mptcp");
+ if (begin) {
+ if (inet_parse_flag("mptcp", begin + strlen(",mptcp"),
+ &addr->mptcp, errp) < 0)
+ {
+ return -1;
+ }
+ addr->has_mptcp = true;
+ }
+#endif
return 0;
}