summaryrefslogtreecommitdiff
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.c116
1 files changed, 113 insertions, 3 deletions
diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
index 05bd50d3f6..bd7958b9f0 100644
--- a/hw/net/virtio-net.c
+++ b/hw/net/virtio-net.c
@@ -737,8 +737,9 @@ 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);
+ if (!ebpf_rss_is_loaded(&n->ebpf_rss)) {
+ virtio_clear_feature(&features, VIRTIO_NET_F_RSS);
+ }
features = vhost_net_get_features(get_vhost_net(nc->peer), features);
vdev->backend_features = features;
@@ -1163,12 +1164,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,
@@ -1283,6 +1351,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);
@@ -1668,7 +1755,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);
@@ -2772,6 +2859,19 @@ static int virtio_net_post_load_device(void *opaque, int version_id)
}
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)) {
+ warn_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));
@@ -3352,6 +3452,10 @@ 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)
@@ -3360,6 +3464,10 @@ static void virtio_net_device_unrealize(DeviceState *dev)
VirtIONet *n = VIRTIO_NET(dev);
int i, max_queues;
+ if (virtio_has_feature(n->host_features, VIRTIO_NET_F_RSS)) {
+ virtio_net_unload_ebpf(n);
+ }
+
/* This will stop vhost backend if appropriate. */
virtio_net_set_status(vdev, 0);
@@ -3403,6 +3511,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)