-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1 iQEcBAABAgAGBQJcfh/0AAoJEO8Ells5jWIRmqIH/RbXUBbnhqBq/X2lDfz8A5o2 9PowtrMj7tT0ZDpFkDtz8UcQVLvMa4A5c6lgQ9kjiCuX3bISjqvkUc0w/TMjgQCX 7AGObzdgoVY58OmLbmmKYduTWbVZf33qton+yee0g92F+O/mroH9PZIUmDOC+Mm9 iU+CFiMHIjfFv9BogTvFk3nf9XD9zMMTJLbFe2iA64KYUNPy2ncURlltlMTSqRDz PDawd7bmuVwWBLekNpwe0vliJC2t5/Cd78Njc7HMuKJsivmZAcFqJQ2xA2XxzmLp bQrYAXiaC3N/gDqgDsNNp/Buuy4pBJKFVAPYveBIqw9LwGYFwkkMZqGb8/9kca0= =zAY7 -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/jasowang/tags/net-pull-request' into staging # gpg: Signature made Tue 05 Mar 2019 07:06:28 GMT # gpg: using RSA key EF04965B398D6211 # gpg: Good signature from "Jason Wang (Jason Wang on RedHat) <jasowang@redhat.com>" [marginal] # gpg: WARNING: This key is not certified with sufficiently trusted signatures! # gpg: It is not certain that the signature belongs to the owner. # Primary key fingerprint: 215D 46F4 8246 689E C77F 3562 EF04 965B 398D 6211 * remotes/jasowang/tags/net-pull-request: tests: Add a test for qemu self announcements hmp: Add hmp_announce_self qmp: Add announce-self command virtio-net: Allow qemu_announce_self to trigger virtio announcements net: Add a network device specific self-announcement ability migration: Switch to using announce timer virtio-net: Switch to using announce timer migration: Add announce parameters net: Introduce announce timer net: netmap: improve netmap_receive_iov() net: netmap: simplify netmap_receive() net: netmap: small improvements netmap_send() net/colo-compare.c: Remove duplicated code Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
0984a157c1
|
@ -936,6 +936,22 @@ Bug: can screw up when the buffer contains invalid UTF-8 sequences,
|
|||
NUL characters, after the ring buffer lost data, and when reading
|
||||
stops because the size limit is reached.
|
||||
|
||||
ETEXI
|
||||
|
||||
{
|
||||
.name = "announce_self",
|
||||
.args_type = "",
|
||||
.params = "",
|
||||
.help = "Trigger GARP/RARP announcements",
|
||||
.cmd = hmp_announce_self,
|
||||
},
|
||||
|
||||
STEXI
|
||||
@item announce_self
|
||||
@findex announce_self
|
||||
Trigger a round of GARP/RARP broadcasts; this is useful for explicitly updating the
|
||||
network infrastructure after a reconfiguration or some forms of migration.
|
||||
The timings of the round are set by the migration announce parameters.
|
||||
ETEXI
|
||||
|
||||
{
|
||||
|
|
33
hmp.c
33
hmp.c
|
@ -334,6 +334,18 @@ void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict)
|
|||
params = qmp_query_migrate_parameters(NULL);
|
||||
|
||||
if (params) {
|
||||
monitor_printf(mon, "%s: %" PRIu64 " ms\n",
|
||||
MigrationParameter_str(MIGRATION_PARAMETER_ANNOUNCE_INITIAL),
|
||||
params->announce_initial);
|
||||
monitor_printf(mon, "%s: %" PRIu64 " ms\n",
|
||||
MigrationParameter_str(MIGRATION_PARAMETER_ANNOUNCE_MAX),
|
||||
params->announce_max);
|
||||
monitor_printf(mon, "%s: %" PRIu64 "\n",
|
||||
MigrationParameter_str(MIGRATION_PARAMETER_ANNOUNCE_ROUNDS),
|
||||
params->announce_rounds);
|
||||
monitor_printf(mon, "%s: %" PRIu64 " ms\n",
|
||||
MigrationParameter_str(MIGRATION_PARAMETER_ANNOUNCE_STEP),
|
||||
params->announce_step);
|
||||
assert(params->has_compress_level);
|
||||
monitor_printf(mon, "%s: %u\n",
|
||||
MigrationParameter_str(MIGRATION_PARAMETER_COMPRESS_LEVEL),
|
||||
|
@ -1558,6 +1570,11 @@ void hmp_info_snapshots(Monitor *mon, const QDict *qdict)
|
|||
|
||||
}
|
||||
|
||||
void hmp_announce_self(Monitor *mon, const QDict *qdict)
|
||||
{
|
||||
qmp_announce_self(migrate_announce_params(), NULL);
|
||||
}
|
||||
|
||||
void hmp_migrate_cancel(Monitor *mon, const QDict *qdict)
|
||||
{
|
||||
qmp_migrate_cancel(NULL);
|
||||
|
@ -1757,6 +1774,22 @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict)
|
|||
p->has_max_postcopy_bandwidth = true;
|
||||
visit_type_size(v, param, &p->max_postcopy_bandwidth, &err);
|
||||
break;
|
||||
case MIGRATION_PARAMETER_ANNOUNCE_INITIAL:
|
||||
p->has_announce_initial = true;
|
||||
visit_type_size(v, param, &p->announce_initial, &err);
|
||||
break;
|
||||
case MIGRATION_PARAMETER_ANNOUNCE_MAX:
|
||||
p->has_announce_max = true;
|
||||
visit_type_size(v, param, &p->announce_max, &err);
|
||||
break;
|
||||
case MIGRATION_PARAMETER_ANNOUNCE_ROUNDS:
|
||||
p->has_announce_rounds = true;
|
||||
visit_type_size(v, param, &p->announce_rounds, &err);
|
||||
break;
|
||||
case MIGRATION_PARAMETER_ANNOUNCE_STEP:
|
||||
p->has_announce_step = true;
|
||||
visit_type_size(v, param, &p->announce_step, &err);
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
|
|
1
hmp.h
1
hmp.h
|
@ -46,6 +46,7 @@ void hmp_sync_profile(Monitor *mon, const QDict *qdict);
|
|||
void hmp_system_reset(Monitor *mon, const QDict *qdict);
|
||||
void hmp_system_powerdown(Monitor *mon, const QDict *qdict);
|
||||
void hmp_exit_preconfig(Monitor *mon, const QDict *qdict);
|
||||
void hmp_announce_self(Monitor *mon, const QDict *qdict);
|
||||
void hmp_cpu(Monitor *mon, const QDict *qdict);
|
||||
void hmp_memsave(Monitor *mon, const QDict *qdict);
|
||||
void hmp_pmemsave(Monitor *mon, const QDict *qdict);
|
||||
|
|
|
@ -359,3 +359,9 @@ sunhme_rx_filter_reject(void) "rejecting incoming frame"
|
|||
sunhme_rx_filter_accept(void) "accepting incoming frame"
|
||||
sunhme_rx_desc(uint32_t addr, int offset, uint32_t status, int len, int cr, int nr) "addr 0x%"PRIx32"(+0x%x) status 0x%"PRIx32 " len %d (ring %d/%d)"
|
||||
sunhme_rx_xsum_calc(uint16_t xsum) "calculated incoming xsum as 0x%x"
|
||||
|
||||
# hw/net/virtio-net.c
|
||||
virtio_net_announce_notify(void) ""
|
||||
virtio_net_announce_timer(int round) "%d"
|
||||
virtio_net_handle_announce(int round) "%d"
|
||||
virtio_net_post_load_device(void)
|
||||
|
|
|
@ -21,12 +21,14 @@
|
|||
#include "qemu/timer.h"
|
||||
#include "hw/virtio/virtio-net.h"
|
||||
#include "net/vhost_net.h"
|
||||
#include "net/announce.h"
|
||||
#include "hw/virtio/virtio-bus.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qapi/qapi-events-net.h"
|
||||
#include "hw/virtio/virtio-access.h"
|
||||
#include "migration/misc.h"
|
||||
#include "standard-headers/linux/ethtool.h"
|
||||
#include "trace.h"
|
||||
|
||||
#define VIRTIO_NET_VM_VERSION 11
|
||||
|
||||
|
@ -148,14 +150,42 @@ static bool virtio_net_started(VirtIONet *n, uint8_t status)
|
|||
(n->status & VIRTIO_NET_S_LINK_UP) && vdev->vm_running;
|
||||
}
|
||||
|
||||
static void virtio_net_announce_notify(VirtIONet *net)
|
||||
{
|
||||
VirtIODevice *vdev = VIRTIO_DEVICE(net);
|
||||
trace_virtio_net_announce_notify();
|
||||
|
||||
net->status |= VIRTIO_NET_S_ANNOUNCE;
|
||||
virtio_notify_config(vdev);
|
||||
}
|
||||
|
||||
static void virtio_net_announce_timer(void *opaque)
|
||||
{
|
||||
VirtIONet *n = opaque;
|
||||
trace_virtio_net_announce_timer(n->announce_timer.round);
|
||||
|
||||
n->announce_timer.round--;
|
||||
virtio_net_announce_notify(n);
|
||||
}
|
||||
|
||||
static void virtio_net_announce(NetClientState *nc)
|
||||
{
|
||||
VirtIONet *n = qemu_get_nic_opaque(nc);
|
||||
VirtIODevice *vdev = VIRTIO_DEVICE(n);
|
||||
|
||||
n->announce_counter--;
|
||||
n->status |= VIRTIO_NET_S_ANNOUNCE;
|
||||
virtio_notify_config(vdev);
|
||||
/*
|
||||
* Make sure the virtio migration announcement timer isn't running
|
||||
* If it is, let it trigger announcement so that we do not cause
|
||||
* confusion.
|
||||
*/
|
||||
if (n->announce_timer.round) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (virtio_vdev_has_feature(vdev, VIRTIO_NET_F_GUEST_ANNOUNCE) &&
|
||||
virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_VQ)) {
|
||||
virtio_net_announce_notify(n);
|
||||
}
|
||||
}
|
||||
|
||||
static void virtio_net_vhost_status(VirtIONet *n, uint8_t status)
|
||||
|
@ -467,8 +497,8 @@ static void virtio_net_reset(VirtIODevice *vdev)
|
|||
n->nobcast = 0;
|
||||
/* multiqueue is disabled by default */
|
||||
n->curr_queues = 1;
|
||||
timer_del(n->announce_timer);
|
||||
n->announce_counter = 0;
|
||||
timer_del(n->announce_timer.tm);
|
||||
n->announce_timer.round = 0;
|
||||
n->status &= ~VIRTIO_NET_S_ANNOUNCE;
|
||||
|
||||
/* Flush any MAC and VLAN filter table state */
|
||||
|
@ -964,13 +994,12 @@ static int virtio_net_handle_vlan_table(VirtIONet *n, uint8_t cmd,
|
|||
static int virtio_net_handle_announce(VirtIONet *n, uint8_t cmd,
|
||||
struct iovec *iov, unsigned int iov_cnt)
|
||||
{
|
||||
trace_virtio_net_handle_announce(n->announce_timer.round);
|
||||
if (cmd == VIRTIO_NET_CTRL_ANNOUNCE_ACK &&
|
||||
n->status & VIRTIO_NET_S_ANNOUNCE) {
|
||||
n->status &= ~VIRTIO_NET_S_ANNOUNCE;
|
||||
if (n->announce_counter) {
|
||||
timer_mod(n->announce_timer,
|
||||
qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) +
|
||||
self_announce_delay(n->announce_counter));
|
||||
if (n->announce_timer.round) {
|
||||
qemu_announce_timer_step(&n->announce_timer);
|
||||
}
|
||||
return VIRTIO_NET_OK;
|
||||
} else {
|
||||
|
@ -2286,6 +2315,7 @@ static int virtio_net_post_load_device(void *opaque, int version_id)
|
|||
VirtIODevice *vdev = VIRTIO_DEVICE(n);
|
||||
int i, link_down;
|
||||
|
||||
trace_virtio_net_post_load_device();
|
||||
virtio_net_set_mrg_rx_bufs(n, n->mergeable_rx_bufs,
|
||||
virtio_vdev_has_feature(vdev,
|
||||
VIRTIO_F_VERSION_1));
|
||||
|
@ -2322,8 +2352,15 @@ static int virtio_net_post_load_device(void *opaque, int version_id)
|
|||
|
||||
if (virtio_vdev_has_feature(vdev, VIRTIO_NET_F_GUEST_ANNOUNCE) &&
|
||||
virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_VQ)) {
|
||||
n->announce_counter = SELF_ANNOUNCE_ROUNDS;
|
||||
timer_mod(n->announce_timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL));
|
||||
qemu_announce_timer_reset(&n->announce_timer, migrate_announce_params(),
|
||||
QEMU_CLOCK_VIRTUAL,
|
||||
virtio_net_announce_timer, n);
|
||||
if (n->announce_timer.round) {
|
||||
timer_mod(n->announce_timer.tm,
|
||||
qemu_clock_get_ms(n->announce_timer.type));
|
||||
} else {
|
||||
qemu_announce_timer_del(&n->announce_timer);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -2546,6 +2583,7 @@ static NetClientInfo net_virtio_info = {
|
|||
.receive = virtio_net_receive,
|
||||
.link_status_changed = virtio_net_set_link_status,
|
||||
.query_rx_filter = virtio_net_query_rxfilter,
|
||||
.announce = virtio_net_announce,
|
||||
};
|
||||
|
||||
static bool virtio_net_guest_notifier_pending(VirtIODevice *vdev, int idx)
|
||||
|
@ -2679,8 +2717,10 @@ static void virtio_net_device_realize(DeviceState *dev, Error **errp)
|
|||
qemu_macaddr_default_if_unset(&n->nic_conf.macaddr);
|
||||
memcpy(&n->mac[0], &n->nic_conf.macaddr, sizeof(n->mac));
|
||||
n->status = VIRTIO_NET_S_LINK_UP;
|
||||
n->announce_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL,
|
||||
virtio_net_announce_timer, n);
|
||||
qemu_announce_timer_reset(&n->announce_timer, migrate_announce_params(),
|
||||
QEMU_CLOCK_VIRTUAL,
|
||||
virtio_net_announce_timer, n);
|
||||
n->announce_timer.round = 0;
|
||||
|
||||
if (n->netclient_type) {
|
||||
/*
|
||||
|
@ -2743,8 +2783,7 @@ static void virtio_net_device_unrealize(DeviceState *dev, Error **errp)
|
|||
virtio_net_del_queue(n, i);
|
||||
}
|
||||
|
||||
timer_del(n->announce_timer);
|
||||
timer_free(n->announce_timer);
|
||||
qemu_announce_timer_del(&n->announce_timer);
|
||||
g_free(n->vqs);
|
||||
qemu_del_nic(n->nic);
|
||||
virtio_net_rsc_cleanup(n);
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include "qemu/units.h"
|
||||
#include "standard-headers/linux/virtio_net.h"
|
||||
#include "hw/virtio/virtio.h"
|
||||
#include "net/announce.h"
|
||||
|
||||
#define TYPE_VIRTIO_NET "virtio-net-device"
|
||||
#define VIRTIO_NET(obj) \
|
||||
|
@ -181,8 +182,7 @@ struct VirtIONet {
|
|||
char *netclient_name;
|
||||
char *netclient_type;
|
||||
uint64_t curr_guest_offloads;
|
||||
QEMUTimer *announce_timer;
|
||||
int announce_counter;
|
||||
AnnounceTimer announce_timer;
|
||||
bool needs_vnet_hdr_swap;
|
||||
bool mtu_bypass_backend;
|
||||
};
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#define MIGRATION_MISC_H
|
||||
|
||||
#include "qemu/notify.h"
|
||||
#include "qapi/qapi-types-net.h"
|
||||
|
||||
/* migration/ram.c */
|
||||
|
||||
|
@ -28,16 +29,7 @@ void blk_mig_init(void);
|
|||
static inline void blk_mig_init(void) {}
|
||||
#endif
|
||||
|
||||
#define SELF_ANNOUNCE_ROUNDS 5
|
||||
|
||||
static inline
|
||||
int64_t self_announce_delay(int round)
|
||||
{
|
||||
assert(round < SELF_ANNOUNCE_ROUNDS && round > 0);
|
||||
/* delay 50ms, 150ms, 250ms, ... */
|
||||
return 50 + (SELF_ANNOUNCE_ROUNDS - round - 1) * 100;
|
||||
}
|
||||
|
||||
AnnounceParameters *migrate_announce_params(void);
|
||||
/* migration/savevm.c */
|
||||
|
||||
void dump_vmstate_json_to_file(FILE *out_fp);
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Self-announce facility
|
||||
* (c) 2017-2019 Red Hat, Inc.
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#ifndef QEMU_NET_ANNOUNCE_H
|
||||
#define QEMU_NET_ANNOUNCE_H
|
||||
|
||||
#include "qemu-common.h"
|
||||
#include "qapi/qapi-types-net.h"
|
||||
#include "qemu/timer.h"
|
||||
|
||||
struct AnnounceTimer {
|
||||
QEMUTimer *tm;
|
||||
AnnounceParameters params;
|
||||
QEMUClockType type;
|
||||
int round;
|
||||
};
|
||||
|
||||
/* Returns: update the timer to the next time point */
|
||||
int64_t qemu_announce_timer_step(AnnounceTimer *timer);
|
||||
|
||||
/* Delete the underlying timer */
|
||||
void qemu_announce_timer_del(AnnounceTimer *timer);
|
||||
|
||||
/*
|
||||
* Under BQL/main thread
|
||||
* Reset the timer to the given parameters/type/notifier.
|
||||
*/
|
||||
void qemu_announce_timer_reset(AnnounceTimer *timer,
|
||||
AnnounceParameters *params,
|
||||
QEMUClockType type,
|
||||
QEMUTimerCB *cb,
|
||||
void *opaque);
|
||||
|
||||
void qemu_announce_self(AnnounceTimer *timer, AnnounceParameters *params);
|
||||
|
||||
#endif
|
|
@ -60,6 +60,7 @@ typedef int (SetVnetLE)(NetClientState *, bool);
|
|||
typedef int (SetVnetBE)(NetClientState *, bool);
|
||||
typedef struct SocketReadState SocketReadState;
|
||||
typedef void (SocketReadStateFinalize)(SocketReadState *rs);
|
||||
typedef void (NetAnnounce)(NetClientState *);
|
||||
|
||||
typedef struct NetClientInfo {
|
||||
NetClientDriver type;
|
||||
|
@ -80,6 +81,7 @@ typedef struct NetClientInfo {
|
|||
SetVnetHdrLen *set_vnet_hdr_len;
|
||||
SetVnetLE *set_vnet_le;
|
||||
SetVnetBE *set_vnet_be;
|
||||
NetAnnounce *announce;
|
||||
} NetClientInfo;
|
||||
|
||||
struct NetClientState {
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
typedef struct AdapterInfo AdapterInfo;
|
||||
typedef struct AddressSpace AddressSpace;
|
||||
typedef struct AioContext AioContext;
|
||||
typedef struct AnnounceTimer AnnounceTimer;
|
||||
typedef struct BdrvDirtyBitmap BdrvDirtyBitmap;
|
||||
typedef struct BdrvDirtyBitmapIter BdrvDirtyBitmapIter;
|
||||
typedef struct BlockBackend BlockBackend;
|
||||
|
|
|
@ -81,8 +81,6 @@ extern bool machine_init_done;
|
|||
void qemu_add_machine_init_done_notifier(Notifier *notify);
|
||||
void qemu_remove_machine_init_done_notifier(Notifier *notify);
|
||||
|
||||
void qemu_announce_self(void);
|
||||
|
||||
extern int autostart;
|
||||
|
||||
typedef enum {
|
||||
|
|
|
@ -45,6 +45,7 @@
|
|||
#include "migration/colo.h"
|
||||
#include "hw/boards.h"
|
||||
#include "monitor/monitor.h"
|
||||
#include "net/announce.h"
|
||||
|
||||
#define MAX_THROTTLE (32 << 20) /* Migration transfer speed throttling */
|
||||
|
||||
|
@ -86,6 +87,15 @@
|
|||
*/
|
||||
#define DEFAULT_MIGRATE_MAX_POSTCOPY_BANDWIDTH 0
|
||||
|
||||
/*
|
||||
* Parameters for self_announce_delay giving a stream of RARP/ARP
|
||||
* packets after migration.
|
||||
*/
|
||||
#define DEFAULT_MIGRATE_ANNOUNCE_INITIAL 50
|
||||
#define DEFAULT_MIGRATE_ANNOUNCE_MAX 550
|
||||
#define DEFAULT_MIGRATE_ANNOUNCE_ROUNDS 5
|
||||
#define DEFAULT_MIGRATE_ANNOUNCE_STEP 100
|
||||
|
||||
static NotifierList migration_state_notifiers =
|
||||
NOTIFIER_LIST_INITIALIZER(migration_state_notifiers);
|
||||
|
||||
|
@ -364,7 +374,7 @@ static void process_incoming_migration_bh(void *opaque)
|
|||
* This must happen after all error conditions are dealt with and
|
||||
* we're sure the VM is going to be running on this host.
|
||||
*/
|
||||
qemu_announce_self();
|
||||
qemu_announce_self(&mis->announce_timer, migrate_announce_params());
|
||||
|
||||
if (multifd_load_cleanup(&local_err) != 0) {
|
||||
error_report_err(local_err);
|
||||
|
@ -739,10 +749,32 @@ MigrationParameters *qmp_query_migrate_parameters(Error **errp)
|
|||
params->max_postcopy_bandwidth = s->parameters.max_postcopy_bandwidth;
|
||||
params->has_max_cpu_throttle = true;
|
||||
params->max_cpu_throttle = s->parameters.max_cpu_throttle;
|
||||
params->has_announce_initial = true;
|
||||
params->announce_initial = s->parameters.announce_initial;
|
||||
params->has_announce_max = true;
|
||||
params->announce_max = s->parameters.announce_max;
|
||||
params->has_announce_rounds = true;
|
||||
params->announce_rounds = s->parameters.announce_rounds;
|
||||
params->has_announce_step = true;
|
||||
params->announce_step = s->parameters.announce_step;
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
AnnounceParameters *migrate_announce_params(void)
|
||||
{
|
||||
static AnnounceParameters ap;
|
||||
|
||||
MigrationState *s = migrate_get_current();
|
||||
|
||||
ap.initial = s->parameters.announce_initial;
|
||||
ap.max = s->parameters.announce_max;
|
||||
ap.rounds = s->parameters.announce_rounds;
|
||||
ap.step = s->parameters.announce_step;
|
||||
|
||||
return ≈
|
||||
}
|
||||
|
||||
/*
|
||||
* Return true if we're already in the middle of a migration
|
||||
* (i.e. any of the active or setup states)
|
||||
|
@ -1116,6 +1148,35 @@ static bool migrate_params_check(MigrationParameters *params, Error **errp)
|
|||
return false;
|
||||
}
|
||||
|
||||
if (params->has_announce_initial &&
|
||||
params->announce_initial > 100000) {
|
||||
error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
|
||||
"announce_initial",
|
||||
"is invalid, it must be less than 100000 ms");
|
||||
return false;
|
||||
}
|
||||
if (params->has_announce_max &&
|
||||
params->announce_max > 100000) {
|
||||
error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
|
||||
"announce_max",
|
||||
"is invalid, it must be less than 100000 ms");
|
||||
return false;
|
||||
}
|
||||
if (params->has_announce_rounds &&
|
||||
params->announce_rounds > 1000) {
|
||||
error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
|
||||
"announce_rounds",
|
||||
"is invalid, it must be in the range of 0 to 1000");
|
||||
return false;
|
||||
}
|
||||
if (params->has_announce_step &&
|
||||
(params->announce_step < 1 ||
|
||||
params->announce_step > 10000)) {
|
||||
error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
|
||||
"announce_step",
|
||||
"is invalid, it must be in the range of 1 to 10000 ms");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1190,6 +1251,18 @@ static void migrate_params_test_apply(MigrateSetParameters *params,
|
|||
if (params->has_max_cpu_throttle) {
|
||||
dest->max_cpu_throttle = params->max_cpu_throttle;
|
||||
}
|
||||
if (params->has_announce_initial) {
|
||||
dest->announce_initial = params->announce_initial;
|
||||
}
|
||||
if (params->has_announce_max) {
|
||||
dest->announce_max = params->announce_max;
|
||||
}
|
||||
if (params->has_announce_rounds) {
|
||||
dest->announce_rounds = params->announce_rounds;
|
||||
}
|
||||
if (params->has_announce_step) {
|
||||
dest->announce_step = params->announce_step;
|
||||
}
|
||||
}
|
||||
|
||||
static void migrate_params_apply(MigrateSetParameters *params, Error **errp)
|
||||
|
@ -1272,6 +1345,18 @@ static void migrate_params_apply(MigrateSetParameters *params, Error **errp)
|
|||
if (params->has_max_cpu_throttle) {
|
||||
s->parameters.max_cpu_throttle = params->max_cpu_throttle;
|
||||
}
|
||||
if (params->has_announce_initial) {
|
||||
s->parameters.announce_initial = params->announce_initial;
|
||||
}
|
||||
if (params->has_announce_max) {
|
||||
s->parameters.announce_max = params->announce_max;
|
||||
}
|
||||
if (params->has_announce_rounds) {
|
||||
s->parameters.announce_rounds = params->announce_rounds;
|
||||
}
|
||||
if (params->has_announce_step) {
|
||||
s->parameters.announce_step = params->announce_step;
|
||||
}
|
||||
}
|
||||
|
||||
void qmp_migrate_set_parameters(MigrateSetParameters *params, Error **errp)
|
||||
|
@ -3274,6 +3359,18 @@ static Property migration_properties[] = {
|
|||
DEFINE_PROP_UINT8("max-cpu-throttle", MigrationState,
|
||||
parameters.max_cpu_throttle,
|
||||
DEFAULT_MIGRATE_MAX_CPU_THROTTLE),
|
||||
DEFINE_PROP_SIZE("announce-initial", MigrationState,
|
||||
parameters.announce_initial,
|
||||
DEFAULT_MIGRATE_ANNOUNCE_INITIAL),
|
||||
DEFINE_PROP_SIZE("announce-max", MigrationState,
|
||||
parameters.announce_max,
|
||||
DEFAULT_MIGRATE_ANNOUNCE_MAX),
|
||||
DEFINE_PROP_SIZE("announce-rounds", MigrationState,
|
||||
parameters.announce_rounds,
|
||||
DEFAULT_MIGRATE_ANNOUNCE_ROUNDS),
|
||||
DEFINE_PROP_SIZE("announce-step", MigrationState,
|
||||
parameters.announce_step,
|
||||
DEFAULT_MIGRATE_ANNOUNCE_STEP),
|
||||
|
||||
/* Migration capabilities */
|
||||
DEFINE_PROP_MIG_CAP("x-xbzrle", MIGRATION_CAPABILITY_XBZRLE),
|
||||
|
@ -3346,6 +3443,10 @@ static void migration_instance_init(Object *obj)
|
|||
params->has_xbzrle_cache_size = true;
|
||||
params->has_max_postcopy_bandwidth = true;
|
||||
params->has_max_cpu_throttle = true;
|
||||
params->has_announce_initial = true;
|
||||
params->has_announce_max = true;
|
||||
params->has_announce_rounds = true;
|
||||
params->has_announce_step = true;
|
||||
|
||||
qemu_sem_init(&ms->postcopy_pause_sem, 0);
|
||||
qemu_sem_init(&ms->postcopy_pause_rp_sem, 0);
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "qemu/coroutine_int.h"
|
||||
#include "hw/qdev.h"
|
||||
#include "io/channel.h"
|
||||
#include "net/announce.h"
|
||||
|
||||
struct PostcopyBlocktimeContext;
|
||||
|
||||
|
@ -36,6 +37,9 @@ struct MigrationIncomingState {
|
|||
*/
|
||||
QemuEvent main_thread_load_event;
|
||||
|
||||
/* For network announces */
|
||||
AnnounceTimer announce_timer;
|
||||
|
||||
size_t largest_page_size;
|
||||
bool have_fault_thread;
|
||||
QemuThread fault_thread;
|
||||
|
|
|
@ -57,13 +57,7 @@
|
|||
#include "sysemu/replay.h"
|
||||
#include "qjson.h"
|
||||
#include "migration/colo.h"
|
||||
|
||||
#ifndef ETH_P_RARP
|
||||
#define ETH_P_RARP 0x8035
|
||||
#endif
|
||||
#define ARP_HTYPE_ETH 0x0001
|
||||
#define ARP_PTYPE_IP 0x0800
|
||||
#define ARP_OP_REQUEST_REV 0x3
|
||||
#include "net/announce.h"
|
||||
|
||||
const unsigned int postcopy_ram_discard_version = 0;
|
||||
|
||||
|
@ -125,67 +119,6 @@ static struct mig_cmd_args {
|
|||
* generic extendable format with an exception for two old entities.
|
||||
*/
|
||||
|
||||
static int announce_self_create(uint8_t *buf,
|
||||
uint8_t *mac_addr)
|
||||
{
|
||||
/* Ethernet header. */
|
||||
memset(buf, 0xff, 6); /* destination MAC addr */
|
||||
memcpy(buf + 6, mac_addr, 6); /* source MAC addr */
|
||||
*(uint16_t *)(buf + 12) = htons(ETH_P_RARP); /* ethertype */
|
||||
|
||||
/* RARP header. */
|
||||
*(uint16_t *)(buf + 14) = htons(ARP_HTYPE_ETH); /* hardware addr space */
|
||||
*(uint16_t *)(buf + 16) = htons(ARP_PTYPE_IP); /* protocol addr space */
|
||||
*(buf + 18) = 6; /* hardware addr length (ethernet) */
|
||||
*(buf + 19) = 4; /* protocol addr length (IPv4) */
|
||||
*(uint16_t *)(buf + 20) = htons(ARP_OP_REQUEST_REV); /* opcode */
|
||||
memcpy(buf + 22, mac_addr, 6); /* source hw addr */
|
||||
memset(buf + 28, 0x00, 4); /* source protocol addr */
|
||||
memcpy(buf + 32, mac_addr, 6); /* target hw addr */
|
||||
memset(buf + 38, 0x00, 4); /* target protocol addr */
|
||||
|
||||
/* Padding to get up to 60 bytes (ethernet min packet size, minus FCS). */
|
||||
memset(buf + 42, 0x00, 18);
|
||||
|
||||
return 60; /* len (FCS will be added by hardware) */
|
||||
}
|
||||
|
||||
static void qemu_announce_self_iter(NICState *nic, void *opaque)
|
||||
{
|
||||
uint8_t buf[60];
|
||||
int len;
|
||||
|
||||
trace_qemu_announce_self_iter(qemu_ether_ntoa(&nic->conf->macaddr));
|
||||
len = announce_self_create(buf, nic->conf->macaddr.a);
|
||||
|
||||
qemu_send_packet_raw(qemu_get_queue(nic), buf, len);
|
||||
}
|
||||
|
||||
|
||||
static void qemu_announce_self_once(void *opaque)
|
||||
{
|
||||
static int count = SELF_ANNOUNCE_ROUNDS;
|
||||
QEMUTimer *timer = *(QEMUTimer **)opaque;
|
||||
|
||||
qemu_foreach_nic(qemu_announce_self_iter, NULL);
|
||||
|
||||
if (--count) {
|
||||
/* delay 50ms, 150ms, 250ms, ... */
|
||||
timer_mod(timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) +
|
||||
self_announce_delay(count));
|
||||
} else {
|
||||
timer_del(timer);
|
||||
timer_free(timer);
|
||||
}
|
||||
}
|
||||
|
||||
void qemu_announce_self(void)
|
||||
{
|
||||
static QEMUTimer *timer;
|
||||
timer = timer_new_ms(QEMU_CLOCK_REALTIME, qemu_announce_self_once, &timer);
|
||||
qemu_announce_self_once(&timer);
|
||||
}
|
||||
|
||||
/***********************************************************/
|
||||
/* savevm/loadvm support */
|
||||
|
||||
|
@ -1765,13 +1698,14 @@ static void loadvm_postcopy_handle_run_bh(void *opaque)
|
|||
{
|
||||
Error *local_err = NULL;
|
||||
HandleRunBhData *data = opaque;
|
||||
MigrationIncomingState *mis = migration_incoming_get_current();
|
||||
|
||||
/* TODO we should move all of this lot into postcopy_ram.c or a shared code
|
||||
* in migration.c
|
||||
*/
|
||||
cpu_synchronize_all_post_init();
|
||||
|
||||
qemu_announce_self();
|
||||
qemu_announce_self(&mis->announce_timer, migrate_announce_params());
|
||||
|
||||
/* Make sure all file formats flush their mutable metadata.
|
||||
* If we get an error here, just don't restart the VM yet. */
|
||||
|
|
|
@ -52,7 +52,6 @@ vmstate_save_state_top(const char *idstr) "%s"
|
|||
vmstate_subsection_save_loop(const char *name, const char *sub) "%s/%s"
|
||||
vmstate_subsection_save_top(const char *idstr) "%s"
|
||||
vmstate_load(const char *idstr, const char *vmsd_name) "%s, %s"
|
||||
qemu_announce_self_iter(const char *mac) "%s"
|
||||
|
||||
# migration/vmstate.c
|
||||
vmstate_load_field_error(const char *field, int ret) "field \"%s\" load failed, ret = %d"
|
||||
|
|
|
@ -2,6 +2,7 @@ common-obj-y = net.o queue.o checksum.o util.o hub.o
|
|||
common-obj-y += socket.o
|
||||
common-obj-y += dump.o
|
||||
common-obj-y += eth.o
|
||||
common-obj-y += announce.o
|
||||
common-obj-$(CONFIG_L2TPV3) += l2tpv3.o
|
||||
common-obj-$(call land,$(CONFIG_VIRTIO_NET),$(CONFIG_VHOST_NET_USER)) += vhost-user.o
|
||||
common-obj-$(call land,$(call lnot,$(CONFIG_VIRTIO_NET)),$(CONFIG_VHOST_NET_USER)) += vhost-user-stub.o
|
||||
|
|
|
@ -0,0 +1,140 @@
|
|||
/*
|
||||
* Self-announce
|
||||
* (c) 2017-2019 Red Hat, Inc.
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "net/announce.h"
|
||||
#include "net/net.h"
|
||||
#include "qapi/clone-visitor.h"
|
||||
#include "qapi/qapi-visit-net.h"
|
||||
#include "qapi/qapi-commands-net.h"
|
||||
#include "trace.h"
|
||||
|
||||
int64_t qemu_announce_timer_step(AnnounceTimer *timer)
|
||||
{
|
||||
int64_t step;
|
||||
|
||||
step = timer->params.initial +
|
||||
(timer->params.rounds - timer->round - 1) *
|
||||
timer->params.step;
|
||||
|
||||
if (step < 0 || step > timer->params.max) {
|
||||
step = timer->params.max;
|
||||
}
|
||||
timer_mod(timer->tm, qemu_clock_get_ms(timer->type) + step);
|
||||
|
||||
return step;
|
||||
}
|
||||
|
||||
void qemu_announce_timer_del(AnnounceTimer *timer)
|
||||
{
|
||||
if (timer->tm) {
|
||||
timer_del(timer->tm);
|
||||
timer_free(timer->tm);
|
||||
timer->tm = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Under BQL/main thread
|
||||
* Reset the timer to the given parameters/type/notifier.
|
||||
*/
|
||||
void qemu_announce_timer_reset(AnnounceTimer *timer,
|
||||
AnnounceParameters *params,
|
||||
QEMUClockType type,
|
||||
QEMUTimerCB *cb,
|
||||
void *opaque)
|
||||
{
|
||||
/*
|
||||
* We're under the BQL, so the current timer can't
|
||||
* be firing, so we should be able to delete it.
|
||||
*/
|
||||
qemu_announce_timer_del(timer);
|
||||
|
||||
QAPI_CLONE_MEMBERS(AnnounceParameters, &timer->params, params);
|
||||
timer->round = params->rounds;
|
||||
timer->type = type;
|
||||
timer->tm = timer_new_ms(type, cb, opaque);
|
||||
}
|
||||
|
||||
#ifndef ETH_P_RARP
|
||||
#define ETH_P_RARP 0x8035
|
||||
#endif
|
||||
#define ARP_HTYPE_ETH 0x0001
|
||||
#define ARP_PTYPE_IP 0x0800
|
||||
#define ARP_OP_REQUEST_REV 0x3
|
||||
|
||||
static int announce_self_create(uint8_t *buf,
|
||||
uint8_t *mac_addr)
|
||||
{
|
||||
/* Ethernet header. */
|
||||
memset(buf, 0xff, 6); /* destination MAC addr */
|
||||
memcpy(buf + 6, mac_addr, 6); /* source MAC addr */
|
||||
*(uint16_t *)(buf + 12) = htons(ETH_P_RARP); /* ethertype */
|
||||
|
||||
/* RARP header. */
|
||||
*(uint16_t *)(buf + 14) = htons(ARP_HTYPE_ETH); /* hardware addr space */
|
||||
*(uint16_t *)(buf + 16) = htons(ARP_PTYPE_IP); /* protocol addr space */
|
||||
*(buf + 18) = 6; /* hardware addr length (ethernet) */
|
||||
*(buf + 19) = 4; /* protocol addr length (IPv4) */
|
||||
*(uint16_t *)(buf + 20) = htons(ARP_OP_REQUEST_REV); /* opcode */
|
||||
memcpy(buf + 22, mac_addr, 6); /* source hw addr */
|
||||
memset(buf + 28, 0x00, 4); /* source protocol addr */
|
||||
memcpy(buf + 32, mac_addr, 6); /* target hw addr */
|
||||
memset(buf + 38, 0x00, 4); /* target protocol addr */
|
||||
|
||||
/* Padding to get up to 60 bytes (ethernet min packet size, minus FCS). */
|
||||
memset(buf + 42, 0x00, 18);
|
||||
|
||||
return 60; /* len (FCS will be added by hardware) */
|
||||
}
|
||||
|
||||
static void qemu_announce_self_iter(NICState *nic, void *opaque)
|
||||
{
|
||||
uint8_t buf[60];
|
||||
int len;
|
||||
|
||||
trace_qemu_announce_self_iter(qemu_ether_ntoa(&nic->conf->macaddr));
|
||||
len = announce_self_create(buf, nic->conf->macaddr.a);
|
||||
|
||||
qemu_send_packet_raw(qemu_get_queue(nic), buf, len);
|
||||
|
||||
/* if the NIC provides it's own announcement support, use it as well */
|
||||
if (nic->ncs->info->announce) {
|
||||
nic->ncs->info->announce(nic->ncs);
|
||||
}
|
||||
}
|
||||
static void qemu_announce_self_once(void *opaque)
|
||||
{
|
||||
AnnounceTimer *timer = (AnnounceTimer *)opaque;
|
||||
|
||||
qemu_foreach_nic(qemu_announce_self_iter, NULL);
|
||||
|
||||
if (--timer->round) {
|
||||
qemu_announce_timer_step(timer);
|
||||
} else {
|
||||
qemu_announce_timer_del(timer);
|
||||
}
|
||||
}
|
||||
|
||||
void qemu_announce_self(AnnounceTimer *timer, AnnounceParameters *params)
|
||||
{
|
||||
qemu_announce_timer_reset(timer, params, QEMU_CLOCK_REALTIME,
|
||||
qemu_announce_self_once, timer);
|
||||
if (params->rounds) {
|
||||
qemu_announce_self_once(timer);
|
||||
} else {
|
||||
qemu_announce_timer_del(timer);
|
||||
}
|
||||
}
|
||||
|
||||
void qmp_announce_self(AnnounceParameters *params, Error **errp)
|
||||
{
|
||||
static AnnounceTimer announce_timer;
|
||||
qemu_announce_self(&announce_timer, params);
|
||||
}
|
|
@ -286,14 +286,6 @@ static bool colo_mark_tcp_pkt(Packet *ppkt, Packet *spkt,
|
|||
{
|
||||
*mark = 0;
|
||||
|
||||
if (ppkt->tcp_seq == spkt->tcp_seq && ppkt->seq_end == spkt->seq_end) {
|
||||
if (colo_compare_packet_payload(ppkt, spkt,
|
||||
ppkt->header_size, spkt->header_size,
|
||||
ppkt->payload_size)) {
|
||||
*mark = COLO_COMPARE_FREE_SECONDARY | COLO_COMPARE_FREE_PRIMARY;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (ppkt->tcp_seq == spkt->tcp_seq && ppkt->seq_end == spkt->seq_end) {
|
||||
if (colo_compare_packet_payload(ppkt, spkt,
|
||||
ppkt->header_size, spkt->header_size,
|
||||
|
|
110
net/netmap.c
110
net/netmap.c
|
@ -154,65 +154,27 @@ static void netmap_writable(void *opaque)
|
|||
qemu_flush_queued_packets(&s->nc);
|
||||
}
|
||||
|
||||
static ssize_t netmap_receive(NetClientState *nc,
|
||||
const uint8_t *buf, size_t size)
|
||||
{
|
||||
NetmapState *s = DO_UPCAST(NetmapState, nc, nc);
|
||||
struct netmap_ring *ring = s->tx;
|
||||
uint32_t i;
|
||||
uint32_t idx;
|
||||
uint8_t *dst;
|
||||
|
||||
if (unlikely(!ring)) {
|
||||
/* Drop. */
|
||||
return size;
|
||||
}
|
||||
|
||||
if (unlikely(size > ring->nr_buf_size)) {
|
||||
RD(5, "[netmap_receive] drop packet of size %d > %d\n",
|
||||
(int)size, ring->nr_buf_size);
|
||||
return size;
|
||||
}
|
||||
|
||||
if (nm_ring_empty(ring)) {
|
||||
/* No available slots in the netmap TX ring. */
|
||||
netmap_write_poll(s, true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
i = ring->cur;
|
||||
idx = ring->slot[i].buf_idx;
|
||||
dst = (uint8_t *)NETMAP_BUF(ring, idx);
|
||||
|
||||
ring->slot[i].len = size;
|
||||
ring->slot[i].flags = 0;
|
||||
pkt_copy(buf, dst, size);
|
||||
ring->cur = ring->head = nm_ring_next(ring, i);
|
||||
ioctl(s->nmd->fd, NIOCTXSYNC, NULL);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static ssize_t netmap_receive_iov(NetClientState *nc,
|
||||
const struct iovec *iov, int iovcnt)
|
||||
{
|
||||
NetmapState *s = DO_UPCAST(NetmapState, nc, nc);
|
||||
struct netmap_ring *ring = s->tx;
|
||||
unsigned int tail = ring->tail;
|
||||
ssize_t totlen = 0;
|
||||
uint32_t last;
|
||||
uint32_t idx;
|
||||
uint8_t *dst;
|
||||
int j;
|
||||
uint32_t i;
|
||||
|
||||
if (unlikely(!ring)) {
|
||||
/* Drop the packet. */
|
||||
return iov_size(iov, iovcnt);
|
||||
}
|
||||
|
||||
last = i = ring->cur;
|
||||
last = i = ring->head;
|
||||
|
||||
if (nm_ring_space(ring) < iovcnt) {
|
||||
/* Not enough netmap slots. */
|
||||
/* Not enough netmap slots. Tell the kernel that we have seen the new
|
||||
* available slots (so that it notifies us again when it has more
|
||||
* ones), but without publishing any new slots to be processed
|
||||
* (e.g., we don't advance ring->head). */
|
||||
ring->cur = tail;
|
||||
netmap_write_poll(s, true);
|
||||
return 0;
|
||||
}
|
||||
|
@ -222,14 +184,17 @@ static ssize_t netmap_receive_iov(NetClientState *nc,
|
|||
int offset = 0;
|
||||
int nm_frag_size;
|
||||
|
||||
totlen += iov_frag_size;
|
||||
|
||||
/* Split each iovec fragment over more netmap slots, if
|
||||
necessary. */
|
||||
while (iov_frag_size) {
|
||||
nm_frag_size = MIN(iov_frag_size, ring->nr_buf_size);
|
||||
|
||||
if (unlikely(nm_ring_empty(ring))) {
|
||||
/* We run out of netmap slots while splitting the
|
||||
if (unlikely(i == tail)) {
|
||||
/* We ran out of netmap slots while splitting the
|
||||
iovec fragments. */
|
||||
ring->cur = tail;
|
||||
netmap_write_poll(s, true);
|
||||
return 0;
|
||||
}
|
||||
|
@ -251,12 +216,24 @@ static ssize_t netmap_receive_iov(NetClientState *nc,
|
|||
/* The last slot must not have NS_MOREFRAG set. */
|
||||
ring->slot[last].flags &= ~NS_MOREFRAG;
|
||||
|
||||
/* Now update ring->cur and ring->head. */
|
||||
ring->cur = ring->head = i;
|
||||
/* Now update ring->head and ring->cur to publish the new slots and
|
||||
* the new wakeup point. */
|
||||
ring->head = ring->cur = i;
|
||||
|
||||
ioctl(s->nmd->fd, NIOCTXSYNC, NULL);
|
||||
|
||||
return iov_size(iov, iovcnt);
|
||||
return totlen;
|
||||
}
|
||||
|
||||
static ssize_t netmap_receive(NetClientState *nc,
|
||||
const uint8_t *buf, size_t size)
|
||||
{
|
||||
struct iovec iov;
|
||||
|
||||
iov.iov_base = (void *)buf;
|
||||
iov.iov_len = size;
|
||||
|
||||
return netmap_receive_iov(nc, &iov, 1);
|
||||
}
|
||||
|
||||
/* Complete a previous send (backend --> guest) and enable the
|
||||
|
@ -272,39 +249,46 @@ static void netmap_send(void *opaque)
|
|||
{
|
||||
NetmapState *s = opaque;
|
||||
struct netmap_ring *ring = s->rx;
|
||||
unsigned int tail = ring->tail;
|
||||
|
||||
/* Keep sending while there are available packets into the netmap
|
||||
/* Keep sending while there are available slots in the netmap
|
||||
RX ring and the forwarding path towards the peer is open. */
|
||||
while (!nm_ring_empty(ring)) {
|
||||
uint32_t i;
|
||||
while (ring->head != tail) {
|
||||
uint32_t i = ring->head;
|
||||
uint32_t idx;
|
||||
bool morefrag;
|
||||
int iovcnt = 0;
|
||||
int iovsize;
|
||||
|
||||
/* Get a (possibly multi-slot) packet. */
|
||||
do {
|
||||
i = ring->cur;
|
||||
idx = ring->slot[i].buf_idx;
|
||||
morefrag = (ring->slot[i].flags & NS_MOREFRAG);
|
||||
s->iov[iovcnt].iov_base = (u_char *)NETMAP_BUF(ring, idx);
|
||||
s->iov[iovcnt].iov_base = (void *)NETMAP_BUF(ring, idx);
|
||||
s->iov[iovcnt].iov_len = ring->slot[i].len;
|
||||
iovcnt++;
|
||||
i = nm_ring_next(ring, i);
|
||||
} while (i != tail && morefrag);
|
||||
|
||||
ring->cur = ring->head = nm_ring_next(ring, i);
|
||||
} while (!nm_ring_empty(ring) && morefrag);
|
||||
/* Advance ring->cur to tell the kernel that we have seen the slots. */
|
||||
ring->cur = i;
|
||||
|
||||
if (unlikely(nm_ring_empty(ring) && morefrag)) {
|
||||
RD(5, "[netmap_send] ran out of slots, with a pending"
|
||||
"incomplete packet\n");
|
||||
if (unlikely(morefrag)) {
|
||||
/* This is a truncated packet, so we can stop without releasing the
|
||||
* incomplete slots by updating ring->head. We will hopefully
|
||||
* re-read the complete packet the next time we are called. */
|
||||
break;
|
||||
}
|
||||
|
||||
iovsize = qemu_sendv_packet_async(&s->nc, s->iov, iovcnt,
|
||||
netmap_send_completed);
|
||||
|
||||
/* Release the slots to the kernel. */
|
||||
ring->head = i;
|
||||
|
||||
if (iovsize == 0) {
|
||||
/* The peer does not receive anymore. Packet is queued, stop
|
||||
* reading from the backend until netmap_send_completed()
|
||||
*/
|
||||
* reading from the backend until netmap_send_completed(). */
|
||||
netmap_read_poll(s, false);
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
# See docs/devel/tracing.txt for syntax documentation.
|
||||
|
||||
# net/announce.c
|
||||
qemu_announce_self_iter(const char *mac) "%s"
|
||||
|
||||
# net/vhost-user.c
|
||||
vhost_user_event(const char *chr, int event) "chr: %s got event: %d"
|
||||
|
||||
|
|
|
@ -480,6 +480,18 @@
|
|||
#
|
||||
# Migration parameters enumeration
|
||||
#
|
||||
# @announce-initial: Initial delay (in milliseconds) before sending the first
|
||||
# announce (Since 4.0)
|
||||
#
|
||||
# @announce-max: Maximum delay (in milliseconds) between packets in the
|
||||
# announcement (Since 4.0)
|
||||
#
|
||||
# @announce-rounds: Number of self-announce packets sent after migration
|
||||
# (Since 4.0)
|
||||
#
|
||||
# @announce-step: Increase in delay (in milliseconds) between subsequent
|
||||
# packets in the announcement (Since 4.0)
|
||||
#
|
||||
# @compress-level: Set the compression level to be used in live migration,
|
||||
# the compression level is an integer between 0 and 9, where 0 means
|
||||
# no compression, 1 means the best compression speed, and 9 means best
|
||||
|
@ -557,10 +569,13 @@
|
|||
#
|
||||
# @max-cpu-throttle: maximum cpu throttle percentage.
|
||||
# Defaults to 99. (Since 3.1)
|
||||
#
|
||||
# Since: 2.4
|
||||
##
|
||||
{ 'enum': 'MigrationParameter',
|
||||
'data': ['compress-level', 'compress-threads', 'decompress-threads',
|
||||
'data': ['announce-initial', 'announce-max',
|
||||
'announce-rounds', 'announce-step',
|
||||
'compress-level', 'compress-threads', 'decompress-threads',
|
||||
'compress-wait-thread',
|
||||
'cpu-throttle-initial', 'cpu-throttle-increment',
|
||||
'tls-creds', 'tls-hostname', 'max-bandwidth',
|
||||
|
@ -572,6 +587,18 @@
|
|||
##
|
||||
# @MigrateSetParameters:
|
||||
#
|
||||
# @announce-initial: Initial delay (in milliseconds) before sending the first
|
||||
# announce (Since 4.0)
|
||||
#
|
||||
# @announce-max: Maximum delay (in milliseconds) between packets in the
|
||||
# announcement (Since 4.0)
|
||||
#
|
||||
# @announce-rounds: Number of self-announce packets sent after migration
|
||||
# (Since 4.0)
|
||||
#
|
||||
# @announce-step: Increase in delay (in milliseconds) between subsequent
|
||||
# packets in the announcement (Since 4.0)
|
||||
#
|
||||
# @compress-level: compression level
|
||||
#
|
||||
# @compress-threads: compression thread count
|
||||
|
@ -653,7 +680,11 @@
|
|||
# TODO either fuse back into MigrationParameters, or make
|
||||
# MigrationParameters members mandatory
|
||||
{ 'struct': 'MigrateSetParameters',
|
||||
'data': { '*compress-level': 'int',
|
||||
'data': { '*announce-initial': 'size',
|
||||
'*announce-max': 'size',
|
||||
'*announce-rounds': 'size',
|
||||
'*announce-step': 'size',
|
||||
'*compress-level': 'int',
|
||||
'*compress-threads': 'int',
|
||||
'*compress-wait-thread': 'bool',
|
||||
'*decompress-threads': 'int',
|
||||
|
@ -692,6 +723,18 @@
|
|||
#
|
||||
# The optional members aren't actually optional.
|
||||
#
|
||||
# @announce-initial: Initial delay (in milliseconds) before sending the
|
||||
# first announce (Since 4.0)
|
||||
#
|
||||
# @announce-max: Maximum delay (in milliseconds) between packets in the
|
||||
# announcement (Since 4.0)
|
||||
#
|
||||
# @announce-rounds: Number of self-announce packets sent after migration
|
||||
# (Since 4.0)
|
||||
#
|
||||
# @announce-step: Increase in delay (in milliseconds) between subsequent
|
||||
# packets in the announcement (Since 4.0)
|
||||
#
|
||||
# @compress-level: compression level
|
||||
#
|
||||
# @compress-threads: compression thread count
|
||||
|
@ -769,7 +812,11 @@
|
|||
# Since: 2.4
|
||||
##
|
||||
{ 'struct': 'MigrationParameters',
|
||||
'data': { '*compress-level': 'uint8',
|
||||
'data': { '*announce-initial': 'size',
|
||||
'*announce-max': 'size',
|
||||
'*announce-rounds': 'size',
|
||||
'*announce-step': 'size',
|
||||
'*compress-level': 'uint8',
|
||||
'*compress-threads': 'uint8',
|
||||
'*compress-wait-thread': 'bool',
|
||||
'*decompress-threads': 'uint8',
|
||||
|
|
|
@ -684,3 +684,46 @@
|
|||
##
|
||||
{ 'event': 'NIC_RX_FILTER_CHANGED',
|
||||
'data': { '*name': 'str', 'path': 'str' } }
|
||||
|
||||
##
|
||||
# @AnnounceParameters:
|
||||
#
|
||||
# Parameters for self-announce timers
|
||||
#
|
||||
# @initial: Initial delay (in ms) before sending the first GARP/RARP
|
||||
# announcement
|
||||
#
|
||||
# @max: Maximum delay (in ms) between GARP/RARP announcement packets
|
||||
#
|
||||
# @rounds: Number of self-announcement attempts
|
||||
#
|
||||
# @step: Delay increase (in ms) after each self-announcement attempt
|
||||
#
|
||||
# Since: 4.0
|
||||
##
|
||||
|
||||
{ 'struct': 'AnnounceParameters',
|
||||
'data': { 'initial': 'int',
|
||||
'max': 'int',
|
||||
'rounds': 'int',
|
||||
'step': 'int' } }
|
||||
|
||||
##
|
||||
# @announce-self:
|
||||
#
|
||||
# Trigger generation of broadcast RARP frames to update network switches.
|
||||
# This can be useful when network bonds fail-over the active slave.
|
||||
#
|
||||
# @params: AnnounceParameters giving timing and repetition count of announce
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# -> { "execute": "announce-self"
|
||||
# "arguments": {
|
||||
# "initial": 50, "max": 550, "rounds": 10, "step": 50 } }
|
||||
# <- { "return": {} }
|
||||
#
|
||||
# Since: 4.0
|
||||
##
|
||||
{ 'command': 'announce-self', 'boxed': true,
|
||||
'data' : 'AnnounceParameters'}
|
||||
|
|
|
@ -225,6 +225,7 @@ check-qtest-i386-$(CONFIG_SLIRP) += tests/test-netfilter$(EXESUF)
|
|||
check-qtest-i386-$(CONFIG_POSIX) += tests/test-filter-mirror$(EXESUF)
|
||||
check-qtest-i386-$(CONFIG_RTL8139_PCI) += tests/test-filter-redirector$(EXESUF)
|
||||
check-qtest-i386-y += tests/migration-test$(EXESUF)
|
||||
check-qtest-i386-y += tests/test-announce-self$(EXESUF)
|
||||
check-qtest-i386-y += tests/test-x86-cpuid-compat$(EXESUF)
|
||||
check-qtest-i386-y += tests/numa-test$(EXESUF)
|
||||
check-qtest-x86_64-y += $(check-qtest-i386-y)
|
||||
|
@ -263,6 +264,7 @@ check-qtest-ppc64-$(CONFIG_PSERIES) += tests/spapr-phb-test$(EXESUF)
|
|||
check-qtest-ppc64-$(CONFIG_PSERIES) += tests/device-plug-test$(EXESUF)
|
||||
check-qtest-ppc64-$(CONFIG_POWERNV) += tests/pnv-xscom-test$(EXESUF)
|
||||
check-qtest-ppc64-y += tests/migration-test$(EXESUF)
|
||||
check-qtest-ppc64-y += tests/test-announce-self$(EXESUF)
|
||||
check-qtest-ppc64-$(CONFIG_PSERIES) += tests/rtas-test$(EXESUF)
|
||||
check-qtest-ppc64-$(CONFIG_SLIRP) += tests/pxe-test$(EXESUF)
|
||||
check-qtest-ppc64-$(CONFIG_USB_OHCI) += tests/usb-hcd-ohci-test$(EXESUF)
|
||||
|
@ -779,6 +781,7 @@ tests/usb-hcd-ehci-test$(EXESUF): tests/usb-hcd-ehci-test.o $(libqos-usb-obj-y)
|
|||
tests/usb-hcd-xhci-test$(EXESUF): tests/usb-hcd-xhci-test.o $(libqos-usb-obj-y)
|
||||
tests/cpu-plug-test$(EXESUF): tests/cpu-plug-test.o
|
||||
tests/migration-test$(EXESUF): tests/migration-test.o
|
||||
tests/test-announce-self$(EXESUF): tests/test-announce-self.o
|
||||
tests/vhost-user-test$(EXESUF): tests/vhost-user-test.o $(test-util-obj-y) \
|
||||
$(qtest-obj-y) $(test-io-obj-y) $(libqos-virtio-obj-y) $(libqos-pc-obj-y) \
|
||||
$(chardev-obj-y)
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* QTest testcase for qemu_announce_self
|
||||
*
|
||||
* Copyright (c) 2017 Red hat, Inc.
|
||||
* Copyright (c) 2014 SUSE LINUX Products GmbH
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "libqtest.h"
|
||||
#include "qapi/qmp/qdict.h"
|
||||
#include "qemu-common.h"
|
||||
#include "qemu/sockets.h"
|
||||
#include "qemu/iov.h"
|
||||
#include "libqos/libqos-pc.h"
|
||||
#include "libqos/libqos-spapr.h"
|
||||
|
||||
#ifndef ETH_P_RARP
|
||||
#define ETH_P_RARP 0x8035
|
||||
#endif
|
||||
|
||||
static QTestState *test_init(int socket)
|
||||
{
|
||||
char *args;
|
||||
|
||||
args = g_strdup_printf("-netdev socket,fd=%d,id=hs0 -device "
|
||||
"virtio-net-pci,netdev=hs0", socket);
|
||||
|
||||
return qtest_start(args);
|
||||
}
|
||||
|
||||
|
||||
static void test_announce(int socket)
|
||||
{
|
||||
char buffer[60];
|
||||
int len;
|
||||
QDict *rsp;
|
||||
int ret;
|
||||
uint16_t *proto = (uint16_t *)&buffer[12];
|
||||
|
||||
rsp = qmp("{ 'execute' : 'announce-self', "
|
||||
" 'arguments': {"
|
||||
" 'initial': 50, 'max': 550,"
|
||||
" 'rounds': 10, 'step': 50 } }");
|
||||
assert(!qdict_haskey(rsp, "error"));
|
||||
qobject_unref(rsp);
|
||||
|
||||
/* Catch the packet and make sure it's a RARP */
|
||||
ret = qemu_recv(socket, &len, sizeof(len), 0);
|
||||
g_assert_cmpint(ret, ==, sizeof(len));
|
||||
len = ntohl(len);
|
||||
|
||||
ret = qemu_recv(socket, buffer, len, 0);
|
||||
g_assert_cmpint(*proto, ==, htons(ETH_P_RARP));
|
||||
}
|
||||
|
||||
static void setup(gconstpointer data)
|
||||
{
|
||||
QTestState *qs;
|
||||
void (*func) (int socket) = data;
|
||||
int sv[2], ret;
|
||||
|
||||
ret = socketpair(PF_UNIX, SOCK_STREAM, 0, sv);
|
||||
g_assert_cmpint(ret, !=, -1);
|
||||
|
||||
qs = test_init(sv[1]);
|
||||
func(sv[0]);
|
||||
|
||||
/* End test */
|
||||
close(sv[0]);
|
||||
qtest_quit(qs);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
qtest_add_data_func("/virtio/net/test_announce_self", test_announce, setup);
|
||||
|
||||
return g_test_run();
|
||||
}
|
|
@ -20,6 +20,7 @@
|
|||
static int verbose;
|
||||
|
||||
static const char *hmp_cmds[] = {
|
||||
"announce_self",
|
||||
"boot_set ndc",
|
||||
"chardev-add null,id=testchardev1",
|
||||
"chardev-send-break testchardev1",
|
||||
|
|
Loading…
Reference in New Issue