mirror of https://github.com/xemu-project/xemu.git
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> -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEERfXHG0oMt/uXep+pBRYzHrxb/ecFAmDA0sEACgkQBRYzHrxb /efn9g/+ICIELbh99KRg6GiZ1BxraixpXgNaN6yjWqdHHTa67/pE0p3xHpLuWFnX pt4SDCXYWZ1XC3Eq4AMXyQr4yq8tUjsKV7H99WI7e8NvYzNPW7C4MlNkiiRUCxyc /SQMrGw6KOByc2PtRBBCW+47aJv1ZaaE09g3s4la6BfT6xeLegncKrxKjWgsxArR BhZVhvmDLn3Njp2iMPLGlDU1+gUcCls2DtQ4WebpyJo511BoIDDzZu8qlLNhKQv0 wCYRB51FsFyZXptELS5NRgPzjbgojp11F6Ugy4qbp/CqMVLBFi78Jq3FSzlLy4IJ SRcPGMTxk1grarb/y+ojrQlPn5VU7U2bgRGOTWKW0pav0+5ehRc4To9wMzTQgv83 tEYwSYCkMBk0Kzr76rxHj39fGnghtsVzd4abXn9Pe5bShebWE1OBriHafDs4fWKV MUgxyp0fpBCC1dHYgUDrQyVHWaQt4nDgFw2xlTuZcWEexBRZZEMBl9ugROtKsbqx 3QXvjn9fw1Vmnf1WPYp80FLwxTGXHi4RHFpIOKzzB5eqf/ggp9YwuuaBMoPhFM2s mh4+A9eo4LEaZcDFxEbxAp1WDLrA1ZyKzerySaJXJaCgd9l2i74PJ4pzQq0ZqVsZ yRZlbnbn7KrgWqMpqRwiPrxePkwZcTmkPz6i1+BrJlZ8KTCZt4I= =eLBm -----END PGP SIGNATURE----- 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>
This commit is contained in:
commit
7fe7fae8b4
|
@ -867,3 +867,16 @@ SRST
|
||||||
``info replay``
|
``info replay``
|
||||||
Display the record/replay information: mode and the current icount.
|
Display the record/replay information: mode and the current icount.
|
||||||
ERST
|
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
|
||||||
|
|
|
@ -1727,3 +1727,17 @@ ERST
|
||||||
.flags = "p",
|
.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,
|
||||||
|
},
|
||||||
|
|
|
@ -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_break(Monitor *mon, const QDict *qdict);
|
||||||
void hmp_replay_delete_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_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
|
#endif
|
||||||
|
|
|
@ -487,15 +487,15 @@ static ssize_t qio_channel_socket_readv(QIOChannel *ioc,
|
||||||
|
|
||||||
memset(control, 0, CMSG_SPACE(sizeof(int) * SOCKET_MAX_FDS));
|
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_iov = (struct iovec *)iov;
|
||||||
msg.msg_iovlen = niov;
|
msg.msg_iovlen = niov;
|
||||||
if (fds && nfds) {
|
if (fds && nfds) {
|
||||||
msg.msg_control = control;
|
msg.msg_control = control;
|
||||||
msg.msg_controllen = sizeof(control);
|
msg.msg_controllen = sizeof(control);
|
||||||
|
#ifdef MSG_CMSG_CLOEXEC
|
||||||
|
sflags |= MSG_CMSG_CLOEXEC;
|
||||||
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
retry:
|
retry:
|
||||||
|
|
|
@ -122,6 +122,10 @@ static int qio_dns_resolver_lookup_sync_inet(QIODNSResolver *resolver,
|
||||||
.ipv4 = iaddr->ipv4,
|
.ipv4 = iaddr->ipv4,
|
||||||
.has_ipv6 = iaddr->has_ipv6,
|
.has_ipv6 = iaddr->has_ipv6,
|
||||||
.ipv6 = iaddr->ipv6,
|
.ipv6 = iaddr->ipv6,
|
||||||
|
#ifdef IPPROTO_MPTCP
|
||||||
|
.has_mptcp = iaddr->has_mptcp,
|
||||||
|
.mptcp = iaddr->mptcp,
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
(*addrs)[i] = newaddr;
|
(*addrs)[i] = newaddr;
|
||||||
|
|
|
@ -292,6 +292,9 @@ static void qio_net_listener_finalize(Object *obj)
|
||||||
QIONetListener *listener = QIO_NET_LISTENER(obj);
|
QIONetListener *listener = QIO_NET_LISTENER(obj);
|
||||||
size_t i;
|
size_t i;
|
||||||
|
|
||||||
|
if (listener->io_notify) {
|
||||||
|
listener->io_notify(listener->io_data);
|
||||||
|
}
|
||||||
qio_net_listener_disconnect(listener);
|
qio_net_listener_disconnect(listener);
|
||||||
|
|
||||||
for (i = 0; i < listener->nsioc; i++) {
|
for (i = 0; i < listener->nsioc; i++) {
|
||||||
|
|
|
@ -38,18 +38,19 @@ void migration_channel_process_incoming(QIOChannel *ioc)
|
||||||
trace_migration_set_incoming_channel(
|
trace_migration_set_incoming_channel(
|
||||||
ioc, object_get_typename(OBJECT(ioc)));
|
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 &&
|
if (s->parameters.tls_creds &&
|
||||||
*s->parameters.tls_creds &&
|
*s->parameters.tls_creds &&
|
||||||
!object_dynamic_cast(OBJECT(ioc),
|
!object_dynamic_cast(OBJECT(ioc),
|
||||||
TYPE_QIO_CHANNEL_TLS)) {
|
TYPE_QIO_CHANNEL_TLS)) {
|
||||||
migration_tls_channel_process_incoming(s, ioc, &local_err);
|
migration_tls_channel_process_incoming(s, ioc, &local_err);
|
||||||
} else {
|
} 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);
|
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);
|
ioc, object_get_typename(OBJECT(ioc)), hostname, error);
|
||||||
|
|
||||||
if (!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 &&
|
if (s->parameters.tls_creds &&
|
||||||
*s->parameters.tls_creds &&
|
*s->parameters.tls_creds &&
|
||||||
!object_dynamic_cast(OBJECT(ioc),
|
!object_dynamic_cast(OBJECT(ioc),
|
||||||
|
@ -99,6 +94,13 @@ void migration_channel_connect(MigrationState *s,
|
||||||
} else {
|
} else {
|
||||||
QEMUFile *f = qemu_fopen_channel_output(ioc);
|
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);
|
qemu_mutex_lock(&s->qemu_file_lock);
|
||||||
s->to_dst_file = f;
|
s->to_dst_file = f;
|
||||||
qemu_mutex_unlock(&s->qemu_file_lock);
|
qemu_mutex_unlock(&s->qemu_file_lock);
|
||||||
|
|
|
@ -20,6 +20,9 @@
|
||||||
#include "ram.h"
|
#include "ram.h"
|
||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
#include "dirtyrate.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 int CalculatingState = DIRTY_RATE_STATUS_UNSTARTED;
|
||||||
static struct DirtyRateStat DirtyStat;
|
static struct DirtyRateStat DirtyStat;
|
||||||
|
@ -48,6 +51,12 @@ static bool is_sample_period_valid(int64_t sec)
|
||||||
return true;
|
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)
|
static int dirtyrate_set_state(int *state, int old_state, int new_state)
|
||||||
{
|
{
|
||||||
assert(new_state < DIRTY_RATE_STATUS__MAX);
|
assert(new_state < DIRTY_RATE_STATUS__MAX);
|
||||||
|
@ -72,13 +81,15 @@ static struct DirtyRateInfo *query_dirty_rate_info(void)
|
||||||
info->status = CalculatingState;
|
info->status = CalculatingState;
|
||||||
info->start_time = DirtyStat.start_time;
|
info->start_time = DirtyStat.start_time;
|
||||||
info->calc_time = DirtyStat.calc_time;
|
info->calc_time = DirtyStat.calc_time;
|
||||||
|
info->sample_pages = DirtyStat.sample_pages;
|
||||||
|
|
||||||
trace_query_dirty_rate_info(DirtyRateStatus_str(CalculatingState));
|
trace_query_dirty_rate_info(DirtyRateStatus_str(CalculatingState));
|
||||||
|
|
||||||
return info;
|
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_dirty_samples = 0;
|
||||||
DirtyStat.total_sample_count = 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.dirty_rate = -1;
|
||||||
DirtyStat.start_time = start_time;
|
DirtyStat.start_time = start_time;
|
||||||
DirtyStat.calc_time = calc_time;
|
DirtyStat.calc_time = calc_time;
|
||||||
|
DirtyStat.sample_pages = sample_pages;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void update_dirtyrate_stat(struct RamblockDirtyInfo *info)
|
static void update_dirtyrate_stat(struct RamblockDirtyInfo *info)
|
||||||
|
@ -361,6 +373,7 @@ void *get_dirtyrate_thread(void *arg)
|
||||||
int ret;
|
int ret;
|
||||||
int64_t start_time;
|
int64_t start_time;
|
||||||
int64_t calc_time;
|
int64_t calc_time;
|
||||||
|
uint64_t sample_pages;
|
||||||
|
|
||||||
ret = dirtyrate_set_state(&CalculatingState, DIRTY_RATE_STATUS_UNSTARTED,
|
ret = dirtyrate_set_state(&CalculatingState, DIRTY_RATE_STATUS_UNSTARTED,
|
||||||
DIRTY_RATE_STATUS_MEASURING);
|
DIRTY_RATE_STATUS_MEASURING);
|
||||||
|
@ -371,7 +384,8 @@ void *get_dirtyrate_thread(void *arg)
|
||||||
|
|
||||||
start_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME) / 1000;
|
start_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME) / 1000;
|
||||||
calc_time = config.sample_period_seconds;
|
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);
|
calculate_dirtyrate(config);
|
||||||
|
|
||||||
|
@ -383,7 +397,8 @@ void *get_dirtyrate_thread(void *arg)
|
||||||
return NULL;
|
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;
|
static struct DirtyRateConfig config;
|
||||||
QemuThread thread;
|
QemuThread thread;
|
||||||
|
@ -404,6 +419,17 @@ void qmp_calc_dirty_rate(int64_t calc_time, Error **errp)
|
||||||
return;
|
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.
|
* 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_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,
|
qemu_thread_create(&thread, "get_dirtyrate", get_dirtyrate_thread,
|
||||||
(void *)&config, QEMU_THREAD_DETACHED);
|
(void *)&config, QEMU_THREAD_DETACHED);
|
||||||
}
|
}
|
||||||
|
@ -424,3 +450,47 @@ struct DirtyRateInfo *qmp_query_dirty_rate(Error **errp)
|
||||||
{
|
{
|
||||||
return query_dirty_rate_info();
|
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");
|
||||||
|
}
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Sample 512 pages per GB as default.
|
* Sample 512 pages per GB as default.
|
||||||
* TODO: Make it configurable.
|
|
||||||
*/
|
*/
|
||||||
#define DIRTYRATE_DEFAULT_SAMPLE_PAGES 512
|
#define DIRTYRATE_DEFAULT_SAMPLE_PAGES 512
|
||||||
|
|
||||||
|
@ -35,6 +34,12 @@
|
||||||
#define MIN_FETCH_DIRTYRATE_TIME_SEC 1
|
#define MIN_FETCH_DIRTYRATE_TIME_SEC 1
|
||||||
#define MAX_FETCH_DIRTYRATE_TIME_SEC 60
|
#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 {
|
struct DirtyRateConfig {
|
||||||
uint64_t sample_pages_per_gigabytes; /* sample pages per GB */
|
uint64_t sample_pages_per_gigabytes; /* sample pages per GB */
|
||||||
int64_t sample_period_seconds; /* time duration between two sampling */
|
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 dirty_rate; /* dirty rate in MB/s */
|
||||||
int64_t start_time; /* calculation start time in units of second */
|
int64_t start_time; /* calculation start time in units of second */
|
||||||
int64_t calc_time; /* time duration of two sampling 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);
|
void *get_dirtyrate_thread(void *arg);
|
||||||
|
|
|
@ -280,6 +280,9 @@ void migration_incoming_state_destroy(void)
|
||||||
g_array_free(mis->postcopy_remote_fds, TRUE);
|
g_array_free(mis->postcopy_remote_fds, TRUE);
|
||||||
mis->postcopy_remote_fds = NULL;
|
mis->postcopy_remote_fds = NULL;
|
||||||
}
|
}
|
||||||
|
if (mis->transport_cleanup) {
|
||||||
|
mis->transport_cleanup(mis->transport_data);
|
||||||
|
}
|
||||||
|
|
||||||
qemu_event_reset(&mis->main_thread_load_event);
|
qemu_event_reset(&mis->main_thread_load_event);
|
||||||
|
|
||||||
|
|
|
@ -49,6 +49,10 @@ struct PostcopyBlocktimeContext;
|
||||||
struct MigrationIncomingState {
|
struct MigrationIncomingState {
|
||||||
QEMUFile *from_src_file;
|
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
|
* Free at the start of the main state load, set as the main thread finishes
|
||||||
* loading state.
|
* loading state.
|
||||||
|
|
|
@ -987,7 +987,8 @@ int multifd_load_cleanup(Error **errp)
|
||||||
for (i = 0; i < migrate_multifd_channels(); i++) {
|
for (i = 0; i < migrate_multifd_channels(); i++) {
|
||||||
MultiFDRecvParams *p = &multifd_recv_state->params[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) {
|
&& OBJECT(p->c)->ref == 1) {
|
||||||
yank_unregister_function(MIGRATION_YANK_INSTANCE,
|
yank_unregister_function(MIGRATION_YANK_INSTANCE,
|
||||||
migration_yank_iochannel,
|
migration_yank_iochannel,
|
||||||
|
@ -1165,6 +1166,11 @@ bool multifd_recv_all_channels_created(void)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!multifd_recv_state) {
|
||||||
|
/* Called before any connections created */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return thread_count == qatomic_read(&multifd_recv_state->count);
|
return thread_count == qatomic_read(&multifd_recv_state->count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
#include "qemu-file-channel.h"
|
#include "qemu-file-channel.h"
|
||||||
#include "qemu-file.h"
|
#include "qemu-file.h"
|
||||||
#include "io/channel-socket.h"
|
#include "io/channel-socket.h"
|
||||||
|
#include "io/channel-tls.h"
|
||||||
#include "qemu/iov.h"
|
#include "qemu/iov.h"
|
||||||
#include "qemu/yank.h"
|
#include "qemu/yank.h"
|
||||||
#include "yank_functions.h"
|
#include "yank_functions.h"
|
||||||
|
@ -106,7 +107,8 @@ static int channel_close(void *opaque, Error **errp)
|
||||||
int ret;
|
int ret;
|
||||||
QIOChannel *ioc = QIO_CHANNEL(opaque);
|
QIOChannel *ioc = QIO_CHANNEL(opaque);
|
||||||
ret = qio_channel_close(ioc, errp);
|
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) {
|
&& OBJECT(ioc)->ref == 1) {
|
||||||
yank_unregister_function(MIGRATION_YANK_INSTANCE,
|
yank_unregister_function(MIGRATION_YANK_INSTANCE,
|
||||||
migration_yank_iochannel,
|
migration_yank_iochannel,
|
||||||
|
|
|
@ -1539,16 +1539,20 @@ static int qemu_rdma_wait_comp_channel(RDMAContext *rdma)
|
||||||
|
|
||||||
if (pfds[1].revents) {
|
if (pfds[1].revents) {
|
||||||
ret = rdma_get_cm_event(rdma->channel, &cm_event);
|
ret = rdma_get_cm_event(rdma->channel, &cm_event);
|
||||||
if (!ret) {
|
if (ret) {
|
||||||
rdma_ack_cm_event(cm_event);
|
error_report("failed to get cm event while wait "
|
||||||
|
"completion channel");
|
||||||
|
return -EPIPE;
|
||||||
}
|
}
|
||||||
|
|
||||||
error_report("receive cm event while wait comp channel,"
|
error_report("receive cm event while wait comp channel,"
|
||||||
"cm event is %d", cm_event->event);
|
"cm event is %d", cm_event->event);
|
||||||
if (cm_event->event == RDMA_CM_EVENT_DISCONNECTED ||
|
if (cm_event->event == RDMA_CM_EVENT_DISCONNECTED ||
|
||||||
cm_event->event == RDMA_CM_EVENT_DEVICE_REMOVAL) {
|
cm_event->event == RDMA_CM_EVENT_DEVICE_REMOVAL) {
|
||||||
|
rdma_ack_cm_event(cm_event);
|
||||||
return -EPIPE;
|
return -EPIPE;
|
||||||
}
|
}
|
||||||
|
rdma_ack_cm_event(cm_event);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -3285,7 +3289,6 @@ static void rdma_cm_poll_handler(void *opaque)
|
||||||
error_report("get_cm_event failed %d", errno);
|
error_report("get_cm_event failed %d", errno);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
rdma_ack_cm_event(cm_event);
|
|
||||||
|
|
||||||
if (cm_event->event == RDMA_CM_EVENT_DISCONNECTED ||
|
if (cm_event->event == RDMA_CM_EVENT_DISCONNECTED ||
|
||||||
cm_event->event == RDMA_CM_EVENT_DEVICE_REMOVAL) {
|
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->return_path->error_state = -EPIPE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
rdma_ack_cm_event(cm_event);
|
||||||
|
|
||||||
if (mis->migration_incoming_co) {
|
if (mis->migration_incoming_co) {
|
||||||
qemu_coroutine_enter(mis->migration_incoming_co);
|
qemu_coroutine_enter(mis->migration_incoming_co);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
rdma_ack_cm_event(cm_event);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int qemu_rdma_accept(RDMAContext *rdma)
|
static int qemu_rdma_accept(RDMAContext *rdma)
|
||||||
|
|
|
@ -126,22 +126,31 @@ static void socket_accept_incoming_migration(QIONetListener *listener,
|
||||||
{
|
{
|
||||||
trace_migration_socket_incoming_accepted();
|
trace_migration_socket_incoming_accepted();
|
||||||
|
|
||||||
|
if (migration_has_all_channels()) {
|
||||||
|
error_report("%s: Extra incoming migration connection; ignoring",
|
||||||
|
__func__);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
qio_channel_set_name(QIO_CHANNEL(cioc), "migration-socket-incoming");
|
qio_channel_set_name(QIO_CHANNEL(cioc), "migration-socket-incoming");
|
||||||
migration_channel_process_incoming(QIO_CHANNEL(cioc));
|
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));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
socket_incoming_migration_end(void *opaque)
|
||||||
|
{
|
||||||
|
QIONetListener *listener = opaque;
|
||||||
|
|
||||||
|
qio_net_listener_disconnect(listener);
|
||||||
|
object_unref(OBJECT(listener));
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
socket_start_incoming_migration_internal(SocketAddress *saddr,
|
socket_start_incoming_migration_internal(SocketAddress *saddr,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
QIONetListener *listener = qio_net_listener_new();
|
QIONetListener *listener = qio_net_listener_new();
|
||||||
|
MigrationIncomingState *mis = migration_incoming_get_current();
|
||||||
size_t i;
|
size_t i;
|
||||||
int num = 1;
|
int num = 1;
|
||||||
|
|
||||||
|
@ -156,6 +165,9 @@ socket_start_incoming_migration_internal(SocketAddress *saddr,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mis->transport_data = listener;
|
||||||
|
mis->transport_cleanup = socket_incoming_migration_end;
|
||||||
|
|
||||||
qio_net_listener_set_client_func_full(listener,
|
qio_net_listener_set_client_func_full(listener,
|
||||||
socket_accept_incoming_migration,
|
socket_accept_incoming_migration,
|
||||||
NULL, NULL,
|
NULL, NULL,
|
||||||
|
|
|
@ -1740,6 +1740,9 @@
|
||||||
#
|
#
|
||||||
# @calc-time: time in units of second for sample dirty pages
|
# @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
|
# Since: 5.2
|
||||||
#
|
#
|
||||||
##
|
##
|
||||||
|
@ -1747,7 +1750,8 @@
|
||||||
'data': {'*dirty-rate': 'int64',
|
'data': {'*dirty-rate': 'int64',
|
||||||
'status': 'DirtyRateStatus',
|
'status': 'DirtyRateStatus',
|
||||||
'start-time': 'int64',
|
'start-time': 'int64',
|
||||||
'calc-time': 'int64'} }
|
'calc-time': 'int64',
|
||||||
|
'sample-pages': 'uint64'} }
|
||||||
|
|
||||||
##
|
##
|
||||||
# @calc-dirty-rate:
|
# @calc-dirty-rate:
|
||||||
|
@ -1756,13 +1760,18 @@
|
||||||
#
|
#
|
||||||
# @calc-time: time in units of second for sample dirty pages
|
# @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
|
# Since: 5.2
|
||||||
#
|
#
|
||||||
# Example:
|
# 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:
|
# @query-dirty-rate:
|
||||||
|
|
|
@ -57,6 +57,8 @@
|
||||||
# @keep-alive: enable keep-alive when connecting to this socket. Not supported
|
# @keep-alive: enable keep-alive when connecting to this socket. Not supported
|
||||||
# for passive sockets. (Since 4.2)
|
# for passive sockets. (Since 4.2)
|
||||||
#
|
#
|
||||||
|
# @mptcp: enable multi-path TCP. (Since 6.1)
|
||||||
|
#
|
||||||
# Since: 1.3
|
# Since: 1.3
|
||||||
##
|
##
|
||||||
{ 'struct': 'InetSocketAddress',
|
{ 'struct': 'InetSocketAddress',
|
||||||
|
@ -66,7 +68,8 @@
|
||||||
'*to': 'uint16',
|
'*to': 'uint16',
|
||||||
'*ipv4': 'bool',
|
'*ipv4': 'bool',
|
||||||
'*ipv6': 'bool',
|
'*ipv6': 'bool',
|
||||||
'*keep-alive': 'bool' } }
|
'*keep-alive': 'bool',
|
||||||
|
'*mptcp': { 'type': 'bool', 'if': 'defined(IPPROTO_MPTCP)' } } }
|
||||||
|
|
||||||
##
|
##
|
||||||
# @UnixSocketAddress:
|
# @UnixSocketAddress:
|
||||||
|
|
|
@ -278,6 +278,11 @@ static int inet_listen_saddr(InetSocketAddress *saddr,
|
||||||
|
|
||||||
/* create socket + bind/listen */
|
/* create socket + bind/listen */
|
||||||
for (e = res; e != NULL; e = e->ai_next) {
|
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,
|
getnameinfo((struct sockaddr*)e->ai_addr,e->ai_addrlen,
|
||||||
uaddr,INET6_ADDRSTRLEN,uport,32,
|
uaddr,INET6_ADDRSTRLEN,uport,32,
|
||||||
NI_NUMERICHOST | NI_NUMERICSERV);
|
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) {
|
for (e = res; e != NULL; e = e->ai_next) {
|
||||||
error_free(local_err);
|
error_free(local_err);
|
||||||
local_err = NULL;
|
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);
|
sock = inet_connect_addr(saddr, e, &local_err);
|
||||||
if (sock >= 0) {
|
if (sock >= 0) {
|
||||||
break;
|
break;
|
||||||
|
@ -687,6 +699,17 @@ int inet_parse(InetSocketAddress *addr, const char *str, Error **errp)
|
||||||
}
|
}
|
||||||
addr->has_keep_alive = true;
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue