aboutsummaryrefslogtreecommitdiff
path: root/hw/net/virtio-net.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/net/virtio-net.c')
-rw-r--r--hw/net/virtio-net.c324
1 files changed, 241 insertions, 83 deletions
diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
index 9c7e85c..eb93607 100644
--- a/hw/net/virtio-net.c
+++ b/hw/net/virtio-net.c
@@ -26,7 +26,7 @@
#include "qemu/option.h"
#include "qemu/option_int.h"
#include "qemu/config-file.h"
-#include "qapi/qmp/qdict.h"
+#include "qobject/qdict.h"
#include "hw/virtio/virtio-net.h"
#include "net/vhost_net.h"
#include "net/announce.h"
@@ -39,14 +39,15 @@
#include "hw/virtio/virtio-access.h"
#include "migration/misc.h"
#include "standard-headers/linux/ethtool.h"
-#include "sysemu/sysemu.h"
+#include "system/system.h"
+#include "system/replay.h"
#include "trace.h"
#include "monitor/qdev.h"
#include "monitor/monitor.h"
#include "hw/pci/pci_device.h"
#include "net_rx_pkt.h"
#include "hw/virtio/vhost.h"
-#include "sysemu/qtest.h"
+#include "system/qtest.h"
#define VIRTIO_NET_VM_VERSION 11
@@ -381,7 +382,7 @@ static void virtio_net_drop_tx_queue_data(VirtIODevice *vdev, VirtQueue *vq)
}
}
-static void virtio_net_set_status(struct VirtIODevice *vdev, uint8_t status)
+static int virtio_net_set_status(struct VirtIODevice *vdev, uint8_t status)
{
VirtIONet *n = VIRTIO_NET(vdev);
VirtIONetQueue *q;
@@ -417,7 +418,7 @@ static void virtio_net_set_status(struct VirtIODevice *vdev, uint8_t status)
timer_mod(q->tx_timer,
qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + n->tx_timeout);
} else {
- qemu_bh_schedule(q->tx_bh);
+ replay_bh_schedule_event(q->tx_bh);
}
} else {
if (q->tx_timer) {
@@ -436,6 +437,7 @@ static void virtio_net_set_status(struct VirtIODevice *vdev, uint8_t status)
}
}
}
+ return 0;
}
static void virtio_net_set_link_status(NetClientState *nc)
@@ -1240,6 +1242,7 @@ static bool virtio_net_attach_ebpf_to_backend(NICState *nic, int prog_fd)
return false;
}
+ trace_virtio_net_rss_attach_ebpf(nic, prog_fd);
return nc->info->set_steering_ebpf(nc, prog_fd);
}
@@ -1253,7 +1256,7 @@ static void rss_data_to_rss_config(struct VirtioNetRssData *data,
config->default_queue = data->default_queue;
}
-static bool virtio_net_attach_epbf_rss(VirtIONet *n)
+static bool virtio_net_attach_ebpf_rss(VirtIONet *n)
{
struct EBPFRSSConfig config = {};
@@ -1264,7 +1267,8 @@ static bool virtio_net_attach_epbf_rss(VirtIONet *n)
rss_data_to_rss_config(&n->rss_data, &config);
if (!ebpf_rss_set_all(&n->ebpf_rss, &config,
- n->rss_data.indirections_table, n->rss_data.key)) {
+ n->rss_data.indirections_table, n->rss_data.key,
+ NULL)) {
return false;
}
@@ -1275,7 +1279,7 @@ static bool virtio_net_attach_epbf_rss(VirtIONet *n)
return true;
}
-static void virtio_net_detach_epbf_rss(VirtIONet *n)
+static void virtio_net_detach_ebpf_rss(VirtIONet *n)
{
virtio_net_attach_ebpf_to_backend(n->nic, -1);
}
@@ -1285,8 +1289,8 @@ static void virtio_net_commit_rss_config(VirtIONet *n)
if (n->rss_data.enabled) {
n->rss_data.enabled_software_rss = n->rss_data.populate_hash;
if (n->rss_data.populate_hash) {
- virtio_net_detach_epbf_rss(n);
- } else if (!virtio_net_attach_epbf_rss(n)) {
+ virtio_net_detach_ebpf_rss(n);
+ } else if (!virtio_net_attach_ebpf_rss(n)) {
if (get_vhost_net(qemu_get_queue(n->nic)->peer)) {
warn_report("Can't load eBPF RSS for vhost");
} else {
@@ -1295,12 +1299,13 @@ static void virtio_net_commit_rss_config(VirtIONet *n)
}
}
- trace_virtio_net_rss_enable(n->rss_data.hash_types,
+ trace_virtio_net_rss_enable(n,
+ n->rss_data.hash_types,
n->rss_data.indirections_len,
sizeof(n->rss_data.key));
} else {
- virtio_net_detach_epbf_rss(n);
- trace_virtio_net_rss_disable();
+ virtio_net_detach_ebpf_rss(n);
+ trace_virtio_net_rss_disable(n);
}
}
@@ -1314,28 +1319,27 @@ static void virtio_net_disable_rss(VirtIONet *n)
virtio_net_commit_rss_config(n);
}
-static bool virtio_net_load_ebpf_fds(VirtIONet *n)
+static bool virtio_net_load_ebpf_fds(VirtIONet *n, Error **errp)
{
int fds[EBPF_RSS_MAX_FDS] = { [0 ... EBPF_RSS_MAX_FDS - 1] = -1};
int ret = true;
int i = 0;
if (n->nr_ebpf_rss_fds != EBPF_RSS_MAX_FDS) {
- warn_report("Expected %d file descriptors but got %d",
- EBPF_RSS_MAX_FDS, n->nr_ebpf_rss_fds);
- return false;
- }
+ error_setg(errp, "Expected %d file descriptors but got %d",
+ EBPF_RSS_MAX_FDS, n->nr_ebpf_rss_fds);
+ return false;
+ }
for (i = 0; i < n->nr_ebpf_rss_fds; i++) {
- fds[i] = monitor_fd_param(monitor_cur(), n->ebpf_rss_fds[i],
- &error_warn);
+ fds[i] = monitor_fd_param(monitor_cur(), n->ebpf_rss_fds[i], errp);
if (fds[i] < 0) {
ret = false;
goto exit;
}
}
- ret = ebpf_rss_load_fds(&n->ebpf_rss, fds[0], fds[1], fds[2], fds[3]);
+ ret = ebpf_rss_load_fds(&n->ebpf_rss, fds[0], fds[1], fds[2], fds[3], errp);
exit:
if (!ret) {
@@ -1347,17 +1351,27 @@ exit:
return ret;
}
-static bool virtio_net_load_ebpf(VirtIONet *n)
+static bool virtio_net_load_ebpf(VirtIONet *n, Error **errp)
{
- bool ret = false;
+ if (!virtio_net_attach_ebpf_to_backend(n->nic, -1)) {
+ return true;
+ }
- if (virtio_net_attach_ebpf_to_backend(n->nic, -1)) {
- if (!(n->ebpf_rss_fds && virtio_net_load_ebpf_fds(n))) {
- ret = ebpf_rss_load(&n->ebpf_rss);
- }
+ trace_virtio_net_rss_load(n, n->nr_ebpf_rss_fds, n->ebpf_rss_fds);
+
+ /*
+ * If user explicitly gave QEMU RSS FDs to use, then
+ * failing to use them must be considered a fatal
+ * error. If no RSS FDs were provided, QEMU is trying
+ * eBPF on a "best effort" basis only, so report a
+ * warning and allow fallback to software RSS.
+ */
+ if (n->ebpf_rss_fds) {
+ return virtio_net_load_ebpf_fds(n, errp);
}
- return ret;
+ ebpf_rss_load(&n->ebpf_rss, &error_warn);
+ return true;
}
static void virtio_net_unload_ebpf(VirtIONet *n)
@@ -1400,17 +1414,17 @@ static uint16_t virtio_net_handle_rss(VirtIONet *n,
n->rss_data.hash_types = virtio_ldl_p(vdev, &cfg.hash_types);
n->rss_data.indirections_len =
virtio_lduw_p(vdev, &cfg.indirection_table_mask);
- n->rss_data.indirections_len++;
if (!do_rss) {
- n->rss_data.indirections_len = 1;
+ n->rss_data.indirections_len = 0;
}
- if (!is_power_of_2(n->rss_data.indirections_len)) {
- err_msg = "Invalid size of indirection table";
+ if (n->rss_data.indirections_len >= VIRTIO_NET_RSS_MAX_TABLE_LEN) {
+ err_msg = "Too large indirection table";
err_value = n->rss_data.indirections_len;
goto error;
}
- if (n->rss_data.indirections_len > VIRTIO_NET_RSS_MAX_TABLE_LEN) {
- err_msg = "Too large indirection table";
+ n->rss_data.indirections_len++;
+ if (!is_power_of_2(n->rss_data.indirections_len)) {
+ err_msg = "Invalid size of indirection table";
err_value = n->rss_data.indirections_len;
goto error;
}
@@ -1481,7 +1495,7 @@ static uint16_t virtio_net_handle_rss(VirtIONet *n,
virtio_net_commit_rss_config(n);
return queue_pairs;
error:
- trace_virtio_net_rss_error(err_msg, err_value);
+ trace_virtio_net_rss_error(n, err_msg, err_value);
virtio_net_disable_rss(n);
return 0;
}
@@ -1641,24 +1655,28 @@ static bool virtio_net_can_receive(NetClientState *nc)
static int virtio_net_has_buffers(VirtIONetQueue *q, int bufsize)
{
+ int opaque;
+ unsigned int in_bytes;
VirtIONet *n = q->n;
- if (virtio_queue_empty(q->rx_vq) ||
- (n->mergeable_rx_bufs &&
- !virtqueue_avail_bytes(q->rx_vq, bufsize, 0))) {
- virtio_queue_set_notification(q->rx_vq, 1);
-
- /* To avoid a race condition where the guest has made some buffers
- * available after the above check but before notification was
- * enabled, check for available buffers again.
- */
- if (virtio_queue_empty(q->rx_vq) ||
- (n->mergeable_rx_bufs &&
- !virtqueue_avail_bytes(q->rx_vq, bufsize, 0))) {
+
+ while (virtio_queue_empty(q->rx_vq) || n->mergeable_rx_bufs) {
+ opaque = virtqueue_get_avail_bytes(q->rx_vq, &in_bytes, NULL,
+ bufsize, 0);
+ /* Buffer is enough, disable notifiaction */
+ if (bufsize <= in_bytes) {
+ break;
+ }
+
+ if (virtio_queue_enable_notification_and_check(q->rx_vq, opaque)) {
+ /* Guest has added some buffers, try again */
+ continue;
+ } else {
return 0;
}
}
virtio_queue_set_notification(q->rx_vq, 0);
+
return 1;
}
@@ -1687,8 +1705,11 @@ static void virtio_net_hdr_swap(VirtIODevice *vdev, struct virtio_net_hdr *hdr)
static void work_around_broken_dhclient(struct virtio_net_hdr *hdr,
uint8_t *buf, size_t size)
{
+ size_t csum_size = ETH_HLEN + sizeof(struct ip_header) +
+ sizeof(struct udp_header);
+
if ((hdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) && /* missing csum */
- (size > 27 && size < 1500) && /* normal sized MTU */
+ (size >= csum_size && size < 1500) && /* normal sized MTU */
(buf[12] == 0x08 && buf[13] == 0x00) && /* ethertype == IPv4 */
(buf[23] == 17) && /* ip.protocol == UDP */
(buf[34] == 0 && buf[35] == 67)) { /* udp.srcport == bootps */
@@ -1885,31 +1906,34 @@ static int virtio_net_process_rss(NetClientState *nc, const uint8_t *buf,
}
static ssize_t virtio_net_receive_rcu(NetClientState *nc, const uint8_t *buf,
- size_t size, bool no_rss)
+ size_t size)
{
VirtIONet *n = qemu_get_nic_opaque(nc);
- VirtIONetQueue *q = virtio_net_get_subqueue(nc);
+ VirtIONetQueue *q;
VirtIODevice *vdev = VIRTIO_DEVICE(n);
- VirtQueueElement *elems[VIRTQUEUE_MAX_SIZE];
- size_t lens[VIRTQUEUE_MAX_SIZE];
- struct iovec mhdr_sg[VIRTQUEUE_MAX_SIZE];
+ QEMU_UNINITIALIZED VirtQueueElement *elems[VIRTQUEUE_MAX_SIZE];
+ QEMU_UNINITIALIZED size_t lens[VIRTQUEUE_MAX_SIZE];
+ QEMU_UNINITIALIZED struct iovec mhdr_sg[VIRTQUEUE_MAX_SIZE];
struct virtio_net_hdr_v1_hash extra_hdr;
unsigned mhdr_cnt = 0;
size_t offset, i, guest_offset, j;
ssize_t err;
- if (!virtio_net_can_receive(nc)) {
- return -1;
- }
+ memset(&extra_hdr, 0, sizeof(extra_hdr));
- if (!no_rss && n->rss_data.enabled && n->rss_data.enabled_software_rss) {
+ if (n->rss_data.enabled && n->rss_data.enabled_software_rss) {
int index = virtio_net_process_rss(nc, buf, size, &extra_hdr);
if (index >= 0) {
- NetClientState *nc2 = qemu_get_subqueue(n->nic, index);
- return virtio_net_receive_rcu(nc2, buf, size, true);
+ nc = qemu_get_subqueue(n->nic, index % n->curr_queue_pairs);
}
}
+ if (!virtio_net_can_receive(nc)) {
+ return -1;
+ }
+
+ q = virtio_net_get_subqueue(nc);
+
/* hdr_len refers to the header we supply to the guest */
if (!virtio_net_has_buffers(q, size + n->guest_hdr_len - n->host_hdr_len)) {
return 0;
@@ -1965,6 +1989,8 @@ static ssize_t virtio_net_receive_rcu(NetClientState *nc, const uint8_t *buf,
sg, elem->in_num,
offsetof(typeof(extra_hdr), hdr.num_buffers),
sizeof(extra_hdr.hdr.num_buffers));
+ } else {
+ extra_hdr.hdr.num_buffers = cpu_to_le16(1);
}
receive_header(n, sg, elem->in_num, buf, size);
@@ -2035,7 +2061,22 @@ static ssize_t virtio_net_do_receive(NetClientState *nc, const uint8_t *buf,
{
RCU_READ_LOCK_GUARD();
- return virtio_net_receive_rcu(nc, buf, size, false);
+ return virtio_net_receive_rcu(nc, buf, size);
+}
+
+/*
+ * Accessors to read and write the IP packet data length field. This
+ * is a potentially unaligned network-byte-order 16 bit unsigned integer
+ * pointed to by unit->ip_len.
+ */
+static uint16_t read_unit_ip_len(VirtioNetRscUnit *unit)
+{
+ return lduw_be_p(unit->ip_plen);
+}
+
+static void write_unit_ip_len(VirtioNetRscUnit *unit, uint16_t l)
+{
+ stw_be_p(unit->ip_plen, l);
}
static void virtio_net_rsc_extract_unit4(VirtioNetRscChain *chain,
@@ -2052,7 +2093,7 @@ static void virtio_net_rsc_extract_unit4(VirtioNetRscChain *chain,
unit->ip_plen = &ip->ip_len;
unit->tcp = (struct tcp_header *)(((uint8_t *)unit->ip) + ip_hdrlen);
unit->tcp_hdrlen = (htons(unit->tcp->th_offset_flags) & 0xF000) >> 10;
- unit->payload = htons(*unit->ip_plen) - ip_hdrlen - unit->tcp_hdrlen;
+ unit->payload = read_unit_ip_len(unit) - ip_hdrlen - unit->tcp_hdrlen;
}
static void virtio_net_rsc_extract_unit6(VirtioNetRscChain *chain,
@@ -2071,7 +2112,7 @@ static void virtio_net_rsc_extract_unit6(VirtioNetRscChain *chain,
/* There is a difference between payload length in ipv4 and v6,
ip header is excluded in ipv6 */
- unit->payload = htons(*unit->ip_plen) - unit->tcp_hdrlen;
+ unit->payload = read_unit_ip_len(unit) - unit->tcp_hdrlen;
}
static size_t virtio_net_rsc_drain_seg(VirtioNetRscChain *chain,
@@ -2118,7 +2159,7 @@ static void virtio_net_rsc_purge(void *opq)
chain->stat.timer++;
if (!QTAILQ_EMPTY(&chain->buffers)) {
timer_mod(chain->drain_timer,
- qemu_clock_get_ns(QEMU_CLOCK_HOST) + chain->n->rsc_timeout);
+ qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + chain->n->rsc_timeout);
}
}
@@ -2220,7 +2261,7 @@ static int32_t virtio_net_rsc_coalesce_data(VirtioNetRscChain *chain,
VirtioNetRscUnit *o_unit;
o_unit = &seg->unit;
- o_ip_len = htons(*o_unit->ip_plen);
+ o_ip_len = read_unit_ip_len(o_unit);
nseq = htonl(n_unit->tcp->th_seq);
oseq = htonl(o_unit->tcp->th_seq);
@@ -2256,7 +2297,7 @@ coalesce:
o_unit->payload += n_unit->payload; /* update new data len */
/* update field in ip header */
- *o_unit->ip_plen = htons(o_ip_len + n_unit->payload);
+ write_unit_ip_len(o_unit, o_ip_len + n_unit->payload);
/* Bring 'PUSH' big, the whql test guide says 'PUSH' can be coalesced
for windows guest, while this may change the behavior for linux
@@ -2354,7 +2395,7 @@ static size_t virtio_net_rsc_do_coalesce(VirtioNetRscChain *chain,
chain->stat.empty_cache++;
virtio_net_rsc_cache_buf(chain, nc, buf, size);
timer_mod(chain->drain_timer,
- qemu_clock_get_ns(QEMU_CLOCK_HOST) + chain->n->rsc_timeout);
+ qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + chain->n->rsc_timeout);
return size;
}
@@ -2592,7 +2633,7 @@ static VirtioNetRscChain *virtio_net_rsc_lookup_chain(VirtIONet *n,
chain->max_payload = VIRTIO_NET_MAX_IP6_PAYLOAD;
chain->gso_type = VIRTIO_NET_HDR_GSO_TCPV6;
}
- chain->drain_timer = timer_new_ns(QEMU_CLOCK_HOST,
+ chain->drain_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
virtio_net_rsc_purge, chain);
memset(&chain->stat, 0, sizeof(chain->stat));
@@ -2667,7 +2708,7 @@ static void virtio_net_tx_complete(NetClientState *nc, ssize_t len)
*/
virtio_queue_set_notification(q->tx_vq, 0);
if (q->tx_bh) {
- qemu_bh_schedule(q->tx_bh);
+ replay_bh_schedule_event(q->tx_bh);
} else {
timer_mod(q->tx_timer,
qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + n->tx_timeout);
@@ -2735,6 +2776,10 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q)
*/
assert(n->host_hdr_len <= n->guest_hdr_len);
if (n->host_hdr_len != n->guest_hdr_len) {
+ if (iov_size(out_sg, out_num) < n->guest_hdr_len) {
+ virtio_error(vdev, "virtio-net header is invalid");
+ goto detach;
+ }
unsigned sg_num = iov_copy(sg, ARRAY_SIZE(sg),
out_sg, out_num,
0, n->host_hdr_len);
@@ -2829,7 +2874,7 @@ static void virtio_net_handle_tx_bh(VirtIODevice *vdev, VirtQueue *vq)
return;
}
virtio_queue_set_notification(vq, 0);
- qemu_bh_schedule(q->tx_bh);
+ replay_bh_schedule_event(q->tx_bh);
}
static void virtio_net_tx_timer(void *opaque)
@@ -2912,7 +2957,7 @@ static void virtio_net_tx_bh(void *opaque)
/* If we flush a full burst of packets, assume there are
* more coming and immediately reschedule */
if (ret >= n->tx_burst) {
- qemu_bh_schedule(q->tx_bh);
+ replay_bh_schedule_event(q->tx_bh);
q->tx_waiting = 1;
return;
}
@@ -2926,7 +2971,7 @@ static void virtio_net_tx_bh(void *opaque)
return;
} else if (ret > 0) {
virtio_queue_set_notification(q->tx_vq, 0);
- qemu_bh_schedule(q->tx_bh);
+ replay_bh_schedule_event(q->tx_bh);
q->tx_waiting = 1;
}
}
@@ -3022,6 +3067,15 @@ static void virtio_net_set_multiqueue(VirtIONet *n, int multiqueue)
virtio_net_set_queue_pairs(n);
}
+static int virtio_net_pre_load_queues(VirtIODevice *vdev)
+{
+ virtio_net_set_multiqueue(VIRTIO_NET(vdev),
+ virtio_has_feature(vdev->guest_features, VIRTIO_NET_F_RSS) ||
+ virtio_has_feature(vdev->guest_features, VIRTIO_NET_F_MQ));
+
+ return 0;
+}
+
static int virtio_net_post_load_device(void *opaque, int version_id)
{
VirtIONet *n = opaque;
@@ -3286,6 +3340,117 @@ static const VMStateDescription vmstate_virtio_net_rss = {
},
};
+static struct vhost_dev *virtio_net_get_vhost(VirtIODevice *vdev)
+{
+ VirtIONet *n = VIRTIO_NET(vdev);
+ NetClientState *nc;
+ struct vhost_net *net;
+
+ if (!n->nic) {
+ return NULL;
+ }
+
+ nc = qemu_get_queue(n->nic);
+ if (!nc) {
+ return NULL;
+ }
+
+ net = get_vhost_net(nc->peer);
+ if (!net) {
+ return NULL;
+ }
+
+ return &net->dev;
+}
+
+static int vhost_user_net_save_state(QEMUFile *f, void *pv, size_t size,
+ const VMStateField *field,
+ JSONWriter *vmdesc)
+{
+ VirtIONet *n = pv;
+ VirtIODevice *vdev = VIRTIO_DEVICE(n);
+ struct vhost_dev *vhdev;
+ Error *local_error = NULL;
+ int ret;
+
+ vhdev = virtio_net_get_vhost(vdev);
+ if (vhdev == NULL) {
+ error_reportf_err(local_error,
+ "Error getting vhost back-end of %s device %s: ",
+ vdev->name, vdev->parent_obj.canonical_path);
+ return -1;
+ }
+
+ ret = vhost_save_backend_state(vhdev, f, &local_error);
+ if (ret < 0) {
+ error_reportf_err(local_error,
+ "Error saving back-end state of %s device %s: ",
+ vdev->name, vdev->parent_obj.canonical_path);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int vhost_user_net_load_state(QEMUFile *f, void *pv, size_t size,
+ const VMStateField *field)
+{
+ VirtIONet *n = pv;
+ VirtIODevice *vdev = VIRTIO_DEVICE(n);
+ struct vhost_dev *vhdev;
+ Error *local_error = NULL;
+ int ret;
+
+ vhdev = virtio_net_get_vhost(vdev);
+ if (vhdev == NULL) {
+ error_reportf_err(local_error,
+ "Error getting vhost back-end of %s device %s: ",
+ vdev->name, vdev->parent_obj.canonical_path);
+ return -1;
+ }
+
+ ret = vhost_load_backend_state(vhdev, f, &local_error);
+ if (ret < 0) {
+ error_reportf_err(local_error,
+ "Error loading back-end state of %s device %s: ",
+ vdev->name, vdev->parent_obj.canonical_path);
+ return ret;
+ }
+
+ return 0;
+}
+
+static bool vhost_user_net_is_internal_migration(void *opaque)
+{
+ VirtIONet *n = opaque;
+ VirtIODevice *vdev = VIRTIO_DEVICE(n);
+ struct vhost_dev *vhdev;
+
+ vhdev = virtio_net_get_vhost(vdev);
+ if (vhdev == NULL) {
+ return false;
+ }
+
+ return vhost_supports_device_state(vhdev);
+}
+
+static const VMStateDescription vhost_user_net_backend_state = {
+ .name = "virtio-net-device/backend",
+ .version_id = 0,
+ .needed = vhost_user_net_is_internal_migration,
+ .fields = (const VMStateField[]) {
+ {
+ .name = "backend",
+ .info = &(const VMStateInfo) {
+ .name = "virtio-net vhost-user backend state",
+ .get = vhost_user_net_load_state,
+ .put = vhost_user_net_save_state,
+ },
+ },
+ VMSTATE_END_OF_LIST()
+ }
+};
+
static const VMStateDescription vmstate_virtio_net_device = {
.name = "virtio-net-device",
.version_id = VIRTIO_NET_VM_VERSION,
@@ -3338,6 +3503,7 @@ static const VMStateDescription vmstate_virtio_net_device = {
},
.subsections = (const VMStateDescription * const []) {
&vmstate_virtio_net_rss,
+ &vhost_user_net_backend_state,
NULL
}
};
@@ -3750,7 +3916,7 @@ static void virtio_net_device_realize(DeviceState *dev, Error **errp)
net_rx_pkt_init(&n->rx_pkt);
if (virtio_has_feature(n->host_features, VIRTIO_NET_F_RSS)) {
- virtio_net_load_ebpf(n);
+ virtio_net_load_ebpf(n, errp);
}
}
@@ -3883,14 +4049,6 @@ static bool dev_unplug_pending(void *opaque)
return vdc->primary_unplug_pending(dev);
}
-static struct vhost_dev *virtio_net_get_vhost(VirtIODevice *vdev)
-{
- VirtIONet *n = VIRTIO_NET(vdev);
- NetClientState *nc = qemu_get_queue(n->nic);
- struct vhost_net *net = get_vhost_net(nc->peer);
- return &net->dev;
-}
-
static const VMStateDescription vmstate_virtio_net = {
.name = "virtio-net",
.minimum_version_id = VIRTIO_NET_VM_VERSION,
@@ -3903,7 +4061,7 @@ static const VMStateDescription vmstate_virtio_net = {
.dev_unplug_pending = dev_unplug_pending,
};
-static Property virtio_net_properties[] = {
+static const Property virtio_net_properties[] = {
DEFINE_PROP_BIT64("csum", VirtIONet, host_features,
VIRTIO_NET_F_CSUM, true),
DEFINE_PROP_BIT64("guest_csum", VirtIONet, host_features,
@@ -3975,10 +4133,9 @@ static Property virtio_net_properties[] = {
VIRTIO_NET_F_GUEST_USO6, true),
DEFINE_PROP_BIT64("host_uso", VirtIONet, host_features,
VIRTIO_NET_F_HOST_USO, true),
- DEFINE_PROP_END_OF_LIST(),
};
-static void virtio_net_class_init(ObjectClass *klass, void *data)
+static void virtio_net_class_init(ObjectClass *klass, const void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
@@ -4000,6 +4157,7 @@ static void virtio_net_class_init(ObjectClass *klass, void *data)
vdc->guest_notifier_mask = virtio_net_guest_notifier_mask;
vdc->guest_notifier_pending = virtio_net_guest_notifier_pending;
vdc->legacy_features |= (0x1 << VIRTIO_NET_F_GSO);
+ vdc->pre_load_queues = virtio_net_pre_load_queues;
vdc->post_load = virtio_net_post_load_virtio;
vdc->vmsd = &vmstate_virtio_net_device;
vdc->primary_unplug_pending = primary_unplug_pending;