Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions hw/net/vhost_net.c
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ static const int user_feature_bits[] = {
VIRTIO_NET_F_MTU,
VIRTIO_F_IOMMU_PLATFORM,
VIRTIO_F_RING_PACKED,
VIRTIO_NET_F_RSS,
VIRTIO_NET_F_HASH_REPORT,

/* This bit implies RARP isn't sent by QEMU out of band */
VIRTIO_NET_F_GUEST_ANNOUNCE,
Expand Down
156 changes: 145 additions & 11 deletions hw/net/virtio-net.c
Original file line number Diff line number Diff line change
Expand Up @@ -515,6 +515,15 @@ static RxFilterInfo *virtio_net_query_rxfilter(NetClientState *nc)
return info;
}

static void virtio_net_allow_vhost(VirtIONet *n, bool allow)
{
int i;
for (i = 0; i < n->max_queues; i++) {
NetClientState *nc = qemu_get_subqueue(n->nic, i)->peer;
nc->vhost_net_disabled = !allow;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder whether we can simply add a check in virtio_net_vhost_status() like:

if (!get_vhost_net(nc->peer))
return;
if (!n->vhost_disabled)
return;

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Of course we can. But I would prefer to make things more clear, i.e. make get_vhost_net() always return meaningful result, even in set_feature() after the vhost_disabled is set. Otherwise people can rely on get_vhost_net() and call its methods which potentially can do something that we do not expect. For example, in migration flow callbacks etc.

}

static void virtio_net_reset(VirtIODevice *vdev)
{
VirtIONet *n = VIRTIO_NET(vdev);
Expand Down Expand Up @@ -552,6 +561,7 @@ static void virtio_net_reset(VirtIODevice *vdev)
assert(!virtio_net_get_subqueue(nc)->async_tx.elem);
}
}
virtio_net_allow_vhost(n, true);
}

static void peer_test_vnet_hdr(VirtIONet *n)
Expand Down Expand Up @@ -596,6 +606,7 @@ static void virtio_net_set_mrg_rx_bufs(VirtIONet *n, int mergeable_rx_bufs,
n->guest_hdr_len = n->mergeable_rx_bufs ?
sizeof(struct virtio_net_hdr_mrg_rxbuf) :
sizeof(struct virtio_net_hdr);
n->rss_data.populate_hash = false;
}

