@@ -138,7 +138,10 @@ typedef struct VirtioNetRssData {
bool enabled_software_rss;
bool redirect;
bool populate_hash;
- uint32_t hash_types;
+ bool peer_hash_available;
+ uint32_t runtime_hash_types;
+ uint32_t supported_hash_types;
+ uint32_t peer_hash_types;
uint8_t key[VIRTIO_NET_RSS_MAX_KEY_SIZE];
uint16_t indirections_len;
uint16_t *indirections_table;
@@ -158,7 +158,7 @@ static void virtio_net_get_config(VirtIODevice *vdev, uint8_t *config)
virtio_host_has_feature(vdev, VIRTIO_NET_F_RSS) ?
VIRTIO_NET_RSS_MAX_TABLE_LEN : 1);
virtio_stl_p(vdev, &netcfg.supported_hash_types,
- VIRTIO_NET_RSS_SUPPORTED_HASHES);
+ n->rss_data.supported_hash_types);
memcpy(config, &netcfg, n->config_size);
/*
@@ -931,6 +931,10 @@ static void virtio_net_set_features(VirtIODevice *vdev, uint64_t features)
features &= ~(1ULL << VIRTIO_NET_F_MTU);
}
+ if (!virtio_has_feature(features, VIRTIO_F_VERSION_1)) {
+ features &= ~(1ULL << VIRTIO_NET_F_HASH_REPORT);
+ }
+
virtio_net_set_multiqueue(n,
virtio_has_feature(features, VIRTIO_NET_F_RSS) ||
virtio_has_feature(features, VIRTIO_NET_F_MQ));
@@ -1224,7 +1228,7 @@ static void rss_data_to_rss_config(struct VirtioNetRssData *data,
{
config->redirect = data->redirect;
config->populate_hash = data->populate_hash;
- config->hash_types = data->hash_types;
+ config->hash_types = data->runtime_hash_types;
config->indirections_len = data->indirections_len;
config->default_queue = data->default_queue;
}
@@ -1255,18 +1259,62 @@ static void virtio_net_detach_epbf_rss(VirtIONet *n)
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)) {
- n->rss_data.enabled_software_rss = true;
+ if (n->rss_data.peer_hash_types &&
+ (n->rss_data.peer_hash_types & n->rss_data.runtime_hash_types) ==
+ n->rss_data.runtime_hash_types) {
+ size_t indirection_table_size =
+ n->rss_data.indirections_len *
+ sizeof(*n->rss_data.indirections_table);
+
+ size_t hash_size = sizeof(NetVnetHash) + indirection_table_size +
+ sizeof(n->rss_data.key);
+
+ g_autofree struct {
+ NetVnetHash header;
+ uint8_t footer[];
+ } *hash = g_malloc(hash_size);
+
+ hash->header.flags =
+ (n->rss_data.redirect ? NET_VNET_HASH_RSS : 0) |
+ (n->rss_data.populate_hash ? NET_VNET_HASH_REPORT : 0);
+ hash->header.types = n->rss_data.supported_hash_types;
+ hash->header.indirection_table_mask =
+ n->rss_data.indirections_len - 1;
+ hash->header.unclassified_queue = n->rss_data.default_queue;
+
+ memcpy(hash->footer, n->rss_data.indirections_table,
+ indirection_table_size);
+
+ memcpy(hash->footer + indirection_table_size, n->rss_data.key,
+ sizeof(n->rss_data.key));
+
+ qemu_set_vnet_hash(qemu_get_queue(n->nic)->peer, hash);
+ n->rss_data.enabled_software_rss = false;
+ } else {
+ if (n->rss_data.peer_hash_types) {
+ NetVnetHash hash = { .flags = 0 };
+ qemu_set_vnet_hash(qemu_get_queue(n->nic)->peer, &hash);
+ }
+
+ 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)) {
+ n->rss_data.enabled_software_rss = true;
+ }
}
- trace_virtio_net_rss_enable(n->rss_data.hash_types,
+ trace_virtio_net_rss_enable(n->rss_data.runtime_hash_types,
n->rss_data.indirections_len,
sizeof(n->rss_data.key));
} else {
- virtio_net_detach_epbf_rss(n);
+ if (n->rss_data.peer_hash_types) {
+ NetVnetHash hash = { .flags = 0 };
+ qemu_set_vnet_hash(qemu_get_queue(n->nic)->peer, &hash);
+ } else {
+ virtio_net_detach_epbf_rss(n);
+ }
+
trace_virtio_net_rss_disable();
}
}
@@ -1297,6 +1345,54 @@ static void virtio_net_unload_ebpf(VirtIONet *n)
ebpf_rss_unload(&n->ebpf_rss);
}
+static bool virtio_net_commit_rss_host_config(VirtIONet *n, Error **errp)
+{
+ bool hash_report = virtio_has_feature(n->host_features, VIRTIO_NET_F_HASH_REPORT);
+ bool rss = virtio_has_feature(n->host_features, VIRTIO_NET_F_RSS);
+ bool peer_hash_available = n->rss_data.peer_hash_available;
+ uint32_t supported_hash_types = n->rss_data.supported_hash_types;
+ uint32_t peer_hash_types = n->rss_data.peer_hash_types;
+
+ if (peer_hash_available &&
+ (supported_hash_types & peer_hash_types) == supported_hash_types) {
+ return true;
+ }
+
+ if ((!rss || ebpf_rss_is_loaded(&n->ebpf_rss) || virtio_net_load_ebpf(n)) &&
+ !hash_report) {
+ return true;
+ }
+
+ if (!get_vhost_net(qemu_get_queue(n->nic)->peer)) {
+ if (rss) {
+ warn_report_once("Can't offload RSS - fallback to software RSS");
+ }
+
+ return true;
+ }
+
+ error_setg(errp, "vhost cannot fulfill hash feature requirements");
+ return false;
+}
+
+static bool virtio_net_configure_rss_host(VirtIONet *n, Error **errp)
+{
+ NetVnetHashCap cap;
+
+ if (qemu_get_vnet_hash_cap(qemu_get_queue(n->nic)->peer, &cap) &&
+ cap.max_indirection_table_length >= VIRTIO_NET_RSS_MAX_TABLE_LEN) {
+ n->rss_data.peer_hash_available = true;
+ n->rss_data.supported_hash_types = cap.types;
+ n->rss_data.peer_hash_types = cap.types;
+
+ return true;
+ }
+
+ n->rss_data.supported_hash_types = VIRTIO_NET_RSS_SUPPORTED_HASHES;
+
+ return virtio_net_commit_rss_host_config(n, errp);
+}
+
static uint16_t virtio_net_handle_rss(VirtIONet *n,
struct iovec *iov,
unsigned int iov_cnt,
@@ -1328,7 +1424,7 @@ static uint16_t virtio_net_handle_rss(VirtIONet *n,
err_value = (uint32_t)s;
goto error;
}
- n->rss_data.hash_types = virtio_ldl_p(vdev, &cfg.hash_types);
+ n->rss_data.runtime_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++;
@@ -1391,12 +1487,12 @@ static uint16_t virtio_net_handle_rss(VirtIONet *n,
err_value = temp.b;
goto error;
}
- if (!temp.b && n->rss_data.hash_types) {
+ if (!temp.b && n->rss_data.runtime_hash_types) {
err_msg = "No key provided";
err_value = 0;
goto error;
}
- if (!temp.b && !n->rss_data.hash_types) {
+ if (!temp.b && !n->rss_data.runtime_hash_types) {
virtio_net_disable_rss(n);
return queue_pairs;
}
@@ -1798,7 +1894,7 @@ static int virtio_net_process_rss(NetClientState *nc, const uint8_t *buf,
net_rx_pkt_set_protocols(pkt, &iov, 1, n->host_hdr_len);
net_rx_pkt_get_protocols(pkt, &hasip4, &hasip6, &l4hdr_proto);
net_hash_type = virtio_net_get_hash_type(hasip4, hasip6, l4hdr_proto,
- n->rss_data.hash_types);
+ n->rss_data.runtime_hash_types);
if (net_hash_type > NetPktRssIpV6UdpEx) {
if (n->rss_data.populate_hash) {
virtio_set_packet_hash(buf, VIRTIO_NET_HASH_REPORT_NONE, 0);
@@ -1904,7 +2000,7 @@ static ssize_t virtio_net_receive_rcu(NetClientState *nc, const uint8_t *buf,
}
receive_header(n, sg, elem->in_num, buf, size);
- if (n->rss_data.populate_hash) {
+ if (n->rss_data.populate_hash && n->rss_data.enabled_software_rss) {
offset = sizeof(mhdr);
iov_from_buf(sg, elem->in_num, offset,
buf + offset, n->host_hdr_len - sizeof(mhdr));
@@ -2952,6 +3048,7 @@ static int virtio_net_post_load_device(void *opaque, int version_id)
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),
@@ -3183,21 +3280,43 @@ static const VMStateDescription vmstate_virtio_net_has_vnet = {
},
};
+static int virtio_net_rss_post_load(void *opaque, int version_id)
+{
+ VirtIONet *n = VIRTIO_NET(opaque);
+ Error *err;
+
+ if (version_id == 1) {
+ n->rss_data.supported_hash_types = VIRTIO_NET_RSS_SUPPORTED_HASHES;
+ }
+
+ if (!virtio_net_commit_rss_host_config(n, &err)) {
+ error_report_err(err);
+ return -ENOTSUP;
+ }
+
+ return 0;
+}
+
static bool virtio_net_rss_needed(void *opaque)
{
- return VIRTIO_NET(opaque)->rss_data.enabled;
+ VirtIONet *n = VIRTIO_NET(opaque);
+
+ return virtio_has_feature(n->host_features, VIRTIO_NET_F_RSS) ||
+ virtio_has_feature(n->host_features, VIRTIO_NET_F_HASH_REPORT);
}
static const VMStateDescription vmstate_virtio_net_rss = {
.name = "virtio-net-device/rss",
- .version_id = 1,
+ .version_id = 2,
.minimum_version_id = 1,
+ .post_load = virtio_net_rss_post_load,
.needed = virtio_net_rss_needed,
.fields = (VMStateField[]) {
VMSTATE_BOOL(rss_data.enabled, VirtIONet),
VMSTATE_BOOL(rss_data.redirect, VirtIONet),
VMSTATE_BOOL(rss_data.populate_hash, VirtIONet),
- VMSTATE_UINT32(rss_data.hash_types, VirtIONet),
+ VMSTATE_UINT32(rss_data.runtime_hash_types, VirtIONet),
+ VMSTATE_UINT32_V(rss_data.supported_hash_types, VirtIONet, 2),
VMSTATE_UINT16(rss_data.indirections_len, VirtIONet),
VMSTATE_UINT16(rss_data.default_queue, VirtIONet),
VMSTATE_UINT8_ARRAY(rss_data.key, VirtIONet,
@@ -3720,15 +3839,8 @@ 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)) {
- if (get_vhost_net(nc->peer)) {
- error_setg(errp, "Can't load eBPF RSS for vhost");
- virtio_net_device_unrealize(dev);
- return;
- }
-
- warn_report_once("Can't load eBPF RSS - fallback to software RSS");
+ if (!virtio_net_configure_rss_host(n, errp)) {
+ virtio_net_device_unrealize(dev);
}
}
Offloading virtio-net hashing to the client can improve the performance since the performance can reuse the hash calculated for RSS for hash reporting as well. Signed-off-by: Akihiko Odaki <akihiko.odaki@daynix.com> --- include/hw/virtio/virtio-net.h | 5 +- hw/net/virtio-net.c | 164 +++++++++++++++++++++++++++------ 2 files changed, 142 insertions(+), 27 deletions(-)