mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
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:
@@ -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
|
||||
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
+54
-15
@@ -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 {
|
||||
|
||||
+102
-1
@@ -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;
|
||||
|
||||
+3
-69
@@ -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
|
||||
|
||||
+140
@@ -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,
|
||||
|
||||
+47
-63
@@ -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"
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user