for (i = 0; i < n->max_queues; i++) {
Expand Down Expand Up @@ -725,9 +736,8 @@ static uint64_t virtio_net_get_features(VirtIODevice *vdev, uint64_t features,
return features;
}

virtio_clear_feature(&features, VIRTIO_NET_F_RSS);
virtio_clear_feature(&features, VIRTIO_NET_F_HASH_REPORT);
features = vhost_net_get_features(get_vhost_net(nc->peer), features);

vdev->backend_features = features;

if (n->mtu_bypass_backend &&
Expand Down Expand Up @@ -877,6 +887,8 @@ static void virtio_net_set_features(VirtIODevice *vdev, uint64_t features)
VirtIONet *n = VIRTIO_NET(vdev);
Error *err = NULL;
int i;
NetClientState *nc = qemu_get_queue(n->nic);
bool disable_vhost = false;

if (n->mtu_bypass_backend &&
!virtio_has_feature(vdev->backend_features, VIRTIO_NET_F_MTU)) {
Expand All @@ -901,14 +913,30 @@ static void virtio_net_set_features(VirtIODevice *vdev, uint64_t features)
virtio_has_feature(features, VIRTIO_NET_F_GUEST_TSO6);
n->rss_data.redirect = virtio_has_feature(features, VIRTIO_NET_F_RSS);

/*
* disable vhost till next reset if there are acked features that
* we can't support with vhost
*/
disable_vhost = n->rss_data.redirect && get_vhost_net(nc->peer) &&
!ebpf_rss_is_loaded(&n->ebpf_rss);

if (n->rss_data.populate_hash && get_vhost_net(nc->peer)) {
disable_vhost = true;
}

if (disable_vhost) {
warn_report("Not using vhost till next reset");
virtio_net_allow_vhost(n, false);
}

if (n->has_vnet_hdr) {
n->curr_guest_offloads =
virtio_net_guest_offloads_by_features(features);
virtio_net_apply_guest_offloads(n);
}

for (i = 0; i < n->max_queues; i++) {
NetClientState *nc = qemu_get_subqueue(n->nic, i);
nc = qemu_get_subqueue(n->nic, i);

if (!get_vhost_net(nc->peer)) {
continue;
Expand Down Expand Up @@ -1151,12 +1179,79 @@ static int virtio_net_handle_announce(VirtIONet *n, uint8_t cmd,
}
}

static void virtio_net_detach_epbf_rss(VirtIONet *n);

static void virtio_net_disable_rss(VirtIONet *n)
{
if (n->rss_data.enabled) {
trace_virtio_net_rss_disable();
}
n->rss_data.enabled = false;

virtio_net_detach_epbf_rss(n);
}

static bool virtio_net_attach_ebpf_to_backend(NICState *nic, int prog_fd)
{
NetClientState *nc = qemu_get_peer(qemu_get_queue(nic), 0);
if (nc == NULL || nc->info->set_steering_ebpf == NULL) {
return false;
}

return nc->info->set_steering_ebpf(nc, prog_fd);
}

static void rss_data_to_rss_config(struct VirtioNetRssData *data,
struct EBPFRSSConfig *config)
{
config->redirect = data->redirect;
config->populate_hash = data->populate_hash;
config->hash_types = data->hash_types;
config->indirections_len = data->indirections_len;
config->default_queue = data->default_queue;
}

static bool virtio_net_attach_epbf_rss(VirtIONet *n)
{
struct EBPFRSSConfig config = {};

if (!ebpf_rss_is_loaded(&n->ebpf_rss)) {
return false;
}

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)) {
return false;
}

if (!virtio_net_attach_ebpf_to_backend(n->nic, n->ebpf_rss.program_fd)) {
return false;
}

return true;
}

static void virtio_net_detach_epbf_rss(VirtIONet *n)
{
virtio_net_attach_ebpf_to_backend(n->nic, -1);
}

static bool virtio_net_load_ebpf(VirtIONet *n)
{
if (!virtio_net_attach_ebpf_to_backend(n->nic, -1)) {
/* backend does't support steering ebpf */
return false;
}

return ebpf_rss_load(&n->ebpf_rss);
}

static void virtio_net_unload_ebpf(VirtIONet *n)
{
virtio_net_attach_ebpf_to_backend(n->nic, -1);
ebpf_rss_unload(&n->ebpf_rss);
}

static uint16_t virtio_net_handle_rss(VirtIONet *n,
Expand Down Expand Up @@ -1271,6 +1366,25 @@ static uint16_t virtio_net_handle_rss(VirtIONet *n,
goto error;
}
n->rss_data.enabled = true;

if (!n->rss_data.populate_hash) {
if (!virtio_net_attach_epbf_rss(n)) {
/* EBPF must be loaded for vhost */
if (get_vhost_net(qemu_get_queue(n->nic)->peer)) {
warn_report("Can't load eBPF RSS for vhost");
goto error;
}
/* fallback to software RSS */
warn_report("Can't load eBPF RSS - fallback to software RSS");
n->rss_data.enabled_software_rss = true;
}
} else {
/* use software RSS for hash populating */
/* and detach eBPF if was loaded before */
virtio_net_detach_epbf_rss(n);
n->rss_data.enabled_software_rss = true;
}

trace_virtio_net_rss_enable(n->rss_data.hash_types,
n->rss_data.indirections_len,
temp.b);
Expand Down Expand Up @@ -1656,7 +1770,7 @@ static ssize_t virtio_net_receive_rcu(NetClientState *nc, const uint8_t *buf,
return -1;
}

if (!no_rss && n->rss_data.enabled) {
if (!no_rss && n->rss_data.enabled && n->rss_data.enabled_software_rss) {
int index = virtio_net_process_rss(nc, buf, size);
if (index >= 0) {
NetClientState *nc2 = qemu_get_subqueue(n->nic, index);
Expand Down Expand Up @@ -2759,13 +2873,6 @@ static int virtio_net_post_load_device(void *opaque, int version_id)
}
}

if (n->rss_data.enabled) {
trace_virtio_net_rss_enable(n->rss_data.hash_types,
n->rss_data.indirections_len,
sizeof(n->rss_data.key));
} else {
trace_virtio_net_rss_disable();
}
return 0;
}

Expand All @@ -2782,6 +2889,26 @@ static int virtio_net_post_load_virtio(VirtIODevice *vdev)
virtio_net_apply_guest_offloads(n);
}

if (n->rss_data.enabled) {
n->rss_data.enabled_software_rss = n->rss_data.populate_hash;
if (!n->rss_data.populate_hash) {
if (!virtio_net_attach_epbf_rss(n)) {
if (get_vhost_net(qemu_get_queue(n->nic)->peer)) {
error_report("Can't post-load eBPF RSS for vhost");
} else {
warn_report("Can't post-load eBPF RSS - fallback to software RSS");
n->rss_data.enabled_software_rss = true;
}
}
}

trace_virtio_net_rss_enable(n->rss_data.hash_types,
n->rss_data.indirections_len,
sizeof(n->rss_data.key));
} else {
trace_virtio_net_rss_disable();
}

return 0;
}

Expand Down Expand Up @@ -3336,6 +3463,9 @@ static void virtio_net_device_realize(DeviceState *dev, Error **errp)
n->qdev = dev;

net_rx_pkt_init(&n->rx_pkt, false);
if (virtio_has_feature(n->host_features, VIRTIO_NET_F_RSS)) {
virtio_net_load_ebpf(n);
}
}

static void virtio_net_device_unrealize(DeviceState *dev)
Expand All @@ -3344,6 +3474,8 @@ static void virtio_net_device_unrealize(DeviceState *dev)
VirtIONet *n = VIRTIO_NET(dev);
int i, max_queues;

virtio_net_unload_ebpf(n);

/* This will stop vhost backend if appropriate. */
virtio_net_set_status(vdev, 0);

Expand Down Expand Up @@ -3386,6 +3518,8 @@ static void virtio_net_instance_init(Object *obj)
device_add_bootindex_property(obj, &n->nic_conf.bootindex,
"bootindex", "/ethernet-phy@0",
DEVICE(n));

ebpf_rss_init(&n->ebpf_rss);
}

static int virtio_net_pre_save(void *opaque)
Expand Down
4 changes: 4 additions & 0 deletions include/hw/virtio/virtio-net.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
#include "qemu/option_int.h"
#include "qom/object.h"

#include "ebpf/ebpf_rss.h"

#define TYPE_VIRTIO_NET "virtio-net-device"
OBJECT_DECLARE_SIMPLE_TYPE(VirtIONet, VIRTIO_NET)

Expand Down Expand Up @@ -130,6 +132,7 @@ typedef struct VirtioNetRscChain {

typedef struct VirtioNetRssData {
bool enabled;
bool enabled_software_rss;
bool redirect;
bool populate_hash;
uint32_t hash_types;
Expand Down Expand Up @@ -209,6 +212,7 @@ struct VirtIONet {
Notifier migration_state;
VirtioNetRssData rss_data;
struct NetRxPkt *rx_pkt;
struct EBPFRSSContext ebpf_rss;
};

void virtio_net_set_netclient_name(VirtIONet *n, const char *name,
Expand Down
2 changes: 2 additions & 0 deletions net/vhost-vdpa.c
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ const int vdpa_feature_bits[] = {
VIRTIO_NET_F_MTU,
VIRTIO_F_IOMMU_PLATFORM,
VIRTIO_F_RING_PACKED,
VIRTIO_NET_F_RSS,
VIRTIO_NET_F_HASH_REPORT,
VIRTIO_NET_F_GUEST_ANNOUNCE,
VIRTIO_NET_F_STATUS,
VHOST_INVALID_FEATURE_BIT
Expand Down